Часы реального времени DS1307

Часы реального времени DS1307

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

Продолжим писать наш код, используя тот же самый проект

Используя написанную функцию перевода из десятичного формата числа в двоично-десятичный, занесём данные в регистры микросхемы, начиная с нулевого адреса (также мы знаем что это и адрес самого первого регистра микросхемы)

I2C_SendByte (0); //Переходим на 0x00

I2C_SendByte ( RTC_ConvertFromBinDec (0)); //секунды

I2C_SendByte ( RTC_ConvertFromBinDec (31)); //минуты

I2C_SendByte ( RTC_ConvertFromBinDec (20)); //часы

I2C_SendByte ( RTC_ConvertFromBinDec (5)); //день недели

I2C_SendByte ( RTC_ConvertFromBinDec (29)); //дата

I2C_SendByte ( RTC_ConvertFromBinDec (1)); //месяц

I2C_SendByte ( RTC_ConvertFromBinDec (16)); //год

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

Ну, и в конце, конечно условие STOP

I2C_SendByte ( RTC_ConvertFromBinDec (16)); //год

I2C_StopCondition ();

Соберем код, установим в скобки правильное время и прошьём контроллер.

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

Можно конечно было бы посмотреть статусы, но раз уж нам всё равно читать показания регистров, то особого смысла в этом не вижу.

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

Теперь в бесконечный цикл начнем писать код считывания данных из регистров

Точно также всё начинается с условия СТАРТ.

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

void I2C_SendByteByADDR ( unsigned char c , unsigned char addr )

I2C_StartCondition (); // Отправим условие START

I2C_SendByte ( addr ); // Отправим в шину адрес устройства + бит чтения-записи

I2C_SendByte ( c ); // Отправим байт данных

I2C_StopCondition (); // Отправим условие STOP

Также заодно напишем функцию чтения обычного байта из шины и чтения последнего байта из шины. У нас такие функции уже были, но они были в особенном файле и были с префиксом EE_, а также там была обработка ошибки. Скопируем их себе теперь в стандартный обычный TWI.c, убрав префиксы и всё лишнее

unsigned char I2C_ReadByte ( void )

TWCR = (1<< TWINT )|(1<< TWEN )|(1<< TWEA );

while (!( TWCR & (1<< TWINT ))); //ожидание установки бита TWIN

return TWDR ; //читаем регистр данных

unsigned char I2C_ReadLastByte ( void )

TWCR = (1<< TWINT )|(1<< TWEN );

while (!( TWCR & (1<< TWINT ))); //ожидание установки бита TWIN

return TWDR ; //читаем регистр данных

Напишем в TWI.h на все эти функции прототипы

void I2C_SendByte ( unsigned char c ); //передача байта в шину

void I2C_SendByteByADDR ( unsigned char c , unsigned char addr ); //передача байта в шину на устройство по адресу

unsigned char I2C_ReadByte ( void ); //читаем байт

unsigned char I2C_ReadLastByte ( void ); //читаем последний байт

И теперь можно начинать писать код в бесконечный цикл функции main().

Отправим адрес 0 (адрес первого регистра) по адресу устройства

//Читаем время

I2C_SendByteByADDR (0,0b11010000); //переходим на адрес 0

Затем вставим задержку на 300 милисекунд. Данную задержку можно и в конце кода вставить, ну давайте попробуем здесь, поэксперементируем, так сказать

I2C_SendByteByADDR (0,0b11010000); //переходим на адрес 0

_delay_ms (300);

Затем посылаем в шину адрес устройства с битом чтения, отправив перед этим условие СТАРТ

I2C_StartCondition (); //Отправим условие START

I2C_SendByte (0b11010001); //отправим в устройство бит чтения

По идее мы вообще так не должны делать, так как у нас в функции, которую мы вызвали перед задержкой было в конце условие СТОП, а это не требуется. Обычно сразу СТАРТ. Но можно и так, всё работает. Если указатель установлен уже туда, куда надо, то можно стразу адрес чтения и начинать читать, а не так, как было в случае с EEPROM.

Дальше читаем все регистры

I2C_SendByte (0b11010001); //отправим в устройство бит чтения

sec = I2C_ReadByte ();

min = I2C_ReadByte ();

hour = I2C_ReadByte ();

day = I2C_ReadByte ();

date = I2C_ReadByte ();

month = I2C_ReadByte ();

year = I2C_ReadLastByte ();

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

В конце чтения отправим в шину условие СТОП

I2C_StopCondition (); //Отправим условие STOP

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

I2C_StopCondition (); //Отправим условие STOP

sec = RTC_ConvertFromDec ( sec ); //Преобразуем в десятичный формат

min = RTC_ConvertFromDec ( min ); //Преобразуем в десятичный формат

hour = RTC_ConvertFromDec ( hour ); //Преобразуем в десятичный формат

day = RTC_ConvertFromDec ( day ); //Преобразуем в десятичный формат

year = RTC_ConvertFromDec ( year ); //Преобразуем в десятичный формат

month = RTC_ConvertFromDec ( month ); //Преобразуем в десятичный формат

date = RTC_ConvertFromDec ( date ); //Преобразуем в десятичный формат

И в коце бесконечного цикла отправим всё это в определённом виде в USART

date = RTC_ConvertFromDec ( 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 ( 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); //перевод каретки

Смещение на 0x30 в вычисление кода символа — это преобразование самой цифры в код цифры. Именно такая разница и есть в таблице ascii.

Можно конечно не париться с таким преобразованием и использовать функцию sprintf и она прекрасно с этим справится, но так интересно, функция sprintf ещё себя покажет, это я уж вам обещаю точно.

Соберём код, откроем терминальную программу, нажмём там Connect, Прошьём контроллер и посмотрим результат

Наши часы отлично ходят.

Программатор, модуль RTC DS1307 с микросхемой памяти и переходник USB-TTL можно приобрести здесь:

📎📎📎📎📎📎📎📎📎📎