Сегодня мы продолжим поиски идеальной микросхемы часов реального времени (RTC). Часы будем изготавливать на основе DS3231. Индикация будет использоваться более удобная для разработки - LCD дисплей, на котором будет отображаться вся информация сразу кроме настроек. В таком виде часы удобно использовать как настольный вариант.
Итак, рассмотрим саму микросхему DS3231. DS3231 - это часы реального времени с экстремально точным ходом (подобрали же производители словечко) благодаря встроенному кварцевому резонатору с температурной компенсацией. Интерфейс передачи данных - I2C. В этой микросхеме есть также вход для напряжения резервной батареи, при отключении основного питания микросхема автоматически переключается на работу от резервной батареи, точность хода от резервной батареи не нарушается. Весьма радует, не правда ли? В 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, которая чаще всего и даром не нужно, только веса добавляет:
Кроме необходимых деталей на плате модуля есть также светодиод, функция которого - индикация подключения питания к выводам. Наверно просто так доставили, для красоты.
Что важно знать при работе с такой микросхемой часов реального времени, так это как же извлечь из нее данные или записать их туда. Часы имеют интерфейс I2C. Для того чтобы осуществить запись данных (а это нужно и для того чтобы прочитать данные) нужно передать условие старта (эти команды осуществляются по средствам аппаратного или программного I2C для микроконтроллера), далее передать адрес микросхемы с битом записи, далее передать адрес регистра к которому будем обращаться и далее передать в этот регистр байт данных, если следом передать еще байт данных, он запишется в следующий регистр и так далее. По окончании нужно передать условие остановки. Графическое изображение выше сказанного на рисунке:
Запись данных необходима для первоначальной настройки, а также для настройки текущего времени. Далее нам нужно постоянно получать данные о текущем времени и даты. Для этого необходимо осуществлять чтение из регистров хранения этой информации. Чтение состоит из двух процедур - установить указатель на нужный регистр и прочитать его. Чтобы установить указатель на нужный регистр, нужно передать условие старта, потом передать адрес микросхемы с битом записи и байт с адресом регистра. Далее либо условие остановки и следом условие старта, либо просто рестарт. Теперь вторая процедура - непосредственно чтение из регистров. Старт передан, далее нужно отправить адрес микросхемы с битом чтения и далее считывать регистры в необходимом количестве, по окончании передать условие остановки. Если информация из регистра была прочитана, то указатель автоматически переходит на следующий за ним регистр без лишних действий со стороны микроконтроллера (мастер устройства). На рисунке проиллюстрировано все выше сказанное относительно чтения регистров по средствам I2C интерфейса:
Адрес микросхемы:
- для записи - 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 или PCF8523 выше, а вот PCA/PCF2129 еще могут потягаться с ней. Среди рассмотренных мною микросхем часов реального времени данный экземпляр на сегодняшний день занимает первое место по функционалу и точности.
Для программирования микроконтроллера Atmega8 необходимо знать конфигурацию фьюз битов (скриншот сделан в программе AVR Studio):
К статье прилагается прошивка для микроконтроллера Atmega8, проект схемы в программе Proteus, а также видео работы часов (в самом начале сработает будильник - загорится светодиод).
Список радиоэлементов
Обозначение | Тип | Номинал | Количество | Примечание | Магазин | Мой блокнот |
---|---|---|---|---|---|---|
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 | Поиск в магазине Отрон | |
R3, R5-R8 | Резистор | 10 кОм | 5 | Поиск в магазине Отрон | ||
R4 | Резистор | 390 Ом | 1 | Поиск в магазине Отрон | ||
R9 | Резистор | 220 Ом | 1 | Поиск в магазине Отрон | ||
LED1 | Светодиод | 1 | Поиск в магазине Отрон | |||
HG1 | LCD-дисплей | 2004A | 1 | Можно заменить на SC1602 | Поиск в магазине Отрон | |
S1-S4 | Тактовая кнопка | TC-A109 | 4 | Поиск в магазине Отрон | ||
Элемент питания | CR2032 | 1 | 3 Вольт | Поиск в магазине Отрон | ||
Держатель CR2032 | 1 | Поиск в магазине Отрон | ||||
Скачать список элементов (PDF)
Прикрепленные файлы:
- 38.hex (9 Кб)
- prott.rar (35 Кб)
- плата от gnom 46.rar (75 Кб)
Комментарии (70) | Я собрал (0) | Подписаться
Для добавления Вашей сборки необходима регистрация
[Автор]
[Автор]
P.S. Если у автомобиля шкала тахометра 8000 оборотов, а нормальные обороты для езды 2-3 тыс, то Вы ездите на 7000?
Избыточность схемы - явная
[Автор]
[Автор]
[Автор]
[Автор]
[Автор]
[Автор]
[Автор]
[Автор]
[Автор]
[Автор]
Подключил к Atmega32, часы в виде модуля на фото в вверху.
Обнаружилась странная особенность. Часы иногда зависают. На дисплее, что-то типа 85:85:85, temp 225. То есть они идут-идут и зависли. Лечится банальным отключением на несколько часов!
Судя по всему, часы боятся касаний пальцами и слишком длинных шлейфов. Скорей всего нужна экранировка. Впрочем DS1307 этим тоже "грешат"...
[Автор]
Хотел узнать, сколько по времени активен сигнал будильника? И еще хотел спросить: если у меня уже есть часы, собранные с применением микросхемы DS1307, возможна ли ее замена на модуль DS3231? Заранее спасибо за ответ
[Автор]
Совместимы только по времени и дате первые 7 регистров.
[Автор]
повторно виснет на инструкции StartCondition, т.е. данная инструкция второй раз уже не выполняется. Почему бы это?
В продолжение вышесказанного. Подтыкаю вместо 3231 модуль с 1307 - все работает. В чем все-таки различие между ними?
I2C инициализирую 100 кГц. Использую свою библиотеку с железным I2C, т.е прописываю TWBR, TWCR, TWDR и тд.
[Автор]
{
unsigned char sec,min,hour,day,date,month,year;
PORTD=0x00;
DDRD=0xFF;
//Скорость USART 115200 при кварцевом генераторе 8MHz
USART_Init (8);
I2C_Init();
while(1)
{
//PORTD|=(1<<PORTD2); //если включаем лапку тут, то
светодиод зажигается во 2м цикле
//Читаем время
I2C_StartCondition(); // генерируем условие СТАРТ
PORTD|=(1<<PORTD2); //если включаем лапку порта тут, то светодиод зажигается только в 1м цикле
I2C_SendByte(0b11010000); //оправляем адрес устройства+бит запись
I2C_SendByte(0);//отправляем байт данных
I2C_StopCondition();//генерируем условие СТОП
// I2C_SendPocket(0,0b11010000);
I2C_StartCondition();
I2C_SendByte(0b11010001);
sec = I2C_RecieveByte();
min = I2C_RecieveByte();
hour = I2C_RecieveByte();
day=I2C_RecieveByte();
date=I2C_RecieveByte();
month=I2C_RecieveByte();
year=I2C_RecieveLastByte();
I2C_StopCondition();
sec = ds1307_perev_for_me(sec);
min = ds1307_perev_for_me(min);
hour = ds1307_perev_for_me(hour);
day = ds1307_perev_for_me(day);
year = ds1307_perev_for_me(year);
month = ds1307_perev_for_me(month);
date = ds1307_perev_for_me(date);
USART_Transmit(date/10+0x30);
USART_Transmit(date%10+0x30);
USART_Transmit('.');
USART_Transmit(month/10+0x30);
USART_Transmit(month%10+0x30);
USART_Transmit('.');
USART_Transmit('2');
USART_Transmit('0');
USART_Transmit(year/10+0x30);
USART_Transmit(year%10+0x30);
USART_Transmit(' ');
USART_Transmit('-');
USART_Transmit(day+0x30);
USART_Transmit('-');
USART_Transmit(' ');
USART_Transmit(' ');
USART_Transmit(hour/10+0x30);
USART_Transmit(hour%10+0x30);
USART_Transmit(':');
USART_Transmit(min/10+0x30);
USART_Transmit(min%10+0x30);
USART_Transmit(':');
USART_Transmit(sec/10+0x30); //Преобразуем число в код числа
USART_Transmit(sec%10+0x30);
USART_Transmit(0x0d);//переход в начало строки
USART_Transmit(0x0a);//переход на новую строку
_delay_ms(1000);
PORTD&=~(1<<PORTD2);
_delay_ms(1000);
}
}
А вот тела некоторых используемых функций
//преобразование из двоично- десятичной системы в двоичную
#define ds1307_perev_for_me(x) ((x>>4)*10)+(0b00001111&x)
//преобразование из двоичной в двоично-десятичную систему
#define ds1307_perev_for_ds(x) ((x/10)<<4)|(x%10)
void I2C_Init (void)
{
TWBR=0x08;//скорость передачи при 8 мгц тактовой частоты получается 100 кгц
}
//----------------------------------------
// отправка команды СТАРТ
void I2C_StartCondition(void)
{
TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
while (!(TWCR & (1<<TWINT)));//ожидание установки бита TWIN
}
//----------------------------------------
// отправка команды СТАРТ
void I2C_StartCondition2(void)
{
TWCR = (1<<TWINT)|(1<<TWSTA);
while (!(TWCR & (1<<TWINT)));//ожидание установки бита TWIN
}
//----------------------------------------
// отправка СТОП
void I2C_StopCondition(void)
{
TWCR = (1<<TWINT)|(1<<TWSTO)|(1<<TWEN);
}
//----------------------------------------
//----------------------------------------
//отправка байта
void I2C_SendByte(unsigned char c)
{
TWDR = c;//загрузка значения в регистр данных
TWCR = (1<<TWINT)|(1<<TWEN);//начаало передачи байта данных
while (!(TWCR & (1<<TWINT)));//ожидание установки бита TWIN
}
//----------------------------------------
//отправка SLA_W + байт данных
void I2C_SendPocket (unsigned char value,unsigned char adres_rw)
{
I2C_StartCondition(); // генерируем условие СТАРТ
I2C_SendByte(adres_rw); //оправляем адрес устройства+бит запись
I2C_SendByte(value);//отправляем байт данных
I2C_StopCondition();//генерируем условие СТОП
}
//----------------------------------------
//добавляем функции чтения по I2C
//чтение байта
unsigned char I2C_RecieveByte(void)
{
unsigned char dat;//переменная хранения значения
TWCR |= (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
while (!(TWCR & (1<<TWINT)));//ожидание установки бита TWIN
dat = TWDR;//читаем регистр данных
return dat;
}
//----------------------------------------
//чтение последнего байта
unsigned char I2C_RecieveLastByte(void)
{
unsigned char dat;//переменная хранения значения
TWCR |= (1<<TWINT)|(1<<TWEN);//чтение последнего байта
while (!(TWCR & (1<<TWINT)));//ожидание установки бита TWIN
dat = TWDR;//читаем регистр данных
return dat;
}
Все это хозяйство в случае применения Далласа1307 работает, а в случае 3231 - нет.
Может дело в резисторах на 10 ком? Может поменять их в модуле на меньший номинал?
[Автор]
[Автор]
Она вроде здесь стоит 10 мкс - для частоты 100 кгц, Почитал даташит на AT24C32D - там вроде значатся частоты 400 кГц и 1 мГц. Не понятно только какую задержку делать для 400 мГц. 1000000 на 400000 не делится. Можно конечно написать свою задержку с помощью серии nop-ов.
Как думаете, в этом ли дело или еще в чем?
[Автор]
Задержка библиотекой стандартной делается, как в ней проблема может быть - не знаю
[Автор]
[Автор]
[Автор]
Вопрос может и некорректный, но я в этом новичок.
[Автор]
[Автор]
Применил Ваш код (немного под мои нужды "причёсанный" и разделённый на *.h и *.c ) - и сразу всё заработало!
Вот ещё бы программного slave бы где нарыть - чтоб с любых свободных выводов использовать...
Естественно стандартная функция задержки тут неприменима, но у меня давно уже есть файл задержек - из моих упражнений по inline assembler, эти функции иногда бывают удобнее стандартной.
С этими задержками связка DS3231 + ATmega1284P работает стабильно.
И ещё - надеюсь не в обиду будет - я почистил текст от переопределений работы с битами. Обычный вариант в С - типа DDR_I2C &= ~(1
Какой смысл питать DS3231 от 3.3 вольт если линии SCL SDA приходят с уровнями 5 вольт? Надо было на линии ставить согласователь уровней 5-3,3 вольта. Есть схема простая на 2х полевых транзисторах.
Вы уверены что запускаете выход 32кГц ?
Запуск находится в регистре 0Fh, а у Вас запись в 0Eh