OpenMV AE3¶
OpenMV AE3 построена на базе Alif Ensemble E3 — двухъядерного SoC ARM Cortex‑M55 (ядро HP на 400 МГц + ядро HE на 160 МГц) с двумя встроенными NPU (HP NPU на 400 МГц / 204 GOPS + HE NPU на 160 МГц / 46 GOPS). Плата объединяет NPU с датчиком глобального затвора PAG7936 на 1 Мп, высокоскоростным USB‑C, Wi‑Fi, Bluetooth 5.1, IMU LSM6DSM, микрофоном и дальномером VL53L8CX 8×8 на основе времени пролёта (time‑of‑flight), и всё это на плате 30 × 30 мм.
Полную документацию, фотографии и размеры см. на странице продукта OpenMV AE3.
Основные характеристики¶
Alif Ensemble E3 — двухъядерный ARM Cortex‑M55 с 128‑битным SIMD Helium, ядро HP на 400 МГц + ядро HE на 160 МГц (~640 / ~256 DMIPS, CoreMark 1748 / 752).
Два NPU: HP NPU на 400 МГц / 204 GOPS + HE NPU на 160 МГц / 46 GOPS для AI/ML — выполняет обнаружение объектов YOLO одновременно с другими задачами.
Аппаратный 2D GPU для масштабирования.
13,5 МБ внутренней SRAM, плюс 5,5 МБ встроенной MRAM и 32 МБ внешней восьмиканальной флеш‑памяти (100 МГц 8‑бит DDR, чтение 200 МБ/с).
4 КБ резервной RAM со встроенными RTC.
PAG7936 — цветной датчик глобального затвора на 1 Мп.
Встроенный IMU (акселерометр + гироскоп LSM6DSM), микрофон и датчик VL53L8CX 8×8 на основе времени пролёта (до 4 м).
Высокоскоростной USB‑C (480 Мбит/с) с фильтрацией EMI и защитой TVS, Wi‑Fi a/b/g/n + Bluetooth 5.1 (чип‑антенна или вариант с U.FL).
10 пользовательских выводов I/O — P0–P3 на боковых разъёмах, P4–P5 на разъёме Qwiic и P6–P9 на разъёме B2B на задней стороне. На разъём B2B также выведены дополнительные линии отладки и восстановления.
Все выводы выдают 3,3 В / толерантны к 3,3 В, 25 мА на вывод, поддерживают прерывания. Входы ADC привязаны к опорному напряжению 1,8 В.
Пользовательский RGB‑светодиод, пользовательская кнопка, переключатель восстановления, разъём Qwiic.
80 мкА в глубоком сне при 3,3 В (24 мА в простое, 50–60 мА в активном режиме).
Предупреждение
Выводы I/O AE3 не толерантны к 5 В. Не подключайте устройство напрямую к 5‑вольтовому MCU, например Arduino Mega, — используйте преобразователь уровней для любого сигнала 5 В.
Распиновка¶
Справочник по выводам¶
AE3 выводит 10 пользовательских выводов на боковые разъёмы (P0–P9). Дополнительные сигналы — включая JTAG и линию восстановления — выведены на разъём B2B (board‑to‑board) на задней стороне платы для шилдов и плат‑носителей.
Имя вывода |
Опорное напряжение |
Функция |
|---|---|---|
P0 |
3,3 В |
SPI0 MOSI / I2C2 SCL / UART4 TX / TIM0 T1 / PDM D3 |
P1 |
3,3 В |
SPI0 MISO / I2C2 SDA / UART4 RX / TIM0 T0 |
P2 |
3,3 В |
SPI0 SCLK / LPI2C SDA / UART5 TX / TIM1 T1 |
P3 |
3,3 В |
SPI0 SS / LPI2C SCL / UART5 RX / TIM1 T0 / PDM C3 |
P4 |
3,3 В |
I2C1 SCL / UART1 TX / TIM2 T1 / PDM C0 / CAN TX |
P5 |
3,3 В |
I2C1 SDA / UART1 RX / TIM2 T0 / PDM D0 / CAN RX |
P6 |
1,8 В |
I2C1 SDA / UART3 CTS / TIM9 T0 (только B2B) |
P7 |
1,8 В |
I2C1 SCL / UART3 RTS / TIM9 T1 (только B2B) |
P8 |
1,8 В |
I3C SDA / UART3 RX / TIM5 T0 / ADC ch S10 (только B2B) |
P9 |
1,8 В |
I3C SCL / UART3 TX / TIM5 T1 / ADC ch S11 (только B2B) |
P10 |
1,8 В |
GPIO / JTAG TCK (только B2B) |
P11 |
1,8 В |
GPIO / JTAG TDO (только B2B) |
P13 |
1,8 В |
GPIO / JTAG TMS (только B2B) |
P14 |
1,8 В |
GPIO / JTAG TDI (только B2B) |
RESET |
3,3 В |
подтяните к GND для сброса платы |
SW |
3,3 В |
пользовательская кнопка (активный низкий уровень) |
LED_RED |
3,3 В |
красный канал RGB‑светодиода (активный низкий уровень) |
LED_GREEN |
3,3 В |
зелёный канал RGB‑светодиода (активный низкий уровень) |
LED_BLUE |
3,3 В |
синий канал RGB‑светодиода (активный низкий уровень) |
Примечание
P0–P5 находятся на боковых разъёмах (привязаны к 3,3 В); P6–P9 выведены только на разъём B2B на задней стороне платы и привязаны к 1,8 В. Подача 3,3 В на вывод, привязанный к 1,8 В, повредит SoC — убедитесь, что любой сигнал, подключённый к разъёму B2B, имеет уровень 1,8 В.
Выводы питания¶
3.3V — основная шина питания AE3. Та же шина 3,3 В выведена на контактные площадки разъёма GPIO, разъём Qwiic и разъём B2B на задней стороне платы.
1.8V — выведено на разъём B2B только как выход. Используйте его для питания периферии с логикой 1,8 В на плате‑носителе B2B; не подавайте на него питание извне платы.
GND — общая земля.
У AE3 нет вывода VIN и зарядного устройства LiPo. Питание можно подавать тремя способами:
USB‑C — встроенный регулятор понижает 5 В от USB до 3,3 В и подаёт их на шину 3,3 В.
Разъём Qwiic — подайте стабилизированное напряжение 3,3 В на разъём Qwiic, чтобы запитать плату от модуля Qwiic.
Контактные площадки 3,3 В на разъёме GPIO / B2B — подайте стабилизированное напряжение 3,3 В на любую из площадок 3,3 В на разъёме I/O или разъёме B2B.
Регулятор USB питает шину через идеальный диод, поэтому внешние источники 3,3 В на стороне Qwiic / GPIO / B2B могут питать плату даже при подключённом USB, не создавая обратного тока в регулятор USB.
Совет
Используйте калькулятор времени работы от батареи, чтобы оценить, сколько AE3 проработает от батареи при заданном соотношении активного режима и глубокого сна.
Выводы восстановления и отладки¶
RESET — подтяните к GND для сброса платы. После отпускания SoC запускается обычным образом.
На передней (со стороны камеры) грани платы, в нижнем левом углу, расположен переключатель восстановления. При включении он выводит SE UART AE3 наружу через USB, чтобы OpenMV IDE могла перепрошить встроенный загрузчик. Тот же режим восстановления можно запустить удалённо, подтянув вывод RECOVERY на разъёме B2B к низкому уровню.
AE3 поддерживает отладку как через SWD, так и через полный JTAG:
Разъём SWD на 1,8 В на боковой стороне платы предназначен для кабеля Tag-Connect ECV3-06-CTX и выводит четыре сигнала SWD (TCK / TMS / TDO / RSTN) плюс GND.
Разъём B2B на задней стороне платы выводит те же отладочные выводы (P10 = TCK, P11 = TDO, P13 = TMS, P14 = TDI) плюс системный RSTN и отдельный JTAG RSTN. Эти выводы можно использовать либо для SWD (TCK + TMS), либо для полного JTAG; линия JTAG RSTN нужна только в режиме полного JTAG.
Все отладочные сигналы привязаны к 1,8 В — перед подключением убедитесь, что ваш отладочный адаптер настроен на логику 1,8 В.
Встроенная периферия¶
Светодиоды¶
У AE3 есть один пользовательский RGB‑светодиод, управляемый программно через machine.LED:
from machine import LED
LED("LED_RED").on()
LED("LED_GREEN").on()
LED("LED_BLUE").on()
Датчик камеры¶
PAG7936 управляется через модуль csi — датчики камеры:
import csi
cam = csi.CSI()
cam.reset()
cam.pixformat(csi.RGB565)
cam.framesize(csi.HD) # 1280×800
cam.snapshot(time=2000) # let auto‑exposure settle
while True:
img = cam.snapshot()
PAG7936 поддерживает режим запуска по триггеру — интегрирование пикселей точно совмещается с каждым вызовом csi.CSI.snapshot, а не с непрерывным тактовым сигналом кадров, что полезно для синхронизации захвата с внешним событием или другим датчиком. Включите его через csi.CSI.ioctl с csi.IOCTL_SET_TRIGGERED_MODE. Частота кадров падает примерно вдвое по сравнению с непрерывным режимом, поскольку считывание больше не конвейеризуется с интегрированием следующего кадра:
cam.ioctl(csi.IOCTL_SET_TRIGGERED_MODE, True)
NPU¶
Два встроенных NPU AE3 (HP NPU на 400 МГц / 204 GOPS + HE NPU на 160 МГц / 46 GOPS) доступны через модуль ml — Машинное обучение. Модели, хранящиеся в файловой системе /rom только для чтения, загружаются напрямую из флеш‑памяти без копирования в RAM, поэтому даже крупные детекторы свободно умещаются рядом с активным буфером кадра. Запустите детектор YOLOv8 на каждом кадре и нарисуйте предсказания поверх живого изображения:
import csi
import time
import ml
from ml.postprocessing.ultralytics import YoloV8
# Initialize the sensor.
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.VGA)
# Load YOLO V8 model from ROM FS.
model = ml.Model("/rom/yolov8n_192.tflite", postprocess=YoloV8(threshold=0.4))
print(model)
# Visualization parameters.
n = len(model.labels)
model_class_colors = [
(int(255 * i // n), int(255 * (n - i - 1) // n), 255)
for i in range(n)
]
clock = time.clock()
while True:
clock.tick()
img = csi0.snapshot()
# boxes is a list of list per class of ((x, y, w, h), score) tuples
boxes = model.predict([img])
# Draw bounding boxes around the detected objects
for i, class_detections in enumerate(boxes):
rects = [r for r, score in class_detections]
labels = [model.labels[i] for j in range(len(rects))]
colors = [model_class_colors[i] for j in range(len(rects))]
ml.utils.draw_predictions(img, rects, labels, colors, format=None)
print(clock.fps(), "fps")
Ядро HE¶
AE3 объединяет два ядра Cortex‑M55 в одном MCU: высокопроизводительное (HP) ядро, на котором работают основной экземпляр MicroPython, камера, HP NPU, USB и т. д.; и энергоэффективное (HE) ядро, которое потребляет значительно меньше энергии и загружает собственный небольшой экземпляр MicroPython. Оба ядра используют общую шину сообщений Open-AMP / RPMsg, поэтому HP‑ядро может отправлять функции Python на HE‑ядро, получать результаты обратно и держать обе половины разделёнными.
Самая простая точка входа — декоратор @openamp.async_remote. Он маршалит функцию Python, отправляет её на HE‑ядро, и HE‑ядро выполняет её как задачу asyncio. После регистрации задач создайте экземпляр openamp.RemoteProc с адресом прошивки HE во флеш‑памяти и вызовите rproc.start(), чтобы загрузить второе ядро. Без функции обратного вызова вывод print() декорированной функции пересылается через конечную точку по умолчанию в stdout HP‑ядра — удобно для «hello world»:
import time
import openamp
@openamp.async_remote
async def task1(ept):
import asyncio
while True:
print("Hello from the HE core!")
await asyncio.sleep(1)
# Boot the HE core. This runs the registered tasks.
rproc = openamp.RemoteProc(0x80320000)
rproc.start()
while True:
print("Hello from the HP core!")
time.sleep(1)
Для двунаправленного обмена сообщениями передайте декоратору функцию обратного вызова. Она выполняется на HP‑ядре всякий раз, когда задача HE вызывает ept.send():
import time
import openamp
def task_callback(src_addr, data):
print("HP received:", data.decode())
@openamp.async_remote(task_callback)
async def task1(ept):
import asyncio
count = 0
while True:
ept.send(f"count = {count}")
count += 1
await asyncio.sleep(1)
rproc = openamp.RemoteProc(0x80320000)
rproc.start()
while True:
time.sleep(1)
У HE‑ядра есть собственный HE NPU (160 МГц, 46 GOPS), поэтому оно может выполнять вторую ML‑модель параллельно с тем, чем занят HP NPU HP‑ядра. Полезное разделение — поместить небольшую постоянно работающую модель‑триггер / классификатор на сторону HE и позволить HP‑ядру реагировать только тогда, когда что‑то интересное помечено флагом — распознавание ключевых слов со встроенного микрофона хорошо подходит, потому что оно непрерывное, малополосное, а HE‑ядро остаётся при значительно более низком энергопотреблении, чем HP. Замороженный помощник ml.apps.MicroSpeech распознаёт «Yes» и «No» из коробки — произносите слова громко и чётко во встроенный микрофон, чтобы запустить обнаружение:
import time
import openamp
def task_callback(src_addr, data):
print("Heard:", data.decode())
@openamp.async_remote(task_callback)
async def task1(ept):
from ml.apps import MicroSpeech
speech = MicroSpeech(gain_db=24)
while True:
label, scores = speech.listen(timeout=0, threshold=0.70)
if label:
ept.send(label)
rproc = openamp.RemoteProc(0x80320000)
rproc.start()
while True:
time.sleep(1)
Для более насыщенного разделения запустите BlazeFace на HP NPU, а HE‑ядро пусть обрабатывает распознавание ключевых слов в фоне — цикл HP накладывает последнее услышанное ключевое слово на кадр камеры:
import csi
import time
import openamp
import ml
from ml.postprocessing.mediapipe import BlazeFace
label = None
label_ticks = 0
LABEL_HOLD_MS = 2000
def task_callback(src_addr, data):
global label, label_ticks
label = data.decode()
label_ticks = time.ticks_ms()
@openamp.async_remote(task_callback)
async def task1(ept):
from ml.apps import MicroSpeech
speech = MicroSpeech(gain_db=24)
while True:
l, scores = speech.listen(timeout=0, threshold=0.70)
if l:
ept.send(l)
# Start the HE core before initializing the camera on the HP core.
rproc = openamp.RemoteProc(0x80320000)
rproc.start()
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.VGA)
csi0.window((400, 400))
model = ml.Model("/rom/blazeface_front_128.tflite",
postprocess=BlazeFace(threshold=0.4))
clock = time.clock()
while True:
clock.tick()
img = csi0.snapshot()
for r, score, keypoints in model.predict([img]):
ml.utils.draw_predictions(img, [r], ("face",),
((0, 0, 255),), format=None)
ml.utils.draw_keypoints(img, keypoints, color=(255, 0, 0))
if label is not None:
if time.ticks_diff(time.ticks_ms(), label_ticks) < LABEL_HOLD_MS:
img.draw_string((4, 4), f"Heard: {label}",
color=(255, 0, 0), scale=2)
else:
label = None
print(clock.fps(), "fps")
HE‑ядро хорошо подходит для постоянно работающих или низкочастотных задач, которые не должны конкурировать с конвейером камеры/NPU на стороне HP, — небольшой ML‑вывод, лёгкий DSP над данными микрофона или IMU и аналогичные фоновые задачи.
Несколько ограничений, которые стоит помнить:
Управляя периферией с HE‑ядра, ограничьтесь микрофоном и IMU — именно для них предназначена сторона HE. Каждым периферийным устройством может владеть только одно ядро одновременно, поэтому выберите для него HP или HE и придерживайтесь этого выбора на всё время работы скрипта.
Тело каждой задачи
@openamp.async_remoteдолжно маршалиться менее чем в 500 байт байткода mpy — держите функцию небольшой и выносите более тяжёлую логику в отдельные библиотечные модули, замороженные в прошивке.Импорты внутри отправленной функции видят только модули, существующие в файловой системе HE‑ядра. У HE‑ядра есть собственный
/romROMFS — отдельный от/romHP‑ядра — поэтому модули и ML‑модели, которые вы хотите сделать доступными на HE, должны быть встроены в образ ROMFS стороны HE, а не HP.
Микрофон¶
Встроенный микрофон захватывается через audio — Модуль Audio. Каждый буфер приходит как знаковый 16‑битный PCM bytearray, что позволяет легко передавать его в ulab/numpy для быстрого DSP. Простой детектор громкости — выводит сообщение всякий раз, когда RMS‑громкость превышает порог:
import audio
from ulab import numpy as np
def loudness(pcmbuf):
samples = np.array(np.frombuffer(pcmbuf, dtype=np.int16), dtype=np.float)
rms = np.sqrt(np.mean(samples ** 2))
if rms > 10000:
print("Loud!", int(rms))
audio.init(channels=1, frequency=16000, gain_db=24)
audio.start_streaming(loudness)
while True:
pass
IMU¶
Встроенный акселерометр + гироскоп LSM6DSM доступны через imu — датчик imu:
import imu
import time
while True:
print(imu.acceleration_mg()) # (x, y, z) in milli‑g
print(imu.angular_rate_mdps()) # (x, y, z) in milli‑deg/s
time.sleep_ms(100)
Датчик времени пролёта¶
AE3 несёт многозонный датчик времени пролёта VL53L8CX 8×8, который возвращает до 64 показаний расстояния на кадр с максимальной дальностью ~4 м. Он доступен через модуль tof — драйвер датчика времяпролётного типа (time-of-flight) — вызовите tof.init(), чтобы запустить датчик, и tof.read_depth(), чтобы получить кадр глубины в виде плоского списка показаний в миллиметрах (по одному на зону):
import tof
tof.init()
while True:
depth, depth_min, depth_max = tof.read_depth()
print("min:", depth_min, "mm max:", depth_max, "mm")
Массив глубины можно также нарисовать поверх цветного кадра с основного датчика — tof.draw_depth() рисует его на существующем image.Image, а tof.snapshot() возвращает свежеотрисованное изображение глубины:
import image
import tof
import csi
# Bring up the VL53L8CX time-of-flight sensor.
tof.init()
# Configure the main camera at VGA RGB565.
cam = csi.CSI()
cam.reset()
cam.pixformat(csi.RGB565)
cam.framesize(csi.VGA)
# Off-screen framebuffer used to compose the camera frame and the
# up-scaled depth heat-map side by side before pushing the result
# back to the live preview.
b = image.Image(640, 480, image.RGB565)
while True:
# Grab a colour frame from the main camera.
img = cam.snapshot()
try:
# Capture TOF data [depth map, min distance, max distance].
# vflip / hmirror align the ToF orientation with the camera.
depth, dmin, dmax = tof.read_depth(vflip=True, hmirror=True)
# Zones with no return read back as 0.0 — clamp them to the
# frame's max distance so the colour palette doesn't show
# them as "closest".
for i in range(0, len(depth)):
if depth[i] == 0.0:
depth[i] = dmax
except RuntimeError:
# The sensor occasionally faults on a frame; reset and skip.
tof.reset()
continue
# Draw the camera frame into the left half of the framebuffer,
# scaled to 60% so it leaves room for the depth heat-map on
# the right.
b.draw_image(img, x=0, y=64+8, x_scale=0.6, hint=image.BILINEAR)
# Up-sample the 8x8 depth array 30x with bicubic smoothing and
# blend it into the right half using the depth palette.
# scale=(0, 400) maps 0-400 mm to the full palette range.
tof.draw_depth(b, depth, x=320+64+16, y=64+8, alpha=255,
hint=image.BICUBIC, x_scale=30, y_scale=30,
scale=(0, 400), color_palette=image.PALETTE_DEPTH)
# Copy the composed framebuffer back into the live preview so
# OpenMV IDE shows both panels.
img.set(b)
Wi‑Fi¶
Встроенный CYW43439 доступен через network — настройка сети как интерфейс станции. После подключения ipconfig("addr4") возвращает пару (ip, netmask):
import network, time
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect("ssid", "password")
while not wlan.isconnected():
time.sleep(1)
print("Wi‑Fi IP:", wlan.ipconfig("addr4")[0])
Bluetooth¶
Тот же CYW43439 также предоставляет Bluetooth 5.1. Используйте aioble — асинхронный BLE для BLE, дружественного к asyncio, — например, рекламируйтесь как периферийное устройство и ждите подключения центрального устройства:
import asyncio
import aioble
async def run():
while True:
conn = await aioble.advertise(250_000, name="OpenMV-AE3")
print("Connected:", conn.device)
await conn.disconnected()
asyncio.run(run())
Справочник по шинам¶
GPIO¶
Используйте machine.Pin для чтения или управления любым из выводов, обозначенных на шелкографии. Выходы — это 3,3 В CMOS и могут принимать/отдавать до 25 мА на вывод.
from machine import Pin
out = Pin("P0", Pin.OUT)
out.on()
out.off()
out.value(1)
inp = Pin("P1", Pin.IN, Pin.PULL_UP)
print(inp.value())
Любой входной вывод также может генерировать прерывание по переходам фронта:
def handler(pin):
print("triggered:", pin)
Pin("P1", Pin.IN, Pin.PULL_UP).irq(
handler, Pin.IRQ_FALLING | Pin.IRQ_RISING,
)
UART¶
Шина |
TX |
RX |
RTS |
CTS |
|---|---|---|---|---|
UART1 |
P4 |
P5 |
— |
— |
UART3 |
P9 |
P8 |
P7 |
P6 |
UART4 |
P0 |
P1 |
— |
— |
UART5 |
P2 |
P3 |
— |
— |
from machine import UART
uart = UART(1, baudrate=115200)
uart.write("hello")
uart.read(5)
UART3 — единственная шина с аппаратным управлением потоком. Поскольку P6–P9 находятся на разъёме B2B и привязаны к 1,8 В, UART3 работает только через преобразователь уровней или плату‑носитель B2B — не подключайте к нему логику 3,3 В напрямую.
I²C¶
Шина |
SCL |
SDA |
|---|---|---|
I2C1 |
P4 |
P5 |
I2C2 |
P0 |
P1 |
LPI2C |
P3 |
P2 |
from machine import I2C
i2c = I2C(1, freq=400_000)
i2c.scan()
i2c.writeto(0x76, b"hi")
Встроенный разъём Qwiic выводит I2C2 на уровне 3,3 В.
I2C1 и I2C2 можно также использовать в режиме целевого устройства (ведомого) через machine.I2CTarget, чтобы предоставить область памяти другому контроллеру I²C:
from machine import I2CTarget
buf = bytearray(32)
target = I2CTarget(1, addr=0x42, mem=buf)
Примечание
Периферийное устройство LPI2C не предоставляется в прошивке. Если бы оно было доступно, оно поддерживало бы только режим целевого устройства (ведомого), а I2C1 и I2C2 уже покрывают как режим контроллера, так и режим целевого устройства.
SPI¶
Шина |
MOSI |
MISO |
SCK |
CS |
|---|---|---|---|---|
SPI0 |
P0 |
P1 |
P2 |
P3 |
from machine import SPI
from machine import Pin
spi = SPI(0, baudrate=10_000_000)
cs = Pin("P3", Pin.OUT, value=1) # CS is not driven by the SPI peripheral
cs.value(0)
spi.write(b"hello")
cs.value(1)
ADC¶
Alif Ensemble E3 выводит два 12‑битных канала ADC на P8 и P9 (только разъём B2B). Оба входа привязаны к 1,8 В — read_u16 возвращает 0–65535 на диапазоне 0–1,8 В на выводе:
from machine import ADC
import time
adc = ADC("P8")
while True:
voltage = adc.read_u16() * 1.8 / 65535
print(voltage)
time.sleep_ms(100)
Предупреждение
Входы ADC AE3 привязаны к 1,8 В, а не к 3,3 В. Подача необработанного сигнала 3,3 В насытит преобразователь и может повредить вывод — понижайте более высокие напряжения внешним делителем.
PWM¶
Вывод |
Таймер / канал |
|---|---|
P0 |
TIM0 T1 |
P1 |
TIM0 T0 |
P2 |
TIM1 T1 |
P3 |
TIM1 T0 |
P4 |
TIM2 T1 |
P5 |
TIM2 T0 |
P6 |
TIM9 T0 (только B2B) |
P7 |
TIM9 T1 (только B2B) |
P8 |
TIM5 T0 (только B2B) |
P9 |
TIM5 T1 (только B2B) |
Управляйте любым из них через machine.PWM:
from machine import Pin, PWM
pwm = PWM(Pin("P0"), freq=1_000, duty_u16=32768)
Программно эмулируемые (bit‑banged) шины¶
machine.SoftI2C и machine.SoftSPI работают на любом GPIO, если вам нужна дополнительная шина.
Тепловой датчик (внешний)¶
Прошивка включает драйвер fir — драйвер теплового датчика (fir == far infrared, дальний инфракрасный) для внешне подключённого теплового тепловизора AMG8833 8 × 8. Подключите модуль к шине I²C, указанной ниже, затем считывайте кадры с помощью fir.init() + fir.snapshot():
import time
import image
import fir
fir.init() # auto‑detects the sensor
clock = time.clock()
while True:
clock.tick()
try:
img = fir.snapshot(x_scale=5, y_scale=5,
color_palette=image.PALETTE_IRONBOW,
hint=image.BICUBIC,
copy_to_fb=True)
except OSError:
continue
print(clock.fps())
Драйвер fir общается с датчиком только через I²C 1 — подключите модуль к P4 (SCL) и P5 (SDA).
Тайминги¶
time¶
Модуль time охватывает блокирующие задержки, монотонные тики и измерение прошедшего времени:
import time
time.sleep(1) # seconds
time.sleep_ms(500)
time.sleep_us(10)
start = time.ticks_ms()
# ...do work...
elapsed = time.ticks_diff(time.ticks_ms(), start)
Виртуальные таймеры¶
machine.Timer планирует периодические или однократные функции обратного вызова, не занимая слот аппаратного таймера. Передайте -1 в качестве id, чтобы использовать виртуальный (программный) таймер:
from machine import Timer
one_shot = Timer(-1)
one_shot.init(period=5_000, mode=Timer.ONE_SHOT,
callback=lambda t: print("once"))
periodic = Timer(-1)
periodic.init(period=2_000, mode=Timer.PERIODIC,
callback=lambda t: print("tick"))
Значения периода задаются в миллисекундах. Вызовите deinit(), чтобы остановить таймер и освободить слот.
Часы реального времени¶
machine.RTC сохраняет реальное время между сбросами, опираясь на 4 КБ встроенной резервной RAM, которая переживает глубокий сон:
from machine import RTC
rtc = RTC()
rtc.datetime((2026, 4, 30, 4, 12, 0, 0, 0)) # Y, M, D, weekday, h, m, s, subsec
print(rtc.datetime())
RTC также работает во время глубокого сна, поэтому его можно использовать как источник пробуждения для machine.deepsleep().
Информация о загрузке и среде выполнения¶
Окно USB‑загрузчика¶
При каждом включении камера запускает короткий загрузчик (несколько секунд), который позволяет OpenMV IDE обновить прошивку без необходимости входить в режим DFU. После истечения окна загрузчик передаёт управление boot.py, а затем main.py.
Работающий скрипт может повторно войти в загрузчик по требованию, вызвав machine.bootloader():
import machine
machine.bootloader()
Файловая система и порядок загрузки¶
Прошивка AE3 монтирует при загрузке до двух файловых систем:
Внутренняя флеш‑память — всегда монтируется в
/flash. По умолчанию содержитmain.pyиREADME.txt; создаётся при самой первой загрузке.ROMFS — файловая система только для чтения с отображением в память по адресу
/rom, используемая для поставки крупных ресурсов данных (например, AI‑моделей), которым выгоден доступ без копирования. Монтируется автоматически MicroPython при старте, до запуска любого пользовательского кода Python.
После монтирования рабочий каталог устанавливается в /flash. Затем интерпретатор выполняет скрипты из этого каталога:
boot.pyвыполняется при каждом программном сбросе (холодная загрузка,Ctrl‑Dиз REPL или всякий раз, когда работающий скрипт завершается).main.pyвыполняется только при холодной загрузке, сразу послеboot.py. Последующие программные сбросы повторно запускаютboot.py, но сразу переходят в REPL — чтобы повторно запуститьmain.py, нужно полностью сбросить плату.
Стандартный main.py, поставляемый на только что прошитой плате, просто мигает синим каналом пользовательского RGB‑светодиода в качестве индикатора работы (два коротких импульса, короткая пауза), чтобы можно было понять, что прошивка загрузилась корректно, без подключённого хоста.
sys.path расширяется и включает обе файловые системы и их подкаталоги lib/, поэтому импортируемые модули могут находиться в /flash/lib или /rom/lib.
При подключении по USB /flash также распознаётся как USB‑накопитель на хосте, что позволяет редактировать boot.py, main.py и любые другие файлы напрямую. Извлеките накопитель перед сбросом камеры, чтобы хост сбросил кэшированные записи.
Примечание
Поскольку ОС обращается с накопителем как с пассивным блочным устройством, файлы, созданные или изменённые кодом, работающим на OpenMV Cam, не появятся, пока хост не перемонтирует накопитель. Если и ОС, и OpenMV Cam пишут в одну и ту же файловую систему одновременно, ОС победит и перезапишет изменения, сделанные камерой.
Примечание
Красный канал пользовательского RGB‑светодиода может ненадолго загораться, пока хост читает с USB‑накопителя или пишет на него, — это управляемый прошивкой индикатор активности, а не неисправность.
Объёмы хранилища¶
AE3 поставляется с:
/flash— файловая система FAT на 8 МБ, чтение/запись./romна HP‑ядре — отображённая в память ROMFS только для чтения на 24 МБ для скриптов и данных, которые HP‑ядро загружает при старте./romна HE‑ядре — ROMFS только для чтения на 1 МБ, принадлежащая HE‑ядру. Модули и ML‑модели, которые вы хотите сделать доступными для задач@openamp.async_remote, должны быть встроены в этот образ, а не в образ HP.
Индикатор аппаратного сбоя¶
Если пользовательский RGB‑светодиод быстро перебирает все цвета — достаточно быстро, чтобы это выглядело как мерцающий белый светодиод, а не отдельные оттенки, — прошивка столкнулась с неустранимым аппаратным сбоем. Перепрошейте прошивку для восстановления; если перепрошивка не помогает, плата может быть физически повреждена.
Программные библиотеки¶
См. индекс библиотек, где приведён полный список модулей — в том числе тех, что уникальны для сборки AE3.