Ds1307 описание. RTC модуль DS1307 подключение к Arduino. Библиотека Arduino для работы с DS1307

DS1307 - микросхема часов реального времени с интерфейсом I2C(TWI) . Часы / календарь хранят следующую информацию: секунды, минуты, часы, день, дату, месяц и год. Конец месяца автоматически подстраивается для месяцев, в которых менее 31 дня, включая поправку для високосного года. Часы работают в 24-часовом или 12-часовом формате с индикатором AM/PM. DS1307 имеет встроенную схему контроля питания, которая обнаруживает пропадание питания и автоматически переключает схему на питание от батареи.

Vbat - вход батареи для любого стандартного 3 Вольтового литиевого элемента или другого источника энергии. Для нормальной работы напряжение батареи должно поддерживаться между 2.5 и 3.5 В. Уровень, при котором запрещён доступ к часам реального времени и пользовательскому ОЗУ, установлен внутренней схемой равным 1.25 x Vbat. Литиевая батарея ёмкостью 35 mAh или больше достаточна для питания DS1307 в течение более чем 10 лет при отсутствии питания.
SCL (Последовательный Тактовый Вход) - SCL используется, чтобы синхронизировать передачу данных через последовательный интерфейс.
SDA (Вход/Выход Последовательных Данных) - SDA - вход / выход данных для 2-проводного последовательного интерфейса. Это выход с открытым стоком, который требует внешнего притягивающего резистора.
SQW/OUT (Меандр / Выходной Драйвер) - Когда бит SQWE установлен в 1, на выходе SQW/OUT вырабатываются импульсы в форме меандра одной из четырех частот: 1 Гц., 4 кГц., 8 кГц., 32 кГц. Вывод SQW/OUT - с открытым стоком, требует внешнего притягивающего резистора.
X1, X2 - выводы для подключения стандартного кристалла кварца 32.768 кГц. Внутренняя схема генератора рассчитана на работу с кристаллом, имеющим номинальную емкость (CL) 12.5 пФ.
GND – Земля.
VCC – питание 5 вольт.

DS1307 работает как ведомое устройство на последовательной шине. Для доступа к нему надо установить состояние START и передать код идентификации устройства, сопровождаемый адресом регистра. К последующим регистрам можно обращаться последовательно, пока не установлено состояние STOP . Когда VСС падает ниже 1.25 x Vbat, устройство прекращает связь и сбрасывает адресный счетчик. В это время оно не будет реагировать на входные сигналы, чтобы предотвратить запись ошибочной информации. Когда VСС падает ниже Vbat, устройство переключается в режим хранения с низким потреблением. При включении питания устройство переключает питание с батареи на VСС , когда напряжение питания превысит Vbat + 0.2V, и реагирует на входные сигналы, когда VСС станет более 1.25 x Vbat. Когда питание находится в пределах нормы, устройство полностью доступно, и данные могут быть записаны и считаны. Когда к устройству подключена трёхвольтовая батарея и VСС ниже 1.25 x Vbat, чтение и запись запрещены. Однако отсчёт времени при этом работает. Когда VСС падает ниже Vbat, питание ОЗУ и отсчёта времени переключается на внешнюю батарею 3 В.

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

Бит 7 регистра 0 - это бит остановки хода часов (Clock Halt). Когда этот бит установлен в 1, генератор остановлен. Когда сброшен в ноль, генератор работает, а часы считают время.

DS1307 может работать в 12-часовом или 24-часовом режиме. Бит 6 регистра часов задаёт один из этих режимов. Когда он равен 1, установлен 12-часовой режим. В 12-часовом режиме высокий уровень бита 5 сообщает о послеполуденном времени. В 24-часовом режиме бит 5 - второй бит 10 часов (20-23 часа).

Регистр управления DS1307 предназначен для управления работой вывода SQW/OUT . Бит OUT - управление выходом. Этот бит управляет выходным уровнем на выводе SQW/OUT , когда генерация меандра запрещена. Если SQWE = 0, логический уровень на выводе SQW/OUT равен 1, если OUT = 1, и 0 - если OUT = 0. SQWE - Разрешение меандра. Когда этот бит установлен в 1, разрешается генерация меандра. Частота меандра зависит от значений битов RS0 и RS1. Эти биты управляют частотой меандра, когда его генерация разрешена. В таблице ниже показаны частоты, которые могут быть заданы RS битами.

DS1307 поддерживает двунаправленные 2-проводную шину и протокол передачи данных. Устройство, которое посылает данные на шину, называется передатчиком, а устройство, получающее данные - приемником. Устройство, которое управляет передачей, называется ведущим. Устройства, которые управляются ведущим - ведомые. Шина должна управляться ведущим устройством, которое вырабатывает последовательные такты (SCL), управляет доступом к шине, и генерирует состояния СТАРТ и СТОП. DS1307 работает как ведомое на 2-х проводной шине.

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

1. Режим записи в DS1307 . Последовательные данные и такты получены через SDA и SCL. После передачи каждого байта передаётся подтверждающий бит ASK . Состояния START и STOP опознаются как начало и конец последовательной передачи. Распознавание адреса выполняется аппаратно после приема адреса ведомого и бита направления. Байт адреса содержит семибитный адрес DS1307, равный 1101000, сопровождаемым битом направления (R/W), который при записи равен 0. После получения и расшифровки байта адреса DS1307 выдаёт подтверждение ASK на линии SDA. После того, как DS1307 подтверждает адрес ведомого и бит записи, ведущий передает адрес регистра DS1307. Тем самым будет установлен указатель регистра в DS1307. Тогда ведущий начнет передавать байты данных в DS1307, который будет подтверждать каждый полученный байт. По окончании записи ведущий сформирует состояние STOP .

2. Режим чтения из DS1307 . Первый байт принимается и обрабатывается как в режиме ведомого приёмника. Однако в этом режиме бит направления укажет, что направление передачи изменено. Последовательные данные передаются по SDA от DS1307, в то время как последовательные такты - по SCL в DS1307. Состояния START и STOP опознаются как начало и конец последовательной передачи. Байт адреса - первый байт, полученный после того, как ведущим сформировано состояние START . Байт адреса содержит семибитный адрес DS1307, равный 1101000, сопровождаемым битом направления (R/W), который при чтении равен 1. После получения и расшифровки байта адреса DS1307 выдаёт подтверждение ASK на линии SDA. Тогда DS1307 начинает передавать данные, начинающиеся с адреса регистра, на которые указывает указатель регистра. Если указатель регистра не записан перед инициированием режима чтения, то первый адрес, который читается - это последний адрес, оставшийся в указателе регистра. DS1307 должен получить неподтверждение NOASK , чтобы закончить чтение.

Рассмотрим особенности работы с DS1307 на примере простых часов, которые будут показывать часы, минуты и секунды. Данные будут выводиться на LCD дисплей 16х2. Две кнопки "Часы+" и "Минуты+" позволят подвести нужное время. Микроконтроллер Atmega 8 тактируется от внутреннего генератора частотой 1 MHz, поэтому не забудьте поменять фьюзы. Ниже представлена схема подключения.

Управляющая программа включает в себя наборы функций работы с шиной TWI, часами DS1307, LCD дисплеем.

I2CInit - инициализация шины;
I2CStart - передача условия START;
I2CStop - передача условия STOP;
I2CWriteByte - запись данных;
I2CReadByte - чтение данных;
DS1307Read - функция чтения данных из DS1307;
DS1307Write - Функция записи данных в DS1307;
lcd_com - передача команды в LCD;
lcd_data - передача данных в LCD;
lcd_string - функция вывода строки в LCD;
lcd_num_to_str - функция вывода символа типа int;
lcd_init - инициализация LCD.

Ниже представлен код программы:

#include #include // Функция инициализация шины TWI void I2CInit(void) { TWBR = 2; // Настройка частоты шины TWSR = (1 << TWPS1)|(1 << TWPS0); // Предделитель на 64 TWCR |= (1 << TWEN); // Включение модуля TWI } // Функция СТАРТ void I2CStart(void) { TWCR = (1 << TWINT)|(1 << TWEN)|(1 << TWSTA); // Передача условия СТАРТ while(!(TWCR & (1 << TWINT))); // Ожидание установки флага TWINT } // Функция СТОП void I2CStop(void) { TWCR = (1 << TWINT)|(1 << TWEN)|(1 << TWSTO); // Передача условия СТОП while(TWCR & (1 << TWSTO)); // Ожидание завершения передачи условия СТОП } // Функция записи данных по шине uint8_t I2CWriteByte(uint8_t data) { TWDR = data; // Загрузка данных в TWDR TWCR = (1 << TWEN)|(1 << TWINT); // Сброс флага TWINT для начала передачи данных while(!(TWCR & (1 << TWINT))); // Ожидание установки флага TWINT // Проверка статуса // Если адрес DS1307+R и принято "подтверждение"(0x18) // или адрес DS1307+W и принято "подтверждение"(0x40) // или передается байт данных и принято "подтверждение"(0x28) if((TWSR & 0xF8) == 0x18 || (TWSR & 0xF8) == 0x40 || (TWSR & 0xF8) == 0x28) return 1; // OK else return 0; // ОШИБКА } // Функция чтения данных по шине uint8_t I2CReadByte(uint8_t *data,uint8_t ack) { // Возвращаем "подтверждение" после приема if(ack) TWCR |= (1 << TWEA); // Возвращаем "неподтверждение" после приема // Ведомое устройство не получает больше данных // обычно используется для распознования последнего байта else TWCR &= ~(1 << TWEA); // Разрешение приема данных после сброса TWINT TWCR |= (1 << TWINT); while(!(TWCR & (1 << TWINT))); // Ожидание установки флага TWINT // Проверка статуса // Если принят байт данных и возвращается "подтверждение"(0x50) // или принят байт данных и возвращается "ненеподтверждение"(0x58) if((TWSR & 0xF8) == 0x50 || (TWSR & 0xF8) == 0x58) { *data = TWDR; // Читаем данные из TWDR return 1; // OK } else return 0; // ОШИБКА } // Функция чтения данных из DS1307 uint8_t DS1307Read(uint8_t address,uint8_t *data) { uint8_t res; I2CStart(); // СТАРТ res = I2CWriteByte(0b11010000); // адрес DS1307+W if(!res) return 0; // ОШИБКА // Передача адреса необходимого регистра res = I2CWriteByte(address); if(!res) return 0; // ОШИБКА I2CStart(); // Повторный СТАРТ res = I2CWriteByte(0b11010001); // адрес DS1307+R if(!res) return 0; // ОШИБКА // Чтение данных с "неподтверждением" res = I2CReadByte(data,0); if(!res) return 0; // ОШИБКА I2CStop(); // СТОП return 1; // OK } // Функция записи данных в DS1307 uint8_t DS1307Write(uint8_t address,uint8_t data) { uint8_t res; I2CStart(); // СТАРТ res = I2CWriteByte(0b11010000); // адрес DS1307+W if(!res) return 0; // ОШИБКА // Передача адреса необходимого регистра res = I2CWriteByte(address); if(!res) return 0; // ОШИБКА res = I2CWriteByte(data); // Запись данных if(!res) return 0; // ОШИБКА I2CStop(); // СТОП return 1; // OK } // Функции работы с LCD #define RS PD0 #define EN PD2 // Функция передачи команды void lcd_com(unsigned char p) { PORTD &= ~(1 << RS); // RS = 0 (запись команд) PORTD |= (1 << EN); // EN = 1 (начало записи команды в LCD) PORTD &= 0x0F; PORTD |= (p & 0xF0); // старший нибл _delay_us(100); PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD) _delay_us(100); PORTD |= (1 << EN); // EN = 1 (начало записи команды в LCD) PORTD &= 0x0F; PORTD |= (p << 4); // младший нибл _delay_us(100); PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD) _delay_us(100); } // Функция передачи данных void lcd_data(unsigned char p) { PORTD |= (1 << RS)|(1 << EN); // RS = 1 (запись данных), EN - 1 (начало записи команды в LCD) PORTD &= 0x0F; PORTD |= (p & 0xF0); // старший нибл _delay_us(100); PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD) _delay_us(100); PORTD |= (1 << EN); // EN = 1 (начало записи команды в LCD) PORTD &= 0x0F; PORTD |= (p << 4); // младший нибл _delay_us(100); PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD) _delay_us(100); } // Функция вывода строки на LCD void lcd_string(unsigned char command, char *string) { lcd_com(0x0C); lcd_com(command); while(*string != "\0") { lcd_data(*string); string++; } } // Функция вывода переменной void lcd_num_to_str(unsigned int value, unsigned char nDigit) { switch(nDigit) { case 4: lcd_data((value/1000)+"0"); case 3: lcd_data(((value/100)%10)+"0"); case 2: lcd_data(((value/10)%10)+"0"); case 1: lcd_data((value%10)+"0"); } } // Функция инициализации LCD void lcd_init(void) { PORTD = 0x00; DDRD = 0xFF; _delay_ms(50); // Ожидание готовности ЖК-модуля // Конфигурирование четырехразрядного режима PORTD |= (1 << PD5); PORTD &= ~(1 << PD4); // Активизация четырехразрядного режима PORTD |= (1 << EN); PORTD &= ~(1 << EN); _delay_ms(5); lcd_com(0x28); // шина 4 бит, LCD - 2 строки lcd_com(0x08); // полное выключение дисплея lcd_com(0x01); // очистка дисплея _delay_us(100); lcd_com(0x06); // сдвиг курсора вправо lcd_com(0x0C); // включение дисплея, курсор не видим } int main(void) { _delay_ms(100); DDRC = 0x00; PORTC = 0xFF; lcd_init(); // Инициализация LCD I2CInit(); // Инициализация шины I2C lcd_string(0x81, "«acГ Ѕa DS1307"); // Часы на DS1307 lcd_string(0xC4, " : : "); // Запускаем ход часов uint8_t temp; DS1307Read(0x00,&temp); temp &= ~(1 << 7); // обнуляем 7 бит DS1307Write(0x00,temp); while(1) { unsigned char hour, minute, second, temp; // Читаем данные и преобразуем из BCD в двоичную систему DS1307Read(0x00,&temp); // Чтение регистра секунд second = (((temp & 0xF0) >> 4)*10)+(temp & 0x0F); DS1307Read(0x01,&temp); // Чтение регистра минут minute = (((temp & 0xF0) >> 4)*10)+(temp & 0x0F); DS1307Read(0x02,&temp); // Чтение регистра часов hour = (((temp & 0xF0) >> 4)*10)+(temp & 0x0F); lcd_com(0xC4); lcd_num_to_str(hour, 2); // Выводим на экран часы lcd_com(0xC7); lcd_num_to_str(minute, 2); // Выводим на экран минуты lcd_com(0xCA); lcd_num_to_str(second, 2); // Выводим на экран секунды if((PINC & (1 << PC0)) == 0) // Если нажата кнопка { while((PINC & (1 << PC0)) == 0){} // Ждем отпускания кнопки hour++; // Увеличиваем часы на 1 if(hour > 23) hour = 0; // Преобразуем из двоичной системы в BCD и записываем в DS1307 uint8_t temp; temp = ((hour/10) << 4)|(hour%10); DS1307Write(0x02, temp); _delay_ms(100); } if((PINC & (1 << PC1)) == 0) // Если нажата кнопка { while((PINC & (1 << PC1)) == 0){} // Ждем отпускания кнопки minute++; // Увеличиваем минуты на 1 if(minute > 59) minute = 0; // Преобразуем из двоичной системы в BCD и записываем в DS1307 uint8_t temp; temp = ((minute/10) << 4)|(minute%10); DS1307Write(0x01, temp); _delay_ms(100); } } }

Тема часов на микросхеме 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… Мне нравится Альтиум, только потому, что для него куча готовых библиотек с элементами, ибо тратить время на набивку собственной библиотеки элементов, на мой взгляд не дело. Общий смысл всех подобных программ одинаков — сначала рисуется электрическая схема.

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

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

Далее переведено Д.С. Иоффе ( dsioffe @ da . ru ) с другом Stylus ом исключительно для собственного понятия. Никакие претензии не принимаются.

Описание

DS1307 - часы реального времени с последовательным интерфейсом - низкая потребляемая мощность, полный BCD календарь, часы плюс 56 байтов энергонезависимого статического ОЗУ. Адрес и данные передаются последовательно через 2-проводную двунаправленную шину. Часы / календарь хранят следующую информацию: секунды, минуты, часы, день, дату, месяц и год. Конец месяца автоматически подстраивается для месяцев, в которых менее 31 дня, включая поправку для високосного года. Часы работают в 24-часовом или 12-часовом формате с индикатором AM/PM. DS1307 имеет встроенную схему контроля питания, которая обнаруживает пропадание питания и автоматически переключает схему на питание от батареи.

Работа

DS1307 работает как ведомое устройство на последовательной шине. Для доступа к нему надо установить состояние START и передать код идентификации устройства, сопровождаемый адресом регистра. К последующим регистрам можно обращаться последовательно, пока не установлено состояние STOP. Когда Vсс падает ниже 1.25 x Vbat, устройство прекращает связь и сбрасывает адресный счетчик. В это время оно не будет реагировать на входные сигналы, чтобы предотвратить запись ошибочной информации. Когда Vcc падает ниже Vbat, устройство переключается в режим хранения с низким потреблением. При включении питания устройство переключает питание с батареи на Vcc, когда напряжение питания превысит Vbat + 0. 2V, и реагирует на входные сигналы, когда Vcc станет более 1.25 x Vbat. Функциональная схема на рисунке 1 показывает главные элементы часов реального времени с последовательным интерфейсом. Следующие параграфы описывают функцию каждого вывода.



Рисунок 1. Функциональная схема DS1307

Описания сигналов

Vcc, GND - постоянное напряжение питания. Vcc = + 5 вольт. Когда питание находится в пределах нормы, устройство полностью доступно, и данные могут быть записаны и считаны. Когда к устройству подключена трёхвольтовая батарея и Vcc ниже 1.25 x Vbat, чтение и запись запрещены. Однако отсчёт времени при этом работает. Когда Vcc падает ниже Vbat, питание ОЗУ и отсчёта времени переключается на внешнюю батарею 3 В.

Vbat - вход батареи для любого стандартного 3 вольтового литиевого элемента или другого источника энергии. Для нормальной работы напряжение батареи должно поддерживаться между 2.5 и 3.5 В. Уровень, при котором запрещён доступ к часам реального времени и пользовательскому ОЗУ,установлен внутренней схемой равным 1.25 x Vbat. Литиевая батарея ёмкостью 35 mAh или больше достаточна для питания DS1307 в течение более чем 10 лет при отсутствии питания.

SCL (Последовательный Тактовый Вход) - SCL используется, чтобы синхронизировать передачу данных через последовательный интерфейс.

SDA (Вход/Выход Последовательных Данных) - SDA - вход / выход данных для 2-проводного последовательного интерфейса. Это выход с открытым стоком, который требует внешнего притягивающего резистора.

SQW/OUT (Меандр / Выходной Драйвер) - Когда бит SQWE установлен в 1, на выходе SQW/OUT вырабатываются импульсы в форме меандра одной из четырех частот: 1 Hz, 4 КГЦ, 8 КГЦ, 32 КГЦ. Вывод SQW/OUT - с открытым стоком, требует внешнего притягивающего резистора.

X1, X2 - выводы для подключения стандартного кристалла кварца 32.768 КГЦ. Внутренняя схема генератора рассчитана на работу с кристаллом, имеющим номинальную емкость (CL) 12.5 pF.

Адресное пространство часов и ОЗУ

Карта адресов для RTC и регистров ОЗУ DS1307 показана на рисунке 2. Регистры RTC расположены по адресам от 00h до 07h. Регистры ОЗУ расположены по адресам от 08h до 3Fh. Во время многобайтового доступа, когда указатель адреса достигает 3Fh, конца пространства ОЗУ, он возвращается к 00h, началу пространства часов.

Примечание переводчика:

Состояния регистров DS 1307 при включении питания (числа двоичные):

(установлено экспериментально при помощи внутрисхемного эмулятора микропроцессора)

seconds 10000000 - значит, при включении питания часы стоят!

minutes0

hours0

day1

date1

month1

year 0

control11


Часы и календарь

Информацию о времени и дате получают, считывая соответствующие регистры. Регистры часов показаны на рисунке 3. Время и календарь устанавливаются или инициализируются путём записи байтов в соответствующие регистры. Содержание регистров времени и календаря хранится в двоично-десятичном (BCD) формате. Бит 7 Регистра 0 - это бит остановки часов (CH). Когда этот бит установлен в 1, генератор остановлен. Когда сброшен в ноль, генератор работает.

DS1307 может работать в 12-часовом или 24-часовом режиме. Бит 6 регистра часов задаёт один из этих режимов. Когда он равен 1, установлен 12-часовой режим. В 12-часовом режиме высокий уровень бита 5 сообщает о послеполуденном времени. В 24-часовом режиме бит 5 - второй бит 10 часов (20-23 часа).

Регистр управления


Регистр управления DS1307 предназначен для управления работой вывода SQW/OUT.

OUT (управление выходом): Этот бит управляет выходным уровнем на выводе SQW/OUT, когда генерация меандра запрещена. Если SQWE = 0, логический уровень на выводе SQW/OUT равен 1, если OUT = 1, и 0 - если OUT = 0.

SQWE (Разрешение меандра): Когда этот бит установлен в 1, разрешается генерация меандра. Частота меандра зависит от значений битов RS0 и RS1.

RS (Выбор частоты): эти биты управляют частотой меандра, когда его генерация разрешена. В таблице 1 показаны частоты, которые могут быть заданы RS битами.

2-проводная последовательная шина

DS1307 поддерживает двунаправленные 2-проводную шину и протокол передачи данных. Устройство, которое посылает данные на шину, называется передатчиком, а устройство, получающее данные - приемником. Устройство, которое управляет передачей, называется ведущим. Устройства, которые управляются ведущим - ведомые. Шина должна управляться ведущим устройством, которое вырабатывает последовательные такты (SCL), управляет доступом к шине, и генерирует состояния СТАРТ и СТОП. DS1307 работает как ведомое на 2-проводной шине. Типичная конфигурация шины, использующая этот 2-проводной протокол, показана на рисунке 4.


Определен следующий протокол шины:

Передача данных может быть начата только тогда, когда шина не занята.

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

Соответственно, были определены следующие состояния шины:

Шина свободна . На обеих линиях: данных и тактов высокий уровень.

Начало передачи данных: переход линии данных от высокого уровня к низкому при высоком уровне на линии тактов определяет условие СТАРТ.

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

Правильные данные : линия считается находящейся в состоянии передачи данных, когда после состояния СТАРТ уровень на линии данных не изменяется во время высокого уровня на линии тактирования. Он должен изменяться только во время низкого уровня тактового сигнала. Имеется один тактовый импульс на бит данных. Каждая передача данных начинается с состояния СТАРТ и заканчивается состоянием СТОП. Число байтов данных, переданных между этими состояниями, не ограничено и определяется ведущимустройством. Информация передаётся байтами, и каждый приемник посылает подтверждение с девятым битом.

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

Передача данных

На рисунках 5, 6, и 7 подробно показано, как передаются данные по 2-проводной шине. В зависимости от состояния бита R/W при передаче, как показано на рисунках 6 и 7, возможны два типа передачи данных:


1. Данные передаются от ведущего к ведомому. Первый байт, переданный ведущим - адрес ведомого. Затем следуют байты данных. Ведомый возвращает подтверждающий бит после каждого полученного байта. Данные передаются, начиная со старшего бита (MSB).

2. Данные передаются от ведомого к ведущему. Первый байт (адрес ведомого) передаётся ведущим. Тогда ведомый возвращает подтверждающий бит. Затем ведомый передаёт байты данных. Ведущий возвращает подтверждающий бит после всех полученных байтов, кроме последнего. В конце последнего полученного байта, возвращается "неподтверждение". Ведущее устройство генерирует все тактовые импульсы и состояния СТАРТ и СТОП. Передача заканчивается формированием состояния СТОП или повторного состояния СТАРТ. Так как повторное состояние СТАРТ является также началом следующей последовательной передачи, шина не будет освобождена. Данные передаются старшим битом (MSB) вперёд. DS1307 может использовать следующие два способа:

1. Режим ведомого приемника (режим записи DS1307): Последовательные данные и такты получены через SDA и SCL. После передачи каждого байта передаётся подтверждающий бит. Состояния СТАРТ и СТОП опознаются как начало и конец последовательной передачи. Распознавание адреса выполняется аппаратно после приема адреса ведомого и бита направления (См. рисунок 6). Байт адреса содержит семибитный адрес DS1307, равный 1101000, сопровождаемым битом направления (R/W), который при записи равен 0. После получения и расшифровки байта адреса DS1307 выдаёт подтверждение на линии SDA. После того, как DS1307 подтверждает адрес ведомого и бит записи, ведущий передает адрес регистра DS1307. Тем самым будет установлен указатель регистра в DS1307. Тогда ведущий начнет передавать байты данных в DS1307, который будет подтверждать каждый полученный байт. По окончании записи ведущий сформирует состояние СТОП.

2. Режим ведомого передатчика (режим чтения из DS1307): Первый байт принимается и обрабатывается как в режиме ведомого приёмника. Однако в этом режиме бит направления укажет, что направление передачи изменено. Последовательные данные передаются по SDA от DS1307, в то время как последовательные такты - по SCL в DS1307. Состояния СТАРТ и СТОП опознаются как начало и конец последовательной передачи (См. рисунок 7). Байт адреса - первый байт, полученный после того, как ведущим сформировано состояние СТАРТ. Байт адреса содержит семибитный адрес DS1307, равный 1101000, сопровождаемым битом направления (R/W), который при чтении равен 1. После получения и расшифровки байта адреса DS1307 выдаёт подтверждение на линии SDA. Тогда DS1307 начинает передавать данные, начинающиеся с адреса регистра, на которые указывает указатель регистра. Если указатель регистра не записан перед инициированием режима чтения, то первый адрес, который читается - это последний адрес, оставшийся в указателе регистра. DS1307 должен получить "Неподтверждение", чтобы закончить чтение.





NOTES:

1. All voltages are referenced to ground.

2. Logic zero voltages are specified at a sink current of 5 mA at V CC =4.5V, V OL =GND for capacitive loads.

3. I CCS specified with V CC =5.0V and SDA, SCL=5.0V.

4. V CC =0V, V BAT =3V.

5. After this period, the first clock pulse is generated.

6. A device must internally provide a hold time of at least 300 ns for the SDA signal (referred to the V IHMIN of the SCL signal) in order to bridge the undefined region of the falling edge of SCL.

7. The maximum t HD:DAT has only to be met if the device does not stretch the LOW period (t LOW) of the SCL signal.

8. C B – total capacitance of one bus line in pF.

9. I CCA – SCL clocking at max frequency = 100 KHz.

10. SCL only.

11. SDA and SQW/OUT




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

Особенности:

  • Очень маленькое энергопотребление. Производитель обещает 10 лет работы часов от одной стандартной батарейки CR2032
  • 56 байт памяти для хранения пользовательских данных. Думаю не особо нужная опция, но может кому-то и пригодится.
  • Программируемый вывод для тактирования внешних устройств. Может выдавать 1 Гц, 4.096 кГц, 8.192 кГц и 32.768 кГц.
  • 24-х часовой и 12-ти часовой режим

Распиновка

Выводы часов расположены следующим образом:

X1, X2 — Выводы для подключения кварцевого резонатора на частоту 32.768 кГц
VBAT — Вывод для подключения 3-х вольтовой батареи резервного питания
GND — Земля
SDA — линия данных шины i2c
SCL — линия тактовых импульсов шины i2c
SQW/OUT – выходной сигнал для тактирования внешних устройств
VCC — питание 5 вольт

Подключение к контроллеру
Обвязка минимальна. Потребуется кварц 32.768 кГц, пара резисторов для работы шины i2c и батарейка на три вольта.

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

Кстати может работать и без кварца. Для этого на ногу X1 подают внешний тактовый сигнал с частотой 32.768 кГц, а X2 остаётся висеть в воздухе.

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

Все числа в памяти хранятся в двоично-десятичном формате. Это значит что в одном байте может хранится сразу две цифры. Например число 0x23 — содержит в себе цифру 2 и цифру 3. На каждую цифру выделяется по 4 бита. Зачем так сделано? Для удобства и экономии памяти. Кроме времени и даты в памяти хранятся несколько бит настроек:

  • Clock Halt — управляет часами. Когда бит установлен то часы стоят. Чтобы запустить ход часов необходимо записать в этот бит 0. После подключения батареи резервного питания, этот бит уставлен и часы не считают время! Об этом нужно помнить.
  • 24/12 — этот бит выбора режима часов. Когда этот бит равен единице то используется 12-ти часовой режим. В противном случае 24-х часовой. Если используется 12-ти часовой режим то пятый бит показывает AM или PM сейчас. Если бит равен 1 то значит PM. В 24-х часовом режиме этот бит используется для хранения десятков часов совместно с битом 4.
  • Output — управляет состоянием ноги SQW/OUT. Бит установлен — на ноге лог 1. Сброшен — на ноге 0. Для управления таким образом, бит SQWE должен быть сброшен.
  • SQWE — когда бит установлен, на ноге SQW/OUT появляются прямоугольные импульсы.
  • RS1, RS0 — этими битами задается частота импульсов. Зависимость частоты от комбинации бит находится в таблице ниже:

Софт

Для работы с часами DS1307 была написана нехитрая библиотека содержащая следующие базовые функции:

DS_start — запускает часы. Запустить часы можно так же установив время.
DS_stop — останавливает часы
DS_set_time — Установка времени. Перед вызовом процедуры нужно поместить в tmp1 — секунды в tmp2 — минуты и в tmp3-часы. Часы в 24-х часовом формате.
DS_get_time: — считывание времени из часов. секунды будут записаны в tmp1, минуты в tmp2, часы в tmp3
DS_get_date: — считывание даты из часов. День будет записан в tmp1, месяц в tmp2, год в tmp3
DS_set_date: — установка даты. Перед вызовом процедуры нужно поместить в tmp1 — день в tmp2 — месяц и в tmp3-год (последние 2 цифры)

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

Тестовая программа позволяет управлять часами через UART (скорость 9600, контроллер работает на частоте 8 мГц). При запуске сразу выдаются время, дата и приглашение ввести команду от 1 до 3. При выборе варианта 1 происходит повторное считывание времени/даты. Вариант 2 позволяет установить время, а вариант 3 дату. Если хочется попробовать поиграть с часами в то в архив с исходником включён файл для симуляции.

Точность
Тут очень многое зависит от применяемого кварца и разводки платы. Даташит сообщает что емкость кварца должна быть 12.5 пф. Говорят, что лучше всего применять кварцы от материнских плат. Для коррекции хода можно подпаять к резонатору подстроечный конденсатором и при помощи него в небольших пределах менять частоту. Лично у меня эти часы работают вторые сутки и отстали на 3 секунды. Что-то мне подсказывает, что дело в ёмкости кварца, попробую другой отпишусь.

Вывод
Неплохие часы. Для любительского применения идеальный вариант. Хотя некоторые пишут о глюках, но я пока не столкнулся.

Во многих проектах Ардуино требуется отслеживать и фиксировать время наступления тех или иных событий. Модуль часов реального времени, оснащенный дополнительной батарей, позволяет хранить текущую дату, не завися от наличия питания на самом устройстве. В этой статье мы поговорим о наиболее часто встречающихся модулях 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-карту или показывая время на экране дисплея. В этой статье мы рассмотрели типичные сценарии использования и варианты подключения наиболее популярных видов модулей.