Часы – конструктор на высокоточном (extremely accurate I2C) чипе DS3231. Часы на DS3231 и AVR-микроконтроллере

31.01.2023 Сервисы

Зачем всё это нужно?

60сек * 60мин *24 часа * 365 дней = 31 536 000 секунд в году.

На каждый из этих миллионов 2 секунды может уходить в ту или иную сторону. 31,5 миллион делим на миллион и умножаем на 2: получаем 63 секунды в год (максимум). Приемлемый вариант? Весьма. Но 1 раз в полгода я бы синхронизировал время, чтобы оно укладывалось в 1 минуту.

Какими способами вообще можно устанавливать время на часах модуля?

Традиционно, начиная с модуля DS3107, время устанавливалось при помощи скетча для Arduino из числа примеров использования библиотеки. Алгоритм такой: открываем скетч, жмём «компилировать и закачать», и при первом запуске контроллера время устанавливается. Остался вопрос: какое время? Откуда Arduino может узнать, какое именно время устанавливать? А очень просто – время компиляции скетча. Однако с таким подходом я вижу несколько недостатков:
  • время компиляции зависит от «мощи» компьютера;
  • время закачивания зависит от скорости передачи скомпилированного скетча в плату Arduino;
  • закачанный скетч – «одноразовый» (устаревает сразу же после закачивания в Arduino).
Как можно «извернуться», чтобы обойти эти ограничения? Ну, например, зная (экспериментально установив) время компилирования, можно «загнать» часы на компьютере на это время вперёд. Потом запустить компиляцию, прошить плату, и время установится. Плюс метода – относительная простота. Минусы – относительно неудобно, относительно неточно, одноразовый способ.

Что ещё можно придумать? Можно, например, выставлять требуемое время в скетче вручную, предусмотреть кнопку, нажатие на которую в нужный момент выставит «руками» указанное время, например, через 2 минуты от текущего момента: пока «зальётся» скетч, пока подготовимся отследить вручную тот самый нужный момент нажатия кнопки, как раз та пара минут и пройдёт. А дальше, глядя на часы в компьютере, дожидаться «того самого» момента, чтобы нажать кнопку. Плюсы – сложнее предыдущего способа, но всё ещё относительно просто, однако точнее, чем первый способ. Минусы – этот способ ещё неудобнее, дольше, всё равно скетч «одноразовый».

Кто виноват и что делать?

Задав себе эти два риторических вопроса, я полез в Интернет искать, кто уже написал синхронизацию времени модуля часов с компьютером. И, как известно, кто ищет – тот всегда находит. Нашёлся вариант с . В теории всё просто: обычный «батник» парсит текущее полное время, полученное «первым» способом (потому что кроме самого времени нужна ещё и дата), увеличивает время на 2 секунды, и «гоняет» пустой цикл до момента, когда настанет это новое, «плюс_две_секундное», время, чтобы «вышвырнуть» данные в COM порт. Причём «новое плюс_две_секундное» время отслеживается другим способом (через %time%, если кому интересно). Но о «косяках» такого решения позже. Данные, «вышвырнутые» в COM порт, Arduino парсит и после этого устанавливает время в модуле. Вроде всё просто, логично и удобно. Но есть очень нехорошее слово «НО». Всё это писал вроде бы немец, и региональные стандарты в Windows у него отличаются от «наших», а в частности, дробная часть отделяется точкой, а не запятой. При запуске с отечественными региональными стандартами «батник» не работает, потому что в нём время выхода из пустого цикла описывается условием сравнения с XX:XX:XX.xxx. Ну так надо вместо точки поставить запятую – и всё, «я всё починил». А вот и не всё (можете проверить, кто ещё помнит, что за такое зло – программировать в «батниках»). Нужно исправлять «батник» более серьёзно. И я его исправил, используя «маты-перематы» и «мануалку» для DOS. «Батник» исправил, но скетч всё равно не работал – время не устанавливалось. То есть данные в порт слались, Arduino их видел, но «что-то пошло не так».

Давайте взглянем, что шлёт «батник» в Arduino и в каком формате (справочно).

Case 83: //S = second case 68: //D = Minute (Daghigheh in Persian) case 72: //H = Hour case 84: //T = Day Of Month (Tag in German) case 77: /// M = Month case 74: /// J = Year (Jahr in German)
Данные шлются в формате S**~D**~H**~T*~M**~J****~, где ~ - 2 байта перевода каретки. Итого, 31 байт. Вроде немного, пришлются данные быстро.

Однако есть и неудобство – как видим, не шлётся день недели. Только день месяца. Для реализации часов с будильниками, зависящими от дней недели, будет «косяк». День недели придётся выставлять «ручками» в скетче, что опять намекает на некоторую «одноразовость» скетча, его неполноценность.

Складывая факторы – неполноценность скетча «с завода», его отказ нормально работать, необходимость исправления «батника» для «наших» широт – я решил разрабатывать всё свое. А раз так, то я могу устранять недостатки и оптимизировать формат данных.

Software и hardware.

Для того, чтобы всё заработало, нужны 2 составляющие: программа для Windows и аппаратно-программная связка Arduino.

Сначала общие данные по протоколу обмена. Коль скоро я стал волен выбирать формат данных для пересылки, я решил, что пересылка 31 байта информации не рациональна, и сократил передаваемые данные до 4 байт. И что, хватило? Что можно поместить в 4 байта? Да, хватило. Поместилось все, что надо. Уверен, многие догадались, что это за 4 байта. Кто не догадался – процитирую фрагмент статьи из Википедии:

UNIX-время (POSIX-время) - система описания моментов во времени, принятая в UNIX и других POSIX-совместимых операционных системах. Определяется как количество секунд, прошедших с полуночи (00:00:00 UTC) 1 января 1970 года (четверг).
UNIX-время представлено целым числом, которое увеличивается с каждой прошедшей секундой без необходимости вычислений для определения года, месяца, дня, часа или минуты для удобства восприятия человеком. Современное UNIX-время согласуется с UTC - отсчет происходит в секундах СИ.

Итак, целое число, хранящее UNIX время, занимает 4 байта, чего хватит до 2 147 483 648 секунд. А потом возможны потенциальные проблемы. Почему потенциальные? Потому что это порог, при достижении которого число может быть интерпретировано, как отрицательное (что и с айфонами многих любопытных товарищей в своё время). Может, но не обязательно будет – зависит от того, растут ли руки программистов из места, предусмотренного природой. Указанное число секунд соответствует 03:14:08 19-янв-2038. До этого времени можно неспешно переходить на 64-битную версию ОС, где время станет храниться в 8-байтной переменной, чего без проблем хватит на следующие 292 миллиарда лет. Существует вероятность, что на наш век этого хватит. А потом придётся обновляться до 128-битной версии UNIX.

Какие проблемы я решил, придя к такому варианту? Первое, сильно снизил количество передаваемых байт, что на миллисекунды увеличивает точность установки времени. Здорово, правда? И второе: я (вероятно) облегчил совместимость с Linux. К моему стыду, я никак не могу привыкнуть к Linux, и пользуюсь в основном только Windows. Для этой самой Windows я могу написать программу пересылки, а для Linux – нет. Но полагаю, что в Linux можно получить значение UNIX-времени намного легче, чем в Windows, и переслать это число в COM порт.

Никаких дополнительных данных, вроде дня недели и так далее, передавать не требуется. Только UNIX время. Всё остальное делается в Arduino.

Теперь немного конкретики непосредственно о первой составляющей – программе для Windows. Программа написана в старой-доброй Delphi. При запуске всплывающее окно просит выбрать COM порт для отправки данных. Выбираем. Остальные настройки следует оставить «дефолтными».

Как работает программа? Она пересчитывает из формата времени Windows данные для формата UNIX, то есть число секунд с полуночи 1 января 1970 года. Затем добавляет 3 секунды и «впадает» в пустой цикл (очевидно, длительностью уже менее тех самых добавочных 3 секунд), выход из которого происходит в нужное количество секунд, как можно ближе к 000 миллисекундам. Иначе говоря, отслеживается наступление самого начала той секунды времени, значение которого должно будет переслаться в Arduino. Согласитесь, пересылать данные о том, что, например, сейчас XXXXXXXXX5 секунд тогда, когда на самом деле уже, например, XXXXXXXXX5 и 756 тысячных (например) секунд, было бы не правильным. Именно для этого нужно отслеживать самое начало секунды для старта передачи данных. После передачи данных программа дружелюбно сообщает статус «Done:)». На этом миссия программы завершается.


Вторая составляющая – аппаратно-программная часть – Arduino. Существует 2 разновидности «железа» для этого проекта: «полная» версия с экраном и кнопкой, и «урезанная» версия для быстрой установки времени модуля, собранная из «г**на и палок». Про их отличия – ниже. «Полная» версия состоит из Arduino Nano, экрана 1602 с «переходником» с I2C в экран, опциональной кнопкой ресета Arduino и пин-хедера(female) для подключения модуля часов. Также, опционально, из корпуса девайса с «няшной» наклейкой. «Урезанная» версия состоит из Arduino (Uno, Nano, Pro Mini + «правильный» переходник на USB с DTR) и 4 проводов для подключения модуля часов.



Как видно из схем, «полная» версия, в дополнение к «урезанной», содержит кнопку для reset"а и экран 1602 с «переходником». Обе версии абсолютно одинаковы по основному функционалу – устанавливать время. Экран нужен лишь для отображения этапов процесса и, по окончании процесса установки времени, отображения свежеустановленных времени, даты и дня недели. Причём данные к тому времени будут уже считываться из самого модуля часов. В «урезанной» версии роль экрана выполняет встроенный в плату Arduino светодиод: по окончании процесса установки нового времени он начнет светиться. Вот и вся индикация.

Для чего же кнопка ресет? Для того, что в полной версии после установки времени Arduino войдёт в бесконечный цикл по отображению того самого свежеустановленного времени, то есть, по сути, станет часами. Причём часами, сделанными на скорую руку, в связи с чем они не смогут заменить нормальные часы в силу нескольких причин (выборка секунд реализована через delay, пропадёт отображение времени при отключении питания). Ведь цель – убедиться, что время синхронизировано верно, не более того. Следовательно, для синхронизации следующего модуля часов без ресета не обойтись (точнее, можно обойтись, если «передёрнуть» USB кабель). Другими словами, назначение кнопки – сугубо утилитарное. При желании, можно обойтись и без неё.

Как же прошивать Arduino, ведь версии «железа» две, а скетч один? Для компиляции «правильной» версии прошивки в заголовке скетча нужно установить желаемое значение параметра fullVersion: true для «полной» версии, или false - для «урезанной». Компилятор таким образом определит, для какой версии «железа» компилировать прошивку.

Итак, схема подключения есть, нужен код скетча. Обратите внимание, что для нормальной работы скетча с «полной» версией нужна библиотека LiquidCrystal I2C by Frank de Brabander (устанавливается из репозитория при помощи Менеджера Библиотек). Также нужна библиотека для поддержки модуля часов, причём не любая:). Качать здесь: . С библиотеками разобрались.

Вот код скетча:

//======================================== настройка, доступная для изменения ======================================== #define fullVersion true //true = "полная" версия с экраном; false = "урезанная" версия со встроенным светодиодом //================================= используемые библиотеки и объявление переменных ================================== #include #include #if (fullVersion) #include #endif unsigned long t1 = 0; //переменная для полученного времени unsigned long t2 = 0; //переменная для полученного времени byte b; //буфер для получения данных из COM порта #if (fullVersion) byte day = 0; #endif DS3231 clock; RTCDateTime dat1; #if (fullVersion) LiquidCrystal_I2C lcd(0x3F,16,2); //китайси полюбили новый адрес для "переходников" с i2c в экран #endif //==================================================================================================================== void setup(){ #if (!fullVersion) //актуально только для "урезанной" версии - начало участка кода pinMode(13, OUTPUT); digitalWrite(13,LOW); #endif //актуально только для "урезанной" версии - конец участка кода clock.begin(); Serial.begin(9600); #if (fullVersion) //актуально только для "полной" версии - начало участка кода lcd.init(); lcd.backlight(); lcd.setCursor(0,0); lcd.print("COMport 9600 8N1"); //подсказка, какие параметры COM порта выбирать в программе lcd.setCursor(0,1); lcd.print("Ready to sync"); //сообщение статуса - готовы к синхронизации delay(1000); #endif //актуально только для "полной" версии - конец участка кода } void loop(){ if (Serial.available()){ //если есть "порох в пороховницах" COM порта Serial.readBytes(b,4); //считаем все 4 байта (другого мы и не ждём) t1=b; t2=(t1<<24); //поместить значение байта в 4-байтную переменную и передвинуть его на 3 байта влево t1=b; t2+=(t1<<16); //поместить значение байта в 4-байтную переменную и передвинуть его на 2 байта влево t1=b; t2+=(t1<<8); //поместить значение байта в 4-байтную переменную и передвинуть его на 1 байт влево t2+=b; //поместить значение байта в 4-байтную переменную clock.setDateTime(t2); //установить полученное время на DS3231 #if (fullVersion) //актуально только для "полной" версии - начало участка кода lcd.clear(); lcd.setCursor(0,0); lcd.print("Done:) : :"); while (true){ //начало бесконечного цикла по отображению свежеустановленных времени и даты dat1 = clock.getDateTime(); if (dat1.day != day){ day = dat1.day; lcd.setCursor(0,1); if (dat1.day < 10) lcd.print("0"); lcd.print(day); lcd.print("-"); switch (dat1.month){ //выбираем буквенное соответствие месяца по цифре case 1:{ lcd.print("Jan"); break; } case 2:{ lcd.print("Feb"); break; } case 3:{ lcd.print("Mar"); break; } case 4:{ lcd.print("Apr"); break; } case 5:{ lcd.print("May"); break; } case 6:{ lcd.print("Jun"); break; } case 7:{ lcd.print("Jul"); break; } case 8:{ lcd.print("Aug"); break; } case 9:{ lcd.print("Sep"); break; } case 10:{ lcd.print("Oct"); break; } case 11:{ lcd.print("Nov"); break; } case 12:{ lcd.print("Dec"); break; } default:{ lcd.print("???"); break; } }//switch month lcd.print("-"); lcd.print(dat1.year); lcd.print(" "); switch(dat1.dayOfWeek){ //выбираем буквенное соответствие дня недели по цифре case 1:{ lcd.print("Mon"); break; } case 2:{ lcd.print("Tue"); break; } case 3:{ lcd.print("Wed"); break; } case 4:{ lcd.print("Thu"); break; } case 5:{ lcd.print("Fri"); break; } case 6:{ lcd.print("Sat"); break; } case 7:{ lcd.print("Sun"); break; } default:{ lcd.print("???"); break; } }//switch dayOfWeek }//if date changed lcd.setCursor(8,0); if (dat1.hour < 10) lcd.print("0"); lcd.print(dat1.hour); lcd.setCursor(11,0); if (dat1.minute < 10) lcd.print("0"); lcd.print(dat1.minute); lcd.setCursor(14,0); if (dat1.second < 10) lcd.print("0"); lcd.print(dat1.second); delay(995); }//while #else //актуально только для "урезанной" версии - начало участка кода digitalWrite(13, HIGH); delay(3000); digitalWrite(13, LOW); #endif //актуально только для "полной" версии - конец участка кода }//if Serial }//loop


Пара фото «полной» версии готового девайса.


Ну и, наконец, видео работы девайса «в бою»:

Где скачать скетч и программу?

Скетч качать (Dropbox).
Программу для Windows качать (Dropbox).

«Плюсы» и «минусы».

Сформулировать «плюсы» и «минусы» в данном случае тяжело. Следовательно, каждый решает для себя сам, что – хорошо, а что – плохо.

Итого.

Мне очень понравилось, как теперь устанавливается время в модулях! При необходимости установить время мне не приходится вспоминать каждый раз, какой же там скетч мне нужен и задумываться, насколько точно будет установлено время в модуле. Более того, скоро будет обзор самодельных часов, куда я встроил такой метод синхронизации – настолько метод мне понравился. Надеюсь, кому-то из читателей метод также придётся кстати.

Проект - свободный, некоммерческий. Каждый вправе использовать данные из обзора в любых целях, кроме коммерческих.

Всем добра.

Планирую купить +48 Добавить в избранное Обзор понравился +60 +114

Описание

Модуль часов реального времени с независимым питанием. Контроллеры Arduino/Genuino не имеют встроенных часов реального времени. Для работы со временем есть функция millis(). Однако, для проектов где требуется время и дата, возможностей данной функции недостаточно и на помощь приходят часы реального времени.

Модуль DS3231 - это недорогие, точные, с работой по протоколу I2C часы реального времени, с температурной компенсацией TCXO. Устройство содержит разъём для батареи типа CR2032 и поддерживает точный отсчет времени когда питание устройства прерывается. Часы поддерживают информацию о секундах, минутах, часах, дне, месяце и годе. Дата на конец месяца автоматически корректируется, для месяцев, содержащих менее чем 31 день, включая коррекцию по високосному году. Работают в одном из режимов: 24- или 12-часовом формате (с AM/PM индикатором). Имеют два программируемых будильника.

Основные характеристики часов:

    Часы реального времени с подсчетом секунд, минут, часов, дня, месяца и года (с корректировкой дат в т.ч. по високосному году до 2100 года)

    Погрешность хода: ±2 минуты в год

    Температурный датчик с погрешностью ±3°С

    Два будильника

Технические характеристики

    Рабочая температура: -40°С - +85°С

    Напряжение питания: 2,3 - 5,5 В

    Напряжение питания батареи: 2,3 - 5,5 В

    Максимальное потребление тока: 650 нА

Физические размеры

    Модуль (Д х Ш х В): 45 х 23 х 15 мм

Плюсы использования

    Высокая точность хода часов

    Имеется два будильника с функцией прерывания

    Широкий диапазон рабочей температуры

Минусы использования

    Дороже некоторых аналогов

Библиотека для работы с модулем

Примеры подключения и использования

Пример 1: В примере иллюстрируется подключение модуля часов к контроллеру, установка времени и даты на часах, вывод времени, даты и дня недели в монитор Serial-порта, а также получение температуры с текущего модуля. (Примеры тестировались на контроллере Smart UNO)

Схема подключения:

Скетч для загрузки:

//Подключение библиотек #include #include "DS3231.h" DS3231 RTC; //Создание объекта DS3231 char weekDay [ 4 ] = { "Sun" , "Mon" , "Tue" , "Wed" , "Thu" , "Fri" , "Sat" } ; //Создание массива дней недели //Создание переменной типа DateTime для задания даты в формате: //год, месяц, день, час, минута, секунда и день недели (от 0 - воскресенье до 6 - суббота) DateTime dt(2016 , 8 , 22 , 16 , 10 , 0 , 1 ) ; void setup () { Serial .begin (9600 ) ; //инициализация Serial-порта Wire.begin () ; //инициализация библиотеки Wire RTC.begin () ; //инициализация часов RTC.adjust (dt) ; //Задание даты-времени так как она задана в переменной dt } void loop () { DateTime now = RTC.now () ; //получение текущей даты и времени //вывод полученного года Serial .print (now.year () , DEC) ; Serial .print ("/" ) ; //вывод месяца Serial .print (now.month () , DEC) ; Serial .print ("/" ) ; //дня Serial .print (now.date () , DEC) ; Serial .print (" " ) ; //часа Serial .print (now.hour () , DEC) ; Serial .print (":" ) ; //минут Serial .print (now.minute () , DEC) ; Serial .print (":" ) ; //секунд Serial .print (now.second () , DEC) ; Serial .println () ; //дня недели Serial .print (weekDay[ now.dayOfWeek () ] ) ; Serial .println () ; delay (1000 ) ; //задержка на 1 сек RTC.convertTemperature () ; //конвертация текущей температуры в регистрах Serial .print (RTC.getTemperature () ) ; //чтение регистров и вывод полученной температуры Serial .println (" C" ) ; delay (1000 ) ; //задержка на 1 сек }

Модуль DS3231 (RTC, ZS-042) — представляет собой недорогую плату с чрезвычайно точными часами реального времени (RTC), с температурной компенсацией кварцевого генератора и кристалла. Модуль включает в себя литиевую батарею, которая поддерживает бесперебойную работу, даже при отключении источник питания. Интегрированный генератор улучшить точность устройства и позволил уменьшить количество компонентов.

Технические параметры

Напряжение питания: 3.3В и 5В
Чип памяти: AT24C32 (32 Кб)
Точность: ± 0.432 сек в день
Частота кварца:32.768 кГц
Поддерживаемый протокол: I2C
Габариты: 38мм x 22мм x 15мм

Общие сведения

Большинство микросхем, таких как DS1307 используют внешний кварцевый генератор частотой 32кГц, но в них есть существенный недостаток, при изменении температуры меняется частота кварца, что приводит к погрешности в подсчете времени. Эта проблема устранена в чипе DS3231, внутрь которого установили кварцевый генератор и датчик температуры, который компенсирует изменения температуры, так что время остается точным (при необходимости, данные температуры можно считать). Так же чип DS3231 поддерживает секунды, минуты, часы, день недели, дата, месяц и год информацию, а так же следит за количеством дней в месяце и делает поправку на високосный год. Поддерживает работу часов в двух форматов 24 и 12, а так-же возможно запрограммировать два будильника. Модуль работает по двух проводной шине I2C.


Теперь немного о самом модуле, построен он на микросхеме DS3231N. Резисторная сборка RP1 (4.7 кОм), необходима для подтяжки линий 32K, SQW, SCL и SDA (кстати, если используется несколько модулей с шиной I2C, необходимо выпаять подтягивающие резисторы на других модулях). Вторая сборка резисторов, необходима для подтяжки линий A0, A1 и A2, необходимы они для смены адресации микросхемы памяти AT24C32N. Резистор R5 и диод D1, служат для подзарядки батарее, в принципе их можно выпаять, так как обычной батарейки SR2032 хватает на годы. Так же установлена микросхема памяти AT24C32N, это как бы бонус, для работы часов RTC DS3231N в ней нет необходимости. Резистор R1 и светодиод Power, сигнализируют о включении модуля. Как и говорилось, модуль работает по шине I2C, для удобства эти шины были выведены на два разъема J1 и J2, назначение остальных контактов, можно посмотреть ниже.Назначение J1
32K: выход, частота 32 кГц
SQW: выход
SDA: линия данных (Serial Dфta)
VCC: «+» питание модуля
GND: «-» питание модуля Назначение J2
SCL: линия тактирования (Serial CLock)
SDA: линия данных (Serial Data)
VCC: «+» питание модуля
GND: «-» питание модуля


Немного расскажу, о микросхеме AT24C32N, это микросхема с 32к памятью (EEPROM) от производителя Atmel, собранная в корпусе SOIC8, работающая по двухпроводной шине I2C. Адрес микросхемы 0x57, при необходимости легко меняется, с помощью перемычек A0, A1 и A2 (это позволяет увеличить количество подключенных микросхем AT24C32/64). Так как чип AT24C32N имеет, три адресных входа (A0, A1 и A2), которые могут находится в двух состояния, либо лог «1» или лог «0», микросхеме доступны восемь адресов. от 0x50 до 0x57.

Подключение DS3231 к Arduino

Необходимые детали:
Arduino UNO R3 x 1 шт.
Часы реального времени на DS3231, RTC, SPI, AT24C32 x 1 шт.
Провод DuPont, 2,54 мм, 20 см, F-M (Female — Male) x 1 шт.
Кабель USB 2.0 A-B x 1 шт.

Подключение:
В данном примере буду использовать только модуль DS3231 и Arduino UNO R3, все данные будут передаваться в «Мониторинг порта». Схема не сложная, необходимо всего четыре провода, сначала подключаем шину I2C, SCL в A4 (Arduino UNO) и SDA в A5 (Arduino UNO), осталось подключить питание GND к GND и VCC к 5V (можно записать и от 3.3В), схема собрана, теперь надо подготовить программную часть.

Библиотеки работающий с DS3231 нет в среде разработке IDE Arduino, необходимо скачать «DS3231 » и добавить в среду разработки Arduino.

Установка времени DS3231
При первом включении необходимо запрограммировать время, откройте пример из библиотеки DS3231 «Файл» -> «Примеры» -> «DS3231» -> «Arduino» -> «DS3231_Serial_Easy», или скопируйте код снизу

/* Тестирование производилось на Arduino IDE 1.8.0 Дата тестирования 31.08.2018г. */ #include // Подключаем библиотеку Wire DS3231 rtc(SDA, SCL); // Инициализация DS3231 void setup() { Serial.begin(115200); // Установка последовательного соединения rtc.begin(); // Инициализировать rtc // Установка времени rtc.setDOW(FRIDAY); // Установить день-недели rtc.setTime(16, 29, 0); // Установить время 16:29:00 (формат 24 часа) rtc.setDate(31, 8, 2018); // Установить дату 31 августа 2018 года } void loop() { Serial.print(rtc.getDOWStr()); // Отправляем день-неделя Serial.print(" "); Serial.print(rtc.getDateStr()); // Отправляем дату Serial.print(" -- "); Serial.println(rtc.getTimeStr()); // Отправляем время delay (1000); // Задержка в одну секунду }

Тестирование производилось на Arduino IDE 1.8.0

Дата тестирования 31.08.2018г.

#include // Подключаем библиотеку Wire

DS3231 rtc (SDA , SCL ) ; // Инициализация DS3231

void setup ()

Serial . begin (115200 ) ; // Установка последовательного соединения

rtc . begin () ; // Инициализировать rtc

// Установка времени

rtc . setDOW (FRIDAY ) ; // Установить день-недели

rtc . setTime (16 , 29 , 0 ) ; // Установить время 16:29:00 (формат 24 часа)

void loop ()

Serial . print (rtc . getDOWStr () ) ; // Отправляем день-неделя

Serial . print (" " ) ;

Serial . print (rtc . getDateStr () ) ; // Отправляем дату

Serial . print (" -- " ) ;

Serial . println (rtc . getTimeStr () ) ; // Отправляем время

delay (1000 ) ; // Задержка в одну секунду

Загружаем скетч в контроллер Arduino и открываем «Мониторинг порта»

Сегодня мы продолжим поиски идеальной микросхемы часов реального времени (RTC). Часы будем изготавливать на основе . Индикация будет использоваться более удобная для разработки - LCD дисплей, на котором будет отображаться вся информация сразу кроме настроек. В таком виде часы удобно использовать как настольный вариант.

Итак, рассмотрим саму микросхему DS3231. DS3231 - это часы реального времени с экстремально точным ходом (подобрали же производители словечко) благодаря встроенному кварцевому резонатору с температурной компенсацией. Интерфейс передачи данных - I 2 C. В этой микросхеме есть также вход для напряжения резервной батареи, при отключении основного питания микросхема автоматически переключается на работу от резервной батареи, точность хода от резервной батареи не нарушается. Весьма радует, не правда ли? В DS3231 поддерживается подсчет секунд, минут, часов, дней месяца (даты), дней недели, месяцев и лет (с учетом високосного года для месяцев). Поддерживается работа в 12 и 24 часовом формате. Имеется 2 будильника с возможностью их настройки и отслеживания состояния. Подстройка точности температурной компенсации. А также два выхода - на 32 кГц (выход составляет 32.768 кГц) и программируемый выход от 1 Гц до 8.192 кГц. Имеется также вывод сброса - RST. микросхема часов реального времени выпускается в корпусе SO-16. Корпус достаточно крупный, но если учитывать что внутри уже имеется кварц, да еще и температурно компенсируемый, то мне кажется, с размерами тут все отлично. У DS3231 есть близнец в виде DS3232, у которого, правда, на 2 ножки больше. Все это очень напоминает продукцию компании NXP - микросхемы часов PCA2129 и PCF2129. Аналогично температурно компенсируемый встроенных кварцевый резонатор, оба такие же близнецы только с разным количеством n.c. выводов и схожими функциями относительно DS3231 помимо хронометрожа времени.

RTC DS3231 имеются в продаже в виде модулей с необходимой обвязкой, а также до комплекта микросхемой EEPROM, которая чаще всего и даром не нужно, только веса добавляет:

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

Что важно знать при работе с такой микросхемой часов реального времени, так это как же извлечь из нее данные или записать их туда. Часы имеют интерфейс I 2 C. Для того чтобы осуществить запись данных (а это нужно и для того чтобы прочитать данные) нужно передать условие старта (эти команды осуществляются по средствам аппаратного или программного I 2 C для микроконтроллера), далее передать адрес микросхемы с битом записи, далее передать адрес регистра к которому будем обращаться и далее передать в этот регистр байт данных, если следом передать еще байт данных, он запишется в следующий регистр и так далее. По окончании нужно передать условие остановки. Графическое изображение выше сказанного на рисунке:

Запись данных необходима для первоначальной настройки, а также для настройки текущего времени. Далее нам нужно постоянно получать данные о текущем времени и даты. Для этого необходимо осуществлять чтение из регистров хранения этой информации. Чтение состоит из двух процедур - установить указатель на нужный регистр и прочитать его. Чтобы установить указатель на нужный регистр, нужно передать условие старта, потом передать адрес микросхемы с битом записи и байт с адресом регистра. Далее либо условие остановки и следом условие старта, либо просто рестарт. Теперь вторая процедура - непосредственно чтение из регистров. Старт передан, далее нужно отправить адрес микросхемы с битом чтения и далее считывать регистры в необходимом количестве, по окончании передать условие остановки. Если информация из регистра была прочитана, то указатель автоматически переходит на следующий за ним регистр без лишних действий со стороны микроконтроллера (мастер устройства). На рисунке проиллюстрировано все выше сказанное относительно чтения регистров по средствам I 2 C интерфейса:

Адрес микросхемы:

  • для записи - 0b11010000
  • для чтения - 0b11010001

Программно код на языке Си будет выглядеть следующим образом:

// функции с часами ======================================================================================================= // инициализация начальных установок void RTC_init(void){ i2c_start_cond(); // запуск i2c i2c_send_byte(RTC_adr_write); // передача адреса устройства, режим записи i2c_send_byte(0x0E); // передача адреса памяти i2c_send_byte(0b00100000); // запустить преобразование температуры и выход на 1 Гц i2c_send_byte(0b00001000); // разрешить выход 32 кГц i2c_stop_cond(); // остановка i2c } // получение времени и даты void RTC_read_time(void){ i2c_start_cond(); // запуск i2c i2c_send_byte(RTC_adr_write); // передача адреса устройства, режим записи i2c_send_byte(0x00); // передача адреса памяти i2c_stop_cond(); // остановка i2c i2c_start_cond(); // запуск i2c i2c_send_byte(RTC_adr_read); // передача адреса устройства, режим чтения sec = bcd(i2c_get_byte(0)); // чтение секунд, ACK min = bcd(i2c_get_byte(0)); // чтение минут, ACK hour = bcd(i2c_get_byte(0)); // чтение часов, ACK wday = bcd(i2c_get_byte(0)); // чтение день недели, ACK day = bcd(i2c_get_byte(0)); // чтение число, ACK month = bcd(i2c_get_byte(0)); // чтение месяц, ACK year = bcd(i2c_get_byte(1)); // чтение год, NACK i2c_stop_cond(); // остановка i2c } // установка времени void RTC_write_time(unsigned char hour1,unsigned char min1, unsigned char sec1){ i2c_start_cond(); // запуск i2c i2c_send_byte(RTC_adr_write); // передача адреса устройства, режим записи i2c_send_byte(0x00); // передача адреса памяти i2c_send_byte(bin(sec1)); // 0x00 секунды (целесообразно ли задавать еще и секунды?) i2c_send_byte(bin(min1)); // 0x01 минуты i2c_send_byte(bin(hour1)); // 0x02 часы i2c_stop_cond(); // остановка i2c } // установка даты void RTC_write_date(unsigned char wday, unsigned char day, unsigned char month, unsigned char year){ i2c_start_cond(); // запуск i2c i2c_send_byte(RTC_adr_write); // передача адреса устройства, режим записи i2c_send_byte(0x03); // передача адреса памяти i2c_send_byte(bin(wday)); // 0x03 день недели (воскресенье - 1, пн 2, вт 3, ср 4, чт 5, пт 6, сб 7) i2c_send_byte(bin(day)); // 0x04 день месяц i2c_send_byte(bin(month)); // 0x05 месяц i2c_send_byte(bin(year)); // 0x06 год i2c_stop_cond(); // остановка i2c } // чтение температуры void RTC_read_temper(void){ i2c_start_cond(); // запуск i2c i2c_send_byte(RTC_adr_write); // передача адреса устройства, режим записи i2c_send_byte(0x11); // передача адреса памяти i2c_stop_cond(); // остановка i2c i2c_start_cond(); // запуск i2c i2c_send_byte(RTC_adr_read); // передача адреса устройства, режим чтения t1 = i2c_get_byte(0); // чтение MSB температуры t2 = i2c_get_byte(1); // чтение LSB температуры i2c_stop_cond(); // остановка i2c t2=(t2/128); // сдвигаем на 6 - точность 0,25 (2 бита) // сдвигаем на 7 - точность 0,5 (1 бит) t2=t2*5; }

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

Да - отличной фишкой DS3231 является то, что эта же микросхема выполняет функции термометра (а то как же еще осуществлять температурную компенсацию) и возможность чтения текущей температуры. Максимальное разрешение температуры составляет 0.25 градусов Цельсия. Также период обновления температуры достаточно большой - около 1 минуты. Да нам быстро то не к чему обновлять ее.

Схема же всего устройства часов выглядит так:

Микроконтроллер был выбран Atmega8 за свою широкую распространенность и небольшую цену. Данный микроконтроллер можно использовать как в корпусе DIP-28, так и в SMD исполнении в корпусе TQFP-32. Резистор R3 необходим для предотвращения самопроизвольного перезапуска микроконтроллера в случае появления случайных помех на выводе PC6. Резистор R3 подтягивает плюс питания к этому выводу, надежно создавая потенциал на нем. Для индикации используется жидко кристаллический (ЖК или LCD) дисплей. Мною использовался дисплей 2004А - 4 строки по 20 символов больше для красоты, поэтому можно применять дисплей более привычный - 2 строки по 16 символов. ЖК дисплей подключается к микроконтроллеру по четырех битной системе. Переменный резистор R2 необходим для регулировки контраста символов на дисплее. Вращением движка этого резистора добиваемся наиболее четких для нас показаний на экране. Подсветка ЖК дисплея организована через вывод "А" и "К" на плате дисплея. Подсветка включается через резистор, ограничивающий ток - R1. Чем больше номинал, тем более тускло будет подсвечиваться дисплей. Однако пренебрегать этим резистором не стоит во избежание порчи подсветки. Кнопки S1 - S4 управляют настройками часов. Светодиод сигнализирует о том, что будильник сработал. Светодиод можно заменить на какую-либо звуковую схему. Резисторы R5 - R8 являются подтягивающими (pull-up) и необходимы для формирования прямоугольных импульсов на выводах микросхемы часов. Также это необходимо для правильной работы протокола I2C. Для питания схемы используется микросхема линейного стабилизатора L7805, ее можно заменить на отечественный аналог пяти вольтового линейного стабилизатора КР142ЕН5А, либо применить другу микросхему стабилизатора напряжения в соответствии с подключением ее в схеме (например LM317 или импульсные стабилизаторы LM2576, LM2596, MC34063 и так далее). Далее 5 вольт стабилизируются другой микросхемой - AMS1117 в исполнении, дающей на выходе 3,3 вольта. Микросхема часов, в соответствии с даташитом, питается от напряжения 3,3 вольта. Однако максимальное напряжение составляет 5,5 вольта. Поэтому Данный стабилизатор можно использовать, а можно и нет, на ваше усмотрение. Стабилизатор напряжения AMS1117 можно также заменить на исполнение ADJ (AMS1117ADJ) - то есть регулируемый вариант, задать необходимое напряжение при таком выборе необходимо будет при помощи двух резисторов, подключаемых к микросхеме в соответствии с даташитом на нее.

Схема была собрана и отлажена с применением отладочной макетной платы для микроконтроллера ATmega8:

Назначение кнопок:

  • S1 - отключает сигнал будильника, либо выходит в главное меню из любого меню настроек
  • S2 - сброс микроконтроллера
  • S3 - изменяет время или дату в меню настроек
  • S4 - вход в меню настроек и перелистывание меню

Вывод 32 кГц может использоваться для контроля частоты кварцевого резонатора. Подключаем к этому выводу частотомер или осциллограф и контролируем частоту:

Как видно из скриншота осциллограммы, частота примерно соответствует 32,768 кГц (примерно в силу ограничения разрешения измерения частоты, а "на глаз" настолько точно трудно определить).

В итоге получились часы со следующими характеристиками:

  • индикация времени
  • индикация даты
  • индикация дня недели
  • индикация активности будильника
  • 1 будильник с выходом сигнала от микроконтроллера
  • индикация температуры окружающей среды (программно реализована только положительная температура, отрицательная, думаю, нам ни к чему)
  • настройки будильника
  • настройки времени
  • настройки даты
  • LCD-дисплей с подсветкой
  • сохранение настроек и продолжение хода часов при отключении основного питания

Подытожим . Микросхема часов реального времени DS3231 является отличным решением. Точность хода сравнительно c какой-нибудь DS1307 или выше, а вот PCA/PCF2129 еще могут потягаться с ней. Среди рассмотренных мною микросхем часов реального времени данный экземпляр на сегодняшний день занимает первое место по функционалу и точности.

Для программирования микроконтроллера Atmega8 необходимо знать конфигурацию фьюз битов (скриншот сделан в программе ):

К статье прилагается прошивка для микроконтроллера Atmega8, проект схемы в программе , а также видео работы часов (в самом начале сработает будильник - загорится светодиод).

Список радиоэлементов

Обозначение Тип Номинал Количество Примечание Магазин Мой блокнот
IC1 МК AVR 8-бит

ATmega8

1 В блокнот
IC2 Часы реального времени (RTC)

DS3231

1 В блокнот
VR1 Линейный регулятор

L7805AB

1 В блокнот
VR2 Линейный регулятор

AMS1117-3.3

1 В блокнот
VD1 Выпрямительный диод

1N4148

1 В блокнот
C1 470 мкФ 1 В блокнот
C2, C3, C5, C7 Конденсатор 100 нФ 4 В блокнот
C4 Электролитический конденсатор 220 мкФ 1 В блокнот
C6, C8 Электролитический конденсатор 10 мкФ 2 В блокнот
R1 Резистор

22 Ом

1 В блокнот
R2 Подстроечный резистор 10 кОм 1 3296W-1-103LF

Отличительные особенности:

  • Точность ±2 ppm в диапазоне температур от 0°C до +40°C
  • Точность ±3.5 ppm в диапазоне температур от-40°C до +85°C
  • Вход для подключения автономного источника питания, позволяющего обеспечить непрерывную работу
  • Рабочий температурный диапазон коммерческий: от 0°C до +70°C индустриальный: -от 40°C до +85°C
  • Низкое потребление
  • Часы реального времени, отсчитывающие секунды, минуты, часы, дни недели, дни месяца, месяц и год с коррекцией високосного года вплоть до 2100
  • Два ежедневных будильника
  • Выход прямоугольного сигнала с программируемой частотой
  • Быстродействующие (400 кГц) I 2 C интерфейс
  • 3.3 В питание
  • Цифровой температурный датчик с точностью измерения ±3°C
  • Регистр, содержащий данные о необходимой подстройке
  • Вход/выход сброса nonRST

Применение:

  • Серверы
  • Электронные электросчетчики
  • Телематическая аппаратура
  • GPS системы

Типовая схема включения DS3231:

Общее описание:

DS3231 - высокоточные часы реального времени (RTC) со встроенными I 2 C интерфейсом, термокомпенсированным кварцевым генератором (TCXO) и кварцевым резонатором. Прибор имеет вход для подключения резервного автономного источника питания, позволяющего осуществлять хронометрирование и измерение температуры даже при отключенном основном напряжении питания. Встроенный кварцевый резонатор повышает срок службы прибора и уменьшает необходимое количество внешних элементов. DS3231 доступен в модификациях с коммерчески и индустриальным рабочим температурным диапазоном и упакован в 300 mil 16 контактный SO корпус.

RTC обеспечивает отсчет секунд, минут, часов, дней недели, дней месяца и года. Дата конца месяца определяется автоматически с учетом високосного года. Часы реального времени работают в 24 или 12- часовом формате с индикацией текущей половины суток (AM/PM). Прибор имеет два ежедневных будильника и выход прямоугольного сигнала с программируемой частотой. Обмен данными с прибором ведется через встроенный последовательный I 2 C совместимый интерфейс.