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

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

▍ Начало


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

В Гугле мне сказали, что для запуска таких часов есть множество решений. Как оказалось, на эту тему в интернете уже десятки статей и видео — похоже, что только ленивый не затронул эту тему. В том есть статьи и Хабре.

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

Для разнообразия решить эту задачу я задумал при помощи простых народных средств, в лице готовых электронных модулей, которые были под рукой. И один из них, разумеется, Arduino Nano.

Модули для проекта

Конечно, найдутся люди, которые скажут: «Опять эти ваши ардуины, ничего-то без них сделать не можете; это как из пушки по микроскопам, а вот в наше-то время это делалось на двух логических микросхемах».

С одной стороны, да, но с другой Arduino и был создан для решения простых задач несоразмерными их сложности мощностями. Таков его путь. И, боюсь, проектами, где имеющемуся вычислительному потенциалу нашлось бы действительно достойное применение — управлением реактором или там лунной ракетой — я займусь нескоро.

Максимально же простая конструкция из доступных в любом ларьке деталей с понятными объяснениями оказалась прекрасным поводом для создания материала, имеющего некоторую познавательную ценность, и в том числе демонстрирующего ограниченность применимости Arduino даже в довольно простых на первый взгляд задачах.

▍ Вторичные часы


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

Ранее я имел лишь общее представление о существовании подобных систем, так как имел возможность лицезреть первичные часы в детстве. Они были красивые, железные, с рядами микросхем под тонированным стеклом, и исправно работали в школьной учительской, не давая никакой возможности немедленно разобрать их на красивые запчасти, чтобы собрать очередной мультивибратор. Осадочек остался.

Первичные часы

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

Чтобы миллионы рабочих могли вовремя узнать, когда нужно уходить с работы, а спешащие на трамвай могли понимать, что уже опоздали или ещё не успели, повсюду были установлены крупные, хорошо заметные часы: на площадях, остановках, в метро, на каждом заводе, в каждой организации и образовательных учреждениях. Тысячи их.

Вторичные часы

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

В двух словах идея заключается в том, что источник точного времени один — первичные часы, а тысячи часов на стенах — только показометр, вторичные часы, получающие информацию о ходе времени по проводам от единственных первичных часов. Те же, в свою очередь, могут синхронизироваться по чему-нибудь ещё, например, сигналам точного времени на радио.

Тоже вторичные часы

Иначе говоря, вторичные часы так называются не потому, что они бывшие в употреблении, а потому что это и не часы вовсе. Это всего лишь шкала со стрелками или табло с цифрами. Они не умеют считать время самостоятельно, только показывают. Для превращения их в полноценные часы требуется источник управляющего сигнала — первичные часы. К одним первичным часам может подключаться несколько десятков вторичных, и все они будут передвигать стрелки одновременно.

К слову, если вы застали фразу «передаём сигналы точного времени», звучавшую раз в сутки на основных радиостанциях и по проводному вещанию в сопровождении шести коротких звуковых сигналов, это оно — источник синхронизации, так называемые сигналы проверки времени, или «шесть точек». Они используются уже более ста лет, и это простейшая система, используемая для задач с низкой ответственностью. Есть множество других, более точных и продвинутых систем, и это весьма интересная тема, однако выходящая за рамки данной статьи и моих компетенций.

Сами вторичные часы могут быть электромеханическими или цифровыми, обладать стрелками или цифровым табло.

Весь механизм вторичных часов

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

Электромагнитный привод

Шайбы имеют смещение относительно друг друга на половину шага выемок, и фактически представляют собой полюса постоянного магнита. Это позволяет элементу поворачиваться на долю оборота при переключении полярности магнитного поля и останавливаться в таком состоянии до следующего изменения полярности. Можно сказать, что это примитивная вариация шагового двигателя.

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

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

▍ Управление часами


Есть много разных систем управления вторичными часами, особенно в наше высокотехнологичное время. Классическая система использует всего два провода, к которым все вторичные часы подключаются параллельным соединением, в том числе и разных типов. Способы передачи сигналов по этим проводам, однако, могут отличаться.

Вторичные часы бывают минутные и секундные. И те и другие имеют две стрелки, часовую и минутную. Разница в том, что минутные часы передвигают стрелки раз в минуту скачком от одного деления к другому, а стрелки секундных часов движутся плавно, с проходом между делениями.

Управляются и те и другие часы постоянным током с типичным напряжением около 24 вольт. Как правило, используется знакопеременная система: напряжение подаётся короткими импульсами, и каждый импульс меняет полярность. Другими словами, происходит цикл из четырёх шагов: ничего, плюс и минус, ничего, минус и плюс. Также есть современные системы с однополярными импульсами. Стрелки сдвигаются на следующую позицию только по приходу очередного импульса.

Как же при такой системе установить время, не залезая под потолок и не снимая каждые часы? Только увеличением частоты следования управляющих импульсов. Это называется «подгоном» — время начинает идти быстрее. Обратный ход не предусмотрен конструкцией электромагнитной системы вторичных часов, если нужно подвести время назад, придётся проходить полный круг вперёд.

Для минутных часов импульсы следуют каждую минуту, а длительность импульса составляет около 1.6 секунды. В режиме подгона частота следования импульсов увеличивается примерно до 4 секунд, что даёт ускорение хода в 15 раз. Для секундных часов частота следования импульсов составляет одну секунду, длительность 0.25 секунды, а в режиме подгона частота 0.5 секунды — ускорение хода всего в два раза.

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

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

Кодировка может включать признак синхронизации в виде серии импульсов с длиной, отличающейся от стандартной. После определённого числа признаков передаётся номер текущего часа, заданный в длительности очередного импульса: чем больше времени, тем длиннее импульс.

▍ Arduino и время


В задачах, связанных с точным отсчётом времени на Arduino часто применяют готовый внешний модуль RTC (Real Time Clock) — часов реального времени. Он считает время внутри себя, в нём предусмотрено всё, что нужно для точного хода, а Arduino только запрашивает показания.

Типичный RTC-модуль на микросхеме DS1302

Причина этого излишнего, на первый взгляд, усложнения конструкции заключается в точности генератора, задающего тактовую частоту Arduino. Как правило, для этих целей используются кварцевые и керамические резонаторы, имеющие отклонение частоты от эталонных 16 МГц порядка 0.01..0.07%, что даёт отклонение 8..60 секунд в сутки.

Но всё же для простого счёта времени достаточно и возможностей самого Arduino, по крайней мере для, таких неответственных проектов, как самодельные часы. Нужно только правильно задействовать имеющиеся ресурсы и выжать из них максимально стабильный счёт.

По сути, задача управления вторичными часами — это вариация на тему простейшего мигания светодиодом, только очень точная по времени.

Типичный Arduino Nano китайского производства

Первая идея, которая может прийти в голову новичку — сделать простейший цикл с delay. Либо так:

void loop() {
    make_pulse();
    delay(1000);
}

Либо, обладая пониманием, что снаружи loop выполняется какой-то код, на что требуется некоторое неопределённое время, так:

void loop() {
    while(1)
    {
        make_pulse();
        delay(1000);
    }
}

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

Можно компенсировать точность хода изменением задержки в delay. Только это должна быть более точная задержка delayMicroseconds, а значение придётся подбирать методом тыка, что потребует немало времени, и к тому же любое изменение кода потребует изменения этой компенсирующей задержки.

Первая часть решения проста. У Arduino (точнее, у контроллера ATmega) есть аппаратные таймеры, способные генерировать прерывания. Нужно задействовать прерывание от такого таймера, работающего в циклическом режиме.

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

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

Таймеры AVR, такие простые и понятные

Таймеры в микроконтроллерах AVR, на базе одного из которых построена платформа Arduino, работают следующим образом. Тактовая частота всего контроллера, задаваемая внешним или внутренним источником, сначала делится на 1, 8, 64, 256 или 1024 — так называемый предварительный делитель (предделитель, прескалер), а потом на заданное значение. Набор таймеров в разных моделях контроллеров отличается. Конкретно в Arduino на базе ATmega32 есть три таймера: два восьмибитных — их максимальный программируемый делитель 256, и один 16-битный — его максимальный делитель 65536.

Для простоты кода я решил ограничиться использованием только одного 8-битного таймера. Мне представлялось, что пара элементарных программных счётчиков-делителей проще в понимании, чем один 16-битный таймер, запускающий другой 8-битный.

Arduino тактируется внешним кристаллом с рабочей частотой 16000000 герц — то есть, столько импульсов тактирования получает система за одну секунду. Чтобы получить секундный счёт, нужно разделить 16000000 на 16000000. Как можно догадаться, такой делитель слегка больше, чем даже максимальный 16-битный делитель таймера.

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

К счастью, тактовая частота в 16 МГц и набор предделителей выбраны таким образом, чтобы любой пред-делитель давал целое число:


16000000/1=16000000
16000000/8=2000000
16000000/64=250000
16000000/256=62500
16000000/1024=15625

Остаётся только выбрать программируемый делитель в допустимом диапазоне, также дающий целое число. Я использую 8-битный таймер, значит, диапазон деления у меня 1..256.

Как можно снова догадаться, даже если я поделю наименьшее из входных значений 15625 на наибольший 8-битный делитель 256, один герц мне не получить (но если хочется, то 16-битным таймером можно). Поэтому я выбрал делитель 125 дающий — магия цифр! — 125 герц: 16000000/1024/125=125.

Но зачем мне 125, когда нужно 1? Я могу поделить это число ещё раз, теперь уже в программе, простейшим счётчиком. Поделив его на 125, я получу секундные периодические импульсы счёта. Также это позволит мне отсчитывать длительность управляющих импульсов с точностью в 1/125 секунды, чего должно быть вполне достаточно для данной задачи.

▍ Питание


Чтобы подключить вторичные часы к Arduino, придётся решить одну проблему: они требуют иные параметры питающего напряжения, нежели привычные 5 или 3.3 вольт, и просто так к выводам микросхемы их подключить нельзя.

Параметры питания вторичных часов бывают разные в зависимости от их модели, номинально это может быть 18 (плюс-минус 4) или 24 вольт (плюс-минус 8). Итого диапазон напряжений от 14 до 32 вольт. Потребляемый электромагнитом ток мизерный, порядка 10-20 мА. Также всё равно понадобится и 5 вольт, для питания Arduino.

Конечно, можно сделать или купить блок питания, обеспечивающий оба напряжения. Или взять источник на 24 вольта, в лице импульсного БП, трансформатора, или 5S-вязанку аккумуляторов 18650, а потом понизить напряжение до 5 вольт с помощью старой доброй КРЕНки (7805).

Но так как ток небольшой, есть способ попроще. Можно применить повышающий DC-DC модуль, например, на микросхеме MT3608, позволяющий получить до 28 вольт от пятивольтового источника с возможностью точной настройки нужного напряжения. Такие модули сейчас производятся в ассортименте и стоят копейки.

Использование повышающего модуля позволяет питать всё это безобразие от обычной телефонной зарядки или любого пауэрбанка, а заодно разместить все компоненты устройства на небольшой площади.

▍ Полярность и H-мост


Следующая проблема, которую требуется как-то решить — периодическая смена полярности управляющего напряжения. Нужно обеспечить возможность подачи на вход вторичных часов плюса и минуса, либо минуса и плюса, либо ничего вообще. Причём управлять подачей нужно логическими уровнями Arduino, тогда как коммутируемое напряжение будет существенно выше.

Для этой задачи человечество давно придумало типовое решение, с помощью которого обычно управляют шаговыми двигателями и моторчиками в радиоуправляемых моделях: H-мост. H — не н-word, в оригинале это буква «эйч», но разницы нет: просто начертание электрической схемы этого устройства напоминает такую букву.

Типовая схема H-моста из выключателей

H-мост состоит из четырёх выключателей. На входе у него плюс и минус источника тока, посередине располагается потребитель. Разные комбинации переключателей направляют плюс и минус на разные полюса потребителя, или отключают ток. Также есть запрещённое состояние, вызывающее короткое замыкание источника тока.

В практической реализации роль выключателей (ключей) могут играть электромеханические реле, биполярные или МОП-транзисторы (MOSFET’ы). Типовая схема включения обладает двумя управляющими входами, синхронно переключающими противоположные пары ключей, а бинарные комбинации наличия сигналов на входах обеспечивают все возможные состояния моста.

Реле для решения задачи управления часами никак не подойдут, потому что их ресурс ограничен примерно 100-200 тысячами переключений. Так как в сутках 86400 секунд, для секундных часов ресурса хватит лишь на сутки работы, а для минутных — от силы на пару месяцев.

Транзисторы же ограничением на количество переключений не обладают, и я без проблем мог бы их использовать. Если бы они у меня были под рукой. Так вышло, что я не нашёл в запасах MOSFET’ов разных типов проводимости, а ехать покупать их было долго и лень. К тому же надо что-то паять, добавлять всякие резисторы.

Возможные состояния H-моста

К счастью, в современном мире есть более простое решение: специализированные микросхемы, реализующие H-мост, и готовые модули на их основе. Их тоже нужно покупать, но хотя бы не нужно много паять. Приятным бонусом является наличие защиты от запрещённого состояния: одинаковые сигналы любого уровня на управляющих входах одинаково приводят к выключению выходного напряжения.

В продаже имеется три популярных вида подобных модулей: маленький на микросхеме L9110S, другой маленький на микросхеме TC1508A, и сильно побольше, с алюминиевым радиатором, на микросхеме L298N. Все модули содержат сразу два H-моста и позволяют управлять парой обычных двигателей или одним шаговым. Выходы Arduino подключается прямо к управляющим входам модулей, внутри модулей предусмотрена развязка питающего и управляющих напряжений.

Готовые модули H-мостов: послабее и помощнее

Разница между модулями в их параметрах. Модуль на микросхеме L9110S может запитываться напряжением от 2.5 до 12 вольт (6 номинальное), на микросхеме TC1508A — от 2 до 10 вольт. Падение выходного напряжения от питающего составляет около 1.5 вольт, продолжительный ток потребления до 0.8 ампер на канал. Модуль на L298N значительно крупнее и мощнее, он коммутирует от 5 до 36 вольт с током до 2 ампер на канал.

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

Сначала я решил попытаться задействовать значительно более компактный модуль на L9910S. К сожалению, его рабочее напряжение до 12 вольт, а выходное будет около 10.5, что значительно ниже необходимых 18-ти.

Я провёл эксперименты, и хотя часы шли при 10.5 вольт, делали они это очень неуверенно, с явным замедлением вращения шайбы — значит, при напряжении ниже номинального не гарантируется точность хода. Поэтому пришлось задействовать крупный модуль на L298N, хотя это и явный перебор по мощности и габаритам.

▍ Код


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


#define PIN_H_BRIDGE_POS    8   //пин управления положительным плечом H-моста
#define PIN_H_BRIDGE_NEG    9   //пин управления отрицательным плечом H-моста

#define PIN_FORWARD_BTN     10  //пин кнопки подгона для установки времени

#define PIN_CONTROL_LED     13  //стандартный светодиод для контроля секундного счёта



//#define CLOCK_TYPE_ONE_SECOND   //для секундных часов, иначе часы считаются минутными



//константы с параметрами хода для соответствующего типа часов

#ifdef CLOCK_TYPE_ONE_SECOND
const unsigned int normal_div = 125;
const unsigned char pulse_duration = 125 * .5;
#else
const unsigned int normal_div = 125 * 60;
const unsigned char pulse_duration = 125 * .5;  //по стандарту 1.6
#endif
const unsigned int adjust_repeat = pulse_duration * 1.5;


//переменные счёта времени и длительности импульса

unsigned int int_counter = 0;
unsigned char led_counter = 0;
unsigned char pulse_polarity = 0;
unsigned char pulse_counter = 0;
unsigned int adjust_counter = 0;
bool adjust_mode = false;



//обработчик прерывания timer0

ISR(TIMER0_COMPA_vect) {

  //делитель частоты прерываний для получения секундных импульсов

  ++int_counter;

  unsigned int int_div = normal_div;
  bool pulse = false;

  if (digitalRead(PIN_FORWARD_BTN) == LOW) //при нажатой кнопке подгона ускоряем ход, первый импульс выдаём сразу же по нажатию
  {
    if (!adjust_mode)
    {
      adjust_mode = true;
      adjust_counter = 0;
      pulse = true;
    }
    else
    {
      ++adjust_counter;
      if (adjust_counter >= adjust_repeat)
      {
        adjust_counter = 0;
        pulse = true;
      }
    }
  }
  else
  {
    adjust_mode = false;
  }

  if (int_counter >= int_div)
  {
    int_counter = 0;
    pulse = true;
  }

  if (pulse)
  {
    //включаем выходное напряжение нужной полярности

    digitalWrite(PIN_H_BRIDGE_POS, pulse_polarity == 0 ? HIGH : LOW);
    digitalWrite(PIN_H_BRIDGE_NEG, pulse_polarity == 0 ? LOW : HIGH);

    //задаём длительность импульса

    pulse_counter = pulse_duration;
    pulse_polarity ^= 1; //смена полярности
  }

  //счётчик длительности импульса

  if (pulse_counter > 0)
  {
    --pulse_counter;

    if (pulse_counter == 0)
    {
      //выключаем выходное напряжение

      digitalWrite(PIN_H_BRIDGE_POS, LOW);
      digitalWrite(PIN_H_BRIDGE_NEG, LOW);
    }
  }

  //дополнительный счётчик для индикации секундных импульсов
  //просто для визуального контроля работы прошивки

  ++led_counter;

  if (led_counter >= 125)
  {
    digitalWrite(PIN_CONTROL_LED, HIGH);
    led_counter = 0;
  }

  if (led_counter == 125 / 8)
  {
    digitalWrite(PIN_CONTROL_LED, LOW);
  }
}


//инициализация

void setup() {

  //выключаем прерывания на время инициализации

  cli();

  //устанавливаем управляющие пины H-моста и входной пин кнопки

  pinMode(PIN_H_BRIDGE_POS, OUTPUT);
  pinMode(PIN_H_BRIDGE_NEG, OUTPUT);
  pinMode(PIN_CONTROL_LED, OUTPUT);
  pinMode(PIN_FORWARD_BTN, INPUT_PULLUP);

  digitalWrite(PIN_H_BRIDGE_POS, LOW);
  digitalWrite(PIN_H_BRIDGE_NEG, LOW);

  //настраиваем timer0 на генерацию прерываний с частотой 125 герц ровно

  TCCR0A = 0;
  TCCR0B = 0;
  TCNT0  = 0;
  OCR0A = 16000000 / 1024 / 125 - 1; //делитель таймера, дающий самую низкую целочисленную частоту (125 герц ровно)
  TCCR0A |= (1 << WGM01);
  TCCR0B |= (1 << CS02) | (1 << CS00);  //входной делитель на 1024
  TIMSK0 |= (1 << OCIE0A);  //прерывание по сравнению с OCR0A

  //разрешаем прерывания, начинается генерация импульсов

  sei();
}


//в основном цикле ничего не происходит, весь код в обработчике прерывания

void loop() {
}

В этом варианте кода я задействовал 8-битный счётчик с предварительным делителем 1024 и заданным делителем 125 для генерации прерываний с частотой 125 герц.

▍ Схема


Итоговая, если позволите так её назвать, схема состоит из трёх готовых модулей, нескольких проводков для их соединения между собой, а также одного диода Шоттки для «защиты от дурака». Кто не дурак, диод может не ставить.

Смысл наличия диода в том, чтобы при подключении питания к DC-DC модулю питались все элементы, а при подключении питания к Arduino (при прошивке) не запитывался DC-DC модуль и не перегружал питающие цепи Arduino, которые в этом случае не смогут сдерживать свой волшебный дым внутри.

Схема соединения модулей

Для проверки схема сначала была собрана на проводках.

Сборка на проводках

После обхода всех граблей я пересобрал её более надёжно, на монтажной плате. Модуль H-моста привинчен на две стойки, DC-DC преобразователь припаян на четыре одноконтактных пина.

Финальная форма устройства

Фактическое потребление устройства по показаниям простенького и вряд ли сильно точного тестера нагрузки USB-портов составляет 30-50 мА в режиме ожидания и 150 мА в момент выдачи импульса.

Измерение потребления тока

Модуль L298N таит сюрприз: на его плате есть 5-вольтовый стабилизатор, позволяющий получить 5 вольт из входящих 24, а также есть прямой вход для 5-вольтового питания. Джампер необходимо снять, иначе возникнет замыкание между двумя источниками, а на пятивольтовую линию подать питание с USB-разъёма.

Джампер питания на плате H-моста

Если же не подавать на 5-вольтовый вход модуля питание, а джампер оставить на месте, это тоже будет работать. Но из-за низкого КПД стабилизатора в модуле ток потребления схемы будет составлять уже 150-250 мА — при питании от аккумулятора разница во времени работы будет довольно существенной.

▍ Проверка временем


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

Для современных механических часов бытового назначения допустима погрешность плюс-минус 40-60 секунд в сутки. Для бытовых же часов с кварцевой стабилизацией нормой считается отклонение до 20 секунд в течение месяца, а у лучших производителей отклонение составляет 5 секунд в год, и эти приборы претендуют уже на звание хронометра — часов высокой точности. Мы уже знаем, что теоретически от Arduino без RTC можно ожидать погрешности 8-60 секунд в сутки, что сравнимо с механическими часами.

Как можно проконтролировать точность? Простейшая идея, которая приходит в голову: нужно запустить часы на некоторый срок, сверить с эталонным источником времени в один момент, и потом в другой. Таким образом, можно понять, насколько проверяемые часы спешат или отстают.

Немного покумекав, как бы мне осуществить такую схему измерений, я решил задействовать современные подручные технические средства во благо колхоза. Я снял на видео с частотой 60 кадров в секунду вторичные часы и эталонные часы в один день, фиксируя момент перемещения минутной стрелки и текущее точное время на экране. Такую же видеозапись я сделал и в другой день. В роли эталонных часов выступил компактный ноутбук под управлением Windows 10, на котором в браузере был открыт веб-сайт, показывающий время с точностью до секунды — такой себе эталон.

Сверка показаний в кадрах видео

Теперь, имея два видео, с помощью покадровой перемотки можно определить долю секунды точного текущего времени в момент перемещения стрелки. Точность измерения составила соответствующее частоте кадров значение, то есть 1/60 секунды. Это, конечно, тоже не атомные часы, да и телефон такой себе эталон точного времени, но этого вполне достаточно для моих целей. По крайней мере, понятно, нет ли критичных проблем. Улучшить точность можно увеличением длительности измерений, например, дав часам поработать неделю.

▍ В поисках точности


Результат измерений указанным способом для моей конструкции в течение ровно 18 часов или 64800 секунд по стрелкам вторичных часов (на такое время хватило заряда небольшого частично заряженного пауэрбанка), составил 18 часов 1 минуту 32 секунды или 64892 секунды по эталонным часам — 0.14%, вдвое больше максимально ожидаемых 0.07%. Даже подсчитывать доли секунды не пришлось. Почему получилось такое огромное расхождение, не вполне понятно — ведь повлиять могло множество факторов, от точности счёта временных интервалов в Arduino до пропуска шагов в механизме часов и точности средств измерения.

Попытка проконтролировать точное время между минутными импульсами по звуку сдвига стрелки также не прояснила ситуацию. Дело в том, что этот звук размазан по времени, стрелка переходит не сразу. Примерное попадание в пики дало около минуты и 0.05 секунды, что в пределах погрешности по угадыванию фактического момента сдвига стрелки.

Измерение точности по аудиозаписи в Wavosaur

Для уточнения фактической частоты следования импульсов я подключил динамическую головку параллельно контрольному светодиоду, который мигает раз в секунду, записал щелчки в звуковой файл и посмотрел время между пиками. Частота следования импульсов получилась 1.001417..1.001438 секунды. То есть отклонение в те же 0.14%. Таким образом, я сделал вывод, что проблема не в механике, а в источнике импульсов.

Так как отклонение определённо возникало именно в частоте следования прерываний от таймера, а не в программных делителях, предпринять можно было немногое: попробовать скорректировать делитель таймера. Однако одна единица моего 8-битного делителя даст разницу в 0.8%, а значит отклонение всё равно будет велико.

Чтобы оперировать меньшими отклонениями, был только один выход: задействовать для счёта времени 16-битный таймер с меньшим предварительным делителем, чтобы значение задаваемого делителя было больше, а значит каждая его единица соответствовала меньшему отклонению.

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

▍ Другой код


Новый вариант кода отличается только установками таймера в функции setup и введением компенсационной константы TIMER1_ADJUST:


  //настраиваем timer1 на генерацию прерываний с частотой 125 герц ровно
  //отличие от версии для timer0 в использовании меньшего пред-делителя и возможности тонкой подстройки

  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1  = 0;
  OCR1A = 16000000 / 8 / 125 - 1 + TIMER1_ADJUST; //делитель таймера, дающий самую низкую целочисленную частоту (125 герц ровно)
  TCCR1B |= (1 << WGM12);
  TCCR1B |= (1 << CS11);      //входной делитель на 8
  TIMSK1 |= (1 << OCIE1A);    //прерывание по сравнению с OCR0A

Для моего экземпляра Arduino подобранное приблизительными расчётами и методом высокоточного тыка значение этой константы составило минус 20 единиц, что дало измеренное отклонение в 0.01% и уход часов на 8 секунд в сутки — теоретически ожидаемая точность. На этом я и успокоился.

▍ За кадром


Современное медиа за редким исключением предпочитает строгое деление на истории успеха или неудач — простой и понятный зрителю формат. Статьи по электронике и различные видео про самоделки могут создать впечатление, что DIY — это когда придумал, купил детали, собрал, и всё заработало. К сожалению, так бывает редко. Обычно же не обходится без приключений, без поисков совершенно пустяковых ошибок, затягивающихся на долгие часы, а иногда и без совершенно загадочных обстоятельств, когда что-то работает или не работает, а потом вдруг перестаёт.

Так вышло и в этот раз. В реальности этот проект выходного дня вместо нескольких часов занял несколько дней, а потом ещё лежал несколько месяцев без движения, ожидая проверки на точность. За это время я:

  • Случайно оторвал USB-разъём у одного повышающего DC-DC модуля.
  • Другому DC-DC модулю испортил USB-разъём его предварительной пропайкой, чтобы случайно не оторвать (припой затёк куда не надо).
  • Испортил USB-провод, пытаясь исправить разъём на модуле (припой затёк в провод тоже).
  • Модуль H-моста на L9110S или не вполне работал изначально, или тихо сгорел, или я сам что-то перемудрил: один из мостов у него работал только в одну сторону (давал одну из полярностей), а второй не работал совсем.
  • Arduino Nano таки испустило дымок и перестало работать, сгорел диод. Заменил, заработало. Так в схеме появился диод, а в тексте шутка про него.
  • Устройство стартовало через раз, срабатывала защита источника питания: забыл джампер на плате H-моста на L298N, замыкающий вход 5 вольт на выход внутреннего стабилизатора.
  • Часы изначально были покрыты многовековыми слоями масляной краски, и для красивого кадра я задумал привести их в относительный порядок. Лучше бы не трогал. Оказалось, что смывка для краски не всегда работает хорошо, и к тому же часы местами оказались в нерастворимой шпаклёвке. В итоге дома была разведена жуткая грязища, а я так и не нашёл времени, чтобы довести начатое до конца и привести их в достойное состояние.

Процесс снятия краски смывкой

Поэтому настоящий трюк — не в том, чтобы научиться понимать что-то в электронике, или красиво паять, а в том, чтобы не опускать руки, и доводить начатое до конца, несмотря на неудачи, происходящие в процессе.

▍ Затраты


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

  • Arduino — 350.
  • Повышающий DC-DC модуль — 190.
  • Модуль H-моста — 220.
  • Монтажная плата — 150.
  • Часы из СССР — бесценны.

Результат

▍ Заключение


Вот так с помощью нехитрых приспособлений можно взять и приспособить одно к другому. Я уже приспособил, и вы тоже можете!

Возможно, эта история получит продолжение: пока я жёг диоды на Ардуинах, поступила новая информация. На горизонте замаячила перспектива подключения вторичных электронных часов со светящимся цифровым табло, ВЧЦ1-С2ПГ 12К-80.

Telegram-канал со скидками, розыгрышами призов и новостями IT ?

Комментарии (35)


  1. KOT2K
    16.07.2024 09:44

    Спасибо за интересный материал!


  1. voldemar_d
    16.07.2024 09:44
    +2

    Случайно оторвал USB-разъём у одного повышающего DC-DC модуля

    У меня как-то в процессе отладки и втыкания/вытыкания кабеля USB-разъем от Arduino Pro Micro отвалился :-)

    Насчет внешнего источника точного времени - может, довести дело до абсурда и приделать к Arduino WiFi-модуль, брать показания времени с какого-нибудь сайта (с парсингом json) и к нему подводить время периодически? Наверное, еще железобетоннее приделать GPS-источник точного времени, только это стоить будет негуманно, скорее всего.

    Где советские вторичные часы удалось найти?


    1. LAutour
      16.07.2024 09:44
      +4

      GPS-источник точного времени, только это стоить будет негуманно, скорее всего.

      Не особо дорого: модуль с антенной - несколько сотен рублей


    1. longmaster
      16.07.2024 09:44
      +4

      Проще сразу на esp всё сделать


      1. voldemar_d
        16.07.2024 09:44
        +1

        Почему не Raspberry Pi?


        1. longmaster
          16.07.2024 09:44
          +6

          Я без сарказма. Esp8266 сейчас стоит дешевле минимальной arduino pro mini, хотя разница в десятки рублей тут несущественна. Зато 8266 имеет кроме весьма неточного rtc, но работающего в deepsleep, ещё и довольно точные системные часы, дающие погрешность 1сек/сутки. Ну и позволяет просто реализовать синхронизацию по NTP готовыми библиотеками, без всякого парсинга с сайтов. Если эксплуатация часов планируется рядом с доступным wifi, то всё получится проще и дешевле, чем мудрить с GPS или автономным RTC. Можно даже реализовать сохранение последнего зафиксированного стрелками времени и автоматом подводить часы после отключения питания. Ресурс ячеек памяти ограничен, но можно периодически сдвигать адрес.


          1. shiru8bit Автор
            16.07.2024 09:44
            +1

            Да, если бы требовалась синхронизация по интернету, я однозначно сделал бы на ESP8266. Она стоит плюс-минус столько же, сделать на ней очень просто. Raspberry Pi — это совсем другая ценовая категория.


    1. dlinyj
      16.07.2024 09:44
      +2

      Где советские вторичные часы удалось найти?

      На Авито, мешке и прочих сландо тысячи их, даже новых


    1. fobo
      16.07.2024 09:44

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


      1. voldemar_d
        16.07.2024 09:44

        Сейчас в коде есть некий параметр, корректирующий неточность хода внутренних часов Arduino. Если есть внешнее точное время, можно попытаться этот параметр рассчитывать автоматически.


        1. fobo
          16.07.2024 09:44

          попытаться можно, но какой в этом смысл, если ты не знаешь как в данный момент установлены стрелки?


          1. voldemar_d
            16.07.2024 09:44

            Хотя бы скорость хода часов корректировать.


      1. shiru8bit Автор
        16.07.2024 09:44

        При желании конечно можно добавить датчик положения часовой стрелки, магнитик плюс датчик Холла. Но это надо дорабатывать уже сами часы, тянуть лишние провода.


  1. HardWrMan
    16.07.2024 09:44
    +2

    Зачем вообще писать какой-то код, участвующий в отсчёте времени и вносящий в это погрешность, если можно просто соединить таймеры каскадно и только менять их настройки по желанию + легко контроллировать состояние?


    1. dlinyj
      16.07.2024 09:44

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


      1. shiru8bit Автор
        16.07.2024 09:44
        +3

        Как я понял из своих экспериментов, дело не в таймерах, а в погрешности генератора тактовой частоты. Если на нормальном Uno стоит кварц, на этих китайских копеечных клонах Nano стоит керамический резонатор, и у него очень большое отклонение для часовых применений. Типа, хватает для стабильного UART, и ладно.


        1. dlinyj
          16.07.2024 09:44
          +1

          Разумеется проблема в самом генераторе, при этом она плавает от температуры, фазы луны и биополя. Поэтому лучше всего использовать внешние часы и не измерять время с помощью таймера на процессоре.

          На самом деле, там если не поленится и открыть даташит, то можно прикинуть погрешность измерения времени с помощью таймера, и окажется что она весьма неважнецкая.


          1. sim2q
            16.07.2024 09:44
            +2

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


          1. VT100
            16.07.2024 09:44

            Ну при чём аппаратный таймер, если дело в точности и стабильности задающего генератора?

            Подстройка хода в цифровых часах была 40+ лет назад, что и показал @shiru8bit . Вопрос только в увеличении длительности интервалов калибровки по мере роста точности (предполагаю - решаемый) и разрядности.


            1. dlinyj
              16.07.2024 09:44

              Вы прекрасно поняли о чем я говорю. Не стоит тратить время на реализацию программного таймера, надо брать внешнее часы.


          1. tituszx
            16.07.2024 09:44
            +2

            Во внешних часах, кроме хорошей DS3231 такой же кварц.

            При этом при относительно стабильной комнатной температуре можно получить ПГ порядка 1 сек в сутки. Меньше уже затруднительно при использовании только 16 битного аппаратного таймера AVR, не хватает разрешающей способности.

            А компенсацию температуры можно сделать по термодатчику, показания которого будут влиять на значение перезагрузки таймера


    1. shiru8bit Автор
      16.07.2024 09:44

      Код не вносит погрешность в отсчёт времени. Для универсальности.


      1. HardWrMan
        16.07.2024 09:44
        +1

        У тебя таймер стреляет 125 раз в секунду и далее идёт программный счётчик. Помимо джиттера на вход прерывания, ты имеешь ещё джиттер реакции на флаг счётчика в main(). Зачем, если можно OCR этого таймера соединить с ICR другого и поделить там на 125 вход, причём сразу сформировать импульс уже на его OCR прямо для управления мотором? А доли этой 1/125 частоты ты можешь смотреть в CNT таймера, если так надо, да ещё и стрелять его UF в прерывание. Решительно непонятно.

        Точность самого кварцевого резонатора вопросов не вызывает. Если нужна прям точность лучше его поменять на внешнего гену с хорошим PPM.


        1. shiru8bit Автор
          16.07.2024 09:44

          Процитирую код:

          //в основном цикле ничего не происходит, весь код в обработчике прерывания

          Про какой джиттер входа прерывания идёт речь в случае с полностью целочисленными делителями, фиксированным временем выполнения основного цикла, и единственным таймером?

          Что за реакция на счётчик в main?

          Все эти игры с каскадированием счётчиков AVR — не для начинающих пользователей Arduino, и статью писать про это бессмысленно. Кто знает, тот знает, кто не знает, тому рано. К тому же, платформа Arduino — это давно уже далеко не только AVR. Для других плат достаточно заменить установку таймера и сохранить логику. Непортируемо, но хотя бы легко адаптируемо.


        1. VT100
          16.07.2024 09:44

          Разве матожидание джиттера не ноль?


  1. dlinyj
    16.07.2024 09:44
    +5

    Новое - это хорошо забытое старое.

    В этом же блоге ранее писал статьи:

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


    1. MaFrance351
      16.07.2024 09:44
      +4

      И, по-хорошему, закопать уже эту DS1302. DS3231 при не сказать, что намного большей стоимости лишена многих недостатков предыдущей.


      1. dlinyj
        16.07.2024 09:44
        +1

        Без сомнения.


      1. LAutour
        16.07.2024 09:44
        +3

        DS3231 при не сказать, что намного большей стоимости лишена многих недостатков предыдущей.

        Оригинальная скорее всего, но обычно все любят использовать китайские модули, которые стоят дешевле самой микросхемы. И в точности работы (да и вообще работоспособности) этой китайщины большой уверенности нет.


        1. MaFrance351
          16.07.2024 09:44
          +5

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

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


          1. VT100
            16.07.2024 09:44
            +2

            В 3231 нет термостабилизации. Там термокомпенсация.


    1. shiru8bit Автор
      16.07.2024 09:44

      Собственнно основной поинт моего варианта статьи для начинающих самодельщиков в понимании, что нормальной точности не будет.


      1. dlinyj
        16.07.2024 09:44
        +1

        Я когда продавал после статьи всю свою систему с первичными часами, коллекционер мне сказал что его замучили эти поделия на ардуинках. Мол, точности никакой, хуже только механика, которая зависит от температуры и влажности. Поэтому самый правильный вариант - это подобрать внешние часы, которые могут генерировать прерывание раз в минуту. Либо, тут достаточно просто опроса и при достижении минуты генерировать импульс перевода стрелки.

        Народ тут начал возмущаться на счёт того, что нет возможности синхронизировать в случае отключения. Тут решения два: ИБП и реализация как была на первичных часах: вводится время на вторичных часах, а далее импульсами сами первичные часы выбирают разницу.


  1. AlexanderS
    16.07.2024 09:44
    +1

    Я тоже подобным образом игрался с вторичными часами. Только у меня задумка была "вечная": солнечные батареи + контроллер + сборка 18650 + ардуина с самопальным шилдом с электроникой. Предполагалось, что контроллер будет всё время спать, потребление будет никакое и этого будет достаточно для подзарядки сборки АКБ днём от солнечных панелей. Но в эффективности панелей с алиэкспресс я жестоко просчитался и работало это только 2-3 суток.

    Всё хотел статью на хабр запилить, годы шли... ну вот... теперь уже прочитал)


    1. shiru8bit Автор
      16.07.2024 09:44

      Интересная идея! Но похоже, что в моём варианте с DC-DC преобразователем для получения 28 вольт нужен не такой уж мизерный ток для передвижения стрелок, что-то около 100 мА в пике. По грубой прикидке мне бы понадобился аккумулятор ёмкостью около 5000-6000 мАч, и как-то успевать дозаряжать его наполовину за сутки. Можно конечно набрать большую батарею 18650 на 36 вольт, заряжать без лишних преобразователей, питать H-мост напрямую от батареи, а для МК понижать тоже DC-DC. Надо считать, и всё равно, наверное понадобится не такая уж маленькая солнечная панель.