Timelib h скачать библиотеку для arduino


Всё, что ты хотел знать о DS1307,
но боялся спросить

В завершении прошлой статьи я приводил ссылку для проверки I 2 C модуля RTC DS3231. Для этого не надо устанавливать никакие библиотеки, достаточно скопировать текст программы в Arduino IDE и кликнуть на загрузку скетча в микроконтроллер. Это одинаково работает как в Arduino IDE, так и в MSP430 Energia и STM32duino.

Однако, больше чем для проверки этот пример не годится, и рано или поздно перед каждым встает вопрос написания своей библиотеки для полноценной работы с RTC. Отчет времени, с календарем или без, довольно распространенная штука, и этот код вы скорее всего будете тащить из проекта в проект. Т.е. это вещь которую проще один раз хорошо сделать, что бы потом к этому не возвращаться.

Сам я уже прошел по этому пути, но т.к. написанный код уже не умещался под спойлерами, поэтому пришлось написать полноценную Arduino — библиотеку. В заключение будет несколько примеров с использованием этой библиотеки, с тем, как на мой взгляд нужно правильно работать с DS1307/DS3231.

Но прежде чем "городить огород", предлагаю взглянуть на готовые решения, одобренные "патриархами" arduino.cc, а именно: библиотеки Time, DS1307RTC, а также DS3232RTC которая работает совместно с библиотекой Time.

    Для начала решим, что нам нужно от RTC типа DS1307/DS3231:

  • Автономный отчет времени, т.е. когда микроконтроллер при старте получает текущее время, а затем он уже считает время самостоятельно и не забивает I 2 C шину трафиком с RTC.
  • Отчет времени по SQW-выводу, когда RTC тактирует счетчик часов микроконтроллера через внешнее прерывание, и микроконтроллер самостоятельно рассчитывает календарные данные и текущее время.
  • Поддержка будильников.
  • Поддержка внесения поправок к ходу часов.
  • Периодическая синхронизация.

Вроде бы немного, и вроде бы несложно.

Весь код я буду тестировать на Arduino Nano, MSP430 Launchpad — Energia и на STM32duino — Blue Pill.

Общая концепция библиотек для работы со временем такая. Имеется базовая библиотека TIME которая ведет через функцию millis() расчет времени при запросе такого через функции библиотеки hour(), minute(), second() и т.д. Библиотека абстрагируется от аппаратной части того или иного хронометра. Она рассчитана на ведение календаря и отчет времени средствами самого микроконтроллера, без подключения RTC. Соответственно библиотеки DS3232RTC и DS1307RTC добавляют функции синхронизации микроконтроллера с RTC.

1) Библиотека Time

Time.h имеет очень лаконичное содержание:

DateString.cpp содержит строковые и числовые константы. Т.о. весь функционал библиотеки заключен в Time.cpp и заголовочном TimeLib.h.

TimeLib.h начинается — воодушевляющими словами: "этот грязный кусок кода. "

Алгоритм работы библиотеки прост. Время хранится в глобальной переменной sysTime в Unix — формате, т.е. в количестве секунд с начала 1970г.

При запросе эта переменная индексируется по смещению выдаваемому функцией millis(), и затем разбивается в структуру календаря:

функции типа hour(), minute() и т.д. возвращают поля этой структуры.

В начале работы с библиотекой следует установить текущее время. Для этого есть два варианта функции setTime(). Первая устанавливает текущее время по POSIX-стандарту времени, т.е. по числу секунд с начала 1970-го года:

Здесь переменной sysTime присваивается входной параметр. Второй вариант функции устанавливает время по набору параметров: дате, минутам, секундам и т.д.

Здесь входные параметры переписываются в структуру календаря, а функция makeTime() переводит эту структуру в POSIX формат.

В библиотеке Time время индексируется следующим образом. При запросе любого параметра календаря, вызываются следующие функции:

Все они вызывают функцию now():

Здесь интересный прием. В начале функции вычисляется не разность в секундах с последней индексации счетчика sysTime, вместо этого, в цикле while, к переменной sysTime прибавляется по единице пока, разница с текущим временем не станет меньше секунды.

Далее вызывается функция refreshCashe()

. которая в свою очередь вызывает breakTime(). Эта функция разбивает POSIX формат даты в общеупотребительный формат.

И вот эта функция вызывается при запросе любого параметра даты. Самый интересный здесь момент — вычисление дня дня недели.

Это самый простой алгоритм вычисления для недели который я встречал.

Ок, теперь установим библиотеку, и из примеров загрузим скетч TimeSerial:

Как видно, после компиляции размер прошивки для MSP430G2553 составил 4496 байт. В этом примере мне понравился способ установки даты, который позволяет это сделать с точностью до секунды. Для этого после загрузки прошивки в микроконтроллер следует открыть окно терминала последовательного порта, и в консоли ввести следующую команду:

Таким способом можно установить дату и в Windows, если воспользоваться для этого CYGWIN. Там имена последовательных портов пишутся как /dev/ttySx, и среди них нужно найти имя своей железки.

Если все получится удачно, то в терминале пойдет такой лог:

Посылать команду с датой, в случае с Energia можно и открытым терминалом последовательного порта. Для Arduino Nano и STM32duino это окно должно быть закрыто, иначе выдаст ошибку: "устройство или ресурс занято".

Т.е. надо сначала послать дату, а потом открыть окно монитора порта. Правда в случае с Arduino Nano имеется проблема. При открытии окна монитора порта, микроконтроллер будет перезагружаться. Что бы это предотвратить, можно поставить электролитический конденсатор на 10мкФ. Плюс ставится на RESET, минус на землю.

Зато в случае с STM32duino таких проблем нет:

При компиляции одного и того же скетча, размер прошивки для MSP430G2553 составил 4496 байт, для ATMega328 — 5254 байт, для stm32f103c8t6 — аж 19308 байт.

2) Библиотека DS1307RTC

Это очень простая библиотека которая реализует программный интерфейс для работы с RTC DS1307. Объявление класса выглядит так:

Метод get() читает RTC и возвращает полученную дату в POSIX формате:

set() — записывает дату в RTC принимая в качестве параметра опять же дату в POSIX формате:

Далее, read() и write() реализуют чтение и запись в RTС. я думаю, что их можно было бы сделать приватными, раз есть get() и set(). Булева переменная exist устанавливается в случае успешного выполнения write() или read(), т.е. это можно воспринимать как флаг наличия чипа на шине. Функции setCalibration() и getCalibration() записывают и читают Control-регистр.

    Немого критики:

  • Нет функции записи в SRAM;
  • Нет записи отдельных битов в control-регистр, т.е. без перезаписи остальных значений.
  • Установку рабочей частоты SQW пина хотелось бы иметь в более удобном виде нежели число типа char.

Ок, теперь практика. Библиотека включает в себя два тестовых скетча: SetTime и ReadTest. Загрузим и скомпилируем первый скетч:

Этот скетч не компилируется в Energia, компилятор выдает что-то вроде: "sscanf() not declared function". STM32duino этот скетч скомпилировался в

50Kb(!) файл прошивки, работоспособность проверять не стал, меня это в любом случае не устраивает. А вот для Arduino Nano, этот пример работает вполне сносно.

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

Можно немного модифицировать пример, что бы получить более осмысленный лог:

Здесь я только добавил команду передачи записываемого в RTC времени в Time библиотеку:

И вместо пустого главного цикла добавил вывод текущего времени из предыдущего примера библиотеки Time:

Второй пример — ReadTest. Здесь производится простое чтение RTC раз в секунду и вывод результатов через UART:

3) Часы реального времени повышенной точности с алгоритмом термокомпенсации DS3231

Эта штука несколько сложнее, но при этом интереснее, чем DS1307. Посмотрим на карту регистров:

Здесь есть два регистра температурного датчика, регистр поправок, управляющий и флаговый регистры. По управлению он не совместим DS1307. Здесь нет CH-бита, вместо OUT/SQW пина имеется INT/SQW и 32kHz пины. С DS1307 совместим только календарь.

На github.com поиском по "ds3231" можно найти библиотеку DS3232RTC которая автором предлагается как замена DS1307RTC. Так же как и DS1307RTC, библиотека работает в паре c библиотекой Time. Несмотря на то, что библиотека называется DS3232RTC, она без проблем работает с чипом DS3231, т.к. от DS3232 он отличается лишь отсутствием SRAM.

Описание класса библиотеки DS3232RTC выглядит так.

Примеры идущие вместе с библиотекой — так себе, а две так еще и требуют установки сторонних библиотек. В качестве отправной точки будем использовать пример TimeRTC. В функции void digitalClockDisplay() Последнюю строку Serial.println() заменим на:

Ок, добились вывода температуры.

Температура в DS3231 хранится в двух регистрах. Целая часть хранится в 0x11, она может иметь знак. Дробная часть хранится в 0x12 в двух старших битах. Т.е. дробная часть может принимать значения: ноль, четверть, половина, три четверти. Младшие биты не "плывут", тенденцию к понижению или повышению показывают стабильно. Замерять по этому термодатчику комнатную температуру невозможно по той причине, что он замеряет температуру чипа а не воздуха. При подключении к питанию, минут за пятнадцать температура чипа поднимается на пару градусов. Т.е. замерять по нему комнатную температуру, это все равно что измерять ее по температуре чипсета материнской платы.

Также в RTC DS3231 имеется два будильника. Первый можно устанавливать с точностью до одной секунды, второй — с точностью до минуты. Бит DY/DT указывает, будут ли регистры 0x0A или 0x0D хранить день недели или число месяца. Биты A1M1,A1M2 и т.д. конфигурируют режим срабатывания: при совпадении минут; при совпадении часа и минут; при совпадении дня, часа, и минут; и т.д. Таблица конфигурации дана в руководстве на чип DS3231:

Состояние битов A1F и A2F флагового регистра 0x0F отображает состояние будильника. В эти биты можно записать только нули, попытка записи единицы ни к чему не приведет. Единица в A1F и A2F записывается только аппаратно.

Попробуем "завести" второй будильник на две минуты. Для этого приведем функцию главного цикла loop() к такому виду:

Компилируем и загружаем в микроконтроллер, после чего ждем срабатывания таймера:

В данном случае, метод alarm() при считывании положительного сигнала срабатывания сразу же сбрасывает флаг обратно в ноль. А так, обычно его надо сбрасывать вручную.

При распечатке флагового регистра, было видно, что были установлены биты 3 и 7. Чтобы понять, за что они отвечают, посмотрим на карту флагового регистра:

Третий бит включает ножку 32kHz, которая генерирует меандр с частотой 32kHz. Седьмой бит — Oscillator Stop Flag (OSF), бит сброса часов, т.е. когда пропадает питание и батарейка и часы сбрасываются. Этот бит устанавливается при включении RTC, может использоваться как флаг для синхронизации/установки времени RTC.

Будильники DS3231 можно использовать для пробуждения из спящего режима микроконтроллера. Приведём скетч к такому виду:

Вывод INT/SQW DS3231 нужно будет соеденить с Digital Pin 2 Arduino. В результате имеем такой лог:

Скетч последовательно устанавливает таймер на две минуты, сначала через второй будильник, а потом через первый. После установки будильника, микроконтроллер отправляется в спящий режим, и пробуждение происходит от внешнего прерывания которое вызывает RTC через INT/SQW пин.

В обычном состоянии вывод INT/SQW подтянут к питанию, точнее говоря он в состоянии open-drain т.е. висит в воздухе, но в готовых модулях он должен быть подтянут к питанию. При срабатывании будильника, INT/SQW подтягивается к земле, вызывая внешнее прерывание, и выводя микроконтроллер из режима "сна".

Как должно быть видно по логу, второй будильник сработал не точно через две минуты, а при 00-секундах. На логе видно, что первые секунды, что показывает Arduino после пробуждения, равняются единице, т.е. величине задержки delay(1000) разделяющих команды sleep_cpu() и digitalClockDisplay(). Затем используется первый будильник который можно "завести" уже с точностью от секунды, и он будит Arduino ровно через две минуты. Опять же две секунды разницы, что видны на логе, равны сумме двух задержек delay(1000);

4) Библиотека DS3231SQW

Пришло время доставать рояль из кустов) В книге Юрий Ревич "Практическое программирование микроконтроллеров Atmel AVR на языке ассемблера", работа с DS1307 описывается так: при включении микроконтроллера, он считывает с RTC текущую дату, и в дальнейшем счет времени ведет самостоятельно через внешнее прерывание срабатывающиее от SQW вывода RTC, с частотой 1Гц, т.е. один раз в секунду.

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

В выше описанных библиотеках, нет режима тактирования от SQW, точнее, сам пин на работу включить можно, в DS3231 он по умолчанию включен, но библиотека Time не умеет работать в таком режиме, она считает время только по собственному таймеру в функции millis(). Поэтому пришлось потратить немного времени и написать свою библиотеку.

Написанная библиотека "сырая" и не оттестированная должным образом. Однако включенные примеры, компилируются и работают по крайней мере а Arduino Nano, и значит режим работы от SQW я смогу показать наглядно на практике.

    режимы работы часов:

  • только через запросы RTC;
  • через синхронизацию по SQW;
  • самостоятельный подсчет времени с периодической синхронизацией от RTC.

Кроме того, что библиотека "сырая", в ней отсутствуют некоторые функции ввиду того, что для меня они бесполезны. Это поддержка "американского" формата времени: am/pm, и запись/чтение SRAM DS1307.

В библиотеке не используется POSIX формат времени, дату (год:месяц:день_недели:число) я храню в самом календаре и в отдельном суточном счетчике я храню количество секунд прошедших с начала суток. Если количество секунд превышает 86400(количество секунд в сутках), то вызывается метод add_day(), который индексирует календарь на один день. Такой подход позволяет избежать расчета <год:месяц:день_недели:число>, каждый раз, когда потребуется узнать час или минуту.

Календарь хранится не в структуре, а в простом массиве из семи байт. Так проще в цикле читать его из RTC. Для доступа к полям календаря служит нумерованный список:

Запрос минут, дня и т.д. выполняется одной функцией:

Будильники хранятся в структуре с битовыми полями:

Чтение в такую структуру производится довольно просто:

Описания классов выглядят так. Базовый класс DS1307:

Для настройки библиотеки на тот или иной режим работы, важно соблюсти правильную последовательность действий. Предлагаю рассмотреть этот процесс на примерах. Скачать библиотеку можно здесь. Для начала, из примеров загрузим скетч "SetSerialTime":

Скетч устанавливает текущее время аналогично тому, как это делал пример TimeSerial из библиотеки Time, только в этом случае, принимаемое время записывается сразу в RTC. Скетч без проблем работает с (MSP430 Launchpad|STM32 BluePill) + DS3231. Для Arduino Nano, повторюсь, во избежании перезагрузки во время открытия UART-сессии, нужно ставить конденсатор на Reset. Это работает, я проверял. Как результат:

Для настройки библиотеки, в setup() используются две команды:

Первая команда — rtc.force_update(); синхронизирует время микроконтроллера с RTC. Вторая команда — rtc.set_self_update(DISABLE), отключает автоматический отчет времени средствами микроконтроллера. Т.е. время каждый раз запрашивается у RTC, конечно при условии, что с момента предыдущего запроса прошло более одной секунды.

Следующий пример может показаться примитивным. В данном случае, в начале работы программы считывается дата с RTC, после чего отчет времени производится уже на стороне микроконтроллера. Настройка библиотеки от предыдущего примера отличается лишь параметром ENABLE в методе set_self_update(). Проверяется работа просто. После запуска отключатся RTC от микроконтроллера, и если в терминале продолжается отчет времени, заначит все в порядке.

Скетч работает нормально как на Arduino, так и на MSP430 Launchpad и STM32duino_BluePill.

Следующие примеры будут уже с использованием SQW вывода RTC. В DS1307 и DS3231 тактирование SQW устанавливается по разному, поэтому для примеры с тактированием через SQW пин для них пришлось разделить.

Сначала загрузим пример для DS1307 и Arduino — DS1307_SQW_Clock. Здесь для настройки библиотеки используются уже знакомые первые две команды:

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

Здесь есть подводный камень, не зная который, можно взорвать моск 😉 Если НЕ соединять SQW пин с Arduino и запустить прошивку на выполнение, она все-равно будет тикать как-будто все OK. Может показаться, что программа не в порядке, контакт SQW не подключен же. Но в setup() мы задали автоматический ход часов без участия RTC, а режим тактирования через SQW задается в перегруженном операторе rtc++. И если прерывание не работает, то и режим тактирования через SQW не задается. Пока не знаю как это сделать лучше, поживем — увидим. Зато, если SQW пин изначально подключен, и в процессе работы отключить его, результат будет вполне ожидаем — счет времени останавливается:

Красным выделен интервал, для когда SQW пин был отключен от Arduino.

С DS3231 дело обстоит таким образом. Пин INT/SQW работает в двух режимах, он может использоваться для тактирования как SQW и он может использоваться для будильников. Кроме того, частота SQW задается в статусном регистре, а не в control-регистре как в DS1307.

Загружаем пример DS3231_SQW_Clock, и в setup() видим такой код:

В DS3231 указание частоты SQW вынесено в отдельный метод: set_sqw(). Кроме того, здесь еще сбрасывается флаг INTCH в control-регистре для перевода INT/SQW пина в SQW режим.

В операциях с status, control регистрами в библиотеке кроется еще один подводный камень. При чтении регистра, библиотека выдает значение сохраненное во время последнего обновления области регистров DS3231. А обновляются эти значения при записи туда. Т.е. при записи в регистр, сначала записывается требуемое значение, а потом, для контроля они считываются. Делалось это во избежании черезмерного доступа к RTC. Т.е. все те значения status и control регистров, что видны в логе, это распечатки последних сохраненных значений. В простейшем случае, при доступе только одного микроконтроллера к RTC и в случае одно экземпляра класса DS3231, это должно работать без проблем.

Скетч работает как на Arduino так и на MSP430 Launchpad:

На STM32duino работает как-то странно, но вроде работает:

Осталось два примера с будильниками. В RTC DS3231 пин INT/SQW с обычном режиме может служить для тактирования часов микроконтроллера. При необходимости использовать будильники, нужно выставить флаг INTСH в control-регистре. Тактирование остановится и микроконтроллер можно будет послать с спящий режим. При выходе из спящего режима, следует сбросить INTCH, после чего часы снова начнут тактироваться.

Первый пример, проверяет работу режимов энергосбережения. Загрузим из примеров скетч Wakeup_Alarm_A2, скомпилируем и загрузим в микроконтроллер:

В Arduino пример работает вроде как корректно.

В MSP430 launchpad тоже все Ок.

А вот c BluePill получается загвоздочка. В спящий режим микроконтроллер входит, и даже просыпается, по крайней мере, прерывание отрабатывается, но микроконтроллер так и не выходит из прерывания. Боюсь, что пока моего опыта по STM32 не хватает чтобы это корректно поправить без SPL и HAL.

Когда работу режимов энергосбережения и выходов их них проверили, можно загрузить в редактор последний пример: SQW_Alaram_A1. Здесь счет времени ведется уже через SQW пин, при установке будильника тактирование с SQW выхода снимается, а сам микроконтроллер погружается в спящий режим. При пробуждении от внешнего прерывания, SQW вывод вновь настраивается на тактирование хода часов. Для Arduino выполнение скетча выглядит так:

А вот в MSP430 в этот раз не сложилось, не выходит из спящего режима:

Подводя итоги, скажу, что остался не рассмотренным aging регистр. Он позволяет вносить поправку к внутреннему таймеру. Минимальная поправка за сутки при этом составит 86400/32768

2.5 сек. Возможно я чего-то недопонимаю, но для меня это кажется как-то многовоа-то, для хронометра у которого гарантированный дрифт в течении года, составляет не более одной минуты, при комнатной температуре, и четыре минуты, при перепадах от -40°C до +40°C. Также остался не понятым мной загадочный алгоритм термокомпенсации TCXO, запустить который можно установив бит CONV в control-регистре. но все остальное кажется разобрали.


Источник: www.count-zero.ru