Мне прилетели из Китая микросхемы и я начал над ними издеваться в целях исследования перед внедрением в будущие часы. Предупреждаю — в данной статье я исследовал только работу со временем, дата и прочие приблуды этих часов меня мало интересовали и я не терял на это время.
Задача: Запись значения времени/чтение значения времени и вывод через USART из микросхемы DS1307.
Микросхема DS1307 — модуль часов реального времени, которым можно управлять по шине данных I2C. Можно конечно было реализовать RTC на базе лишь одного микроконтроллера, но проще подцепить DS1307 + часовой кварц и не запариваться. Начинаем как обычно с вкуривания даташита. Выдергиваем оттуда схему применения и собираем ее:
Выцепляем из даташита организацию памяти микросхемы:
Как я уже отметил ранее, меня интересует лишь время, поэтому будем юзать только адреса 00h, 01h, 02h. Данные в регистрах хранятся в формате BCD.
Вкратце о BCD: смешанный формат хранения данных. Число разбивается на две части, в младшей части байта — обычный бинарный формат, в старшей — фактически число в бинарном формате, смещенное влево на 4 бита и умноженное на 10 . Пример — 0b00110101 => 0b0011 = 3*10 = 30, 0b0101 = 5 — число = 35.
Таким образом, нам нужно проделать некоторые манипуляции чтобы записать нормально число в регистр. Я создал две функции: одна конвертирует десятичное число в BCD, другая конвертирует BCD в десятичный формат.
Конвертация из BCD в десятичную форму:
unsigned char BCDconv (unsigned char source) {
unsigned char temp_min=0; //переменная для единиц unsigned char temp_maj=0; //переменная для десятков temp_min = source&15; //помещаем младшую часть байта в переменную (15 = 0b00001111) temp_maj = source >> 4; //Смещаем старшую часть вправо на 4 бита temp_maj *= 10; return temp_maj+temp_min; //возвращаем десятичное значение }
Дальше нам надо определиться с процедурами записи и чтения в часы, лезем в даташит, там красивая картинка:
На картинке показана процедура записи, с этим вопросов не возникает, создаем функции записи часов, минут, секунд.
void SetHour(unsigned char hours) { //записываем значение в часы
i2c_start(); i2c_tx(0b11010000); i2c_tx(0x02); //адрес часов i2c_tx(DCBconv(hours)); //запихиваем нужное значение i2c_stop(); }
void SetMin(unsigned char minutes) { //записываем значение в минуты
i2c_start(); i2c_tx(0b11010000); i2c_tx(0x01); //адрес минут i2c_tx(DCBconv(minutes)); //запихиваем нужное значение i2c_stop(); }
void SetSeconds(unsigned char seconds) { //записываем значение в секунды i2c_start(); i2c_tx(0b11010000); i2c_tx(0x00); //адрес секунд i2c_tx(DCBconv(seconds)); //впихиваем нужное значение i2c_stop();
}
C чтением есть один небольшой нюанс, на который я вначале не обратил внимания:
Из даташита: The DS1307 must receive a Not Acknowledge to end a read.
А я как образцовый slave отсылал постоянно acknowledge бит . Таким образом:
Потом я это уже и на картинке заметил, так что читаем даташит внимательно, если не хотим потерять много времени впустую.
unsigned char ReadHour() {
unsigned char temp = 0; i2c_start(); i2c_tx(0b11010000); i2c_tx(0x02); i2c_start(); i2c_tx(0b11010001); temp = i2c_rx(0); //читаем значение часов (0) означает, что мы не шлем acknowledge i2c_stop();
return BCDconv(temp);
}
unsigned char ReadMin() {
unsigned char temp = 0; i2c_start(); i2c_tx(0b11010000); i2c_tx(0x01); i2c_start(); i2c_tx(0b11010001); temp = i2c_rx(0); //читаем значение минут (0) означает, что мы не шлем acknowledge i2c_stop();
return BCDconv(temp);
}
unsigned char ReadSeconds() {
unsigned char temp = 0; i2c_start(); i2c_tx(0b11010000); i2c_tx(0x00); i2c_start(); i2c_tx(0b11010001); temp = i2c_rx(0); //читаем значение часов (0) означает, что мы не шлем acknowledge
i2c_stop();
return BCDconv(temp);
}
Теперь сварганим процедуры установки и отображения времени по USART.
По клавише 1 — данные в терминале перезагружаются, я специально не стал делать постоянное отображение времени, чтобы не поймать левых косяков. По клавише 2 запускается процедура установки времени: