Найти в Дзене

Подключаем часы реального времени DS1307 к микроконтроллеру AVR. Схемотехника и код в CodeVisionAVR (часть 1)

Для тех, кто забыл или не знал, что такое RTC и как они работают - статья: В прошлый раз мы подключали такие часы к Arduino: Теперь пришло время подключения "напрямую" к микроконтроллеру AVR. Часы DS1307 и DS3231 имеют интерфейс I2C (более дешевые DS1302 используют свой интерфейс, напоминающий SPI). Необходимо подключить выводы питания к питанию (да ладно!), рядом с ножкой VCC рекомендуется ставить развязывающий керамический конденсатор на 0,1 мкФ. Интерфейсные выводы SDA, SCL (данные, такты) подключаются к соответствующим выводам микроконтроллера. Подтягивающие резисторы необходимы для работы интерфейса I2C, такое уж у него исполнение, с открытым стоком. Сопротивление обычно берут около 10 кОм, но можно взять другое из диапазона 2,2-10 кОм. Выход SQW/OUT прямоугольных импульсов частотой 1 Гц / 4 кГц / 8 кГц / 32 кГц или просто постоянного 0/1 (в зависимости от настройки) тоже нужно использовать с подтягивающим резистором. Если он вам не нужен, можно оставить его "висеть", как на иллю
Оглавление

Для тех, кто забыл или не знал, что такое RTC и как они работают - статья:

В прошлый раз мы подключали такие часы к Arduino:

Теперь пришло время подключения "напрямую" к микроконтроллеру AVR.

Подключение часов к МК

Часы DS1307 и DS3231 имеют интерфейс I2C (более дешевые DS1302 используют свой интерфейс, напоминающий SPI).

Подключение DS1307 к ATmega328P
Подключение DS1307 к ATmega328P

Необходимо подключить выводы питания к питанию (да ладно!), рядом с ножкой VCC рекомендуется ставить развязывающий керамический конденсатор на 0,1 мкФ.

Интерфейсные выводы SDA, SCL (данные, такты) подключаются к соответствующим выводам микроконтроллера.

Подтягивающие резисторы необходимы для работы интерфейса I2C, такое уж у него исполнение, с открытым стоком. Сопротивление обычно берут около 10 кОм, но можно взять другое из диапазона 2,2-10 кОм.

Выход SQW/OUT прямоугольных импульсов частотой 1 Гц / 4 кГц / 8 кГц / 32 кГц или просто постоянного 0/1 (в зависимости от настройки) тоже нужно использовать с подтягивающим резистором. Если он вам не нужен, можно оставить его "висеть", как на иллюстрации выше.

Батарейка нужна для резервного питания, если отключить питание схемы, часы продолжат идти и отсчитывать время. В теории стандартная литиевая батарейка CR2032 на 48 мА*ч может поддерживать работу часов DS1307 в течение 10 лет.

Если резервное питание не требуется, вывод VBAT надо заземлить.

Некоторые модели часов (например, DS1340) имеет встроенный зарядник для аккумулятора, который настраивается через соответствующие регистры.

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

Внимательно почитаем даташит к часам и Appnote по кварцам:

  1. Кварц должен быть часовой, с резонансной частотой 32,768 кГц
  2. Нагрузочная емкость кварца должна равна указанной в даташите Load Capacitance (у DS1307 генератор рассчитан на емкость 12,5 пФ, например).
    Чем лучше согласованы емкости генератора и кристалла, тем точнее будут идти часы.
  3. Точность хода будет зависеть от точности кварца (Frequency Tolerance [ppm]).
    Чем меньше это число, тем меньше будет "уходить" частота часов.
  4. Кварц должен быть расположен как можно ближе к выводам X1, X2 часов.
    Дорожки, соединяющие их, должны быть
    как можно короче.
    Так
    снижается паразитная емкость, "мешающая" точному ходу, и уменьшается влияние внешних наводок (сами понимаете, любой провод, даже очень короткий - это антенна :)
  5. Ширина дорожек и мест припайки кварца должна быть как можно меньше, чтобы ловить меньше помех.
  6. Вокруг кварца желательно сделать земляное кольцо (guard ring), чтобы защитить от помех.
  7. Под кварцем, дорожками и пинами X1, X2 не должно быть сигнальных линий, потому что они могут навести помехи.
    Расстояние до дорожек с сигналами должно быть не менее 0,2 дюйма (5,08 мм).
  8. Для повышения помехоустойчивости можно припаять кварц к земле. Земля должна быть "местечковой", отделенной от основной, потому что по основной течет куча токов, которые могут навести помехи.
Иллюстрация для последних трех пунктов
Иллюстрация для последних трех пунктов

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

С подключением вроде разобрались, теперь по(ш)кодим немного :)

Сначала напишем код с высоким уровнем абстракции в CodeVisionAVR, а в следующей части будет уже подробное описание кода для DS1307 и интерфейса I2C.

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

Используем библиотеки в CodeVisionAVR

Полный проект на GitHub (к сожалению, сейчас на Дзене нельзя вставлять код в виде Gist):

AVR-libs/ds1307_avr at master · VeronicaBionicle/AVR-libs

В CodeVisionAVR есть свои библиотеки для часов DS1307 и интерфейса I2C:

#include <i2c.h> // Функции I2C
#include <ds1307.h> // Функции DS1307

Параметры I2C (скорость передачи и пины SDA, SCL) настраиваются в меню Project - Configure, далее в пункте C Compiler - Libraries - I2C.

Выбираем скорость интерфейса 100 кГц, пины настроила такие же, какие у ATmega328P с аппаратным I2C - PC4, PC5.

Основных функций в библиотеке немного, рассмотрим по порядку:

Настройка часов

void rtc_init(unsigned char rs, unsigned char sqwe, unsigned char out);

Что "делает" каждый аргумент?

  • rs - настраивает частоту сигнала на выходе SQWE
  • sqwe - включает/выключает меандр на выходе SQWE (1/0)
  • out - задает состояние OUT при выключенном сигнале SQWE (1/0)

В Datasheet есть такая табличка, из которой следует, при SQWE=1 на выходе будет меандр, частота которого настраивается сочетанием битов RS0 и RS1 (число от 0 до 3 в аргументе rs). Если же SQWE=0, то на выходе SQWE/OUT будет состояние бита OUT (аргумента out) - 1 или 0.

-4

Приведу пример настройки выхода SQWE на сигнал с частотой 4 кГц (если не собираетесь менять настройки в программе, можно задать в виде DEFINE'ов):

#define RS0 0
#define RS1 1

unsigned char rs = (0 << RS1)|(1 << RS0);
unsigned char sqwe = 1;
unsigned char out = 0;

rtc_init(rs, sqwe, out);

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

Данные функции предельно просты в использовании, вводим нужные значения, и они запишутся в часы.

Установка даты

void rtc_set_date(
unsigned char week_day, // день недели (1-7)
unsigned char day, // день
unsigned char month, // месяц
unsigned char year // год (00-99)
)

Пример установки даты: Пятница 20.12.2024

rtc_set_date(6, 20, 12, 24);

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

void rtc_set_time(
unsigned char hour, // часы
unsigned char min, // минуты
unsigned char sec // секунды
)

Пример установки времени: 16:01:30

rtc_set_time(16, 1, 30);

Несколько примечаний:
- Часы принимают в регистры даты и времени любые данные, не пишите туда
32 февраля 25:61 :)
Валидация вводимых даты-времени остается за вами.
- День недели - это просто число от 1 до 7, увеличивающееся в полночь.
Какие "человеческие" дни недели будут соответствовать этому числу, решает программист. Можно считать, что 1 - это "понедельник", как у нас на Руси заведено, а можно и "Sunday", как у буржуев.

Получение времени и даты

Данные функции тоже просты в использовании, передаем переменные под "части" даты или времени по ссылке, функции записывают в них значения из часов.

Получение даты

void rtc_get_date(
unsigned char *week_day, // день недели
unsigned char *day, // день
unsigned char *month, // месяц
unsigned char *year // год
)

Пример получения даты с выводом в UART:

const char * weekdays[7] =
{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
unsigned char week_day, day, month, year;

rtc_get_date(&week_day, &day, &month, &year);
printf("%02u\\%02u\\%02u %p\r", day, month, year, weekdays[week_day-1]);

Получение времени

void rtc_get_time(
unsigned char *hour, // часы
unsigned char *min, // минуты
unsigned char *sec // секунды
)

Пример получения времени с выводом в UART:

unsigned char hour, minute, second;
rtc_get_time(&hour,&minute,&second);
printf("%02u:%02u:%02u\r", hour, minute, second);

Покажем на модели в Proteus:

-5

Вот небольшая статья по интерфейсу UART, если нужны пояснения по коду:

Ниже приведу варианты настройки выхода SQWE/OUT:

Такой подход немногим хардкорнее Arduino, скажете вы... Верно!

Во второй части я написала "полный" код для подключения RTC DS1307 к ATmega328P - начиная от подключения по I2C и заканчивая самими функциями для часов.
Сразу похвастаюсь, что библиотека вышла более функциональной, чем в CodeVisionAVR, и позволяет останавливать и запускать часы, выбирать формат 24/12 для часов, а также использовать внутреннюю память DS1307 для хранения пользовательских данных.

Можете дать мне копеечку на кофе, если понравилась статья, а можете и не давать :)

Еще парочка статей про программирование микроконтроллеров AVR