Работа с I2C и SPI на примере часов реального времени PCA2129T

Все началось с того, что ко мне в руки попала микросхема PCA2129T. После непродолжительного “гугления” выяснилось, что это чудо техники производит NXP Semiconductors аж в Нидерландах. Хотя это не особенно интересно, гораздо интереснее оказался даташит на данную микросхему. Скачивается он с сайта производителя, а если быть точным, то вот здесь. Скачиваем, открываем и начинаем внимательно изучать, попутно осуществляя перевод с английского языка.

Итак, изучив первую страницу даташита, выясняем, что PCA2129T обычные часы реального времени (далее RTC), со встроенным кварцем на 32,768 КГц. и температурной компенсацией оного. Да ещё и оптимизирована для высокоточных устройств с низким потреблением. Кроме этого, микросхема может работать на шинах I2C и SPI. Вот этот момент, конечно не совсем понятен. Я лично так и не смог представить себе устройство в котором требуется подключить RTC именно по SPI, потратив на это целых 4 (ну или 3) вывода микроконтроллера. Разве что у вас уже подключено 157 устройств по I2C (а больше и нельзя), а часов для полного счастья как раз и не хватает. Ну да ладно, он никому не мешает. Читаем дальше:

  • Осциллятор с температурной компенсацией и интегрированными конденсаторами;
  • Точность хода 3ppm (part per million), температуре от -30 до +80 градусов Цельсия. Т.е. часта кварца может изменяться на 3 миллионных. Очень даже неплохо, я бы даже сказал замечательно. Обычные часовые кварцы имеют минимальное отклонение 5ppm и это у самых-самых, обычно все намного хуже и ppm переваливает за 10;
  • Год, месяц, день, часы, минуты, секунды и подстройка под високосный год (ну этим уже никого не удивишь);
  • Функция штампа времени. Забавная штуковина, например не хотим мы, чтобы пользователь ковырял наше устройство. Берем и вешаем на крышку микропереключатель. Крышку открыли, контакт замкнулся, и в память микросхемы записалась дата и время. Очень удобно, особенно если после вскрытия устройство работать перестала, а пользователь клянется, что ничего не трогал. Либо может поставить “штамп” при смене батарейки, чтобы не забыть когда поменяли;
  • Опять описание интерфейсов;
  • Резервная батарея. Куда же без нее;
  • Контроль напряжения батареи. А вот это полезная штука, не надо АЦП МК использовать. Стукнули микросхеме, она все измерила и сказала нам пора менять или нет;
  • Детектор отключения внешнего питания;
  • Сброс при подаче питания;
  • Детектор остановки осциллятора;
  • Выход прерываний. На него можно вывести ежесекундное/минутное прерывание, прерывание сторожевого таймера и прерывания от будильника;
  • Программируемый выход.

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

Тут все просто: питание (Vss, Vdd), интерфейсы (SDA/CE, SDO, SDI, SCL), переключение интерфейсов (IFS) резервная батарея (вход Vbat и выход BBS), вывод прерываний (INT), вход для “штампа” даты (TS), программируемый выход (CLKOUT) и ещё 4 вывода для которых производители не придумали никаких функций.

Теперь самое интересное — описание адресов регистров памяти и хранящихся в них данных. В самом начале памяти хранятся данные контрольных регистров, т.е. настройки микросхемы. Дальше идет время и дата. Все, что связано с временем и датой хранится хитрым образом. За десятки чисел отвечают одни биты 8-миюитного числа в памяти, а за единицы – другие. Это называется BCD — двоично-десятичный код. Потому при чтении и записи этих данных следует позаботиться об их преобразовании. Дальше хранится время будильника (тоже в BCD), настройки температурной компенсации и программируемого выхода. Следующие две ячейки памяти занимают настройки сторожевого таймера, сразу за ними идут регистры “штампа” времени. И завершается все это дело настройками точности хода, значение варьируется от -7 до +8 сек. Да, есть ещё два регистра, которые можно использовать для хранения своих данных. В общем, не так уж и много отличий от DS1307.

Ну вот, с регистрами памяти разобрались, теперь можно попытаться запустить это чудо техники. Паяем микросхему вот на такую плату (ну или на любую другую):

А сверху наклеиваем бумажку с названием выводов разъема:

Размеры, конечно, гигантские по сравнению с той же DS3107. Зато производитель обещает высокую точность хода, плюс не надо искать высокоточный кварц. И из внешних компонентов лишь 3 резистора, ну и пара конденсаторов по питанию. Кстати о питании. Минимальное напряжение всего 1,8В., а максимально 6,5В. хотя производитель рекомендует не превышать 4,2В. Да, сжечь ее достаточно трудно. Один раз перепутал провода питания и подал питание наоборот. Обнаружил это, когда что-то задымило. Микросхема раскалилась, но выжила. После охлаждения и правильной подачи питания заработала, как ни в чем не бывало.

Итак, теперь все готово для работы. Начнем с I2C, там проводов меньше. Сразу скажу, что I2C буду использовать программный. Он, конечно, не имеет таких широких возможностей, как аппаратный, но для работы с такой примитивной микросхемой его вполне хватит. Для написания библиотеки программного I2C нам потребуется описание его принципов работы. Сложного ничего нет, открываем спецификацию протокола и читаем. В общем виде работа I2C выглядит следующим образом:

  • Начало передачи определяется Start последовательностью — переход линии SDA на низкий уровень при высоком уровне SCL;
  • При передаче информации от Master к Slave, ведущий генерирует такты на SCL и выдает биты на SDA. Которые ведомый считывает когда SCL становится 1.
  • При передачи информации от Slave к Master, ведущий генерирует такты на SCL и считывает данные с линии SDA. А ведомый следит за состоянием SCL и после того, как SCL уходит в 0, отправляет на SDA бит, который мастер считывает, после того как переводит SCL в высокий уровень.
  • Заканчивается все STOP последовательностью. Когда при высоком уровне на SCL линия SDA переходит из низкого в высокий уровень.

   Ну вот, вроде все просто. Да, AVR МК содержат аппаратный I2C, который производители обозвали TWI. Вообще очень удобная штука, не надо думать о том, как идет передача, все разруливается аппаратно. Ещё и целую кучу ошибок может отслеживать. Для полного счастья к нему приделали прерывания. Т.е. подключили, настроили МК и все работает. Но есть все же один маленький минус – жесткая привязка к определенным выводам МК. И вот бы весь порт под какую-нибудь периферию задействовать, например как тут /me/mc/mc187.php (очень удобно было все лампы подключить на один порт), а там I2C. А теперь начинаем думать, работает интерфейс вроде не очень сложно, а что если реализовать его програмно? Конечно, при этом мы потеряем отслеживание ошибок и прерывания, зато обретем полную свободу действий. Тем более, если устройство всего одно, ещё и установлено на плате вместе с МК, то отслеживать ошибки нам не очень то и нужно. Устройство ведь без нашего ведома никуда убежать не может. Ага, значит все вполне реализуемо. Берем и пишем библиотеку. Весь код я в статье приводить не буду, а то она получится слишком длинной, опишу лишь основные функции. Все остальное будет в исходниках, которые я снабдил комментариями. Итак, вот функции:

i2c_init() – настройка портов МК и установка обеих шин в состояние 1.

i2c_start() – отправка СТАРТ последовательности.

i2c_stop() – отправка СТОП последовательности.

i2c_write() – отправка данных.

i2c_read() – прием данных.

Ну вот и все, теперь пишем библиотеку для самой PCA2129. Сначала заглянем в даташит, в раздел Interfaces и найдем там диаграммы записи и чтения. Вот эти:

Для записи (верхняя диаграмма) все просто, оправляем адрес устройства + бит чтения/записи (0), адрес ячейки памяти и данные. Ну и не забываем про СТАРТ и СТОП. Для чтения (нижняя диаграмма) все немного сложнее. Сначала отправляем адрес устройства + бит чтения/записи (0) и адрес ячейки памяти. После этого отправляем СТОП и снова СТАРТ. А теперь отправляем адрес устройства + бит чтения/записи (1) и начинаем читать данные. Т.е. все что от нас требуется – это отправлять адреса соответствующих ячеек памяти и читать/писать данные. Значит, мы можем легко и непринужденно написать библиотеку для PCA2129. Получилось вот так (код как всегда в прикрепленных исходниках):

pca2129_init()– инициализация

pca2129_set_time() – установка времени

pca2129_get_time() – чтение времени

pca2129_set_date() – установка даты

pca2129_get_date() – чтение даты

pca2129_set_alarm() – установка будильника

pca2129_get_alarm() – чтение состояния будильника

low_bat() – чтение состояния батареи

WDT() – настройка сторожевого таймера

Ну вот, собственно, и все. Подключаем PCA2129 к отладочной плате (на ней стоит ATmega16).

Включаем и… Ура, заработало!

А ведь и правда, работает. И время, и дата, даже будильник и определения заряда батареи. Функцию “штампа” времени я не писал, но если понадобится, дописать не проблема.

 Итак, с I2C все понятно. Перемещаемся на SPI. Он тоже есть в программной реализации. Как раз те самые выводы, к которым подсоединяется программатор: MOSI, MISO, SCK. Ничем особенным эта шина не отличается. По SCK шлем тактовые импульсы, а по MOSI и MISO идет обмен данными (MOSI – выход, MISO – вход). Да, ещё нужна линия CE (chip enable), именно она говорит устройству на линии о начале обмена данными. МК опускает линию в 0 и начинает обмен. Да, у каждого устройства на шине своя линия CE. Обмен данными по SPI происходит ещё проще, чем по I2C. Да, она ещё и более скоростная, до 6,5 Мбит/с. Вполне можно подключать высокопроизводительный АЦП или ЦАП. Мы же будем подключать часы. Обмен данными выглядит следующим образом:

Как видно из диаграмм, все элементарно. По сути это обычный сдвиговый регистр. При записи используется лишь вывод MOSI МК, при чтении добавляется вывод MISO. Причем запись и чтение данных идут при противоположенных состояниях линии SCK. Как именно — зависит от режима работы SPI, а их целых 4. Описание всех режимов есть в соответствующей документации, поэтому здесь я их приводить не буду. Тем более обычно используется режим 0.

Для SPI я написал библиотеку для программной и аппаратной реализации. При программной реализации можно выбрать любой порт МК, а также вынести линию MISO на любой порт, не зависимо от остальных выводов. Кроме этого можно объединить выводы MOSI и MISO, сэкономив один вывод МК. В аппаратной реализации все стандартно, подключаемся к определенным выводам МК и работаем. Зато можно выбрать 4 режима работы, скорость передачи данных, использовать прерывания и использовать режим Slave (например, для связи 2-х МК). Да, PCA2129 не осуществляет синхронную передачу данных, поэтому и в библиотеке программной реализации ее нет. Но если понадобится, можно без проблем ее добавить. В обеих библиотеках функции называются одинаково и имеют одинаковые параметры. Выглядят они вот так:

spi_init()– инициализация SPI;

spi_write() – передача данных;

spi_read() – чтение данных;

Осталось лишь немного изменить библиотеку для работы с PCA2129 (ту самую, что написали для работы по I2C). Что мы и делаем. Теперь подключаем плату с микросхемой к отладочной плате по SPI (не забывая переставить джампер SPI/I2C и снять джампер I/S).

Подаем питание, и… Снова работает! Только проводов стало больше.

А вот и видео работы:

Итак, подведем итоги. PCA2129T оказалась отличной микросхемой часов реального времени. Из плюсов стоит отметить высокую точность хода, которая гораздо выше, чем можно получить при использовании внешнего кварца), температурная компенсация (она и дает такую точность), отслеживание уровня заряда резервной батареи, “штамп” времени, сторожевой таймер и вывод прерываний. Но без минусов все равно не обошлось, размер микросхемы великоват, намного больше, чем та же DS1307. На мой взгляд, минус не такой уж и значительный, плюсы намного его превосходят. На этом все. К статье прилагаю все библиотеки, описанные в статье. Все они обильно прокомментированы.

Прикрепленные файлы:

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

Ваш адрес email не будет опубликован.