Как разогнать millis()?

VVViktor

✩✩✩✩✩✩✩
31 Дек 2021
39
7
Здравствуйте. В контексте особенно длительных таймеров возник вопрос базовой (тактовой) коррекции таймера millis(). Пока копаю в сторону регистра OSCCAL. Ведь millis() связан c Timer0, который тактируется основным кварцем? Поправьте, если я не прав.
 

rkit

★★★✩✩✩✩
5 Фев 2021
508
127
OSCCAL не имеет отношения к внешнему генератору
хочешь чинить millis - ставь точный кварц, или переписывай реализацию
 

bort707

★★★★★★✩
21 Сен 2020
3,066
914
@VVViktor, поясните, что вы хотите сделать? Что такое " разогнать миллис"? До какой скорости разгонять будем?
 

VVViktor

✩✩✩✩✩✩✩
31 Дек 2021
39
7
@bort707,
Часовой таймер на millis() отстает на 3 секунды в час. Недельный таймер уходит, соответственно более, чем на 15 минут. Хотелось подтолкнуть источник тактирования millis().
 

Геннадий П

★★★★★★✩
14 Апр 2021
1,974
633
45
@VVViktor, Встроенный таймер не предназначен для использования в качестве точного времени. Используй RTC.
 
  • Лойс +1
Реакции: VVViktor

bort707

★★★★★★✩
21 Сен 2020
3,066
914
Если это отставание равномерное ( хотя вообще это не так) - то самое простое добавить программную коррекцию. Грубо - если миллис отстает на 3сек в сутки - значит если раз в 8 часов добавлять 1с - миллис всегда будет врать не более чем на секунду.

Но вообще частота осциллятора зависит от многих факторов, - например от температуры. Поэтому если ваше устройство не будет термостатировано, то простых методов повысить точность миллис нет.
 
Изменено:
  • Лойс +1
Реакции: Un_ka

VVViktor

✩✩✩✩✩✩✩
31 Дек 2021
39
7
@bort707,
Спасибо за ответ. Два алгоритма с коррекцией по RTC через установленные промежутки времени уже написаны и работают. Вариант с осциллятором - третий. С точки зрения кода должен быть самым оптимальным. Программа у меня по сути первая. Непосредственную свою задачу она выполняет. С целью обучения и оптимизации пытаюсь решить задачу разными методами. В случае с коррекцией осциллятора регулярная сверка с RTC не отменяется. Просто хочу понять механизм. В тот ли регистр я лезу?
 

Геннадий П

★★★★★★✩
14 Апр 2021
1,974
633
45
В случае с коррекцией осциллятора регулярная сверка с RTC не отменяется. Просто хочу понять механизм. В тот ли регистр я лезу?
У вас принцип работы программы в корне не верный.
У каждого RTC есть выход, который обычно программируется на 1Гц сигнал (или на бОльшую частоту, если нужно точное время в долях секунды). Как правило этот выход подается на ногу контроллера, по прерыванию с которой идет увеличение счетчика и опционально проверка если нужен отсчет заданного времени. И никаких шаманств с бубном не нужно, часы тикают, контроллер не занят и занимается своими делами.
 
  • Лойс +1
Реакции: VVViktor

VVViktor

✩✩✩✩✩✩✩
31 Дек 2021
39
7
@Геннадий П,
Спасибо. Это я тоже понимаю. Программу с этим подходом, который, конечно же, более оптимальный, я тоже напишу. Нужно будет просто проводок добавить и, возможно, переписать блок для работы с прерываниями. 4 пина уже используются PInChangeInterrupt. Но там прерывания назначаются внутри класса. Не знаю можно ли параллельно пользоваться PinChangeInterrupt из разных классов.

Сейчас просто хочу допилить уже написанное, а заодно понять, как это работает. А именно коррекция осциллятора.Танцы с бубном ради танцев).
 

Геннадий П

★★★★★★✩
14 Апр 2021
1,974
633
45
@Boroda22, Да, например DS1307, есть выход SQW/OUT, делитель на котором можно запрограммировать на определенную частоту. Вешаем его на свободный вход контроллера, который поддерживает прерывания и в прерывании увеличиваем счетчик.

1647872261502.png
1647872286194.png

DS3231 можно запрограммировать либо на выход сигнала 1Гц, либо как будильник на определенное время/дату, в этом случае можно уводить контроллер в полный сон до срабатывания будильника если другие задачи не требуются.

DS1302 не имеет вывода сигнала.
 
  • Лойс +1
Реакции: VVViktor и Boroda22

VVViktor

✩✩✩✩✩✩✩
31 Дек 2021
39
7
@Геннадий П,

В случае с DS3231Mini сигнал брать с 3го пина микросхемы?

Его нужно усиливать? Подключать к базе транзистора?
 

Геннадий П

★★★★★★✩
14 Апр 2021
1,974
633
45
@VVViktor, Да, с 3-й ноги, усиливать не нужно, но подтяжка к плюсу обязательна (внешним резистором или в контроллере)
 
  • Лойс +1
Реакции: VVViktor

VVViktor

✩✩✩✩✩✩✩
31 Дек 2021
39
7
@Геннадий П,
Спасибо. Я так понимаю, для программной реализации нужна полноценная библиотека вроде RTCLib? Упрощенный вариант Micro3231 от Gyver не позволяет программировать регистры DS32331?
 

VVViktor

✩✩✩✩✩✩✩
31 Дек 2021
39
7
@Геннадий П,
Спасибо, Геннадий. То есть, при подтянутом к питанию пине мне нужно будет просто ловить состояние FALLING в прерывании?
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,266
1,303
Москва
Не очень понимаю суть вопроса.. Скажем так. Я имею кривой кварц, и у меня есть код что то типа
if (millis()-time1>1000)
{
}
Т.е. я делаю что то раз в секунду. При этом таймер миллис у меня отстает. Скажем на 0.5% (равномерно!)
Тогда сделав код
if (millis()-time1>995)
{
}
я получу более точное выполнение.

А еще эту самое значение для 1 секунды можно вынести в define и использовать его глобально.
 
  • Лойс +1
Реакции: VVViktor

VVViktor

✩✩✩✩✩✩✩
31 Дек 2021
39
7
@Старик Похабыч,

Да, этот вариант у меня уже проработан. Просто в моем случае отставание значительно меньше. За одну секунду системного таймера проходит примерно 1.000833392 секунды реального времени. Цифра эта получилась при float делении замеренного в секундах отрезка времени прошедшего на RTC за 3600 секунд системного таймера. При применении этого коэффициента к требуемым значениям моих длительных таймеров я получаю вполне корректную работу устройства. Просто это не оптимально. Пусть и редкие, но вычисления float на ардуино занимают память, да и неграмотно это по-моему. Зато запросто позволяет реализовать корректно выглядящий обратный отсчет, например, недельного таймера с отображением на LCD. Вот и стал искать простой способ это обойти. Хотя... можно же просто прибавить к значению обратного отсчета часовую поправку (в моем случае это -3 секунды), помноженную на количество полных часов оставшегося времени.. Подумаю. Вроде, должно получиться.
 

Геннадий П

★★★★★★✩
14 Апр 2021
1,974
633
45
Пусть и редкие, но вычисления float на ардуино занимают память, да и неграмотно это по-моему.
Если нужна работа с float, то почему бы и нет? Просто занимает гораааааздо дольше времени чем работа с целыми числами.

Примерные цифры в циклах при различных операциях:
1647884044224.png
 
  • Лойс +1
Реакции: VVViktor и Boroda22

VVViktor

✩✩✩✩✩✩✩
31 Дек 2021
39
7
@Геннадий П,
Да, в общем, почему бы и да. Уже реализован у меня этот вариант, и цифра та пересчитывается сначала раз в час, а потом - по результатам перепроверки. Увеличиваем поверочный интервал на час при отклонении таймера на 1с. Если нет отклонения - не увеличиваем. И так до 23 часов поверочного интервала, что позволяет добиться удовлетворительной синхронизации с RTC с ошибкой ~1c в сутки. Просто хочу еще красивее сделать)
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,266
1,303
Москва
Если такой вариант. К примеру вычислить за какой период накапливается 1 мс. Или 2. Допустим за 34053 мс
Имеем таймер счета секунд (целых)
if (tmr1-millis()>999)
{
tmr1=millis();
secundy++;
}
if (tmr2-millis()>34053)
{
tmr1=tmr1-2;
tmr2=millis();
}

примерно так, но не факт, что это будет красивее
 
  • Лойс +1
Реакции: VVViktor

VVViktor

✩✩✩✩✩✩✩
31 Дек 2021
39
7
@Старик Похабыч,

Этот вариант, без деления, тоже уже готов. Только там без милисекунд. Для получения милисекундной точности мне нужно придумать способ получать замеры с RTC в милисекундах. Тогда даже код менять не придется. Поправку моя программа уже считает путем вычитания друг из друга последовательно записанных штампов времени в секундах и сравнения полученного значения с периодом поверки (первоначально 3600с). Получаем разницу, накопленную за час. Таким образом, можно обойтись без деления. А можно просто делить реальный час (который либо больше, либо меньше 3600с) на системные 3600с. И применять полученный коэффициент ко всем операциям со значениями таймеров. Вообще вся эта возня уже оформлена в отдельный класс, и выглядит почти "красиво")