Начало

Крайне ограниченные аппаратные ресурсы, древняя архитектура микроконтроллера, отсутствие экосистемы библиотек. Только 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 порту компьютера для заливки прошивки

Platformio

Среда разработки и средство управления зависимостями (библиотеками)

Включает необходимые инструменты за исключением gcc и clang

SDCC

Компилятор языка С

Компиляция исходного кода прошивки.

gcc

Компилятор языка С

Компиляция модульных тестов.
Не обязателен.

clang

Компилятор языка С

Компиляция модульных . Не обязателен.

Scons

Система автоматизированной сборки кода

Аналог make/cmake.
Устанавливать отдельно не обязательно

stcgal

Программа для заливки прошивки в микроконтроллер

Устанавливать отдельно не обязательно

Программное обеспечение было развернуто под 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
{
L1 = (uint64_t) 0x1,
L2 = (uint64_t) 0x2,
L3 = (uint64_t) 0x4,
...
L56 = (uint64_t) 0x80000000000000,
L57 = (uint64_t) 0x100000000000000,
} led_t;

Используя описанные структуры данных описание страницы анимации выглядит читаемым и понятным.

// Должны гореть нечетные светодиоды в верхней строке.
const
ehgk_page_t 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 не ходите. Их нет.

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

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


  1. Yuri0128
    08.02.2025 05:43

    Есть несколько замечаний по вашей статье:

    Экстримально

    Хоть и "Экстрим", но все-таки "экстремально".

    1кбайт флеши и 256 байт ОЗУ для микроконтроллеров "низшего" сегмента - это нормально и даже неплохо, есть контроллеры с 32 байт ОЗУ. А тут еще и EPPROM завезли.

    51 серия хоть и старье, но на нее есть ну просто куча написанного всего разного - посему норм (есть и контроллеры с USB2 на борту, обеспечивающие High Speed USB). Не жалуйтесь.

    Ваше видение несколько неоптимальное. На песочные часы нужно оччень немного флеши и совсем чуть-чуть ОЗУ. А если на чистом асме - то ваще копейки. Но это так, к слову, просто в 1 кбайт флеши можно четверо часов засунуть и еще место останется для наворотов.

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

    А так - ну поигрались. Себе что-то доказали. Но оптимальностью не пахнет, ваша "прошика" интереса не представляет. Одно радует - нету посыла на телеграм-канал! А то это уже таким привычным становится, что аж жуть


    1. mikeGolovanov Автор
      08.02.2025 05:43

      Ошибку поправил.

      А где можно набраться лучших практик программирования прошивок для "слабеньких" контроллеров?


      1. Jury_78
        08.02.2025 05:43

        Какой нибудь PIC16F88 - полно примеров.


  1. nik_vr
    08.02.2025 05:43

    Гайвер около года назад делал подобные часы (как раз как доработку китайских).


    1. mikeGolovanov Автор
      08.02.2025 05:43

      Пока нашел только именно часы с отображением времени.

      Ссылочкой не поделитесь?