3.1. Мікроконтролери

OpenMV Cam працює на мікроконтролері (MCU): єдиному чипі, який поєднує процесор (CPU), оперативну пам’ять (RAM), флеш-пам’ять для зберігання програми та набір периферійних пристроїв – апаратних блоків для взаємодії із зовнішнім світом.

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

MicroPython огортає ці регістри у класи всередині модуля machine. machine.Pin(...) повертає об’єкт, що керує виводом GPIO (загального призначення) – провідником, який чип може утримувати у високому (близько 3,3 В) або низькому (близько 0 В) стані, або зчитувати як один із цих двох станів, коли ним керує зовнішній сигнал. machine.ADC(...) відкриває доступ до аналого-цифрового перетворювача, який вимірює напругу на виводі та повертає її у вигляді числа. machine.UART(...) реалізує UART (universal asynchronous receiver/transmitter) – периферійний пристрій, що надсилає і приймає байти по одному біту через пару провідників: TX (передача) та RX (приймання). Інші класи охоплюють решту периферійних пристроїв. Скрипт читає та записує Python-об’єкти; MicroPython перетворює кожне звернення на відповідні читання та записи регістрів, а ті переміщують біти на фізичних провідниках.

A single chip outline containing labelled blocks -- CPU, RAM, flash, and a row of peripheral blocks (GPIO, ADC, Timer/PWM, UART/SPI/I2C, CAN) connected by an internal bus, with arrows from each peripheral exiting the chip toward physical pin labels.

MCU об’єднує CPU, пам’ять і периферійні пристрої в одному чипі. Кожен периферійний пристрій доступний у Python через клас у модулі machine.

3.1.1. Головний цикл

Майже кожна програма для мікроконтролера має одну і ту ж структуру: одноразове налаштування на початку скрипта (імпорт модулів, конфігурація виводів, відкриття шин), а потім нескінченний цикл while True: наприкінці. Всередині циклу програма зчитує вхідні дані, приймає рішення та оновлює виходи знову і знову. Цикл і є програмою; коли скрипт завершується, пристрій перестає щось виконувати.

# setup, runs once
from machine import Pin
led = Pin("P0", Pin.OUT)

# main loop, runs forever
while True:
    led.value(1)
    # ... do work ...
    led.value(0)
    # ... do other work ...

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

3.1.2. Керування реальним часом

Настільна програма виконується паралельно з іншими. Операційна система планує її роботу через один або кілька потоків – незалежних потоків виконання, між якими вона перемикається щомілісекунди. Коли один потік очікує введення-виведення (диск, мережа, переміщення миші), ОС передає CPU іншому потоку. Програма здебільшого керується подіями: менеджер вікон викликає ваш код, коли надходить ввід; бібліотека HTTP відновлює ваш код, коли байти надходять на сокет. Якась більша система викликає ваш код.

Програма для мікроконтролера – це протилежність. За замовчуванням немає операційної системи, планувальника і жодного іншого потоку. Щойно показаний головний цикл є єдиним циклом. Периферійні пристрої генерують переривання або виставляють прапорці стану; цикл опитує їх або безпосередньо обробляє переривання. Якщо цикл зупиняється у time.sleep_ms(1000), пристрій нічого не робить ту одну секунду; немає іншого потоку, який заповнив би паузу.

Із цього випливають два наслідки, що застосовуються повсюдно:

  • Час реальний. Двічі зчитати вивід у щільному циклі займає мікросекунди; затримка на десять мілісекунд означає десять мілісекунд, протягом яких нічого іншого не відбувається. Відповіддю на це є патерн неблокуючого таймінгу.

  • Апаратне забезпечення реальне. Встановлення machine.Pin.value у 1 подає приблизно 3,3 В на фізичний провідник; встановлення в 0 подає приблизно 0 В. Інші частини схеми відразу бачать цю напругу – включно з компонентами, яким вивід може завдати шкоди, якщо керується неправильно.