Часы реального времени 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 можно приобрести здесь: