Рейтинг@Mail.ru

Как подключить часы реального времени (RTC) к Arduino

автор:
3 comments Arduino
Print Friendly, PDF & Email
В данной статье мы подключим два модуля с таймерами реального времени (или real-time clock, RTC) – DS3231 и DS1307 – к Arduino. Оба модуля работают по интерфейсу I2C и питаются от батарейки формата 2032.

Нам понадобятся:

  • Arduino UNO или иная совместимая плата;
  • модуль ZS-042 с часами реального времени DS3231;
  • модуль с часами реального времени DS1307;
  • соединительные провода (рекомендую вот такой набор);
  • макетная плата (breadboard);
  • персональный компьютер со средой разработки Arduino IDE.

1Подключение к Arduino модуля ZS-042 с часами реального времени DS3231

Модуль ZS-042 с часами реального времени (RTC) имеет следующие характеристики:

  • Календарь до 2100 года с отсчётами секунд, минут, часов, числа месяца, месяца, дня недели и года (с учётом високосных годов);
  • 12- или 24-часовой формат;
  • 2 будильника;
  • напряжение питания: 3,3 или 5 В;
  • точность: ± 0.432 сек в день;
  • внутренний кварцевый генератор с частотой 32768 Гц;
  • поддерживаемый протокол: I2C со скоростью от 100 до 400 кГц;
  • габариты: 38×22×15 мм;
  • диапазон рабочих температур −40…+85°C.

На модуле присутствуют: микросхема таймера реального времени DS3231 (1 на рисунке), микросхема памяти AT24C32 объёмом 32 кбит (2 на рисунке), места для трёх перемычек A0, A1 и A2 (3 на рисунке), с помощью которых можно менять адресацию памяти микросхемы памяти; место для батареи питания размером 2032 (4 на рисунке).

Внешний вид модуля ZS-042
Внешний вид модуля ZS-042

Назначение выводов модуля такое:

НазваниеНазначение
32Kвыход генератора 32 кГц;
SQWвыход прямоугольного сигнала; частота задаётся с помощью регистра управления 0x0E и может составлять 1, 1024, 4096 или 8192 Гц;
SCLшина тактовых импульсов интерфейса I2C;
SDAшина данных интерфейса I2C;
VCCпитание – 3,3 или 5 вольт;
GNDземля.

С противоположной стороны модуля выводы SCL, SDA, питание и земля дублируются. На выходе 32K постоянно присутствует сигнал с встроенного кварцевого генератора:

Сигнал на выходе 32K модуля ZS-042
Сигнал на выходе 32K модуля ZS-042

Теперь нужно подключить модуль к Arduino. Мы уже знаем, что линия SDA нужно подключать к пину A4 Arduino UNO и Nano, а линию SCL – к пину A5. Для питания возьмём выход 5V платы Arduino, землю модуля соединим с землёй Arduino.

Схема подключения модуля ZS-042 с таймером DS3231 к Arduino
Схема подключения модуля ZS-042 с таймером DS3231 к Arduino

Вот как это выглядит вживую:

Модуль ZS-042 с таймером DS3231 подключён к Arduino
Модуль ZS-042 с таймером DS3231 подключён к Arduino

Рассмотрим диаграммы записи и чтения для таймера реального времени DS3231:

Обзор передачи данных по последовательной шине I2C
Обзор передачи данных по последовательной шине I2C
Диаграмма записи и диаграмма чтения таймера реального времени DS3231
Диаграмма записи и диаграмма чтения таймера реального времени DS3231

Как видно, тут всё стандартно для интерфейса I2C. Осталось только узнать, какие регистры за что отвечают, и мы будем готовы начать обмен данными с таймером DS3231. А вот и карта регистров:

Карта регистров таймера реального времени DS3231
Карта регистров таймера реального времени DS3231

Первым делом нужно выставить дату и время. А затем нужно будет только читать значение времени и календаря. Расширенные функции – установка будильников и т.д. – всё это делается аналогично, поэтому останавливаться на этом не будем. Итак, чтобы выставить дату и время, нас интересуют регистры 0x00…0x06. Для записи значений в них, нужно послать команду записи, указать начальный адрес (0x00), а дальше – 7 байтов, сформированных для нужной даты и времени. Например, чтобы записать дату 02 января 2019 года, среда, и время 19 час 30 мин 00 сек, нужно отправить ведомому устройству с I2C адресом 0x68 массив: 00 00 30 19 03 02 01 19. Скетч, который реализует это, будет таким:

#include <Wire.h>

void setup() {
  Wire.begin(); // старт i2c
  Wire.beginTransmission(0x68); // начинаем обмен с DS3231 с i2c адресом 0x68
  byte arr[] = {0x00, 0x00, 0x30, 0x19, 0x03, 0x02, 0x01, 0x19};
  Wire.write(arr, 8); // записываем 8 байтов массива arr
  Wire.endTransmission(); // завершение передачи
}

void loop() {
// здесь не делаем ничего
}

Вот как выглядит диаграмма записи этого массива в память таймера реального времени DS3231:

Диаграмма выставления времени на RTC DS3231
Диаграмма выставления времени на RTC DS3231

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

Скетч для чтения времени с часов DS3231 (разворачивается)
#include <Wire.h>
const byte DS3231 = 0x68; // I2C адрес таймера DS3231

void setup() {
  Wire.begin(); 
  Serial.begin(9600);
}

void loop() {
  Wire.beginTransmission(DS3231); // начинаем обмен с DS3231
  Wire.write(byte(0x00)); // записываем адрес регистра, с которого начинаем чтение!!!
  Wire.endTransmission(); // завершаем передачу

  byte t[7]; // массив для хранения даты и времени
  int i = 0; // индекс текущего элемента массива
  Wire.beginTransmission(DS3231); // начинаем обмен с DS3231
  Wire.requestFrom(DS3231, 7); // запрашиваем 7 байтов у DS3231
  while(Wire.available()) { // пока есть данные от DS3231
    t[i] = Wire.read(); // читаем 1 байт и сохраняем в массив t
    i++; // инкрементируем индекс элемента массива
  }
  Wire.endTransmission(); // завершаем обмен
  
  printDateTime(t); // выводим дату и время
  delay(1000); // пауза 1 секунда
}

// разбирает считанный массив и выводит дату и время 
void printDateTime(byte *arr) {
  if (arr[4]<10) Serial.print("0"); // дополнение нулями слева для выравнивания
  Serial.print(arr[4], HEX); // выводим дату
  Serial.print(".");
  if (arr[5]<10) Serial.print("0"); 
  Serial.print(arr[5], HEX); // выводим месяц
  Serial.print(".20"); // 2000-ые годы подразумеваются
  Serial.print(arr[6], HEX); // выводим год
  Serial.print(" ");
  if (arr[2]<10) Serial.print("0"); 
  Serial.print(arr[2], HEX); // выводим час
  Serial.print(":");
  if (arr[1]<10) Serial.print("0"); 
  Serial.print(arr[1], HEX); // выводим минуты
  Serial.print(":");
  if (arr[0]<10) Serial.print("0"); 
  Serial.println(arr[0], HEX); // выводим секунды  
}

Обратите внимание, что каждую итерацию цикла loop() мы записываем адрес регистра 0x00. Если этого не делать, то мы будем каждый раз сдвигаться по карте регистров на 7 позиций, и возвращаемые данные будут совсем не те, что мы ожидаем.

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

Вывод даты и времени в монитор последовательного порта
Вывод даты и времени в монитор последовательного порта

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

Временная диаграмма чтения регистров времени DS3231
Временная диаграмма чтения регистров времени DS3231

Напоследок давайте немного усложним нашу программу и будем читать также значение температуры:

Скетч для чтения времени и температуры с часов DS3231 (разворачивается)
#include <Wire.h>
const byte DS3231 = 0x68; // I2C адрес таймера DS3231

void setup() {
  Wire.begin(); 
  Serial.begin(9600);
}

void loop() {
  Wire.beginTransmission(DS3231); // начинаем обмен с DS3231
  Wire.write(byte(0x00)); // записываем адрес регистра, с которого начинаются данные даты и времени
  Wire.endTransmission(); // завершаем передачу

  byte dateTime[7]; // 7 байтов для хранения даты и времени
  int i = 0; // индекс текущего элемента массива
  Wire.beginTransmission(DS3231); // начинаем обмен с DS3231
  Wire.requestFrom(DS3231, 7); // запрашиваем 7 байтов у DS3231
  while(Wire.available()) // пока есть данные от DS3231
  {
    dateTime[i] = Wire.read(); // читаем 1 байт и сохраняем в массив dateTime
    i+=1; // инкрементируем индекс элемента массива
  }
  Wire.endTransmission(); // завершаем передачу

  printDateTime(dateTime); // выводим дату и время
 
  Wire.beginTransmission(DS3231); // начинаем новый обмен с DS3231
  Wire.write(byte(0x11)); // записываем адрес регистра, с которого начинается температура
  Wire.endTransmission(); // завершаем передачу

  i = 0; // обнуляем счётчик элементов массива
  byte temp[2]; // 2 байта для хранения температуры
  Wire.beginTransmission(DS3231); // начинаем обмен с DS3231
  Wire.requestFrom(DS3231, 2); // запрашиваем 2 байта у DS3231
  while(Wire.available()) 
  {
    temp[i] = Wire.read();
    i+=1;
  }
  Wire.endTransmission();

  printTemp(temp); // выводим температуру
    
  delay(1000); // пауза на 1 сек
}

// выводит дату и время
void printDateTime(byte *dateTime) {
  if (dateTime[4]<10) Serial.print("0"); 
  Serial.print(dateTime[4], HEX); // выводим дату
  Serial.print(".");
  if (dateTime[5]<10) Serial.print("0"); 
  Serial.print(dateTime[5], HEX); // выводим месяц
  Serial.print(".20");
  Serial.print(dateTime[6], HEX); // выводим год
  Serial.print(" ");
  if (dateTime[2]<10) Serial.print("0"); 
  Serial.print(dateTime[2], HEX); // выводим час
  Serial.print(":");
  if (dateTime[1]<10) Serial.print("0"); 
  Serial.print(dateTime[1], HEX); // выводим минуты
  Serial.print(":");
  if (dateTime[0]<10) Serial.print("0"); 
  Serial.print(dateTime[0], HEX); // выводим секунды
}

// выводит температуру
void printTemp(byte *temp) {
  Serial.print("\t"); // символ табуляции для разделения между временем и температурой
  float temperature = getTemp(temp);
  Serial.print(temperature);
  Serial.println("oC"); // градусы Цельсия
}

// преобразует содержимое регистров 0x11 и 0x12 в значение температуры
// для подробностей см. datasheet на ds3231
float getTemp(byte *temp){
  float temperature = temp[0]; // целая часть
  temperature += (temp[1]*0.25/100); // дробная часть
  return temperature;
}

Вот как теперь выглядит вывод нашей программы:

Вывод даты, времени и температуры в монитор последовательного порта
Вывод даты, времени и температуры в монитор последовательного порта

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

2Подключение к Arduino модуля с часами реального времени DS1307

Таймер DS1307 в отличие от DS3231 проще по функциональности: он имеет меньше регистров, не имеет встроенного датчика температуры и встроенного генератора тактовой частоты. Не имеет он также и функции будильника. Шина I2C функционирует только на частоте 100 кГц. Модуль с часами реального времени DS1307 может выглядеть вот так:

Внешний вид модуля с часами реального времени DS1307
Внешний вид модуля с часами реального времени DS1307

Здесь номером 1 обозначена микросхема собственно таймера DS1307, номер 2 – микросхема памяти AT24C32 объёмом 32 кбит, 3 – кварцевый резонатор с частотой 32,768 кГц, 4 – держатель для батареи типа 2032.

На модуле имеются две группы контактов: P1 и P2. Группа P2 имеет стандартные выводы для шины I2C, плюс дополнительный вывод DS, к которому можно подключить внешний датчик температуры DS18B20. Группа P1 имеет большее число контактов:

НазваниеНазначение вывода
SQВыход прямоугольного сигнала 30 кГц;
DSподключение внешнего датчика температуры DS18B20;
SCLшина тактирования интерфейса I2C;
SDAшина данных интерфейса I2C;
VCCпитание модуля – 3.3 или 5 вольт;
GNDземля;
BATвход питания от внешней батареи с напряжением в диапазоне 2,0…3,5 В.

Подключение этого модуля к Arduino осуществляется абсолютно так же, как и рассмотренного ранее: VCC модуля – 5V Arduino, GND – GND, SDA – A4, SCL – A5.

Теперь пришла пора познакомиться с устройством регистров часов DS1307. Карта регистров приведена на рисунке:

Карта регистров часов реального времени DS1307
Карта регистров часов реального времени DS1307

Если присмотреться, увидим, что регистры 0x00…0x06 в точности совпадают с аналогичными регистрами рассмотренного таймера DS3231, а регистр 0x07 отвечает за частоту генерируемого прямоугольного сигнала. Кроме того, I2C адрес DS1307 также аналогичен адресу модуля DS3231. Поэтому логично предположить, что скетч установки времени подойдёт и здесь. В этом легко убедиться, если загрузить скетч в Arduino с подключённым модулем DS1307. Не забудьте только обновить установочный массив в соответствии с временем, которое будете выставлять на часах. Пример разобран в предыдущем разделе.

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

Модуль с таймером DS1307 подключён к Arduino
Модуль с таймером DS1307 подключён к Arduino

Библиотеки для работы с часами реального времени DS1307 и DS3231

В архиве лежат две разные библиотеки для Arduino (используйте ту, которая будет вам наиболее удобна), а также технические описания (datasheet) на микросхемы DS1307 и DS3231.

Установка библиотек проводится стандартным способом: помещением директории с библиотекой в директорию libraries среды Arduino IDE или через меню Sketch Include Library. Проще всего начать знакомство с библиотекой с изучения примеров, которые появятся в меню File Examples после установки библиотеки. Там имеются примеры и установки времени, и чтения показаний часов.

Last modified onСуббота, 18 May 2019 19:04 Read 4897 times
Ключевые слова: :

Поделиться

Print Friendly, PDF & Email

3 comments

  • дмитрий
    дмитрий Пятница, 15 Март 2019 16:53 Ссылка на комментарий

    спасибо работает:)

  • Алексей
    Алексей Вторник, 14 May 2019 06:05 Ссылка на комментарий

    В характеристиках к модулю ZS-042 указано:
    - диапазон рабочих температур 0…40°C;
    - микросхема DS3231 имеет внутренний термометр с диапазоном от −40…+85°C.
    Это как??? Модуль может измерять температуру в диапазоне в котором сам не может работать?

  • aave1
    aave1 Суббота, 18 May 2019 19:01 Ссылка на комментарий

    Алексей, спасибо за замечание. Поправил.

Leave a comment

Make sure you enter all the required information, indicated by an asterisk (*). HTML code is not allowed.

  1. Arduino это...
  2. Arduino это...
  3. Arduino это...
Отличный способ начать знакомство с электроникой, микроконтроллерами и программированием!
Замечательное средство для создания собственных электронных устройств, которые пригодятся в быту или для развлечения!
Уникальный конструктор, для которого разработаны десятки совместимых датчиков и модулей!
next
prev