Дрейф таймера Arduino Nano

https://youtu.be/oVZgFsWsKPM][1]

Я новичок в Arduino. Я сделал 12-часовые 7-сегментные часы с общим катодом. Я сделал 7-сегментный индикатор на светодиодах, используя Arduino Nano.

Если я использую длинный период в 1000 миллисекунд, мои часы отстают на 6 минут каждые 24 часа. Но если я использую 994 миллисекунды, они спешат на 30 секунд каждые 24 часа.

Мой код:

#include "SevSeg.h"

SevSeg Display;
const unsigned long period = 1000; //одна секунда
const unsigned long led_period = 500; //миллисекунда мигания светодиода
unsigned long startMillis;
unsigned long led_startMillis;
unsigned long currentMillis;
unsigned long led_currentMillis;
const int hrs_btn = A0;
const int min_btn = A1;
const int ledPin = A2;
int Hrs = 12;
int Min = 0;
int Sec = 0;
int Time;
int ledState = LOW;
void setup()
{
    pinMode(hrs_btn, INPUT_PULLUP);
    pinMode(min_btn, INPUT_PULLUP);
    pinMode(ledPin, OUTPUT);
    byte numDigits = 4;
    byte digitPins[] = {10, 11, 12, 13};
    byte segmentPins[] = {2, 3, 4, 5, 6, 7, 8, 9};
    bool resistorsOnSegments = false;
    bool updateWithDelays = false;
    byte hardwareConfig = COMMON_CATHODE;
    bool leadingZeros = true;
    bool disableDecPoint = true;
    Display.begin(hardwareConfig, numDigits, digitPins, segmentPins, resistorsOnSegments, updateWithDelays, leadingZeros, disableDecPoint);
    Display.setBrightness(100);
}

void loop()
{
    currentMillis = millis();
    if (currentMillis - startMillis >= period)
    {
        Sec = Sec + 1;
        startMillis = currentMillis;
    }
    led_currentMillis = millis();
    if (led_currentMillis - led_startMillis >= led_period)
    {
        led_startMillis = led_currentMillis;
        if (ledState == LOW)
        {
            ledState = HIGH;
            if (digitalRead(hrs_btn) == LOW)
            {
                Hrs = Hrs + 1;
            }
            if (digitalRead(min_btn) == LOW)
            {
                Min = Min + 1;
                Sec = 0;
            }
        }
        else
        {
            ledState = LOW;
        }
        digitalWrite(ledPin, ledState);
    }
    if (Sec == 60)
    {
        Sec = 0;
        Min = Min + 1;
    }
    if (Min == 60)
    {
        Min = 0;
        Hrs = Hrs + 1;
    }
    if (Hrs == 13)
    {
        Hrs = 1;
    }
    Time = Hrs * 100 + Min;
    Display.setNumber(Time);
    Display.refreshDisplay();
}

, 👍-1

Обсуждение

Вы можете использовать micros() вместо millis() для более точной калибровки, но не ждите слишком большой точности от керамического резонатора Nano., @Edgar Bonet

Если вам нужна более высокая точность, вы можете приобрести такой модуль, как [DS3231](https://www.amazon.sg/s?k=ds3231&ref=mr_referred_us_sg_sg), который имеет гораздо более точную тактовую частоту., @hcheung

Я использую готовый код, но не знаю программирования. Подскажите, пожалуйста, как автоматически настроить 30 секунд после 24 часов с помощью редактирования кода., @jagpal singh


3 ответа


Лучший ответ:

0

Все часы (за исключением разве что атомных) имеют дрейф. Если нужна большая точность, нужно ввести поправочный коэффициент.

Раньше я позволял вводить корректирующую величину. Это ошибка за 24 часа. Затем код делит её на 24 часа и корректирует время для каждого цикла.

Это может повысить точность, если вы сохраните в коде переменную миллисекунд для корректировки.

,

В коде уже есть переменная миллисекунд, но я ценю вашу идею более сложного решения. Однако, поскольку автор вопроса не умеет программировать, это сложнее, чем использовать micros(), как предлагается в другом месте, и в данном случае слишком сложно., @the busybee


2

Ваш генератор Arduino слишком медленный из-за:

(24 часа - 6 минут) / 24 часа = (24 * 60 - 6 минут) / (24 * 60 минут) = 0,995833333 (около 0,4%).

После изменения постоянного периода на 994 осциллятор стал слишком быстрым:

(24 часа + 30 секунд) / 24 часа = (24 * 60 * 60 + 30 секунд) / (24 * 60 * 60 секунд) = 1,00034722222 (около 0,03%).

Вы можете настроить константу периода на некоторое следующее целое число, 995 или 996.

Согласно вашему дополнительному вопросу, вы рассматриваете использование микросекунд для повышения точности. Боюсь, это вам не поможет, поскольку неточность связана с аппаратным обеспечением. В зависимости от температуры и напряжения питания частота будет меняться. Для более точной работы вам потребуется другой подход.

В любом случае, вот необходимые изменения для использования микросекунд:

  • Замените вызов millis() на micros() там, где он используется для времени, в первом операторе loop().
  • Умножьте константу периода на 1000, в миллисекунде 1000 микросекунд: const unsigned long period = 1000000;
  • Переименуйте переменные времени startMillis и currentMillis в startMicros и currentMicros. Это не обязательно для работающей программы, но необходимо для чтения кода человеком.

Затем можно начать корректировать постоянный период. Будьте готовы к многократным повторениям корректировки.

И, пожалуйста, не расстраивайтесь, если оборудование не оправдает ваших ожиданий. Недаром цифровые часы используют кварцевый генератор с подстройкой частоты или получают время из внешних, гораздо более точных источников.

,

Бро, можешь добавить строку в мой оригинальный код, чтобы добавить задержку в 15 секунд через каждые 12 часов? Таким образом, часы будут отставать на 30 секунд в течение 24 часов., @jagpal singh

Когда я использую 995, он теряет более 30 секунд, но с 994 он выигрывает 30 секунд. Мне нужно 994,5, это невозможно., @jagpal singh

@jagpalsingh Именно поэтому я добавил способ использования микросекунд. Пожалуйста, примените предложенные изменения., @the busybee

Братан, я сделал, как ты сказал. Изменил константу на 994500, теперь жду 24 часа, чтобы увидеть результат. 1000000×1000÷994,5, @jagpal singh

Спасибо, братан, ты молодец, у меня это работает., @jagpal singh


2

Как отметил hcheung в комментарии, не стоит ожидать, что будет удобно Точность, обеспечиваемая функциями millis() или micros() платы Arduino. Это связано с тем, что Эти функции используют источник тактовой частоты Arduino, который для Nano керамический резонатор. Такие резонаторы, как правило, имеют значительный дрейф, Это не проблема, так как можно откалибровать дрейф. Проблема что скорость дрейфа не постоянна: в зависимости от температуры и случайные изменения внутри резонатора, он ускоряется и замедляется в несколько непредсказуемым образом (частично предсказуемым, если вы следите за (температура). Arduino с кварцевым тактированием дал бы гораздо лучшие результаты. Более подробную информацию по этой теме, включая фактическое измерение точности, можно найти здесь. рекомендую отличный пост в блоге Тактовая частота Arduino точность, Йорис ван Рантвейк.

В качестве решения hcheung рекомендует использовать что-то вроде DS3231 RTC, и я поддерживаю эту рекомендацию.

Если же точность вас не интересует, и вы хотите откалибровать В любом случае, часы Arduino будут смещаться, поэтому я предлагаю вам использовать RTClib от Adafruit библиотека. Помимо поддержки реальных чипов RTC, таких как DS3231, эта Библиотека предоставляет «мягкие» RTC в классе RTC_Micros. Этот класс реализует ту же логику, которую вы используете для продвижения вашего часы, за исключением того, что они делают это правильно (ваша логика немного ошибочна). Кроме того, класс имеет метод adjustDrift(), который делает Именно такой тип корректировки дрейфа вы пытаетесь реализовать. Параметр метода — это поправка на дрейф в ppm (частях на миллион), где положительная коррекция ускоряет ход часов. Шесть минут в день. составляет около 4000 ppm.

Вот упрощённая версия вашего скетча, основанная на RTC_Micros. Для Для простоты я исключил код, управляющий миганием светодиода и временем. корректировка.

#include <RTClib.h>
#include <SevSeg.h>

const int clockDriftCorrection = +4000;  // в ppm

RTC_Micros rtc;  // программные RTC
SevSeg Display;

void setup()
{
    rtc.begin(DateTime());  // начало в 12:00
    rtc.adjustDrift(-clockDriftCorrection);
    Display.begin(/* display settings... */);
    Display.setBrightness(100);
}

void loop()
{
    DateTime now = rtc.now();
    Display.setNumber(now.twelveHour() * 100 + now.minute());
    Display.refreshDisplay();
}
,

Хорошо, но теперь мои часы показывают то же самое точно, что и часы на телефоне, нет ни секунды смещения за последние 18 часов., @jagpal singh