Сопряжение ds1307 c микроконтроллером avr. Подключение DS1307 к микроконтроллерам AVR. Пример проекта с i2C модулем часов и дисплеем

Модуль часов реального времени DS1307
Tiny RTC I2C module 24C32 memory DS1307 clock

Небольшой модуль, выполняющий функции часов реального времени. Выполнен на базе микросхемы DS1307ZN+ . Непрерывный отсчет времени происходит благодаря автономному питанию от батареи, установленной в модуль. Также модуль содержит память EEPROM объемом 32 Кбайт, сохраняющую информацию при отключении всех видов питания. Память и часы связаны общей шиной интерфейса I2C. На контакты модуля выведены сигналы шины I2C. При подключении внешнего питания происходит подзарядка батареи через примитивную цепь подзарядки. На плате имеется место для монтажа цифрового датчика температуры DS18B20. В комплект поставки он не входит.
Использование этого устройства происходит при измерении временных интервалов более недели приборами на основе микроконтроллера. Задействовать собственные ресурсы МК для этой цели неоправданно, а зачастую невозможно. Обеспечить бесперебойное питание на длительный срок дорого, установить батарею для питания МК нельзя из-за значительного тока потребления. Тут на выручку приходит модуль часов реального времени DS1307.
Также модуль часов реального времени DS1307 благодаря наличию собственной памяти позволяет регистрировать данные событий, происходящих несколько раз в сутки, например измерения температуры. Журнал событий в дальнейшем считывается из памяти модуля. Эти возможности позволяют использовать модуль в составе автономной автоматической метеостанции или для исследований климата в труднодоступных местах: пещерах, вершинах скал. Становится возможным регистрировать тензопараметры архитектурных сооружений, например опор мостов и других. При оснащении прибора радиосвязью достаточно установить его в исследуемой местности.

Характеристики

Напряжение питания 5 В
Размеры 27 х 28 х 8,4 мм

Электрическая схема

Устройство обменивается данными с электроникой прибора с помощью сигналов SCL и SDA. Микросхема IC2 - часы реального времени. Конденсаторы С1 и С2 снижают уровень помех в линии питания VCC. Резисторы R2 и R3 обеспечивают надлежащий уровень сигналов SCL и SDA. С вывода 7 микросхемы IC2 поступает сигнал SQ, состоящий из прямоугольных импульсов частотой 1 Гц. Он используется для проверки работоспособности МС IC2. Компоненты R4, R5, R6, VD1 обеспечивают подзарядку батареи BAT1. Для хранения данных модуль часов реального времени DS1307 содержит микросхему IC1 - долговременная память. US1 - датчик температуры. Сигналы модуля и линии питания выведены на соединители JP1 и P1.

Информационная шина

I2C это стандартный последовательный интерфейс посредством двух сигнальных линий SCL, SDA и общего провода. Линии интерфейса образуют шину. К линиям интерфейса I2C можно подключить несколько микросхем, не только микросхемы модуля. Для идентификации микросхемы на шине, а именно записи данных в требуюмую МС и определения от какой МС поступают данные. Каждая микросхема имеет уникальный адрес для проложенной шины. DS1307 имеет Адрес 0x68. Он записан на заводе-изготовителе. Микросхема памяти имеет адрес 0x50. В программное обеспечение Arduino входит программная библиотека, обеспечивающая поддержку I2C.

Микросхема часов реального времени

DS1307 обладает низким энергопотреблением, обменивается данными с другими устройствами через интерфейс I2C, содержит память 56 байт. Содержит часы и календарь до 2100 г. Микросхема часов реального времени обеспечивает другие устройства информацией о настоящем моменте: секунды, минуты, часы, день недели, дата. Количество дней в каждом месяце учитывается автоматически. Есть функция компенсации для високосного года. Имеется флаг, чтобы определить, работают часы в 24-часовом режиме или 12-часовом режиме. Для работы в режиме 12 часов микросхема имеет бит, откуда считываются данные для передачи о периоде времени: до или после обеда.

Микросхема долговременной памяти

Рисунок модуля часов реального времени DS1307 со стороны батареи с установленным датчиком температуры U1.

Батарея

В держатель на обратной стороне платы устанавливается литиевая дисковая батарея CR2032. Она выпускается множеством производителей, например изготовленная фирмой GP обеспечивает напряжение 3,6 В и ток разряда 210 мАч. Батарея подзаряжается во время включения питания, с таким режимом работы литиевой батареи мы сталкиваемся на материнской плате компьютера.

Подзарядка батареи

Программное обеспечение

Для работы модуля в составе Arduino вполне подойдет устаревшая библиотека с сайта Adafruit под названием RTCLib. Скетч называется DS1307.pde. Существует обновленная версия . Следует скачать архив, распаковать его, переименовать и скопировать библиотеку в свой каталог библиотек Arduino.

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

Для этого следует использовать скетчи
SetRTC устанавливает время в часах в соответствии со временем, которое указано в скетче.
GetRTC выводит время.
Оба скетча требуют библиотеку Wire и определить адрес I2C. Чтобы установить адрес часов на шине I2C, используйте этот I2C сканер .

Соединение с Arduino Mega.

Подключите SCL и SDA к соответствующим контактам 21 и 20 на Arduino Mega 2560. Подключите питание.

Соединение с Arduino Uno


Установите время в скетче SetRTC и загрузите в Arduino. Затем нажмите кнопку сброса для установки часов. Теперь загрузите скетч GetRTC. Откройте последовательный монитор и смотрите. Есть специальная библиотека времени . Она имеет много различных функций, которые могут быть полезны в зависимости от ситуации. Чтобы установить время, используя библиотеку нужно скачать . При использовании скетча можно синхронизировать часы реального времени с часами персонального компьютера.

Во многих проектах Ардуино требуется отслеживать и фиксировать время наступления тех или иных событий. Модуль часов реального времени, оснащенный дополнительной батарей, позволяет хранить текущую дату, не завися от наличия питания на самом устройстве. В этой статье мы поговорим о наиболее часто встречающихся модулях RTC DS1307, DS1302, DS3231, которые можно использовать с платой Arduino.

Модуль часов представляет собой небольшую плату, содержащей, как правило, одну из микросхем DS1307, DS1302, DS3231.Кроме этого, на плате практически можно найти механизм установки батарейки питания. Такие платы часто применяется для учета времени, даты, дня недели и других хронометрических параметров. Модули работают от автономного питания – батареек, аккумуляторов, и продолжают проводить отсчет, даже если на Ардуино отключилось питание. Наиболее распространенными моделями часов являются DS1302, DS1307, DS3231. Они основаны на подключаемом к Arduino модуле RTC (часы реального времени).

Часы ведут отсчет в единицах, которые удобны обычному человеку – минуты, часы, дни недели и другие, в отличие от обычных счетчиков и тактовых генераторов, которые считывают «тики». В Ардуино имеется специальная функция millis(), которая также может считывать различные временные интервалы. Но основным недостатком этой функции является сбрасывание в ноль при включении таймера. С ее помощью можно считать только время, установить дату или день недели невозможно. Для решения этой проблемы и используются модули часов реального времени.

Электронная схема включает в себя микросхему, источник питания, кварцевый резонатор и резисторы. Кварцевый резонатор работает на частоте 32768 Гц, которая является удобной для обычного двоичного счетчика. В схеме DS3231 имеется встроенный кварц и термостабилизация, которые позволяют получить значения высокой точности.

Сравнение популярных модулей RTC DS1302, DS1307, DS3231

В этой таблице мы привели список наиболее популярных модулей и их основные характеристики.

Название Частота Точность Поддерживаемые протоколы
DS1307 1 Гц, 4.096 кГц, 8.192 кГц, 32.768 кГц Зависит от кварца – обычно значение достигает 2,5 секунды в сутки, добиться точности выше 1 секунды в сутки невозможно. Также точность зависит от температуры. I2C
DS1302 32.768 кГц 5 секунд в сутки I2C, SPI
DS3231 Два выхода – первый на 32.768 кГц, второй – программируемый от 1 Гц до 8.192 кГц ±2 ppm при температурах от 0С до 40С.

±3,5 ppm при температурах от -40С до 85С.

Точность измерения температуры – ±3С

I2C

Модуль DS1307

DS1307 – это модуль, который используется для отсчета времени. Он собран на основе микросхемы DS1307ZN, питание поступает от литиевой батарейки для реализации автономной работы в течение длительного промежутка времени. Батарея на плате крепится на обратной стороне. На модуле имеется микросхема AT24C32 – это энергонезависимая память EEPROM на 32 Кбайт. Обе микросхемы связаны между собой шиной I2C. DS1307 обладает низким энергопотреблением и содержит часы и календарь по 2100 год.

Модуль обладает следующими параметрами:

  • Питание – 5В;
  • Диапазон рабочих температур от -40С до 85С;
  • 56 байт памяти;
  • Литиевая батарейка LIR2032;
  • Реализует 12-ти и 24-х часовые режимы;
  • Поддержка интерфейса I2C.

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

Взаимодействие с другими устройствами и обмен с ними информацией производится с помощью интерфейса I2C с контактов SCL и SDA. В схеме установлены резисторы, которые позволяют обеспечивать необходимый уровень сигнала. Также на плате имеется специальное место для крепления датчика температуры DS18B20.Контакты распределены в 2 группы, шаг 2,54 мм. В первой группе контактов находятся следующие выводы:

  • DS – вывод для датчика DS18B20;
  • SCL – линия тактирования;
  • SDA – линия данных;
  • VCC – 5В;

Во второй группе контактов находятся:

  • SQ – 1 МГц;
  • BAT – вход для литиевой батареи.

Для подключения к плате Ардуино нужны сама плата (в данном случае рассматривается Arduino Uno), модуль часов реального времени RTC DS1307, провода и USB кабель.

Чтобы подключить контроллер к Ардуино, используются 4 пина – VCC, земля, SCL, SDA.. VCC с часов подключается к 5В на Ардуино, земля с часов – к земле с Ардуино, SDA – А4, SCL – А5.

Для начала работы с модулем часов нужно установить библиотеки DS1307RTC, TimeLib и Wire. Можно использовать для работы и RTCLib.

Проверка RTC модуля

При запуске первого кода программа будет считывать данные с модуля раз в секунду. Сначала можно посмотреть, как поведет себя программа, если достать из модуля батарейку и заменить на другую, пока плата Ардуино не присоединена к компьютеру. Нужно подождать несколько секунд и вытащить батарею, в итоге часы перезагрузятся. Затем нужно выбрать пример в меню Examples→RTClib→ds1307. Важно правильно поставить скорость передачи на 57600 bps.

При открытии окна серийного монитора должны появиться следующие строки:

Будет показывать время 0:0:0. Это связано с тем, что в часах пропадает питание, и отсчет времени прекратится. По этой причине нельзя вытаскивать батарею во время работы модуля.

Чтобы провести настройку времени на модуле, нужно в скетче найти строку

RTC.adjust(DateTime(__DATE__, __TIME__));

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

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

Считывание времени. Как только модуль настроен, можно отправлять запросы на получение времени. Для этого используется функция now(), возвращающая объект DateTime, который содержит информацию о времени и дате. Существует ряд библиотек, которые используются для считывания времени. Например, RTC.year() и RTC.hour() – они отдельно получают информацию о годе и часе. При работе с ними может возникнуть проблема: например, запрос на вывод времени будет сделан в 1:19:59. Прежде чем показать время 1:20:00, часы выведут время 1:19:00, то есть, по сути, будет потеряна одна минута. Поэтому эти библиотеки целесообразно использовать в случаях, когда считывание происходит нечасто – раз в несколько дней. Существуют и другие функции для вызова времени, но если нужно уменьшить или избежать погрешностей, лучше использовать now() и из нее уже вытаскивать необходимые показания.

Пример проекта с i2C модулем часов и дисплеем

Проект представляет собой обычные часы, на индикатор будет выведено точное время, а двоеточие между цифрами будет мигать с интервалом раз в одну секунду. Для реализации проекта потребуются плата Arduino Uno, цифровой индикатор, часы реального времени (в данном случае вышеописанный модуль ds1307), шилд для подключения (в данном случае используется Troyka Shield), батарейка для часов и провода.

В проекте используется простой четырехразрядный индикатор на микросхеме TM1637. Устройство обладает двухпроводным интерфейсом и обеспечивает 8 уровней яркости монитора. Используется только для показа времени в формате часы:минуты. Индикатор прост в использовании и легко подключается. Его выгодно применять для проектов, когда не требуется поминутная или почасовая проверка данных. Для получения более полной информации о времени и дате используются жидкокристаллические мониторы.

Модуль часов подключается к контактам SCL/SDA, которые относятся к шине I2C. Также нужно подключить землю и питание. К Ардуино подключается так же, как описан выше: SDA – A4, SCL – A5, земля с модуля к земле с Ардуино, VCC -5V.

Индикатор подключается просто – выводы с него CLK и DIO подключаются к любым цифровым пинам на плате.

Скетч. Для написания кода используется функция setup, которая позволяет инициализировать часы и индикатор, записать время компиляции. Вывод времени на экран будет выполнен с помощью loop.

#include #include "TM1637.h" #include "DS1307.h" //нужно включить все необходимые библиотеки для работы с часами и дисплеем. char compileTime = __TIME__; //время компиляции. #define DISPLAY_CLK_PIN 10 #define DISPLAY_DIO_PIN 11 //номера с выходов Ардуино, к которым присоединяется экран; void setup() { display.set(); display.init(); //подключение и настройка экрана. clock.begin(); //включение часов. byte hour = getInt(compileTime, 0); byte minute = getInt(compileTime, 2); byte second = getInt(compileTime, 4); //получение времени. clock.fillByHMS(hour, minute, second); //подготовка для записывания в модуль времени. clock.setTime(); //происходит запись полученной информации во внутреннюю память, начало считывания времени. } void loop() { int8_t timeDisp; //отображение на каждом из четырех разрядов. clock.getTime();//запрос на получение времени. timeDisp = clock.hour / 10; timeDisp = clock.hour % 10; timeDisp = clock.minute / 10; timeDisp = clock.minute % 10; //различные операции для получения десятков, единиц часов, минут и так далее. display.display(timeDisp); //вывод времени на индикатор display.point(clock.second % 2 ? POINT_ON: POINT_OFF);//включение и выключение двоеточия через секунду. } char getInt(const char* string, int startIndex) { return int(string - "0") * 10 + int(string) - "0"; //действия для корректной записи времени в двухзначное целое число. В ином случае на экране будет отображена просто пара символов. }

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

Программу можно немного модернизировать. При отключении питания выше написанный скетч приведет к тому, что после включения на дисплее будет указано время, которое было установлено при компиляции. В функции setup каждый раз будет рассчитываться время, которое прошло с 00:00:00 до начала компиляции. Этот хэш будет сравниваться с тем, что хранятся в EEPROM, которые сохраняются при отключении питания.

Для записи и чтения времени в энергонезависимую память или из нее нужно добавить функции EEPROMWriteInt и EEPROMReadInt. Они нужны для проверки совпадения/несовпадения хэша с хэшем, записанным в EEPROM.

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

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

Алгоритм работы следующий:

  • Подключение всех компонентов;
  • Проверка – на экране монитора должны меняться ежесекундно время и дата. Если на экране указано неправильное время, нужно добавить в скетч функцию RTC.write (tmElements_t tm). Проблемы с неправильно указанным временем связаны с тем, что модуль часов сбрасывает дату и время на 00:00:00 01/01/2000 при выключении.
  • Функция write позволяет получить дату и время с компьютера, после чего на экране будут указаны верные параметры.

Заключение

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

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

Update 17.10.2015
Вначале это была серия статей, целью которых было рассказать про создание устройства с нуля до состояния готовности, но внезапно у меня появилась аллергия на все что называется «часы», поэтому я слил все в одну статью. Устройство закончено на 99.9%, (осталось закрутить винты), но сделать это ой как не просто 🙂 Как только аллергия пройдет появится окончательная фотка.

Начнем с того, что пока нам ничего не известно про ds1307 кроме того, что с ее помощью делают часы. Поэтому качаем документацию, на эту микросхему и читаем список «вкусностей», которыми она обладает. Итак, из первого абзаца в целом понятно, что она обладает низким энергопотреблением, информация передается по I2C, можно узнать дату и время, 12 и 24 часовой формат, автоматическая подстройка даты. Но самое интересное это схема (TYPICAL OPERATING CIRCUIT).

Курим даташит и пытаемся разобраться что к чему. Идем слева направо, CPU — микроконтроллер (то есть наша atmega), два резистора, написано pull up — значит подтягивающие (можно взять по 10к), кварц на 32768Гц, сама микросхема и батарейка. Выход SQW/OUT может дрыгаться с частотой 1Hz, 4kHz, 8kHz, 32kHz, пока нам это не интересно. Пожалуй, этой информации пока достаточно, хочется уже чего нибудь накодить 🙂

Создаем проект в CodeVision, в разделе I2C находим ds1307 и включаем его в проект. Хорошо бы еще выводить куда нибудь информацию, например на LCD и пара кнопок не помешает.

Все что нужно это LCD настроить на порт D и три кнопки с подтяжкой на вход. Далее нужно вывести на LCD время, для этого заглянем в мануал CodeVision и возьмем оттуда пример. Оказывается все просто — есть функция устанавливающая время:
rtc_set_time(3,0,0); //установить 03:00:00

т.е. после вызова данной функции в переменных h, m, s будут находиться часы(h), минуты(m) и секунды(s). Осталось вывести их на экран. Уж это мы умеем делать)
Итоговый код будет выглядеть так:

#include #include #include #include // Alphanumeric LCD Module functions #asm .equ __lcd_port= 0x12 ; PORTD #endasm #include char lcd_buf[ 33 ] ; void main(void ) { char hour, min, sek; PORTC= 0x07 ; DDRC= 0x00 ; // I2C Bus initialization i2c_init() ; rtc_init(0 , 0 , 0 ) ; // LCD module initialization lcd_init(16 ) ; rtc_set_time(3 , 0 , 0 ) ; while (1 ) { rtc_get_time(& hour,& min,& sek) ; lcd_clear() ; lcd_gotoxy(0 , 0 ) ; sprintf (lcd_buf, "%2d:%02d:%02d\n " , hour, min, sek) ; lcd_puts(lcd_buf) ; delay_ms(500 ) ; } ; }

#include #include #include // Alphanumeric LCD Module functions #asm .equ __lcd_port=0x12 ;PORTD #endasm #include char lcd_buf; void main(void) { char hour,min,sek; PORTC=0x07; DDRC=0x00; // I2C Bus initialization i2c_init(); // DS1307 Real Time Clock initialization rtc_init(0,0,0); // LCD module initialization lcd_init(16); rtc_set_time(3,0,0); while (1) { rtc_get_time(&hour,&min,&sek); lcd_clear(); lcd_gotoxy(0,0); sprintf(lcd_buf,"%2d:%02d:%02d\n",hour,min,sek); lcd_puts(lcd_buf); delay_ms(500); }; }

Собираем и тестируем в протеусе:

Схема и прошивка

Продолжим модернизировать нашу прошивку. Начнем со следующей задумки: у DS1307 есть выход SQW/OUT, который может генерировать несколько частот. Если настроить этот выход на 1Гц, и подать этот сигнал на вход внешнего прерывания, то получится, что раз в секунду 1307 будет дергать «за хвост» нашу atmega8. Для меги это будет сигналом к тому, что пора обновлять время. Это позволит не нагружать микроконтроллер постоянным обновлением времени, информация о текущем времени будет обновляться ровно раз в секунду.

Добавим в проект внешнее прерывание по низкому уровню (low level) на ножке Int1 и включим подтяжку. Выход DS1307 настроим на частоту 1Гц. Кстати, читать мануалы полезно, нашел интересную особенность — подтягивающие резисторы на ножках SCL, SDA должны быть 3,3k — 4,7k. Учтем это.

Получившийся код будет выглядеть так:

interrupt [ EXT_INT1] void ext_int1_isr(void ) { time_flag= 1 ; }

interrupt void ext_int1_isr(void) { time_flag=1; }

В прерывании выставляем флаг, разрешающий вывод времени, в основном цикле, если флаг выставлен, то показываем время, если не выставлен ничего не делаем.

if (time_flag== 1 ) { rtc_get_time(& hour,& min,& sek) ; lcd_gotoxy(0 , 0 ) ; sprintf (lcd_buf, "%02d:%02d:%02d\n " , hour, min, sek) ; lcd_puts(lcd_buf) ; }

if(time_flag==1) { rtc_get_time(&hour,&min,&sek); lcd_gotoxy(0,0); sprintf(lcd_buf,"%02d:%02d:%02d\n",hour,min,sek); lcd_puts(lcd_buf); }

Теперь перейдем к следующему вопросу, на сколько эффективно использовать sprintf? Чтобы не разводить пустых разговоров, приведу 2 куска кода, которые выполняют одно и тоже — выводят информацию о времени на дисплей.

Первый вариант, уже нам известный:

sprintf (lcd_buf, "%02d:%02d:%02d\n " , hour, min, sek) ; lcd_puts(lcd_buf) ;

sprintf(lcd_buf,"%02d:%02d:%02d\n",hour,min,sek); lcd_puts(lcd_buf);

Согласитесь просто в использовании и наглядно. Теперь вариант номер 2:

lcd_putchar(hour/ 10 + 0x30 ) ; lcd_putchar(hour% 10 + 0x30 ) ; lcd_putchar(":" ) ; lcd_putchar(min/ 10 + 0x30 ) ; lcd_putchar(min% 10 + 0x30 ) ; lcd_putchar(":" ) ; lcd_putchar(sek/ 10 + 0x30 ) ; lcd_putchar(sek% 10 + 0x30 ) ;

lcd_putchar(hour/10+0x30); lcd_putchar(hour%10+0x30); lcd_putchar(":"); lcd_putchar(min/10+0x30); lcd_putchar(min%10+0x30); lcd_putchar(":"); lcd_putchar(sek/10+0x30); lcd_putchar(sek%10+0x30);

Не очень наглядно, но разобраться можно. Как мы их будем сравнивать? Делается это очень просто — запускаем отладчик AVR STUDIO и смотрим количество тактов затраченных на их выполнение. Итак, «барабанная дробь», результаты… Первый кусок кода выполнялся 16 466 тактов, что равносильно 2 058,25 мкс, при рабочей частоте в 8МГц, для второго куска кода эта цифра составила 12 278 тактов или 1 534,75 мкс. Согласитесь, снизить время выполнения, а значит и разгрузить микроконтроллер на ~25% достаточно весомая причина, чтобы не использовать sprintf. Выкидываем sprintf из нашего проекта, в след за ним можно выкинуть stdio.h и lcd_buf.

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

while (1 ) { if (time_flag== 1 ) { show_time() ; //показать информацию о текущем времени } } ;

while (1) { if(time_flag==1) { show_time(); //показать информацию о текущем времени } };

Объявление самой функции будет выглядеть так:

void show_time() { rtc_get_time(& hour,& min,& sek) ; lcd_gotoxy(0 , 0 ) ; lcd_putchar(hour/ 10 + 0x30 ) ; lcd_putchar(hour% 10 + 0x30 ) ; lcd_putchar(":" ) ; lcd_putchar(min/ 10 + 0x30 ) ; lcd_putchar(min% 10 + 0x30 ) ; lcd_putchar(":" ) ; lcd_putchar(sek/ 10 + 0x30 ) ; lcd_putchar(sek% 10 + 0x30 ) ; time_flag= 0 ; }

void show_time() { rtc_get_time(&hour,&min,&sek); lcd_gotoxy(0,0); lcd_putchar(hour/10+0x30); lcd_putchar(hour%10+0x30); lcd_putchar(":"); lcd_putchar(min/10+0x30); lcd_putchar(min%10+0x30); lcd_putchar(":"); lcd_putchar(sek/10+0x30); lcd_putchar(sek%10+0x30); time_flag=0; }

Теперь в нашу прошивку, нужно добавить вывод даты. Установка даты, производится следующей функцией:

rtc_set_date(6,13,10,13); //6- день недели, 13 - день, 10 - месяц, 13 - год

rtc_get_date(&week_day,&day,&month,&year); //день недели, день, месяц, год

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

#include // I2C Bus functions #asm .equ __i2c_port= 0x18 ; PORTB .equ __sda_bit= 0 .equ __scl_bit= 1 #endasm #include // DS1307 Real Time Clock functions #include // Alphanumeric LCD functions #include char hour= 0 , min= 0 , sek= 0 , day= 0 , month= 0 , year= 0 , week_day= 0 ; bit time_flag= 0 ; char menu= 0 ; // External Interrupt 1 service routine interrupt [ EXT_INT1] void ext_int1_isr(void ) { time_flag= 1 ; } void show_time() { rtc_get_time(& hour,& min,& sek) ; rtc_get_date(& week_day,& day,& month,& year) ; lcd_gotoxy(0 , 0 ) ; lcd_putchar(hour/ 10 + 0x30 ) ; lcd_putchar(hour% 10 + 0x30 ) ; lcd_putchar(":" ) ; lcd_putchar(min/ 10 + 0x30 ) ; lcd_putchar(min% 10 + 0x30 ) ; lcd_putchar(":" ) ; lcd_putchar(sek/ 10 + 0x30 ) ; lcd_putchar(sek% 10 + 0x30 ) ; lcd_gotoxy(0 , 1 ) ; lcd_putchar(day/ 10 + 0x30 ) ; lcd_putchar(day% 10 + 0x30 ) ; lcd_putchar("/" ) ; lcd_putchar(month/ 10 + 0x30 ) ; lcd_putchar(month% 10 + 0x30 ) ; lcd_putchar("/" ) ; lcd_putchar(year/ 10 + 0x30 ) ; lcd_putchar(year% 10 + 0x30 ) ; time_flag= 0 ; } void main(void ) { PORTC= 0x0F ; DDRC= 0x00 ; PORTD= 0x08 ; DDRD= 0x00 ; // I2C Bus initialization i2c_init() ; // DS1307 Real Time Clock initialization // Square wave output on pin SQW/OUT: On // Square wave frequency: 1Hz rtc_init(0 , 1 , 0 ) ; // External Interrupt(s) initialization // INT0: Off // INT1: On // INT1 Mode: Low level GICR|= 0x80 ; MCUCR= 0x00 ; GIFR= 0x80 ; // LCD module initialization lcd_init(16 ) ; rtc_set_time(12 , 0 , 0 ) ; rtc_set_date(6 , 13 , 10 , 13 ) ; #asm("sei") while (1 ) { if (time_flag== 1 ) { show_time() ; } } ; }

#include // I2C Bus functions #asm .equ __i2c_port=0x18 ;PORTB .equ __sda_bit=0 .equ __scl_bit=1 #endasm #include // DS1307 Real Time Clock functions #include // Alphanumeric LCD functions #include char hour=0,min=0,sek=0,day=0,month=0,year=0,week_day=0; bit time_flag=0; char menu=0; // External Interrupt 1 service routine interrupt void ext_int1_isr(void) { time_flag=1; } void show_time() { rtc_get_time(&hour,&min,&sek); rtc_get_date(&week_day,&day,&month,&year); lcd_gotoxy(0,0); lcd_putchar(hour/10+0x30); lcd_putchar(hour%10+0x30); lcd_putchar(":"); lcd_putchar(min/10+0x30); lcd_putchar(min%10+0x30); lcd_putchar(":"); lcd_putchar(sek/10+0x30); lcd_putchar(sek%10+0x30); lcd_gotoxy(0,1); lcd_putchar(day/10+0x30); lcd_putchar(day%10+0x30); lcd_putchar("/"); lcd_putchar(month/10+0x30); lcd_putchar(month%10+0x30); lcd_putchar("/"); lcd_putchar(year/10+0x30); lcd_putchar(year%10+0x30); time_flag=0; } void main(void) { PORTC=0x0F; DDRC=0x00; PORTD=0x08; DDRD=0x00; // I2C Bus initialization i2c_init(); // DS1307 Real Time Clock initialization // Square wave output on pin SQW/OUT: On // Square wave frequency: 1Hz rtc_init(0,1,0); // External Interrupt(s) initialization // INT0: Off // INT1: On // INT1 Mode: Low level GICR|=0x80; MCUCR=0x00; GIFR=0x80; // LCD module initialization lcd_init(16); rtc_set_time(12,0,0); rtc_set_date(6,13,10,13); #asm("sei") while (1) { if(time_flag==1) { show_time(); } }; }

Результат:

Схема и прошивка:

Перейдем к организации меню. Самый главный вопрос состоял в том, как оно все должно выглядеть, т.е. нужно было сформулировать техническое задание(т.з.).
Мне хотелось, чтобы это были отдельные экраны:
-главный экран;
-экран настройки времени;
-экран настройки даты;
-экран настройки будильника.

При этом обойтись четырьмя кнопками — вверх, вниз, влево, вправо. Переход между экранами должен осуществляться кнопками вверх и вниз. Настройка производится на соответствующем экране. Применяемый микроконтроллер atmega8. Вот такое примитивное т.з.

То что размер кода будет достаточно большой было понятно изначально. При этом нужно было его разбить на логически связанные части. С частями все понятно — обработка одного экрана одна часть кода. Поэтому начал с того, что основной цикл разбил на четыре части, переключения между которыми производится оператором switch. Внутрь засунул функции — пустышки. Кнопки 0(вверх) и 3(вниз) порта C позволяют изменить переменную menu. Таким образом мы скачем между менюшками. Но пока такая прошивка еще работать не могла, ибо функции еще не определены.

while (1 ) { switch (menu) { case 0 : show_time() ; break ; case 1 : set_time() ; break ; case 2 : set_date() ; break ; case 3 : set_alarm() ; break ; } } ;

while (1) { switch(menu) { case 0: show_time(); break; case 1: set_time(); break; case 2: set_date(); break; case 3: set_alarm(); break; } };

Следующий шаг определение этих функций, изначально я нарисовал статичные названия, вроде lcd_puts(«Set time»); функции получились, такими.

void set_alarm() { ////////просмотр настроек будильника lcd_gotoxy(0,0); lcd_puts("Set alarm"); } void set_time() { ////////просмотр настроек времени lcd_gotoxy(0,0); lcd_puts("Set time"); }

Теперь это уже была рабочая прошивка, в которой можно было переключаться между менюшками и смотреть статичные надписи. Настало время оживить эти надписи. С главным экраном проблем не было, вывод времени/даты аналогичен предыдущему уроку.

Встал следующий вопрос: как организовать сам процесс настройки? Поразмыслив, мне показалась интересной следующая идея: переходим кнопками вверх/вниз в интересующее нас меню, нажимаем вправо, появляется курсор, говорящий нам о том, что идет процесс настройки. Кнопками вверх/вниз мы изменяем величину, влево/вправо скачем курсором между настраиваемыми параметрами. Когда курсор находится под последним параметром, повторное нажатие кнопки вправо позволяет выйти из настройки. Курсор при этом скрывается, что говорит о том что мы вышли из настройки и можем снова переключаться между менюшками.

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

void set_alarm() //Функция обработки будильника { //режим отображения меню настроек будильника if(sub_alarm==0) { if(PINC.0==0) //кнопка вверх - смена экрана меню { menu=0; ..... } } //подменю настройки будильника if(sub_alarm==1) { if(PINC.0==0) //кнопка вверх - увеличить величину { a_hour++; .... } }

Есть еще такой момент, когда зашли в подменю настройки одна и таже кнопка (возьмем опять в качестве примера кнопку вверх), может менять часы, а может минуты. Поэтому была введена еще одна переменная subProgram.
Например:

if(PINC.0==0) //кнопка вверх { if(subProgram==1) //subProgram=1 - изменяем часы { a_hour++; ... } if(subProgram==2) //subProgram=2 - изменяем минуты { a_min++; ... } if(subProgram==3) //subProgram=3 изменяем флаг будильника { ... } }

Единственное о чем стоит упомянуть это спецсимвол, который выводится на главном экране, когда будильник включен.

Пример взят из примеров в папке CodeVision\examples\lcd char.

typedef unsigned char byte; //переопределяем тип flash byte char_table[ 8 ] = { //рисуем свой символ 0b10000000 , 0b10000100 , 0b10001110 , 0b10001110 , 0b10001110 , 0b10011111 , 0b10100100 , 0b11000000 } ; // function used to define user characters void define_char(byte flash * pc, byte char_code) { byte i, address; address= (char_code<< 3 ) | 0x40 ; for (i= 0 ; i< 8 ; i++ ) lcd_write_byte(address++,* pc++ ) ; } void main(void ) { byte i, address; lcd_init(16 ) ; define_char(char_table, 0 ) ; //Грузим символ в лсд while (1 ) { lcd_putchar(0 ) ; //выводим символ на дисплей }

typedef unsigned char byte; //переопределяем тип flash byte char_table={ //рисуем свой символ 0b10000000, 0b10000100, 0b10001110, 0b10001110, 0b10001110, 0b10011111, 0b10100100, 0b11000000}; // function used to define user characters void define_char(byte flash *pc,byte char_code) { byte i,address; address=(char_code<<3)|0x40; for (i=0; i<8; i++) lcd_write_byte(address++,*pc++); } void main(void) { byte i,address; lcd_init(16); define_char(char_table,0); //Грузим символ в лсд while(1) { lcd_putchar(0); //выводим символ на дисплей }

Рисовать можно символ 5х7, единичка — пиксел закрашен, ноль — не закрашен. Получился символ колокольчика.

Прошивка

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

Начнем с печатной платы, для этого требуется программа, которая позволяет рисовать печатки. Существует множество подобных программ: P-cad, Altium, Sprint layout… Мне нравится Альтиум, только потому, что для него куча готовых библиотек с элементами, ибо тратить время на набивку собственной библиотеки элементов, на мой взгляд не дело. Общий смысл всех подобных программ одинаков — сначала рисуется электрическая схема.

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

Остается только удобно расположить элементы и соединить их проводниками.

Обновлено 23.07.2018. Всем привет. Для работы с часами, в прошлой статье был рассмотрен интерфейс TWI, на который мы сегодня будем ссылаться. Ну что ж начнем. Данные часы являются TWI совместимыми, т.е. принцип обмена данными по шине будет таким же как мы и рассматривали.

На рисунке ниже представлено расположение выводов, описание, и сам вид наших часов или как далее будем их называть RTC (Real-time clock) — часы реального времени или генератор импульсов времени. Данный “девайс” DS1307 считает секунды, минуты, часы, день месяца, месяц, день недели и год вместе с високосными. Календарь действителен до 2100 года. Я думаю на наш век хватит:).

Как видно из описания имеется вход для аварийного питания от батареи, при отключенном внешнем питание. В этом режиме RTC поддерживает только свое основное назначение – отсчет времени, без внешних запросов. Напряжение питания батареи должно быть 2 – 3.5V. В техническом описание пишется что при заряде более 48 мА/ч, при температуре 25 град Цельсия, наша схема продержится около 10 лет. Более чем надо. На рисунке ниже представлена “таблеточка” CR2032 и крепление, которые будем использовать.

Теперь пройдемся по внешнему питанию. Рабочее напряжение часов 5В с небольшим диапазоном 4,5 -5,5В. Напряжение от батареи 3В(минимум 2, максимум 3,5В) Работа RTC делится на три режима по напряжению:

1. Vcc=5В – чтение, запись, отсчет;
2. Vcc= ниже 1,25*Vbat , но выше Vbat +0.2V — только отсчет батареи от внешнего питания.
3. Vcc ниже Vbat: RTC и ОЗУ переходит на питание от батареи. Потребление в активном состоянии 1,5 мА, от батареи 500-800нА.
Напряжение для передачи/приема информации:
Логический 0: -0.5В — +0.8В
Логическая 1: 2.2 В – Vcc+0.3В

Как и в прошлых постах попробуем запустить в Proteus. Отладим код. И перенесем все в железо. Ниже приведена схема подключения.

Где SQW/OUT – это вывод часов который можно запрограммировать на вывод частоты 1Гц, 4.096Гц, 8.192Гц и 32,768Гц. Т.е. можно использовать для внешнего прерывания контроллера с периодичностью в 1 с. Очень полезная функция. Но нам не пригодится. Кстати он тоже с открытым коллектором, поэтому необходим подтягивающий резистор. Номинал 4,7 кОм.

Выводы Х1 и Х2 – к ним подключаем кварцевый резонатор с частотой 32,768 кГц. Либо можно применить внешний тактовый генератор с той же частотой. Но при этом вывод X1 подключается к сигналу, а X2 остаётся неподключенным (висеть в воздухе.).

Ну и выводы SDA и SCL, с которыми мы познакомились в прошлой статье.

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

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

3. Контур в виде защитного кольца необходимо поместить вокруг кристалла, что помогает изолировать кристалл от шума.

4. Проводники поместить в кольцо и и подключить к заземлению.

5. Припаиваем резонатор к земле. Если земля разведена верно и есть уверенность.

На рисунке ниже видно контур и место припая к земле.

Как подключать разобрались. Идем далее – разберемся как с ним работать. RTC является программируемым и имеет 8 байт специальных регистров для его конфигурации и энергонезависимую статическую память 56 байтов. Для обмена информации необходима 2-х проводная шина данных, т.е. последовательная шина данных- который мы рассмотрели в прошлой статье. Итак для работы пробежимся по даташиту. Что нам необходимо:

Таблица регистров. Рисунок ниже. Первые восемь регистров – для вывода и программирования наших часов. При обращении по адресу 00H к 7-му биту(CH) и установкой его в 0 –запускаем часы. Хочется отметить, что конфигурация регистров может быть любая, поэтому при первом запуске необходимо его настроить под свои требования. Остальные семь битов единицы и десятки секунд.

01H – Минуты.
02H – Часы, которые настраиваются:
— Бит 6 – при 1 вывод 12 часовой формат, 0 – 24.
— Бит 5 – при 1 (при 12 часовом формате) PM , 0-AM
— Бит 5 – (при 24 ч формате) это вывод второго десятка часов (20-23часа.)
— Бит4 – первый десяток часов, остальные биты это единицы часов.
03H – день недели;
04H – дата;
05H – месяц года
06H – год.

Ну и последний регистр 07H. Данный регистр является управляющим.Где OUT отвечает за управление выводом SQW/OUT. Ниже таблица включения вывода.

OUT
SQWE
SQW/OUT
1
0
1
0
0
0

SQWE – при установке этого бита в,1 на вывода выходят импульсы с заданной частотой,которая устанавливается,битами RS1 и RS0.

Этот вывод нам не пригодится в проекте. Хотя для него я развел на плате дорожку. В качестве экспериментов может быть где то в будущем и применим, ведь здесь можно сделать прерывании в 1 с.

Теперь имея всю необходимую информацию, напишем функции для работы с часами. А также запустим проект в Proteus . Который будет иметь следующий вид:

Обратите внимание, что резонатор в Proteus, можно и не подключать к часам(обведенное красным).

На рисунке выведен терминал часов, который отображает время, которое в свою очередь привязано к системному времени. Терминал отладчика протокола I2C или TWI, на котором отображается время отправки и приема сигнала, где D0 – передаваемая команда, D1 - прием. Ниже я буду выводить скриншоты терминала с результатом работы программы.

Программа. Рассмотрев основные настройки часов напишем функцию инициализации.

/*Функция инициализации включает в себя установку скорости обмена данных по формуле(в предыдущей статье), установка пред делителя и включение модуля TWI*/
void init_DS1307 (void)
{
TWBR = 2; /*При частоте 1 МГц */
TWSR = (0 << TWPS1)|(0 << TWPS0); /*Пред делитель на 64*/
TWCR |= (1 << TWEN); /*Включение модуля TWI*/
}

void write_DS1307 (uint8_t reg, uint8_t time) /*передаем два параметра: адрес регистра, к которому будем обращаться и передаваемую информацию*/
{
/* Формируем состояние СТАРТ, выставляя разряды регистра управления*/
TWCR = (1<
/*Разрешить работу модуля TWEN; Сформировать состояние старт TWSTA; Сбросить флаг TWINT */
/*Ждем окончания формирования условия старт, т.е. пока не установится флаг, код статуса = 08*/
while (!(TWCR & (1<
/*Далее перелаем пакет адреса (адрес устройства). Содержимое пакета загружается в регистр TWDR*/
TWDR = 0xd0; /*0b1101000 + 0 – адрес + бит записи*/
/*Сбрасываем флаг для передачи информации*/
TWCR = (1<
/*Ждем установки флага*/
while (!(TWCR & (1<
/*передаем регистр к которому будем обращаться*/
TWDR = reg;
TWCR = (1<
while (!(TWCR & (1<
/*Передаем информацию для записи в байт регистра*/
TWDR = time;
TWCR = (1<
while (!(TWCR & (1<
/*формируем состояние СТОП*/
TWCR = (1<
}

В этой функции мы передали три байта, адрес устройства, адрес регистра и байт информации для записи в этот регистр и сформировали состояние СТОП.

Осталась последняя функция чтения. Ниже формат чтения.

В данной функции выполняется передача байта адреса устройства +бит записи, байт адреса регистра для установки на него указатель, выполнение условия ПОВСТАР, передача байта адреса устройства +бит чтения, чтение регистра, адрес которого мы передали ранее.

Если мы будем обращаться к часам в формате чтения, то при повторном обращении к часам указатель сдвигается на один байт вниз включая 56 байт ОЗУ, от 00H до 3FH. При достижении последнего адреса, указатель переходит на адрес 00.

/*Функция чтения данных из DS1307*/
uint8_t read_DS1307 (uint8_t reg) /*Передаем адрес регистра*/
{
uint8_t time;
/*формируем состояние СТАРТ*/
TWCR = (1<
while (!(TWCR & (1<
TWDR = 0xd0; /*Передаем адрес + бит записи*/
TWCR = (1<
while (!(TWCR & (1<
TWDR = reg; /*Адрес регистра*/
TWCR = (1<
while (!(TWCR & (1<
/*формируем состояние ПОВСТАР*/
TWCR = (1<
while (!(TWCR & (1<
TWDR = 0xd1; /*Передаем адрес + бит чтения*/
TWCR = (1<
while (!(TWCR & (1<
/*считываем данные*/
TWCR = (1<
while (!(TWCR & (1<
time = TWDR;
time = (((time & 0xF0) >> 4)*10)+(time & 0x0F);
/*формируем состояние СТОП*/
TWCR = (1<
return time;
}

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

#include
#include
uint8_t time;
void init_DS1307 (void);
uint8_t read_DS1307 (uint8_t reg);
void write_DS1307 (uint8_t reg, uint8_t time);
int main (void)
{
DDRC = 0×00; /*Выставляем порт как вход*/
PORTC = 0xFF; /*Подтягиваем сопротивление*/
init_DS1307;
while (1)
{
_delay_ms (50);
read_DS1307 (0×04); /*Чтение регистра даты*/
}
}

Ниже результат выполнения программы чтение даты.

В окне отладчика I2C (TWI ) видно что сначала посылается адрес регистра в RTC (зеленый кружочек), в данном случае 04, который отвечает за дату месяца, и далее часы передают ответ 21 (красный кружочек).

Когда мы запустим часы в железе, нам необходимо будет занести настоящее время. Ниже пример программы изменения минут.

while (1)
{
_delay_ms (500);
read_DS1307 (0×01); /*Считываем минуту*/
_delay_ms (500);
write_DS1307 (0×01, 15); /*Записываем необходимую минуту*/
_delay_ms (500);
read_DS1307 (0×01); /*Считываем минуту*/
}

На рисунке видно, что сначала идет обращение к регистру 01, считывается минута 23. Далее мы используем функцию записи, и вносим значение 15. При следующей функции чтения у нас на табло часов значение 15.

Ну и последний пример программы это вывод значений всех регистров

while (1)
{
delay_ms (500);
read_DS1307 (0×00);
_delay_ms (500);
read_DS1307 (0×01);
_delay_ms (500);
read_DS1307 (0×02);
_delay_ms (500);
read_DS1307 (0×03);
_delay_ms (500);
read_DS1307 (0×04);
_delay_ms (500);
read_DS1307 (0×05);
_delay_ms (500);
read_DS1307 (0×06);
_delay_ms (500);
}

На рисунке ниже видно, что вывелись данные 7-ми регистров.

Исходник с проектом прилагается:

(Скачали: 601 чел.)

На этом все. В следующей статьеподключим часы в железе, выведем время на индикатор и познакомимся с двоично-десятичным форматом для работы с часами. Всем пока.

В статье вы познакомитесь с отличным модулем часов реального времени на батарейке.

С помощью этого модуля можно отслеживать время в ваших проектах на Arduino даже в случае перепрограммирования или отключения питания. Это один из необходимых элементов для проектов будильников, сигнализаций, снятия показаний с датчиков в режиме реального времени. Одна из самых популярных моделей модуля часов реального времени - DS1307. Именно на нем мы и остановимся. Модуль отлично сочетается с микроконтроллерами Arduino, на которых питание логики равно 5 В.

Особенности модуля от компании-производителя Adafruit (китайцы предлагают аналогичные варианты раза в три-четыре дешевле):

  • Все включено: чип, обвязка, батарейка;
  • Легко собирается и прост в использовании;
  • Устанавливается на любую макетную плату или подключается напрямую с помощью проводов;
  • Есть отличные библиотеки и скетчи-примеры;
  • Два отверстия для монтажа;
  • Продолжительность работы - около пяти лет!

Модуль часов реального времени может быть уже распаянным, а может продаваться в виде отдельных комплектующих, пайка которых займет около 15-ти минут, не более.

Что такое часы реального времени?

Часы реально времени - это... часы. Модуль работает от автономного питания - батарейки и продолжает вести отсчет времени, даже если на вашем проекте на Arduino пропало питание. Используя модуль реального времени, вы можете отслеживать время, даже если вы захотите внести изменения в ваш скетч и перепрограммировать микроконтроллер.

На большинстве микроконтроллеров, в том числе и Arduino, есть встроенный счетчик временни, который называется millis(). Есть и встроенные в чип таймеры, которые могут отслеживать более длительные промежутки времени (минуты или дни). Так зачем же вам отдельным модуль часов? Основная проблема в том, что millis() отслеживает время только с момента подачи питания на Arduino. То есть, как только вы отключили плату, таймер сбрасывается в 0. Вша Arduino не знает, что сейчас, например, четверг или 8-е марта. Все, чего вы можете добиться от встроенного счетчика - это "Прошло 14000 миллисекунд с момента последнего включения".

Например вы создали программу и хотите вести отсчет времени с этого момента. Если вы отключите питание микроконтроллера, счетчик времени собьется. Примерно так, как это происходит с дешевыми китайскими часами: когда садится батарейка, они начинают мигать с показанием 12:00.

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

На рисунке ниже показана материнская плата компьютера с часами реального времени DS1387. В часах используется литиевая батарея, поэтому они разрослись в размерах.

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

Пока батарейка в самом модуле не исчерпает свой заряд, DS1307 будет вести отсчет времени, даже если Arduino отключен от питания или перепрограммируется.

Узлы, из которых состоит модуль часов реального времени

Детали модуля часов реального времени DS1307 от компании Adafruit
Рисунок Обозначение Описание Производитель Количество
IC2 Чип часов реального времени DS1307 1
Q1 32.768 КГц, 12.5 пФ кристалл Generic 1
R1, R2 1/4 Вт 5% 2.2 КОм резистор Красный, Красный, Красный, Золотой Generic 2
C1 0.1 мкФ керамический конденсатор Generic 1
Рельса на 5 контактов (1x5) Generic 1
Батарейка 12 мм 3 В литиевая батарейка CR1220 1
12mm coin cell holder Keystone 3001 1
Плата Adafruit Industries 1

Сборка модуля часов реального времени

Сборка часов реального времени DS1307 компании Adafruit
Фото Пояснения

Подготовьтесь к сборке. Проверьте наличие всех необходимых деталей и инструментов. Установите монтажную плату в тисках.

Нанесите немного припоя на отрицательный контакт батареи.

Установите два резистора 2.2 КОм и керамический конденсатор. Как именно вы их расположите - неважно. Полярность не имеет значения. После этого установите кристалл (также симметрично), держатель (холдер) для батарейки и чип часов реального времени. Чип модуля реального времени надо установить таким образом, чтобы отметка (паз) на чипе располагалась в соответствии с обозначением на монтажной плате. Внимательно посмотрите на фото слева, там чип установлен верно.


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

Удалите остатки контактов от резисторов, кристалла и конденсатора.

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

Установите батарейку. Плоская часть батареи должна быть сверху. В среднем батарейка будет служить около 5 лет. Даже если батарейка села, не оставляйте слот для нее пустым.

Библиотека Arduino для работы с DS1307

DS1307 легко подключается к любому микроконтроллеру с питанием логики 5 В и возможностью I2C подключения. Мы рассмотрим подключение и использование этого модуля с Arduino .

Будем использовать библиотеку RTClib для получения и настройки показаний с DS1307. Если у вас есть вопросы по учтановке дополнительных библиотек Arduino - ознакомьтесь с этой инструкцией .

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

  • КУПИТЬ Arduino Uno R3 ;
  • КУПИТЬ Breadboard ;
  • КУПИТЬ модуль часов реального времени DS1307 ;

На часах реального премени 5 пинов: 5V, GND, SCL, SDA и SQW.

  • 5V используется для питания чипа модуля часов реального времени, когда вы делаете к нему запрос для получения данных о времени. Если сигнал 5 В не поступает, чип переходит в "спящий" режим.
  • GND - общая земля. Обязательно подключается в схему.
  • SCL - контакт i2c часов - необходим для обмена данными с часами реального времени.
  • SDA - контакт, по которому через i2c передаются данные с часов реального времени.
  • SQW дает возможность настроить вывод данных в виде square-wave. В большинстве случаев этот контакт не используется.

Если вы настроили аналоговый пин 3 (цифровой 17) в режим OUTPUT и HIGH, а аналоговый пин 2 (цифровой 16) в режим OUTPUT и LOW, вы можете запитывать часы реального времени непосредственно от этих контактов!

Подключите аналоговый пин 4 на Arduino к SDA. Аналоговый пин 5 на Arduino подключите к SCL.


Скетч для Arduino

Проверка часов реального времени

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

Для начала давайте посмотрим, что произойдет, если мы извлечем батарейку и заменим ее на другую, пока Arduino не подключен к USB. Подождите 3 секунды и извлеките батарейку. В результате чип на часах реального времени перезагрузится. После этого вставьте код, который приведен ниже (код также можно выгрузить в меню Examples→RTClib→ds1307 в Arduino IDE) и загрузите его на Arduino.

Вам также понадобится библиотека OneWire.h, скачть ее можно

.

// функции даты и времени с использованием часов реального времени DS1307, подключенные по I2C. В скетче используется библиотека Wire lib

#include <Wire.h>

#include "RTClib.h"

Serial.begin(57600);

if (! RTC.isrunning()) {

Serial.println("RTC is NOT running!");

// RTC.adjust(DateTime(__DATE__, __TIME__));

DateTime now = RTC.now();

Serial.print("/");

Serial.print("/");

Serial.print(now.day(), DEC);

Serial.print(" ");

Serial.print(":");

Serial.print(":");

Serial.println();

Serial.print(now.unixtime());

Serial.print("s = ");

Serial.println("d");

// рассчитываем дату: 7 дней и 30 секунд

DateTime future (now.unixtime() + 7 * 86400L + 30);

Serial.print(" now + 7d + 30s: ");

Serial.print(future.year(), DEC);

Serial.print("/");

Serial.print(future.month(), DEC);

Serial.print("/");

Serial.print(future.day(), DEC);

Serial.print(" ");

Serial.print(future.hour(), DEC);

Serial.print(":");

Serial.print(future.minute(), DEC);

Serial.print(":");

Serial.print(future.second(), DEC);

Serial.println();

Serial.println();

Теперь откройте окно серийного монитора и убедитесь, что скорость передачи данных установлена корректно: на 57600 bps.

В результате вы должны увидеть в окне серийного монитора примерно следующее:


Если в часах реального времени пропадет питание, отобразится 0:0:0. Секунды отсчитываться перестанут. После настройки времени, пойдет новый отсчет. Именно по этой причине извлекать батарейку во время работы модуля часов реального времени нельзя.

Настройка времени на модуле часов

В этом же скетче раскомментируйте строку, которая начинается с RTC.adjust:

// строка ниже используется для настройки даты и времени часов

RTC.adjust(DateTime(__DATE__, __TIME__));

Процесс настройки даты и времени реализован очень элегантно. В эту строку попадают данные с вашего счетчика на персональном компьютере (в момент компилляции кода). Эти данные используются для прошивки вашего модуля часов реального времени. То есть, если время на вашем ПК настроено неверно, рекомендуем сначала исправить этот баг, а потом переходить к прошивке модуля часов для Arduino.

После настройки, откройте серийный монитор и убедитесь, что часы настроены корректно:


Все. С этого момента и на протяжении ближайших нескольких лет настраивать DS1307 не придется.

Считывание показаний времени с DS1307

После настройки часов реального времени DS1307, может отправлять к ним запросы. Давайте рассмотрим часть скетча, в которой реализованы эти запросы.

DateTime now = RTC.now();

Serial.print(now.year(), DEC);

Serial.print("/");

Serial.print(now.month(), DEC);

Serial.print("/");

Serial.print(now.day(), DEC);

Serial.print(" ");

Serial.print(now.hour(), DEC);

Serial.print(":");

Serial.print(now.minute(), DEC);

Serial.print(":");

Serial.print(now.second(), DEC);

Serial.println();

По сути существует один вариант для получения времени с использованием часов реального времени. Для этого используется функция now(), которая возвращает объект DateTime. В этом объекте содержаться данные про год, месяц, день, час, минуту и секунду.

Есть ряд библиотек для часов реального времени, в которых предусмотрены функции вроде RTC.year() и RTC.hour(). Эти функции вытягивают отдельно год и час. Но их использование сопряжено с рядом проблем: если вы сделаете запрос на вывод минут в момент времени, например, 3:14:59, то есть, прямо перед тем как показания минут должны приравняться к "15" (3:15:00), полученные данные будут равны 3:14:00 - то есть, вы потеряете одну минуту.

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

Есть еще один формат данных, которые мы можем подучить - количество секунд от полуночи, 1-го января 1970 года. Для этого используется функция unixtime ():

Serial.print(" since 1970 = ");

Serial.print(now.unixtime());

Serial.print("s = ");

Serial.print(now.unixtime() / 86400L);

Serial.println("d");

Так как в одном дне 60*60*24 = 86400 секунд, можно перевести полученное значение в дни и года. Очень удобный вариант, если вам надо отследить, сколько времени прошло с момента последнего запроса. Например, если прошло 5 минут с момента последнего последнего обращения Arduino к часам реального времени DS1307, значение, которое вернет функция unixtime() будет больше на 300.

Оставляйте Ваши комментарии, вопросы и делитесь личным опытом ниже. В дискуссии часто рождаются новые идеи и проекты!