Синхронність проти Асинхронності: як змусити STM32, ESP32, Raspberry Pi робити 10 справ одночасно

Це одні з фундаментальних понять у розумінні роботи мікроконтролерів та процесорів, без яких неможливо побудувати жодну серйозну систему автоматизації.

Синхронність (Блокуючий код)

Синхронність — це виконання завдань суворо послідовно. Кожне завдання виконується одне за одним, і кілька процесів ніколи не запускаються паралельно.

Уявіть, що перед контролером стоїть задача: крутити мотор 10 секунд на швидкості 50, після цього повернути сервопривід на 15 градусів, але при цьому всьому — ще й блимати світлодіодом кожні 3 секунди, поки крутиться мотор. У суворій синхронній логіці цю задачу повністю контролер не зможе виконати ніколи в житті.

Як це працює на практиці: спочатку контролер запустить мотор і завмре на 10 секунд через команду delay(10000). Потім він прокинеться і слухняно поверне сервомотор на 15 градусів. А ось поблимати світлодіодом кожні 3 секунди він просто не зміг — бо в той момент, коли треба було змінити стан діода, процесор перебував у глухому очікуванні логіки мотора. Світлодіод у цей час просто перебуватиме в одному стані (або горітиме, або ні). Тільки після того, як повернеться серва, контролер знову почне крутити цикл loop() з самого початку.

Асинхронність (Неблокуючий код)

Асинхронність — це виконання завдань паралельно. Якщо ми візьмемо ту саму задачу (мотор на 10 секунд, серва та блимання діодом кожні 3 секунди), то в асинхронному режимі всі ці 3 умови виконуватимуться незалежно одна від одної. Контролер без проблем впорається з усім, що йому доручено.

Але асинхронність буває різною. Її можна розділити на софтверну асинхронність (ілюзію паралельності) та апаратну асинхронність.

1. Софтверна асинхронність

Тут ми йдемо на хитрість, користуючись тим, що сучасні процесори працюють на дуже високих частотах. Ми не кажемо йому спати 10 секунд функцією delay, а змушуємо крутитися в нескінченному циклі мільйони разів на секунду і щоразу перевіряти «годинник» (функцію millis() в Arduino або системний таймер в STM32).

Код починає працювати як супер-ефективний менеджер:

  • Коло 1: «Так, мотор крутиться вже 1 мілісекунду? Рано вимикати. Світлодіод блимав? Ні, пройшла лише 1 мілісекунда. Сервопривід треба крутити? Ні, мотор ще працює».
  • Коло 500 000: «О, пройшло 3 секунди! Блимаю діодом. Мотор крутиться? Так, пройшло лише 3 секунди, хай крутиться далі».
  • Коло 2 000 000: «О, пройшло 10 секунд! Вимикаю мотор, даю команду повернути сервопривід».

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

2. Апаратна асинхронність

А ось тут ми підходимо до того, чому я обираю STM32. В цих чіпах задачу можна вирішити чесно, апаратно і взагалі без навантаження обчислювального ядра. Ми просто роздаємо завдання окремим залізним блокам (периферії) всередині чіпа, які працюють абсолютно незалежно від головного процесора:

  • Таймеру №1 ми кажемо: «Генеруй ШІМ-сигнал для мотора на швидкості 50 і вимкни його рівно через 10 секунд». Таймер відповідає «прийнято» і крутить мотор сам.
  • Таймеру №2 ми кажемо: «Блимай оцим діодом кожні 3 секунди». Таймер бере під контроль ніжку діода і блимає сам.
  • Ядро процесора в цей же мікросекундний момент спокійно бере і повертає сервопривід на 15 градусів.

У результаті: Процесор виконав свою команду за наносекунди й може «спати» або рахувати складну математику. В цей час залізо STM32 асинхронно, самостійно та ідеально точно за часом крутить мотор і блимає діодом.

Порівняння мікроконтролерів за апаратними таймерами

Апаратні таймери є у всіх мікроконтролерах, але вони кардинально відрізняються своєю кількістю та можливостями. Саме через це я свого часу і перейшов на STM32, про це я писав в цій статті.

Arduino (AVR)

Популярна Arduino Uno теж має апаратні таймери, але їх всього 3 штуки. Через це нашу з вами задачу на ній уже важко виконати. Щойно проєкт стає трохи складнішим — апаратні ресурси Arduino закінчуються. Там немає контролера DMA (прямого доступу до пам'яті), а АЦП (оцифровка датчиків) потребує постійної уваги процесора. Ми знову змушені повертатися назад і робити все софтверно — руками самого процесора.

ESP32

З цим чіпом усе чудово, він дуже потужний. У нього є і круті таймери, і DMA, і спеціальні апаратні блоки для генерації ШІМ під мотори (MCPWM). Але в чому підвох? ESP32 створювалася для бездротового зв'язку (Wi-Fi та Bluetooth). Тому її архітектура заточена під роботу з операційною системою реального часу FreeRTOS.

В ESP32 зазвичай використовують софтверну асинхронність, розбиваючи задачі на потоки та ядра: одне ядро обслуговує Wi-Fi, а інше — крутить мотор у FreeRTOS. Це круто, але це все одно навантажує обчислювальне ядро і «жере» багато енергії.

Король апаратної асинхронності — STM32

STM32 проектувався суто під індустріальні завдання (станки, автомобілі, робототехніка, квадрокоптери), де в пріоритеті не Wi-Fi, а залізна точність. Всередині STM32 архітектура периферії доведена до абсолюту:

  • Кількість таймерів: Їх там може бути понад 10–15 штук. Існують спеціальні Advanced-таймери, які вміють самі (без участі процесора) генерувати складні комплементарні сигнали для трифазних моторів із вбудованим захистом від короткого замикання.
  • Повноцінний DMA (Direct Memory Access): Він інтегрований майже в кожен блок. Можна сказати: «DMA, візьми дані з АЦП (датчика температури), перенеси їх в оперативку, а коли закінчиш — просто смикни перериванням». Процесор у цей час взагалі відпочиває.
  • Система подій (Event System / TRG): У сучасних серіях (наприклад, STM32G0) блоки можуть спілкуватися між собою в залізі взагалі без процесора. Таймер №1 закінчив відлік -> сам подав апаратний сигнал на запуск АЦП -> АЦП зняв покази -> передав через DMA в пам'ять. Процесор у цей момент може буквально спати.

А що ж по Raspberry Pi?

А ось усім відома «малинка» в апаратному плані виявляється слабкою! Raspberry Pi — це мікропроцесор, а не мікроконтролер. Це повноцінний комп'ютер, як ваш ноутбук, просто розпаяний на маленькій платі. У неї шалена обчислювальна потужність (4 ядра, гігабайти оперативки), але її GPIO-піни — це просто «голі перемикачі».

  • Брак апаратних таймерів: Якщо в STM32 є десяток потужних залізних таймерів під ШІМ, то у великої Raspberry Pi їх усього 2–4 канали на всю плату. Навіть найпростішу задачу — згенерувати 6 ідеально рівних ШІМ-сигналів для моторів — Raspberry Pi апаратно зробити не може.
  • Все через софт: Щоб крутити мотори чи блимати діодами з «малинки», програмісти змушені писати код на Python чи C++, який смикає ніжки процесора вручну. Але оскільки над цим кодом стоїть «важка» ОС Linux, яка постійно відволікається на фонові процеси, ваші сигнали починають тремтіти (виникає джиттер).

Резюме по великій Raspberry Pi: Вона неймовірно сильна в софтверній асинхронності (може одночасно крутити сайт, обробляти відео з камери та писати логи), але вона абсолютно безпорадна в апаратній асинхронності реального часу.

Магічний виняток: чіп RP2040 (Raspberry Pi Pico)

Британці з Raspberry Pi чудово розуміли цей мінус. Тому त्यांनी створили свій власний мікроконтролер — RP2040. І ось у ньому вони реалізували унікальну фішку, якої немає навіть в STM32 — блок PIO (Programmable I/O).

Це абсолютний космос для апаратної асинхронності. Всередині чіпа, окрім головного процесора, є маленькі «міні-комп'ютери», які працюють окремо. Ви можете написати для цих PIO-блоків мікрокод (буквально на 5–10 команд), і вони будуть самі, на рівні заліза, генерувати будь-які протоколи чи сигнали з шаленою швидкістю, взагалі не чіпаючи головні ядра процесора.

Чекаю на ваші коментарі під цим постом! Дуже цікава тема для дискусій!


 


Коментарі (0)

Наразі немає коментарів. Будьте першим!


Залишити коментар