STM32 з нуля без HAL. Частина 22: Blue Pill, Raspberry Pi, Luckfox і автобот

Отже, серія добігла кінця. Можливо, це не повне логічне завершення, але виконано усе, що планувалось, і навіть більше.

Опубліковано трохи за двадцяти статей за пів року. Кожна з них має свої недоліки: десь тема не до кінця розкрита, десь навпаки задалеко відійшов убік від основного формату. Десь, як писали в коментарях, було забагато пафосу, а комусь не подобались ліричні відступи. Та я писав так, як сам люблю читати – трохи пафосу, трохи лірики, трохи спроб пожартувати, нехай і не завжди вдало, головне, щоб можна було відтворити те ж саме, але швидше. Хотілось, щоб було трушно, живо, пізнавально – так, як ніде немає, принаймні українською точно.

Ця стаття могла бути просто фіналом серії про STM32. Але за шість місяців експериментів STM32 поступово переставав бути головним героєм. Навколо Blue Pill виростала ціла система: Raspberry Pi став мозком, Luckfox отримав свої задачі, Pi Pico — ще один вузол, а всі ці шматки поступово складались в Автобота.

Звісно, це дуже вузька тема, і не така актуальна, як вайбкодинг чи те, коли програмістів замінить ШІ. Скоро. Вже замінив). Якщо замінив – то навіщо витрачати час на читання й практичний досвід? Тому що embedded – це не професія, а стан душі, стиль життя. Особисто я, чим би не займався, все одно буду щось майструвати колись більше, колись менше. Хтось ходить на рибалку, хтось увесь вільний час проводить у гаражі, а хтось у цей час крутить у руках порожню банку від коли чи пакувальну коробочку й думає, а що такого цікавого можна з цього зробити, якщо використати ще й зламаний електрочайник.

Звісно, час не стоїть на місці, і пристрої еволюціонують – формат DIP вже давно поступився іншим, які так просто й не попаяєш, та, може, це вже й не так потрібно, адже є всілякі шилди, які часто достатньо просто запрограмувати й з’єднати провідцями. Але все одно ж, мабуть, кортить знати, як воно працює десь там, глибоко всередині.

Тож хотілось, щоб ця серія увібрала в себе максимум того, що зустрічається в сучасному embedded, і цього було достатньо для розуміння – подобається воно тобі чи ні. Ну а щоб це не перетворилось на купу роздроблених статей, у цій статті ми все це систематизуємо й пофілософствуємо, що з цього можна зробити та куди рухатись далі.

Місяць 1 — свій HAL з нуля

Частина 1: GPIO, регістри, переривання — фундамент усієї серії і можливо сама цікава частина. Bare-metal STM32F103 без CubeMX, без жодної готової бібліотеки — тільки три файли й даташит. Пряма робота з регістрами GPIO (ODR, IDR, BSRR), обов’язкове тактування периферії через RCC перед першим доступом, обробка кнопки через EXTI-переривання, дебаг живцем через GDB, і лінкер-скрипт з нуля з поясненням кожної секції пам’яті. Досі тримає абсолютний рекорд серії — 4678 переглядів і 21 коментар, і жодна пізніша стаття цей рекорд не наблизила навіть близько.

Частина 2: UART bit-bang → HAL — чому програмна (bit-bang) реалізація UART дає “кракозябри” на швидкостях вище кількох тисяч бод, і математика похибки baud rate через ділення BRR-регістра. Перехід до апаратного USART з правильним розрахунком дільників. У коментарях уже тоді хтось згадав DMA для UART-буферів — рання, ще не реалізована згадка теми, яка згодом стане центральною.

Частина 3: переривання, PWM, SPI, I2C — розширення HAL на решту базової периферії: EXTI глибше, таймери й ШІМ-генерація, SPI та I2C для підключення дисплеїв і сенсорів. Ця стаття — та основа, на яку через кілька місяців ляже зчитування з MPU6050 через I2C1.

Частина 4: AVR vs STM32 — навмисно орієнтована на читачів з Arduino/AVR-бекграундом: порівняльна таблиця архітектурних відмінностей, приклади коду поруч для прямого зіставлення, покроковий план переходу для тих, хто звик до pinMode()/digitalWrite() і вперше бачить прямий доступ до регістрів.

Місяць 2 — STM32 як периферія Linux

Частина 5: Buildroot, Luckfox — поворотний момент серії: STM32 перестає бути головним героєм. Перший крок у embedded Linux на платі за $15 (Luckfox Pico Pro) — збірка власного Buildroot-образу, класична пастка зі змінною $BUILDROOT, яка “зникає” між сесіями терміналу, і прошивка через Maskrom-режим. Найпопулярніша стаття другого місяця.

Частина 6: U-Boot — кастомізація завантажувача на рівні коду: власна shell-команда на C, зміна затримки старту, і прошивка модифікованого образу через MTD прямо з активної системи, без USB-кабелю чи Maskrom.

Частина 7: Device Tree, 3 UART, Python — перший справжній міст embedded Linux ↔ STM32. Модифікація Device Tree overlay, одночасна робота трьох UART-портів, і Python на Luckfox, що керує bare-metal прошивкою STM32 через serial — той самий патерн де “Linux мозок, STM32 окремі вузли”.

Місяць 3 — Luckfox думає, STM32 діє

Частина 8: GStreamer, якого немає — очікуваний стандартний GStreamer на Luckfox відсутній, довелось розбиратись з Rockchip-специфічним медіастеком: rkipc, IQ-tuning файли, підключення MIPI-камери MIS5001. Нагадування про те, що з одного SoC (System on a Chip або система на кристалі) не переноситься автоматично на інший.

Частина 9: NPU, RetinaFace, перший inference — перше реальне використання вбудованого NPU: детекція обличчя через RetinaFace, боротьба з версійними конфліктами між librknnmrt.so і прикладом рантайму, збірка C-застосунку під uclibc-toolchain Luckfox.

Частина 10: Face detection, камера -> реле — перший повний, наскрізний пайплайн серії: Luckfox детектує обличчя через NPU, сервер на PC звіряє через DeepFace-embeddings, і за результатом команда йде через UART на STM32, який керує реле через зсувний регістр SN74HC595N. “Luckfox думає, STM32 діє” — неформальний девіз усієї подальшої архітектури.

Місяць 4 — kernel space

Частина 11: kernel hello world — перехід у kernel space, найбільш “статусний” крок для embedded-розробника: printk замість printf, kmalloc замість malloc, живий kernel oops при помилці з вказівником замість цивілізованого segfault, і параметри модуля через sysfs.

Частина 12: LED driver, чотири шляхи — одна й та сама задача (керування світлодіодом) вирішена чотирма різними способами з kernel space: libgpiod з userspace, прямий sysfs, і повноцінний platform-драйвер з підтримкою Device Tree — наочна демонстрація, що “правильний” спосіб залежить від контексту задачі.

Частина 13: /dev/mydev — character device driver з нуля: реєстрація драйвера в ядрі, реалізація file_operations (open/read/write/release), і робочий /dev/mydev, доступний зі звичайного userspace-коду як звичайний файл.

Частина 14: HC-SR04, помилка −517 — драйвер для ультразвукового далекоміра зіткнувся з сучасною Linux Driver Model, яка суттєво відрізняється від того, що описують старі туторіали. Загадкова помилка −517 (EPROBE_DEFER, не одразу очевидна з коду) стала центральним сюжетом статті — приклад того, як legacy-приклади з інтернету ламаються на актуальному ядрі.

Місяць 5 — FreeRTOS

Частина 15: FreeRTOS на Blue Pill — перший запуск FreeRTOS без CubeMX: ручне клонування FreeRTOS-Kernel, окрема конфігурація збірки під конкретний проєкт, і перехід з дефолтних 8MHz HSI на 72MHz через PLL — з усіма пастками, які цей перехід тягне за собою для таймінгів решти HAL.

Частина 16: черги, семафори, UART interrupt — FreeRTOS-черги, mutex, і UART, переведений на переривання замість polling. Пояснення, як планувальник розподіляє процесорний час між тасками різних пріоритетів навколо неблокуючого коду. 1608 переглядів, 20 коментарів — другий за силою пік залучення серії, і на відміну від Ч19, тут дискусія була про сам матеріал (binary vs mutex semaphores).

Частина 17: pytest знайшов баг — автоматизовані тести embedded-прошивки через pytest ловлять UART-баг, який неможливо було помітити, просто дивлячись у термінал. Бонусом — розбір архітектури різних підходів до bootloader’ів (MCUboot, Optiboot, TinyUF2).

Місяць 6 — ADC, TDOA, фінал

Частина 18: OpenCV на Pi — автономна детекція руху через MOG2 на Raspberry Pi, боротьба з пастками NoIR-камери (magenta-tint, холодний старт фонової моделі, “застигання” моделі під час запису кліпу). 1104 перегляди, і аномальні 0 коментарів — найнижче залучення серії відносно переглядів, гучний сигнал про те, що серія затягнулась, але ж тре було виконати усе що заплановано.

Частина 19: ADC — ADC для новачків з нуля, на прикладах Arduino, максимально розкрита тема перед фінальним register-level кодом для STM32. 1011 переглядів, 42 коментарі — але, як розібрано в наступному розділі, більшість цієї дискусії була не про сам матеріал.

Частина 20: два мікрофони, один регістр — dual simultaneous ADC mode і тижневий DMA-детектив: реальний баг з переплутаним офсетом регістра (SMPR2/JOFR1), і недокументована поведінка ADC_CR2.DMA-біта, який гейтить пакування даних навіть без реального DMA-трансферу. 394 перегляди, 7 коментарів.

Частина 21: звідки прийшов звук — фізична TDOA-збірка на KY-037, провал наївної крос-кореляції для реверберуючого звуку, перехід на timer input capture через цифровий вихід компаратора. Фінал — надійний бінарний напрямок замість недосяжного точного кута. 278 переглядів, 1 коментар.

Коментар, який не встиг отримати відповідь

Під Ч21 flyman запитав: «добрався до триангуляції, а якщо дві-три плати?». Влучне продовження думки, якою закінчилась та стаття: точний TDOA на одній платі недосяжний, бо STM32F103 дає тільки два синхронних ADC-канали.

Відповідь – так, думаю саме так і масштабується TDOA поза межі одного Blue Pill. Два-три Blue Pill, кожен зі своєю парою мікрофонів (2+2 чи 2+2+2), синхронізовані спільним зовнішнім тригером – той самий SWSTART, тільки поданий одночасно на всі плати ззовні, а не з кожного власного коду. Кожна плата рахує свій локальний diff (той самий бінарний Ліво/Право з Ч21), і вже STM32 чи Pi зверху зводить кілька пар в один справжній кут — це вже не одна затримка, а система рівнянь з кількох баз одночасно, класична multilateration. Дорожче за одну плату з triple ADC (STM32F4), зате не потребує зміни архітектури — той самий HAL, розтиражований.

Мультилатерація (англ. multilateration, MLAT) — це метод визначення точного місця розташування об’єкта шляхом вимірювання різниці в часі приходу (TDOA) сигналу до кількох приймачів із відомими координатами.
Головне про метод
  • Принцип роботи: Об’єкт (наприклад, літак) випромінює сигнал. Станції на землі приймають його у різний час. Комп’ютер обчислює різницю та визначає координати.
  • Сфера використання: Найчастіше застосовується в авіації для відстеження літаків замість традиційних радарів.
  • Точність: Забезпечує високу точність спостереження на землі та у повітрі.
  • Перевага: Не потребує встановлення додаткового дорогого обладнання на борту судна.
Різниця з іншими методами
  • Трилатерація: Вимірює абсолютну відстань до об’єкта (як у GPS).
  • Мультилатерація: Вимірює різницю у відстанях (відносні дані).

◆ Чому не просто MQTT

Якби задача була “передати число з датчика на сервер” – MQTT був би простішим і однорідним рішенням, і я б його обрав без вагань. Легша протокольна модель, менше залежностей, працює на будь-чому з TCP/IP-стеком, включно з uclibc-системами типу Luckfox чи Orange Pi, де повноцінний ROS2 просто не збереться.

Але в мене не датчик, що передає число. У мене – Автобот з камерою, мікрофонним масивом, роборукою, і купою вузлів, яким треба координувати поведінку, а не просто обмінюватись показниками. ROS2 виправдовує себе саме тоді, коли з’являється робото-логіка: tf2 (трансформації координат між частинами робота – камера дивиться в один бік, рука рухається в іншій системі координат, TDOA дає напрямок у третій), rviz (візуалізація всього цього одним поглядом), стандартизовані повідомлення для sensor data, actions для довгих задач на кшталт “піди й подивись”.

MQTT – це коли вузли просто говорять одне з одним. ROS2 – коли вузли розуміють спільну модель світу.

◆ Архітектура

Raspberry Pi – мозок. Повний ROS2 Lyrical Luth, micro-ROS Agent (приймає підключення від легких клієнтів по UART), і власні rclpy-ноди – камера, детекція руху.

STM32 і Pi Pico – micro-ROS Client. Не повний ROS2 (замало ресурсів), а полегшена клієнтська бібліотека, що спілкується з micro-ROS Agent на Pi через UART. Той самий STM32, на якому я писав HAL і FreeRTOS-таски, тепер публікує дані ADC/TDOA й отримує команди як звичайна ROS2-нода, тільки через легший транспортний шар.

Luckfox і Orange Pi флотилія – MQTT->ROS2 bridge. uclibc не тягне ROS2 native – ці плати лишаються на MQTT, а окремий bridge-нод на Pi перекладає MQTT-повідомлення в ROS2-топіки і назад. Найкращий приклад “ROS2 не всюди, і це нормально” – там, де плата не тягне повноцінний стек, простіший протокол лишається правильним вибором для конкретно цього вузла.

◆ Встановлення ROS2 на Pi OS/Trixie

Помітив суттєвий прорахунок – досі в серії жодного разу не показав, як саме ROS2 опинився на Pi – виправляю, бо це справді окремий, не зовсім тривіальний крок.

Офіційно ROS2 Lyrical Luth (реліз 22.05.2026) підтримує тільки Ubuntu 26.04. Готових бінарників під Debian Trixie/Raspberry Pi OS в офіційних репозиторіях нема. Рішення — community-збірка rpi-bullseye-ros2 від Ar-Ray, яка збирає ROS2 саме під Pi OS і публікує готовий .deb:

Встановлення тягне чимало залежностей – на Pi 3B(+) реально займе 10-15 хвилин.

⚠ Проблемка: ros2 --version в Lyrical не існує як прапорець – команда ros2 не розпізнає --version і просто друкує usage. Версію можна перевірити інакше, наприклад apt show ros-lyrical-desktop, або просто довіритись тому, що встановилось саме те, що качав.

Перевірка, що DDS-discovery взагалі живий – класичний talker/listener, два окремі термінали:

Якщо в другому терміналі побачив [INFO] [listener]: I heard: [Hello World: N] раз на секунду, без пропущених номерів – ROS2-шар підтверджено робочий. У моєму випадку спрацювало з першого разу, що для community-збірки дводенної давнини свіженького релізу – приємна несподіванка.

◆ Що вже випробувано, а що ще попереду

Підтверджено, перевірено на залізі:

  • ROS2 Lyrical Luth на Raspberry Pi через community-збірку (Ar-Ray rpi-bullseye-ros2, ros-lyrical-desktop .deb) — офіційно ROS2 Lyrical підтримує тільки Ubuntu 26.04, а Pi працює на Pi OS/Debian Trixie
  • talker/listener тест пройшов чисто – базовий publish/subscribe цикл живий

Ще попереду, як хотелка, але не факт:

  • Camera publisher rclpy-нод ще не написаний
  • micro-ROS Client на STM32 (через micro-ROS Agent + UART transport) – архітектура визначена, код ще не зібраний
  • MQTT->ROS2 bridge-нод для Luckfox/Orange Pi – концептуально зрозумілий, не реалізований
  • Інтеграція TDOA-напрямку й wake-word “Катя” як реальних ROS2-топіків, що керують поведінкою робота – той самий сценарій “камера повертається на голос”, заради якого й затівалась уся TDOA-подорож Ч21

Це не фінал у сенсі “усе готово”. Це фінал у сенсі “що заплановано виконано, бо немає цілі, а є тільки шлях” – решта стане окремими статтями чи бонус-матеріалом, коли кожен шматок пройде той самий процес: спробував, зламалось, знайшов чому, виправив, задокументував, але не сьогодні… точно не сьогодні

◆ Що далі

Список того, що лишається:

  • Написати й перевірити micro-ROS Client на STM32 – цікаве продовження всього, що вже є в HAL_stm32
  • Camera publisher-нод на Pi – з’єднати вже робочий MOG2-детектор (Ч18) з ROS2-топіком
  • MQTT-bridge для Luckfox – з’єднати вже робочий RetinaFace/FaceNet NPU-пайплайн з тією ж системою
  • Звести TDOA (Ч21) і Катю в один сценарій: почула – визначила напрямок – повернула камеру

Плюс беклог, який назбирався за дорогу і нікуди не подівся: власний Bootloader, “смішний робот” на ESP32-S3, Orange Pi флотилія як окрема стаття, “Season 2” – IoT Fleet Management. Серія “STM32 з нуля без HAL” як наскрізна нумерація завершується тут. Автобот – ні.


Післямова. Що показує аналіз залучення

Оскільки останнім часом, окрім суто інженерії, мене серйозно зацікавив контент-маркетинг, я вирішив детальніше розібрати метрики своїх попередніх публікацій. Можливо, цей досвід стане комусь у пригоді, адже це чиста практика та реальні цифри, які точно знадобляться кожному, хто намагається ділитися технічними знаннями в мережі.

Порівняння Частини 18 і Частини 19 виявилось не таким простим, як здавалось на перший погляд, і показало дещо, чого я в момент публікації не планував і не передбачав.

  • Ч18 (0 коментарів): писалась суто технічно, без особистого гачка у фіналі. Стаття про OpenCV детекцію руху на Pi вийшла корисною, але занадто “ідеальною” — скоріше нудною, без інтриги чи конфлікту.
  • Ч19 (42 коментарі): а от тут зчинився просто епічний срач, і чесно — я його не планував. Бурхлива дискусія закрутилася навколо другорядного елемента — бонусної врізки про flyback-перетворювач.

Схему узяв з Facebook, де під тим самим зображенням уже палали сотні коментарів. Мені вона здалась просто кумедною ілюстрацією — свіжий приклад вірусного поп-арту, не більше. Але, як з’ясувалось постфактум, схема виявилась ідеальним детонатором саме для інженерної аудиторії: щось видимо “не так”, кожен може перевірити сам, і кожен хоче показати, що перевірив правильно.

Кілька поважних інженерів (Tim Cock, Serhii Tyshchenko, Dmitriy Mozgovoy) розгорнули в коментарях цілий консиліум і детально, по кісточках розібрали, чому цей варіант не запрацює — щиро, добросовісно, без хейту. За що я їм дуже вдячний. Сам ADC-матеріал у цій дискусії майже загубився — навіть один з коментаторів прямо відзначив: “та наче стаття про ADC, чи ви тільки на схемах знаєтесь?”.

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

Щоб розуміти ефективність контенту, треба розуміти природу самого майданчика. Це не рецензований науково-популярний журнал, де колегія професорів оцінює точність формул. Це величезна блог-платформа для ІТ-спільноти, яка живе за жорсткими законами соціальних мереж.

Написання статей на форумі дає автору купу профітів: миттєвий вихід на величезну цільову аудиторію, прокачування особистого бренду та пасивний рекрутинг, коли пропозиції про роботу знаходять вас самі. Але головне паливо місцевих алгоритмів це перфекціонізм та специфічна токсичність аудиторії (новачкам на замітку). Будь-який коментар, навіть праведний гнів олдскульного інженера проти кривої схеми, алгоритмічно піднімає статтю у топ розділу “Обговорення” і приносить нові тисячі переглядів.

Аналізуючи число успішних кейсів в ІТ-спільнотах, можна виділити кілька залізобетонних тригерів залучення:

  • Holywars (священні війни): достатньо поміж іншим кинути в текст безапеляційну фразу у стилі “ми розробляли цей модуль на С++, бо Rust для вбудованих систем надто переоцінений і складний”. І все, справу зроблено. Фанати технологій самі розгонять ваш допис до сотень коментарів, навіть якщо сама стаття була взагалі про залізо та схемотехніку. Це працює безвідмовно у будь-якій сфері. Згадайте, як роками в індустрії було мейнстримом щоп’ятниці «ховати» та хейтити PHP — кожен вважав своїм обов’язком написати, що «мова мертва». Хоча культова фраза «PHP народжений помирати» взагалі-то була геть не про технічний бік чи перспективи мови, а про суто внутрішню філософію обробки запитів, де скрипт вмирає після кожного виконання. Але кого цікавила суть, коли треба було розпалити якісний срач? Зараз місце PHP як головного хайпового громовідводу впевнено посів Rust, тому кинути камінь у його город це гарантований спосіб безкоштовно підняти охоплення статті в кілька разів.
  • Факап-сторі (байт на кризу): суха теорія архітектури залучає одиниць. Але розповідь у форматі інженерного детективу — “як одна неправильна полярність транзистора спалила прототип за $5000 за дві години до презентації інвесторам” – гарантує вірусне охоплення. Люди підсвідомо обожнюють аналізувати чужі помилки.
  • Штучна дилема (залишений гачок): замість крапки у фіналі ставиться запитання, яке змушує інших інженерів зайти в коментарі й похвалитися власним досвідом. Наприклад: “ми тут використали внутрішній 12-бітний SAR ADC через обмежений бюджет, а яку архітектуру для таких проєктів зазвичай обираєте ви у своїй практиці?”.

Підсумок: Частина 18 показала занудну сухість підручника, а Частина 19 – випадкове, непередбачене маркетингове охоплення через необачно обрану ілюстрацію. Різниця між цими двома статтями показала, що справжнє мистецтво ведення технічного блогу на форумі полягає в тому, щоб балансувати на межі глибокої експертизи й маленького “гачка” для внутрішнього перфекціоніста читача і робити це свідомо.

Справа ваша: можливо, хтось скаже, що цією ШІ-картинкою я назавжди поховав довіру до всієї серії статей. Але давайте будемо чесними: мій цикл — це не інструкція для сліпого копіпасту, а пригода і технічне дослідження. І цей маркетологічний зріз – така ж частина дослідження поведінки інженерних екосистем, як і замір таймінгів АЦП.
А якщо відкинути іронію і подивитися на ситуацію глобально, то виникне логічне запитання: а скільки взагалі варіантів кастомних HAL чи подібних лонгрідів про bare-metal існує в україномовному просторі?
Відповідь доволі сумна: їх майже немає. Наш сегмент інтернету катастрофічно бідний на глибокий хардварний контент.

Дякую всім, хто читав, коментував, ловив клікбейтні заголовки й пропонував binary semaphores замість mutex. Побачимось у наступних статтях, при надії).

Опубліковано в STM32

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *