Начало
Крайне ограниченные аппаратные ресурсы, древняя архитектура микроконтроллера, отсутствие экосистемы библиотек. Только datasheet, схема устройства и компилятор С с полным доступом к hardware. Все для настоящих техно-ретро-гиков! Погнали!
Описание конструктора
Найти конструктор на алиэкспресс можно по ключевым словам "57 led electronic hourglass kit".
В продаже встречаются две разновидности: на базе МК STC15W204S/STC15W201S в корпусах SOP-16 и SOIC-16.
Максимальный размер прошивки для STC15W201S 1кБ, размер памяти 256 байт. Экстремально мало.
Я почти опустил руки. Но конструктор то работает, значит создание прошивки возможно.

После подачи питания светодиоды начинают зажигаться и гаснуть эмулируя поведение песочных часов.
Микроконтроллеры серии STC15 являются современными клонами знаменитого 8-битного микроконтроллера Intel 8051. Согласно спецификации (datasheet) STC15 работают в 8-12 раз быстрее, чем Intel 8051.
Почему бы не скачать готовый код?
Довольно быстро были обнаружены два проекта Ceptimus и Rick-100.
Проект Ceptimus имеет хорошее описание аппаратной части, но код страдает типовыми болезнями "junior embedded developer". "Спагетти код" с загрузкой в служебные регистры МК "магических чисел" внутри одного C-файла. Версионного контроля нет. Где вносить изменения для корректировки паттернов анимации быстро не понять.
Проект Rick-100 имеет хорошую структуру кода, но cоздание паттернов анимаций возможно только с помощью графического Web-редактора в виде файла прошивки EEPROM. Быстро делать новые паттерны анимации очень удобно. А вот для для экспериментов со структурой и алгоритмами кода прошивки не годится.
Итак, продолжаем погружение в тему разработки прошивки.
Если не интересны детали кода получившнейся прошивки, то можно сразу загрузить одну из прошивок /firmware/stage8.hex или firmware/stage9.hex. Меняя данные в файле pages_definitions.h можно создавать новые анимации.
Схемотехника конструктора
Электрическая принципиальная схема конструктора приведена ниже.

Схема питается от источника 5-2,7 вольта.
Пятьдесят семь светодиодов объединены в 6 групп, подключаемых к выводам МК. В каждой группе, за исключением последней, половина светодиодов подключены анодом к одному из выводов порта P3, а катодом к одному из выводов порта P1, а половина в обратной полярности. Последняя группа содержит всего два светодиода в группе с обратной полярностью.
Для управления светодидами задействованы пины (они же выводы, ножки, GPIO) P3.0, P3.1, P3.3, P3.5, P3.6 порта P3 и P1.0..P1.5 порта P1. К пину PЗ.2 подключена кнопка регулировки скорости анимации.
Для управления подобной схемой необходимо три состояния сигнала на выходах МК (tristate pins): логический ноль, логическая единица и состояние выского входного сопротивления (вывод сигнала на пин отключен).
Светодиоды подключены напрямую к выводам микроконтроллера без резисторов ограничения тока. Один пин может работать пропустить ток ~20 mA, но общий ток всех пинов МК не должен быть больше 90 mA.
Программа должна быстро и кратковременно включать только один светодиод из 57, затем гасить его и переходить к следующему. Инертность зрения создаст впечатление, что нужные светодиды горят постоянно.
Набор инструментов разработчика или toolchain
Toolchain бесплатный и с открытым кодом:
Наименование |
Назначение ПО |
Комментарий |
CHG340g или аналог |
USB-2-TTL адаптер |
Подключение микроконтроллера к USB порту компьютера для заливки прошивки |
Среда разработки и средство управления зависимостями (библиотеками) |
Включает необходимые инструменты за исключением gcc и clang |
|
Компилятор языка С |
Компиляция исходного кода прошивки. |
|
gcc |
Компилятор языка С |
Компиляция модульных тестов. |
clang |
Компилятор языка С |
Компиляция модульных . Не обязателен. |
Система автоматизированной сборки кода |
Аналог make/cmake. |
|
Программа для заливки прошивки в микроконтроллер |
Устанавливать отдельно не обязательно |
Программное обеспечение было развернуто под Debian без каких-либо значительных сложностей.
CHG340g также был приобретен на алиэкспресс за более чем умеренную сумму.
На плате конструктора предусмотрен штыревой разъем с четырьмя выводами для для заливки прошивки: VCC, GND, TxD, RxD. Рекомендуется собрать программатор, но можно и без него - перед началом заливки прошивки не подключать контакт GND, ожидая необходимой стадии работы stcgal. Подробности опубликовал в репозитории https://github.com/mgoblin/STC-programmator
Внимание! МК STC не имеют возможности извлечь из них прошивку, только перезаписать новую поверх существующей.
Создаем прошивку для управления светодиодами
HAL и dependency management
Периферия МК программируется через регистры специальных функций (special function registers, далее SFR). Если вся прошивка пишется в стиле прямой рабты с регистрами SFR, то быстро превращается в набор "магического кода"
EA = 1;
TCON |= 0x20;
Выходом из ситуации в мире embedded разработки является HAL (hardware abstraction layer).
Производитель STC не предоставляет HAL.
Не беда, написал HAL сам в формате header-only библиотеки для Platformio. Побробности тут https://github.com/mgoblin/STC15lib.
Подключение опубликованной в реестре Platformio библиотеки тривиально. В файл platformio.ini проекта необходимо добавить строку
lib_deps = https://github.com/mgoblin/STC15lib.git#0.9.0
Platformio берет на себя загрузку кода и управлению зависимостями кода от библиотек. Приятная и нужная фича.
Другая библиотека HAL UniSTC выложена в свободный доступ, но не интегрирована с Platformio. Возможно и не стал бы писать свою реализацию HAL, если бы нашел ее раньше.
What the heck is this? Или сущности предметной области
Итак, окончательно структурируем поняния предметной области и их взаимосвязи.
Анимация - последовательность отображения страниц.
Скорость анимации регулируется временем отображения каждой страницы. Анимация имеет скорость, позволяющую человеку воспринять каждую страницу.
Страница - описание состояния всех 57-ми светодидов.
Светодиод - идентифицируется номером от 1 до 57. На электрической принципиальной схеме и печатной плате светодиды обозначены как L1-L57.
Отображение страницы - быстрая (незаметная человеческому глазу) циклическая итерация. На каждом витке итерации отображается состояние только одного светодиода.
Формат описания страницы
Страница - это 64-битовое число. Младшие 57 бит хранят состояние светодиодов страницы.
typedef uint64_t ehgk_page_t;
Перечисление с именами светодиодов и их битовым масками:
typedef enum led_t
L56 = (uint64_t) 0x80000000000000,
{
L1 = (uint64_t) 0x1,
L2 = (uint64_t) 0x2,
L3 = (uint64_t) 0x4,
...
L57 = (uint64_t) 0x100000000000000,
} led_t;
Используя описанные структуры данных описание страницы анимации выглядит читаемым и понятным.
// Должны гореть нечетные светодиоды в верхней строке.
ehgk_page_t
const page1 = L1 | L3 | L5 | L7;
Анимация - это просто массив страниц, объявленный в отдельном файле pages_definition.h.
#define PAGES_COUNT 2
const ehgk_page_t animation[PAGES_COUNT] =
{
L1 | L3 | L5 | L7,
L8 | L13,
};
Переменные с модификатором const размещаются не в области оперативной памяти (SDRAM), а в область хранения прошивки (flash).
Конечно, это не визуальный конструктор, но удобный механизм описания страниц на уровне кода с хранением в прошивке МК.
Управляем режимами пинов
Напомню, что выводы портов P3 и P1 должны уметь принимать три состояния: логическая единица, логический ноль и режим высокого входного сопротивления (вывод сигнала отключен).
Режим работы пинов МК конфигурируется через SFR PXM0, PXM1, где X - номер порта. Каждый бит соответствует одному выводу порта.
Для установки логического нуля или единицы нужен режим push-pull (strong pull-up output). PХM1[7 : 0] = 0, PXM0 = 1. Для режима высокого входного сопротивнления PХM1[7 : 0] = 1, PXM0 = 0.
HAL в модуле pin реализованы макросы для установки режима пинов порта.
Прошивки с кодом настройки пинов выложены в папках stage1-4 проекта.
Страничный итератор
Отображение страницы циклический процесс итерации по странице. В голову пришла мысль, а что если одну итерацию разделить на вычисление режима/значений пинов и применение вычисленных значений к регистрам МК. Реализация итератора стала изюминкой всей дальнейшей работы над прошивкой.
Итератор хранит состояние процесса итерации (условно, номер светодиода страницы). При каждом вызове итератора производится вычисление значений P3M0, P3M1, P1M0, P1M1, P1, P3 для отображения состояния текущего светодиода, а затем инкремент номера светодиода. После 57-го светдиода итератор "прыгает" на начало страницы.
Итератор реализован в локальной библиотеке ehgk_page в файлах ehgk_page_interator.h и ehgk_page_interator.c.
Логика итератора может быть скомпилирована для "большого компьютера" и оттестирована без загрузки в МК. Можно использовать итератор как в основном методе прошивки, так и в обработчике прерываний таймера. Смена страниц, установка режимов и значений регистров МК, логика вычиления нужного состояния пинов разделены.
Пример работы с итератором размещен в подпапке stage5.

Регулировка скорости анимации
Задача состоит из двух частей: "поймать" нажатие кнопки и изменить время задержки между сменой страниц.
В режиме пинов input-only работает триггер Шмидта. Кнопка подключена к пину P3.2, являющемуся выводом внешнего прерывания INT0. Не нужно постоянно опрашивать значение пина P3.2 и бороться с дребезгом контактов кнопки.
Задержку смены страниц можно вынести в переменную итд. Однако, я стал просто менять делитель тактовой частоту MK, устанавливая значения в регистре CLK_DIV.
Собираем все вместе
Прошивка, похожая на оригинальную расположена в папке stage8.
Разберем код прошивки.
В заголовочном файле page_definitions.h опредены две анимации: основная в массиве pages и анимация течения песка через горловину песочных часов в массиве l29_l30_pages.
В основном файле прошивки button_press_handler.c определен метод отображения страницы в течении заданного времени
void displayPage(uint16_t iteration_delay)
{
// Iterate through page LEDs and on/of LEDs according to page definition
for(uint16_t i = 0; i < iteration_delay; i++)
{
ehgk_iterator_next();
P1 = iter_result.p1;
P3 = iter_result.p3;
P1M0 = iter_result.p1m0;
P1M1 = iter_result.p1m1;
P3M0 = iter_result.p3m0;
P3M1 = iter_result.p3m1;
}
}
Метод применяет полученные из итератора значения к регистрам МК. Результат вызова итератора находится в переменной iter_result. Количество циклов регулирует длительность отображения страницы.
Следующая часть прошивки - обработчик прерывания INT0, вызываемого нажатием кнопки. Меняет частоту МК для изменения общей скорости анимации.
void int0_ISR() __interrupt(0)
{
// Three low CLK_DIV bits are frequency divider scaler
if(CLK_DIV == MIN_CPU_FREQ_DIVIDER)
{
CLK_DIV = MAX_CPU_FREQ_DIVIDER;
}
else
{
CLK_DIV--;
}
}
И в финале точка входа в код прошивки - метод main
void main()
{
// Configure button press handler
enable_mcu_interrupts();
enable_int0_interrupt();
set_int0_interrupt_trigger(ONLY_FALLING_EDGE);
// Set animation initial speed
set_frequency_divider_scale(MAX_CPU_FREQ_DIVIDER);
while (1)
{
// Iterate through pages
for(uint16_t page_idx = 0; page_idx < PAGES_COUNT; page_idx++)
{
// Select next page to display
ehgk_iterator_init(pages[page_idx]);
// Display selected page
displayPage(ORDINAL_PAGE_DELAY);
// Animate sand flow
ehgk_iterator_init(pages[page_idx] | L29);
displayPage(SAND_FLOW_DELAY);
ehgk_iterator_init(pages[page_idx] | L30);
displayPage(SAND_FLOW_DELAY);
}
}
}
До входа в бесконечный цикл while(1) производим подключение обработчика нажания кнопки и установку начальной (самой медленной) скорости анимации, т.е самой низкой частоты работы МК.
Далее "прокручиваем" основную анимацию из массива pages. После отображения каждой страницы основной анимации показываем короткую анимацию течения песка.
Новая анимация
В конце моего приключения сделал прошивку с анимацией показанной ниже
В этой прошивке в файле pages_definitions.h заменен паттерн в массиве pages, а анимации течения песка удалена.
Идеи по дальнейшему развитию
По моему скромному мнению в каждой программе всегда есть что улучшить и добавить. Ниже я описал некоторые еще не реализованные идеи.
Реализовать и сравнить с текущим алгоритм изменения скорости анимации используя переменную задержки анимации
Реализовать различное время отображения каждой страницы анимации
Код итерации страницы вынести в таймер
Реализовать опцию хранения определения страниц анимации в EEPROM
Использовать idle режим МК для экономии потребления энергии
Сделать визуальный редактор страниц
Заключение
Провел время с пользой и получил удовольствие, хотя порой было непросто. Прошивка получилась менее 1кБ, SDRAM хатило с запасом.
Материалы к статье опубликовал на github
https://github.com/mgoblin/ElectronicHourGlassKit - прошивка
https://github.com/mgoblin/STC-programmator - программатор STC
https://github.com/mgoblin/STC15lib - Драфт HAL
В мой телеграм канал и Instagram не ходите. Их нет.
Надеюсь, что читателям статья окажется полезной и они поделятся своими анимациями в комментариях. Возможно, кто то сможет улучшить и развить код прошивки.
Комментарии (26)
nik_vr
08.02.2025 05:43Гайвер около года назад делал подобные часы (как раз как доработку китайских).
mikeGolovanov Автор
08.02.2025 05:43Пока нашел только именно часы с отображением времени.
Ссылочкой не поделитесь?
Splinter91
08.02.2025 05:43Вроде бы он ети делал, я тоже впомнил то видео)
https://youtu.be/XumZW0UDqJc?si=clcTi88kuR42oI0j ето часы
А ето похоже как у вас https://www.youtube.com/watch?v=qgJ202w2RAM
nik_vr
08.02.2025 05:43Вот страница на его сайте: https://kit.alexgyver.ru/tutorials/digisand/
mikeGolovanov Автор
08.02.2025 05:43Спасибо. Интересно.
Но, на алиэкспресс уже продается как есть вместе с печатной платой.
У Гайвера все же другая матрица и код на ардуино.
kalapanga
08.02.2025 05:43Понятие "песочные часы" подразумевает вполне конкретный алгоритм анимации, имитирующей пересыпание песка из одной ёмкости в другую. Какие ещё "паттерны анимации"? Тупое последовательное переключение диодиков - так себе достижение. И к песочным часам отношения не имеет.
На первом видео в статье вроде как есть попытка реализовать "песок". Но черезвычайно примитивная и уродливая. С физикой и математикой не справились абсолютно.
GardenerX
08.02.2025 05:43В режиме пинов input-only работает триггер Шмидта. Кнопка подключена к пину P3.2, являющемуся выводом внешнего прерывания INT0. Не нужно постоянно опрашивать значение пина P3.2 и бороться с дребезгом контактов кнопки.
Триггер Шмидта на входе не гарантирует отсутствие дребезга контактов. У вас дребезг от 0 до напряжения подтяжки входа, а гистерезис триггера Шмидта - наверное около вольта. Другой вопрос что по этому входу запускается прерывание, которое по идее должно отключать на какое-то время вход, но это другая песня, к триггеру Шмидта отношения не имеет.
Идея сделать такую игрушку интересная, но выбор комплектующих - жесть жестяная, как по мне. У вас 57 выводов. Идем на чипдип, первое что попадается STM32G070RBT6, Микроконтроллер ARM Cortex-M0+, 32-бит, 64МГц, 128К Flash, 32К RAM, 59 I/O [LQFP-64] за 190р и вам не мультепликсировать, просто 57 проводов через резисторы по наверное сотне ом. Платка для QFP-64 стоит рублей 40. Еще кусок оргстекла в котором насверлить (а лучше нафрезеровать на ЧПУ) отверстий и вставить в них светодиоды - все железо, если я правильно понял. Программировать STM32 уже наверное в детсадах учат, спецов по программированию STM32 в стране уже наверное больше чем дворников и ИИ помощников тоже море. При всем этом респект, диванных советчиков еще больше, а как чтобы самому спаять - так в кусты.Yuri0128
08.02.2025 05:43А чем вам мультиплексирование не угодило? Вы считаете, что куча проводов будет сильно лучше? Чего-то я думаю, что в жизни куча проводов будет много хуже мультиплексирования. Да и само программирование на низком уровне stm32 все-же сильно сложнее (да, сложнее) простенького 51 контроллера. То, что в ардуино ему какой-то скетч набрать, чтобы хоть как-то работало - ну так еще не щначит, что сам контроллер простой как... ну, кусок алюминия. Он сильно сложнее 51-го, описанного в статье.
xSVPx
08.02.2025 05:43Есть готовые модули скажем 8х8 светиков управляемые по i2c, есть и больше.
Совершенно непонятно зачем брать 64ногого монстра для этой задачи, чтобы что ?
Yuri0128
08.02.2025 05:43Ага, а есть адресные светодиоды. Но, читаем внимательно заголовок, - а там уже про готовую конструкцию с Аликспреса речь.
sergyk2
08.02.2025 05:43Максимальный размер прошивки для STC15W201S 1кБ, размер памяти 256 байт. Экстремально мало.
я недавно к китайским светодиодам генератор сигнала на пик12 делал, больше половины памяти осталось неиспользовано
vrtmn
08.02.2025 05:43Спасибо за статью! Сам собрал такие же и тоже интересовался прошивкой, нашел проект Rick-100, но ничего в итоге делать с ним не стал, само устройство показалось не осбо вдохновляющим.
Но зато я упоролся по моддингу другого набора с Али - часов на STC15W408AS. Собрал набор без особых ожиданий, но результат уж очень понравился эстетически - крупные индикаторы, приятный корпус - простота и удобство. Ради интереса поискал альтернативную прошивку и нашел вот этот проект - https://github.com/zerog2k/stc_diyclock. Он уже в тот был заархивирован, так что я сделал форк и начал с ним работать - сначала по мелочи поправил что мне хотелось отрефакторил код, а потом решил расширить и хардварную часть - а именно добавить сегменты для отображения секунд - и все получилось! Да еще и с синхронизацией по NTP - счастью нет предела! :)
Вот ссылка, если интересно: https://github.com/vrtmn/stc_diyclockmikeGolovanov Автор
08.02.2025 05:43Тоже собирал такое. Прошивку https://github.com/zerog2k/ смотрел, неплохо.
Вашу прошивку, увы, не заметил. В описание репозитория на github было бы неплохо добавить тэгов 8051, STC итд. Тогда поиск будет проще.
А так репозиторий выглядит очень достойно, большое спасибо, что поделились. Буду иметь ввиду, когда/если дойдут руки.
vrtmn
08.02.2025 05:43Спасибо! (Тэги к репе добавил)
К этим часам я сделал еще один мод - отображение времени в двоичном формате (aka BCD Clock).
Вместо 7-сегментных индикаторов - 3мм светодиоды, минимальные изменения в прошивке, полностью влезает в оригинальный корпус.
На удивление получилось прикольно, поставил на столе, - несколько дней и уже легко время читается (хотя медленнее конечно, по сравнению с обычными). Код пока не опубликовал, но в ближайший месяц сделаю
tormozedison
08.02.2025 05:43А когда конструктор приезжает, там контроллер уже прошитый? Но вы решили всё равно сделать самостоятельно ещё лучше, да при таких ограниченных ресурсах? Это круто!
Можно поместить в цифровые часы вместо двоеточия между часами и минутами.
GardenerX
08.02.2025 05:43Проблема этой статьи на мой взгляд в том, что явным образом не указано, что понимание, повторение и развитие проекта доступно только "для настоящих техно-ретро-гиков", каковых среди читателей этой статьи один их десяти тысяч примерно. Нет также информации, что такой подход к управлению светодиодами устарел лет 20 назад, когда появились дешевые микроконтроллеры в корпусах с большим количеством выводов.
Почти все, кто прочитают статью, прочитают ее только для того, чтобы убедиться что им недоступно или понимание или повторение или развитие проекта. В то же время функция этой платы простая. Достаточно взять демо-плату Nucleo с необходимым количеством линий ввода-вывода, установить среду разработчика, загрузить пример и изучать код по миганию светодиодом - цикл из кажется 3 операторов. Потом подключать по очереди остальные светодиоды, наращивать код. А когда все будет работать - собрать все на своей плате. Школьнику доступно.
GambitOZ
08.02.2025 05:43Какая то прям китайско дешманская схема. Светодиоды без токоограничивающих резисторов и сразу на пин порта. 500мВт максимальная мощность рассеивания корпуса dip16.Я к тому что если случится какой то косяк и все светодиоды зажгутся разом то есть шанс спалить проц если не по превышению по току так от перегрева.
Yuri0128
Есть несколько замечаний по вашей статье:
Хоть и "Экстрим", но все-таки "экстремально".
1кбайт флеши и 256 байт ОЗУ для микроконтроллеров "низшего" сегмента - это нормально и даже неплохо, есть контроллеры с 32 байт ОЗУ. А тут еще и EPPROM завезли.
51 серия хоть и старье, но на нее есть ну просто куча написанного всего разного - посему норм (есть и контроллеры с USB2 на борту, обеспечивающие High Speed USB). Не жалуйтесь.
Ваше видение несколько неоптимальное. На песочные часы нужно оччень немного флеши и совсем чуть-чуть ОЗУ. А если на чистом асме - то ваще копейки. Но это так, к слову, просто в 1 кбайт флеши можно четверо часов засунуть и еще место останется для наворотов.
Ну и битовая маска светодиодов - как по мне, так ваша реализация самой процедуры (разделение на поименование и потом мучение с набором в скрин) весьма неоднозначна и для меня - очень неудобна и слишком увеличивающая объем кода (исходного).
А так - ну поигрались. Себе что-то доказали. Но оптимальностью не пахнет, ваша "прошика" интереса не представляет. Одно радует - нету посыла на телеграм-канал! А то это уже таким привычным становится, что аж жуть
mikeGolovanov Автор
Ошибку поправил.
А где можно набраться лучших практик программирования прошивок для "слабеньких" контроллеров?
Jury_78
Какой нибудь PIC16F88 - полно примеров.
sergyk2
вот только зачем? новые контроллеры толще и дешевле.
Jury_78
Старый конь борозды не испортит.