Найти в Дзене
За_тех_кто_в_коде();

Пишем библи... пару функций для работы с часами DS1302 для Ардуино.

Порой можно встретить жалобы на разнообразные Ардуино библиотеки. Мол, тяжелые, местами тормозные… Зачастую, это не совсем проблема в коде. Порой библиотеки пишутся универсальные, под модельный ряд датчиков, которые имеют различия в регистрах. А для связи используют функции из Arduino IDE, которые в свою очередь пишутся под некоторое разнообразие микроконтроллеров, типа Wire.h . За универсальность приходится платить. Да и в конце концов, кому не что-то не нравится тот пишет сам. Запонадобились мне часики для проекта. Во многих современных микроконтроллерах часы уже есть на борту, подключай только батарейку, часовой кварц, а функции работы с внутренней периферией пишет обычно производитель МК. Но микроконтроллеры Ардуино не столь современные, поэтому часики приходится прилаживать внешние. Качнул одну библиотечку, а там чтобы время посмотреть более 9Кб нужно на Flash записать и около 400байт ОЗУ потрачено.    Ну это что-то ну как-то очень дорого, для самых простых часов DS1302. Для

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

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

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

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

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

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

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

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

-2

CE – это смысловой аналог Chip Select. Так я его и обозначаю. И далее линия тактирования и линия данных.

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

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

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

-3

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

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

-4

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

-5

-6

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

-7

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

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

-8

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

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

-9

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

-10

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

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

https://disk.yandex.ru/d/IK4QpQifCN2yMA