Написание библиотеки для работы с часами DS1302


RTC DS1302 Data_Transfer.


    Порой можно встретить жалобы на разнообразные Ардуино библиотеки. Мол, тяжелые, местами тормозные…

   Зачастую, это не совсем проблема в коде. Порой библиотеки пишутся универсальные, под модельный ряд датчиков, которые имеют различия в регистрах. А для связи зачастую используются функции из Arduino IDE, которые в свою очередь пишутся под некоторое разнообразие микроконтроллеров, типа Wire.h . За универсальность приходится платить. Да и в конце концов, кому не что-то не нравится тот пишет сам.

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

   Качнул одну библиотечку, а там чтобы время посмотреть более 9Кб нужно на Flash записать и около 400байт ОЗУ потрачено.    Ну это что-то ну как-то очень дорого, для самых простых часов DS1302. Для Ардуино Нано это что-то как-то очень расточительно.

   И тут я припомнил, что уже как-то начинал писать библиотеку для данных часов. Она достаточно проста и не затейлива. Поэтому можно на неё посмотреть по подробнее и разобрать написание библиотек, хотя это конечно громко сказано, скорее классов и функций для работы с различными датчиками.

   Стоит отметить, что большинство библиотек пишется на С++, то есть с использованием классов и созданием объектов. Хотя, при ближайшем рассмотрении выясняется, что никакие из особенностей плюсов не используются. Ни наследование, ни передача объектов аргументом метода и т.д. Создание множества объектов, например температурных датчиков, тоже не актуально, так как и обычная функция аргументом    принимает либо уникальный пин датчика (Chip Select например), либо адрес, если это I2C подключение. Но в целом, я полностью поддерживаю написание именно на плюсах, ибо необходимо переходить на плюсы, хотя не так давно я топил за Си, и не понимал, зачем они мне «плюсы» вообще нужны.    Что в классах актуально для простых датчиков, так это то что можно сократить количество аргументов в рабочих методах. Вынести базовую часть настроек в конструктор, дополнительные настройки в Set метод, а получение данных организовать через Get метод.

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

   Для начала, не плохо, хотя бы бегло пройтись по даташиту, возможно с онлайн переводчиком. Выяснить тип подключения и допустимое напряжение питания и высокого уровня сигнала. Для подключения используется 3х проводная шина, по смыслу схожая с SPI.

RTC DS1302 Data_Transfer.

   CE – это смысловой аналог Chip Select. Так я его и обозначаю. И далее линия тактирования и линия данных. Вроде все понятно, клацаем, проталкиваем или получаем данные. Но как показывает практика, обычно, на таких картинках сокрыто информации больше, чем написано в самом даташите. На первый взгляд особенностей нет, но если присмотреться внимательнее, в верхнем блоке чтения, в первом байте происходит цикл записи команды (адреса регистра) и стрелочки тактирования смотрят вверх, то есть часы ожидают возрастающего фронта тактового импульса. Далее, в байте чтения, данные приходят по спадающему импульсу. То есть, если написал всё вроде верно, но ничего не едет, то смотреть нужно в первую очередь на картинку. Конкретно данный даташит еще написан старой школой, и можно что-то выудить информативное из текстового описания. В случае например с дисплеями, там 90% информации на картинках. Оставшиеся 10% это 2-3 предложения описания работы регистра. И из этих 10, половина полезной информации приходится на название регистра.

   Обычно библиотеки работают с использованием 2х основных методов. Первое: получение «сырых» данных из датчика-модуля, обычно это функции чтения/записи I2C, SPI и пр.    Второе: приведение данных к человеческому виду. Иногда это просто некоторое число, где каждая единица это например сколько-то милливольт. И зная диапазон измерений, методом пропорции высчитывается текущее значение напряжения, как это работает например с монитором тока. В случае с часами, данные упакованы крайне странно, на первый взгляд. В первой тетраде старший разряд десятичного, двухзначного числа, в младшей – младший разряд. Хотя все можно было бы впихнуть в один байт.

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

Пример кода. Начало функции

    Для начала, стандартная настройка участвующих в работе пинов. Все на «выход» Константная переменная timer, это что-то типа delay, для небольшого ожидания, чтобы прокрутить цикл.    Если писать по правильному, то нужно было бы обернуть строку в условную компиляцию и указать, что это для микроконтроллеров, с частотой работы 16Мгц. Более шустрым МК потребуется обождать чуть большее количество циклов.

   Далее, так как в данной схеме подключения отсутствует вывод WR/RD необходимо уточнить формат байта для записи и для чтения.

Пример кода.

    Но в данном случае не нужно ни во что особенно вникать. Команды на запись и на чтение уже записаны с учетом того что 7й бит все «1» На запись не четные, на чтение четные, с учетом правила положения нулевого бита. Поэтому просто используем нужную команду из списка, в так называемой карте регистров. В подобную таблицу обычно сведены не только команды, но и состояние регистров после рестарта, и некоторая другая, полезная информация. Важный объем информации по работе с датчиком, который относится не к подключению, а уже к самой работе с устройством.

Пример кода.

    После записи команды начинаем чтение приходящих байтов. Здесь предварительно необходимо знать в каком виде они приходят. Старший бит впереди или младший. Либо выяснить это уже по факту полученных данных и внести правки в код.

Пример кода.

   Вариант сдвинуть >> вправо и +128. Это аналогично тому как если сдвинуть влево << и сделать +1, если установлен высокий уровень сигнала. Варианты битовых операций. Работают когда интересующий нас бит находится заведомо в нулевом значений, как после сдвига, например. Но это так, нюансы битовых операций.


   Далее приведение полученных данных к нормальному виду.

Пример кода.

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


   Напоследок о таймингах.

RTC_DS1302_Timing

    Когда соединение происходит по I2C или SPI, нет нужды вникать в тайминги, всем этим рулит блок микроконтроллера отвечающий за прием и передачу, мы лишь читаем или пишем в определенные регистры. А в некоторых случаях устройства, например дисплеи работают намного быстрее чем Ардуинка и нет нужды что-то соблюдать, нужно лишь все делать в правильной последовательности. Но в подобном случае на тайминги следует обратить внимание. Интересуют данные пункты:

Описание

   В примере ниже, функции работы с портами Arduino IDE я заменил на самописные, которые занимают в итоге меньше места. Но когда прошивка готовится под конкретное устройство и используемые пины заведомо известны, нет смысла использовать функции работы с портами, можно сразу в функции указывать интересующие порты. Если требуется прям серьезно ужать прошивку. Тут все достаточно просто. Регистр DDR(литера порта) указывает направление, аналог pinMode. OUTPUT или INPUT, а регистр PORT(литера порта). В примере это более понятно, в самих функциях работы с портами. Лишь значение маски заменить на её значение, лучше в двоичном формате, для наглядности.

   Так вот, если в блоке чтения, Digital_Write (SCLK, LOW); и Digital_Write (SCLK, HIGH); заменить на прямое обращение к регистрам микроконтроллера, часы за Ардуино уже поспевать не будут и данные посыплются.    Тут уже необходимо организовывать задержки. Так как при вызове функции тратятся такты на копирование аргументов, вход в ветвление и пр. и все работает без принудительных тормозов.




  Пример. Функции для работы с RTC DS1302 + функции для GPIO Arduino



  Эта программа является свободным ПО: вы можете распространять и/или модифицировать её согласно условиям Основной Общественной Лицензии GNU, опубликованной Организацией Свободного Программного Обеспечения, 3-ей версии Лицензии, либо любой последующей версией.


   












      Статьи


  
    









   Изготовление рекламы Изготовление мебели  Общестроительные работы 3D моделирование       Статьи
  Благоустройство тер.
   Общее портфолио    Мебель для мастерской   Установка бордюров   Портфолио   Контакты
   Наружняя реклама       Фасадные работы   Модели   Образец договора
   Внутреняя реклама       Утепление гаража   Инфо
   Таблички, наклейки       Косметический ремонт   Фото г.Надым
  Бетонные работы   Фото пригороды г.Надым