Библиотека AVR для работы с шиной I2C и с часами реального времени PCF8583

Мне нужно было сделать часы на основе микросхемы, имеющей IC интерфейс. Микросхема RTC, т.н. «часы реального времени» PCF8583.

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

Но была одна проблемка — писАть код жутко не хотелось, и я решил найти готовый код в интернете. Как позже выяснилось, найти «на свою голову». Скачал пример работы с IC, подправил, прошил микроконтроллер. Не заработало. Стал ковырять код, искать причину неработоспособности… и ужаснулся!! Запись в некоторых случаях велась во весь порт сразу, а не в конкретные биты. Таким образом, если на порт повесить ещё что-то, например, дисплей, то скорее всего, оно работать не будет. Также неправильно было реализовано чтение данных по шине (без генераций условия окончания приёма, или просто без NACK). Но это пол-беды. Основная проблема в другом. Зачастую автор кода выставлял в порт логическую «1», а как мы знаем, шина IC управляется «притягиванием» выводов SDA и SCL к общему проводу. А логическая «1» на шине, в свою очередь, формируется за счёт подтяжки к плюсу питания резисторами на 4,7 килоом. Таким образом, если на выходе микроконтроллера выставить логическую «1», а ведомое устройство «притянет» этот выход к общему проводу, то получится «ба-бах» короткое замыкание. Мне это очень не понравилось, и я решил изобрести свой велосипед  написать свою библиотеку, а вернее даже 2 библиотеки: одна для работы с шиной IC, а другая непосредственно для работы с часами реального времени PCF8583. Да, кстати, код написан в AVR Studio 6.

Для того, чтобы подключить библиотеку IC к проекту, нужно прописать её через include, как на картинке, а также скопировать библиотеку в папку с проектом.

После чего, необходимо открыть файл «i2c.h», и указать ножки микроконтроллера, которые будут выступать в роли шины IC. По умолчанию шина настроена на ножки PC0 (SCL) и PC1 (SDA). А настройка делается вот тут:

Всё, библиотеку I2C мы подключили, ножки настроили, библиотека готова к работе. Пример использования:

 i2c_init ();    // Инициализация шины I2C  i2c_start_cond();   // старт шины  i2c_send_byte (0xA0);   // адрес устройства, которое висит на шине  i2c_send_byte (0x10);   // байт данных, который записываем в устройство  i2c_send_byte (0x10);   // ещё один байт данных, который записываем в устройство  i2c_stop_cond();   // стоп шины

После стоп-условия, мы можем проверить, всё ли у нас в порядке с шиной IC. Для этого нужно прочитать переменную «i2c_frame_error». Если всё нормально, то в ней будет 0. Если один из выводов шины не «подтянулся» к питанию, и логическая «1» не установилась на шине, то библиотека генерирует ошибку, и записвает в переменную «i2c_frame_error» циферку 1. Читать переменную «i2c_frame_error» нужно после стоп-условия. На рисунке ниже продемонстрирую как работает контроль ошибки:

Теперь займёмся подключением библиотеки часов реального времени PCF8583. Для этого нужно проделать те же самые действия. Скопируем в папку с проектом файл «PCF8583.h», и пропишем его в include, как на фото:

Готово. Библиотека часов реального времени PCF8583 подключена. Она не требует каких-либо настроек, поэтому можно сразу приступать к чтению времени и даты с микросхемы. Обращаю внимание, что библиотека PCF8583 работает при помощи библиотеки I2C, поэтому если хотим работать с PCF8583, то нужно подключить обе библиотеки!

Пример использования библиотеки (запись и чтение времени и даты):

 // Инициализация шины I2C  i2c_init ();   // Подготавливаем время и дату для записи в микросхему PCF8583  PCF_hour=23;   // 23 часа  PCF_min=59;   // 59 минут  PCF_day=31;   // 31 число  PCF_month=12;   // 12 месяц - декабрь  PCF_year=0;   // год (0 - не високосный)  PCF_weekday=6;   // 6 день недели (воскресенье)  // Записываем время и дату в микросхему PCF8583  PCF_write_hh_mm_ss();  // Считываем время и дату из микросхемы PCF8583  PCF_read_hh_mm_ss();   Пример работы с оперативной памятью (запись и чтение):  // Подготавливаем 5 байт для записи в микросхему PCF8583  PCF_data_ram_1=255;   // байт 1  PCF_data_ram_2=255;   // байт 2  PCF_data_ram_3=255;   // байт 3  PCF_data_ram_4=255;   // байт 4  PCF_data_ram_5=255;   // байт 5   // Записываем 5 байт в микросхему PCF8583  PCF_write_ram();   // Считываем 5 байт из микросхемы PCF8583  PCF_read_ram(); 

Чтение из микросхемы ещё проще – достаточно вызвать функцию PCF_read_hh_mm_ss() после чего, время и дата появятся в переменных, откуда их только забирай. Для чтения оперативной памяти соответственно используем функцию PCF_read_ram()  после чего данные забираем в переменных PCF_data_ram_N

Вот список переменных, где и что хранится:

 // время и дата  PCF_hour=0; // время, часы (от 0 до 23, защита от переполнения при записи и чтении)  PCF_min=0;  // время, минуты (от 0 до 59, защита от переполнения при записи и чтении)  PCF_sec=0;  // время, секунды (только для чтения, при записи сбрасываются в 00)  PCF_day=0;  // день (от 1 до 31, защита от переполнения при записи и чтении)  PCF_weekday=0 // день недели (0-понедельник; 6-воскресенье, защита от переполнения при записи и чтении)  PCF_month=0; // месяц (от 1 до 12, защита от переполнения при записи и чтении)  PCF_year=0; // год (0-високосный; 1,2,3-невисокосные, защита от переполнения при записи и чтении)   // оперативная память  PCF_data_ram_1;   // Данные (ОЗУ PCF8583), байт 1  PCF_data_ram_2;   // Данные (ОЗУ PCF8583), байт 2  PCF_data_ram_3;   // Данные (ОЗУ PCF8583), байт 3  PCF_data_ram_4;   // Данные (ОЗУ PCF8583), байт 4  PCF_data_ram_5;   // Данные (ОЗУ PCF8583), байт 5

Теперь расскажу про защиту от переполнения. Допустим, мы забыли подключить микросхему. Прочитаем данные с микросхемы, и… прочитается байт 11111111, или число 255. Всё дело в том, что в основе шины IC лежат 2 подтягивающих резистора, вот они то и выдают нам логические «единички» если микросхема не подключена. Для защиты от подобных случаев, в библиотеке PCF8583 я сделал защиту от переполнений, которая следит за тем, чтобы часики не показывали вам 62 часа 81 минуту… Наличие переполнения можно проследить, прочитав переменную «PCF_overflow». Если в ней 0, значит ошибок переполнения не было. Если в ней 1 или более, значит ошибки переполнения имеются. Читать переменную «PCF_overflow» нужно после функции чтения даты и времени PCF_read_hh_mm_ss()

Для наглядности, проект AVR Studio 6 под ATmega32 прилагается. Перекомпилировать можно под любой AVR. В проекте я также подключил дисплей для визуального контроля. При подаче питания, микроконтроллер устанавливает 23 часа 59 минут, 31 декабря, Воскресенье. И через минуту становится 00 часов 00 минут, 1 января, Понедельник.

Теперь расскажу, почему я говорил про «кривой» календарь этой микросхемы. Всё дело в том, что микросхема не умеет хранить текущий календарный год, а хранит лишь флаг високосного года. Короче говоря:
0 – високосный год
1 – не високосный год
2 – не високосный год
3 – не високосный год

И так по циклу 0-1-2-3-0-1-2-3-0…

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

Также прилагаю в конце статьи небольшой видеоотчёт. В программировании я можно сказать новичок, хоть и программирую уже 3 года (понемногу), за код строго не судите, если есть какие-либо дополнения и замечания, пишите, будем исправлять. Всем удачных самоделок!

Список радиоэлементов

Обозначение Тип Номинал Количество Примечание Магазин Мой блокнот
МК AVR 8-бит ATmega32 1
Часы реального времени (RTC) PCF8583 1
LCD-дисплей WH1602 1
Резистор 4.7 кОм 2
Кварц 32768 Гц 1