AVR Урок 17. Часы реального времени DS1307. Часть 2



 

Урок 17

Часть 2

 

Часы реального времени 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(); 

while(1)

 

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

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

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

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

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

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

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

 

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 (адрес первого регистра) по адресу устройства

 

while(1)

{

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

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

 

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

 

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

_delay_ms(300);

 

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

 

_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();

 

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

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

 

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, Прошьём контроллер и посмотрим результат

 

image04

 

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

 

 

Предыдущая часть Программирование МК AVR Следующий урок

 

Исходный код

 

 

Документация на микросхему DS1307

 

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

Программатор USBASP USBISP с адаптером USBASP USBISP 3.3 с адаптером

Модуль RTC DS1307 с микросхемой памяти

Переходник USB-TTL лучше купить такой (сейчас у меня именно такой и он мне больше нравится)

 

 

Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)

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

 

Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)

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

9 комментариев на “AVR Урок 17. Часы реального времени DS1307. Часть 2
  1. Vlad:

    Превосходно. Особенно помогли ошибки, которые Вы в ходе урока разбираете и устраняете. Мне это помогло в поиске своих ошибок гораздо больше чем многотомники по С++. Благодарю Вас за прекрасные уроки. 

  2. Alexey:

    При Установке времени. Зачем возводить Hex Число в Hex
    31= 0011 0001. Мы сдвигаем его влево на полубайт = 0000 0011 и умножаем на 10 получается 0011 0000. и прибавляем к нему:
    0000 1111 * 31

    0000 1111
    &
    0011 0001 (31)Hex
    =
    0000 0001

    а
    0011 0000+0000 0001=0011 0001
    Следовательно ничего не изменилось, мы просто возвели шестнадцатиричный блок в десятичный разделили на десятки и единицы и добавили к нему 0x30(0x30+3=0x33=Символ 3) и отправили в Hex по Uart.
    Можно записывать сразу в шестнадцатиричной системе.
    I2C_SendByte(0x00); //секунды
    I2C_SendByte(0x31); //минуты
    I2C_SendByte(0x20); //часы
    I2C_SendByte(0x05); //день недели
    I2C_SendByte(0x29); //дата
    I2C_SendByte(0x01); //месяц
    I2C_SendByte(0x16); //год
    Это сократит длину кода.

  3. W4d1m:

    Здравствуйте! А как вывести это все на дисплей 20×4 по I2C? К примеру, секунды выводил. В main дисплей инициализировал, в while процедуру считывания времени прописал. Преобразования в том числе. Через sendcharlcd(sec/10+0x30) и sendcharlcd(sec%10+0x30) на дисплей вывожу. Какая то чепуха выводится. Разделительные знаки присутствуют а вот остальное кракозябрами, секунды — случайный знако-символьный набор. В терминале также не пойми что. По отдельности все работает как надо. Подскажите, что я упустил? И, да, большое спасибо Вам за уроки! Всегда интересно узнавать что-то новое и полезное. Спасибо!

  4. qwas:

    А что интересно админ ответит на написанный выше комментарий?
    Или он только отвечает на комментарии типа «превосходно» или «чудесно»?

    • qwas,
      Здравствуйте!
      Пока, к сожалению, на все комментарии отвечать времени нет.
      Да и всегда я отвечал только на те комментарии, на которые у меня есть ответ, так как врать не привык, не так воспитан.
      Потихоньку отделаюсь от годовой отчётности и вернусь.
      Поэтому я никогда не запрещаю, а наоборот даже приветствую ответы посетителей ресурса.

  5. Семён Морозов:

    Здравствуйте! Не подскажите, микросхема ds1302 написано в даташите 3-wire интерфейс, кроме clk и sda еще есть reset. Её можно к i2c интерфейсу подключить, установив линию reset в верхний уровень?
    С Уважением, Семён. Спасибо.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*