OpenMV AE3¶
OpenMV AE3 zbudowano wokół układu Alif Ensemble E3 — dwurdzeniowego SoC z procesorami ARM Cortex‑M55 (rdzeń HP 400 MHz + rdzeń HE 160 MHz) z dwiema wbudowanymi jednostkami NPU (NPU HP 400 MHz / 204 GOPS + NPU HE 160 MHz / 46 GOPS). Płytka łączy jednostki NPU z 1‑megapikselowym sensorem global‑shutter PAG7936, USB‑C high‑speed, Wi‑Fi, Bluetooth 5.1, IMU LSM6DSM, mikrofonem oraz dalmierzem time‑of‑flight VL53L8CX 8×8, a wszystko to na płytce o wymiarach 30 × 30 mm.
Pełną kartę katalogową, zdjęcia i wymiary znajdziesz na stronie produktu OpenMV AE3.
Najważniejsze cechy¶
Alif Ensemble E3 — dwurdzeniowy ARM Cortex‑M55 ze 128‑bitowym SIMD Helium, rdzeń HP 400 MHz + rdzeń HE 160 MHz (~640 / ~256 DMIPS, CoreMark 1748 / 752).
Dwie jednostki NPU: NPU HP 400 MHz / 204 GOPS + NPU HE 160 MHz / 46 GOPS do zadań AI/ML — uruchamia wykrywanie obiektów YOLO równolegle z innymi obciążeniami.
Sprzętowy 2D GPU do skalowania.
13,5 MB wewnętrznej pamięci SRAM oraz 5,5 MB wbudowanej pamięci MRAM i 32 MB zewnętrznej pamięci octal flash (100 MHz 8‑bit DDR, odczyt 200 MB/s).
4 KB pamięci backup RAM z wbudowanym RTC.
Kolorowy 1‑megapikselowy sensor global‑shutter PAG7936.
Wbudowane IMU (akcelerometr + żyroskop LSM6DSM), mikrofon oraz sensor VL53L8CX 8×8 time‑of‑flight (do 4 m).
USB‑C high‑speed (480 Mb/s) z filtrowaniem EMI i ochroną TVS, Wi‑Fi a/b/g/n + Bluetooth 5.1 (antena na chipie lub opcja U.FL).
10 pinów I/O użytkownika — P0–P3 na bocznych złączach, P4–P5 na złączu Qwiic oraz P6–P9 na złączu B2B z tyłu. Dodatkowe linie debugowania i odzyskiwania są również poprowadzone do złącza B2B.
Wszystkie piny: wyjście 3,3 V / tolerancja 3,3 V, 25 mA na pin, z obsługą przerwań. Wejścia ADC są odniesione do 1,8 V.
Dioda RGB użytkownika, przycisk użytkownika, przełącznik odzyskiwania, złącze Qwiic.
80 µA w trybie deep sleep przy 3,3 V (24 mA w spoczynku, 50–60 mA w trybie aktywnym).
Ostrzeżenie
Piny I/O płytki AE3 nie tolerują 5 V. Nie podłączaj urządzenia bezpośrednio do mikrokontrolera 5 V, takiego jak Arduino Mega — do każdego sygnału 5 V użyj konwertera poziomów logicznych.
Wyprowadzenia¶
Opis pinów¶
AE3 udostępnia 10 pinów użytkownika na bocznych złączach (P0–P9). Dodatkowe sygnały — w tym JTAG oraz linia odzyskiwania — są poprowadzone do złącza B2B (board‑to‑board) z tyłu płytki, z myślą o nakładkach i płytkach bazowych.
Nazwa pinu |
Odniesienie |
Funkcja |
|---|---|---|
P0 |
3,3 V |
SPI0 MOSI / I2C2 SCL / UART4 TX / TIM0 T1 / PDM D3 |
P1 |
3,3 V |
SPI0 MISO / I2C2 SDA / UART4 RX / TIM0 T0 |
P2 |
3,3 V |
SPI0 SCLK / LPI2C SDA / UART5 TX / TIM1 T1 |
P3 |
3,3 V |
SPI0 SS / LPI2C SCL / UART5 RX / TIM1 T0 / PDM C3 |
P4 |
3,3 V |
I2C1 SCL / UART1 TX / TIM2 T1 / PDM C0 / CAN TX |
P5 |
3,3 V |
I2C1 SDA / UART1 RX / TIM2 T0 / PDM D0 / CAN RX |
P6 |
1,8 V |
I2C1 SDA / UART3 CTS / TIM9 T0 (tylko B2B) |
P7 |
1,8 V |
I2C1 SCL / UART3 RTS / TIM9 T1 (tylko B2B) |
P8 |
1,8 V |
I3C SDA / UART3 RX / TIM5 T0 / ADC kanał S10 (tylko B2B) |
P9 |
1,8 V |
I3C SCL / UART3 TX / TIM5 T1 / ADC kanał S11 (tylko B2B) |
P10 |
1,8 V |
GPIO / JTAG TCK (tylko B2B) |
P11 |
1,8 V |
GPIO / JTAG TDO (tylko B2B) |
P13 |
1,8 V |
GPIO / JTAG TMS (tylko B2B) |
P14 |
1,8 V |
GPIO / JTAG TDI (tylko B2B) |
RESET |
3,3 V |
zewrzyj do GND, aby zresetować płytkę |
SW |
3,3 V |
przycisk użytkownika (aktywny w stanie niskim) |
LED_RED |
3,3 V |
czerwony kanał diody RGB (aktywny w stanie niskim) |
LED_GREEN |
3,3 V |
zielony kanał diody RGB (aktywny w stanie niskim) |
LED_BLUE |
3,3 V |
niebieski kanał diody RGB (aktywny w stanie niskim) |
Informacja
P0–P5 znajdują się na bocznych złączach (odniesienie 3,3 V); P6–P9 są dostępne wyłącznie na złączu B2B z tyłu płytki i są odniesione do 1,8 V. Podanie 3,3 V na pin odniesiony do 1,8 V uszkodzi SoC — upewnij się, że każdy sygnał podłączony do złącza B2B ma poziom 1,8 V.
Piny zasilania¶
3.3V — główna szyna zasilania płytki AE3. Ta sama szyna 3,3 V jest dostępna na padach lutowniczych złącza GPIO, na złączu Qwiic oraz na złączu B2B z tyłu płytki.
1.8V — dostępne na złączu B2B wyłącznie jako wyjście. Użyj go do zasilania urządzeń peryferyjnych o logice 1,8 V na nośniku B2B; nie podawaj na nie zasilania z zewnątrz płytki.
GND — masa wspólna.
AE3 nie ma pinu VIN ani ładowarki LiPo. Może być zasilany na trzy sposoby:
USB‑C — wbudowany stabilizator obniża 5 V z USB do 3,3 V i podaje to napięcie na szynę 3,3 V.
Złącze Qwiic — podaj stabilizowane zasilanie 3,3 V na złącze Qwiic, aby zasilać płytkę z modułu Qwiic.
Złącze GPIO / pady 3,3 V B2B — podaj stabilizowane zasilanie 3,3 V na dowolny z padów 3,3 V na złączu I/O lub na złączu B2B.
Stabilizator USB zasila szynę przez diodę idealną, więc zewnętrzne źródła 3,3 V po stronie Qwiic / GPIO / B2B mogą zasilać płytkę nawet wtedy, gdy USB jest nadal podłączone, bez wstecznego zasilania stabilizatora USB.
Wskazówka
Skorzystaj z kalkulatora czasu pracy na baterii, aby oszacować, jak długo AE3 będzie działać na baterii przy zadanym współczynniku wypełnienia trybu aktywnego / deep sleep.
Piny odzyskiwania i debugowania¶
RESET — zewrzyj do GND, aby zresetować płytkę. Zwolnienie tej linii pozwala SoC uruchomić się normalnie.
Na przedniej (od strony kamery) stronie płytki, w lewym dolnym rogu znajduje się przełącznik odzyskiwania. Po włączeniu wymusza on wyprowadzenie UART SE płytki AE3 przez USB, dzięki czemu OpenMV IDE może ponownie wgrać wbudowany bootloader. Ten sam tryb odzyskiwania można uruchomić zdalnie, zwierając pin RECOVERY na złączu B2B do stanu niskiego.
AE3 obsługuje zarówno debugowanie SWD, jak i pełny JTAG:
Złącze SWD 1,8 V na boku płytki jest przeznaczone do kabla Tag-Connect ECV3-06-CTX i wyprowadza cztery sygnały SWD (TCK / TMS / TDO / RSTN) oraz GND.
Złącze B2B z tyłu płytki udostępnia te same piny debugowania (P10 = TCK, P11 = TDO, P13 = TMS, P14 = TDI) oraz systemowy RSTN i osobny JTAG RSTN. Piny te można wykorzystać do SWD (TCK + TMS) lub do pełnego JTAG; linia JTAG RSTN jest potrzebna wyłącznie w trybie pełnego JTAG.
Wszystkie sygnały debugowania są odniesione do 1,8 V — przed podłączeniem upewnij się, że Twój adapter debugowania jest skonfigurowany na logikę 1,8 V.
Wbudowane urządzenia peryferyjne¶
Diody LED¶
AE3 ma pojedynczą diodę RGB użytkownika, sterowaną programowo przez machine.LED
from machine import LED
LED("LED_RED").on()
LED("LED_GREEN").on()
LED("LED_BLUE").on()
Sensor kamery¶
PAG7936 jest sterowany przez moduł csi — sensory kamery
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 obsługuje tryb wyzwalany — integracja pikseli jest dokładnie zsynchronizowana z każdym wywołaniem csi.CSI.snapshot, a nie z wolnobieżnym zegarem ramki, co przydaje się do synchronizacji rejestracji ze zdarzeniem zewnętrznym lub innym sensorem. Włącz go przez csi.CSI.ioctl z csi.IOCTL_SET_TRIGGERED_MODE. Liczba klatek na sekundę spada do mniej więcej połowy trybu wolnobieżnego, ponieważ odczyt nie nakłada się już potokowo z integracją kolejnej ramki:
cam.ioctl(csi.IOCTL_SET_TRIGGERED_MODE, True)
NPU¶
Dwie wbudowane jednostki NPU płytki AE3 (NPU HP 400 MHz / 204 GOPS + NPU HE 160 MHz / 46 GOPS) są udostępnione przez moduł ml — Uczenie maszynowe. Modele zapisane w systemie plików tylko do odczytu /rom ładują się bezpośrednio z pamięci flash bez kopiowania do RAM, więc nawet duże detektory mieszczą się wygodnie obok bufora ramki na żywo. Uruchom detektor YOLOv8 na każdej ramce i narysuj predykcje na obrazie na żywo:
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")
Rdzeń HE¶
AE3 zawiera dwa rdzenie Cortex‑M55 w jednym MCU: rdzeń wysokiej wydajności (HP), który uruchamia główną instancję MicroPython, kamerę, NPU HP, USB itd.; oraz rdzeń wysokiej efektywności (HE), który pracuje przy znacznie niższym poborze mocy i uruchamia własną, niewielką instancję MicroPython. Oba rdzenie współdzielą magistralę komunikatów Open-AMP / RPMsg, dzięki czemu rdzeń HP może wysyłać funkcje Pythona do rdzenia HE, odbierać wyniki i utrzymywać obie połówki rozdzielone.
Najprostszym punktem wejścia jest dekorator @openamp.async_remote. Serializuje on funkcję Pythona, przesyła ją do rdzenia HE, a rdzeń HE uruchamia ją jako zadanie asyncio. Po zarejestrowaniu zadań utwórz instancję openamp.RemoteProc z adresem flash oprogramowania HE i wywołaj rproc.start(), aby uruchomić drugi rdzeń. Bez wywołania zwrotnego wynik print() udekorowanej funkcji jest przekazywany domyślnym punktem końcowym do standardowego wyjścia rdzenia HP — przydatne dla „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)
W przypadku komunikacji dwukierunkowej przekaż dekoratorowi wywołanie zwrotne. Wywołanie zwrotne uruchamia się na rdzeniu HP za każdym razem, gdy zadanie HE wywoła 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)
Rdzeń HE ma własną jednostkę NPU HE (160 MHz, 46 GOPS), więc może uruchamiać drugi model ML równolegle z tym, czym akurat zajmuje się NPU HP rdzenia HP. Użytecznym podziałem jest umieszczenie małego, zawsze aktywnego modelu wyzwalacza / klasyfikatora po stronie HE i pozwolenie rdzeniowi HP reagować dopiero wtedy, gdy zostanie zasygnalizowane coś interesującego — wykrywanie słów kluczowych z wbudowanego mikrofonu pasuje tu dobrze, ponieważ jest ciągłe, o niskiej przepustowości, a rdzeń HE pozostaje przy znacznie niższym poborze mocy niż HP. Zamrożony pomocnik ml.apps.MicroSpeech rozpoznaje „Yes” i „No” od razu po wyjęciu z pudełka — wypowiedz te słowa głośno i wyraźnie do wbudowanego mikrofonu, aby wyzwolić wykrywanie:
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)
Aby uzyskać bogatszy podział, uruchom BlazeFace na NPU HP, podczas gdy rdzeń HE obsługuje w tle wykrywanie słów kluczowych — pętla HP nakłada ostatnio usłyszane słowo kluczowe na ramkę z kamery:
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")
Rdzeń HE doskonale nadaje się do obciążeń zawsze aktywnych lub o niskiej częstotliwości, które nie powinny konkurować z potokiem kamery/NPU po stronie HP — niewielkie wnioskowanie ML, lekki DSP na danych z mikrofonu lub IMU oraz podobne zadania działające w tle.
Kilka ograniczeń, o których warto pamiętać:
Sterując urządzeniami peryferyjnymi z rdzenia HE, ogranicz się do mikrofonu i IMU — to do nich strona HE została zaprojektowana. Każde urządzenie peryferyjne może w danym momencie należeć tylko do jednego rdzenia, więc wybierz dla niego HP lub HE i pozostań przy tym wyborze przez cały czas działania skryptu.
Treść każdego zadania
@openamp.async_remotemusi serializować się do mniej niż 500 bajtów bytecode’u mpy — utrzymuj funkcję małą i wydzielaj cięższą logikę do osobnych modułów bibliotecznych, które są zamrażane w oprogramowaniu układowym.Importy wewnątrz wysyłanej funkcji widzą tylko moduły istniejące w systemie plików rdzenia HE. Rdzeń HE ma własny ROMFS
/rom— odrębny od/romrdzenia HP — więc moduły i modele ML, które mają być dostępne na HE, muszą być wbudowane w obraz ROMFS po stronie HE, a nie po stronie HP.
Mikrofon¶
Wbudowany mikrofon jest rejestrowany przez audio — Moduł Audio. Każdy bufor dociera jako 16‑bitowy ze znakiem bytearray w formacie PCM, co pozwala trywialnie podać go do ulab/numpy na potrzeby szybkiego DSP. Prosty detektor głośności — wypisuje komunikat, gdy głośność RMS przekroczy próg:
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¶
Wbudowany akcelerometr + żyroskop LSM6DSM jest udostępniony przez imu — sensor 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)
Sensor time‑of‑flight¶
AE3 jest wyposażony w wielostrefowy sensor time‑of‑flight VL53L8CX 8×8, który zwraca do 64 odczytów odległości na ramkę, przy maksymalnym zasięgu ~4 m. Jest on udostępniony przez moduł tof — sterownik sensora time-of-flight — wywołaj tof.init(), aby uruchomić sensor, oraz tof.read_depth(), aby pobrać ramkę głębi jako płaską listę odczytów w milimetrach (po jednym na strefę):
import tof
tof.init()
while True:
depth, depth_min, depth_max = tof.read_depth()
print("min:", depth_min, "mm max:", depth_max, "mm")
Tablicę głębi można też narysować na kolorowej ramce z głównego sensora — tof.draw_depth() nanosi ją na istniejący obiekt image.Image, natomiast tof.snapshot() zwraca świeżo wyrenderowany obraz głębi:
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¶
Wbudowany CYW43439 jest udostępniony przez network — konfiguracja sieci jako interfejs stacji. Po połączeniu ipconfig("addr4") zwraca parę (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¶
Ten sam CYW43439 udostępnia również Bluetooth 5.1. Użyj aioble — Asynchroniczne BLE do obsługi BLE przyjaznej dla asyncio — na przykład rozgłaszaj się jako urządzenie peryferyjne i czekaj na połączenie urządzenia centralnego:
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())
Opis magistral¶
GPIO¶
Użyj machine.Pin, aby odczytywać lub sterować dowolnym z opisanych na sitodruku pinów. Wyjścia są 3,3 V CMOS i mogą przyjmować/dostarczać do 25 mA na pin.
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())
Każdy pin wejściowy może też wywołać przerwanie przy zmianach na zboczu:
def handler(pin):
print("triggered:", pin)
Pin("P1", Pin.IN, Pin.PULL_UP).irq(
handler, Pin.IRQ_FALLING | Pin.IRQ_RISING,
)
UART¶
Magistrala |
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 to jedyna magistrala ze sprzętowym sterowaniem przepływem. Ponieważ P6–P9 znajdują się na złączu B2B i są odniesione do 1,8 V, UART3 działa tylko przez konwerter poziomów logicznych lub nośnik B2B — nie podłączaj do niego bezpośrednio logiki 3,3 V.
I²C¶
Magistrala |
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")
Wbudowane złącze Qwiic wyprowadza I2C2 przy 3,3 V.
I2C1 i I2C2 mogą być również używane w trybie podrzędnym (slave) przez machine.I2CTarget, aby udostępnić obszar pamięci innemu kontrolerowi I²C:
from machine import I2CTarget
buf = bytearray(32)
target = I2CTarget(1, addr=0x42, mem=buf)
Informacja
Urządzenie peryferyjne LPI2C nie jest udostępnione w oprogramowaniu układowym. Gdyby było udostępnione, obsługiwałoby wyłącznie tryb podrzędny (slave), a I2C1 i I2C2 już pokrywają zarówno pracę kontrolera, jak i urządzenia podrzędnego.
SPI¶
Magistrala |
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 udostępnia dwa 12‑bitowe kanały ADC na P8 i P9 (tylko złącze B2B). Oba wejścia są odniesione do 1,8 V — read_u16 zwraca 0–65535 w zakresie 0–1,8 V na pinie:
from machine import ADC
import time
adc = ADC("P8")
while True:
voltage = adc.read_u16() * 1.8 / 65535
print(voltage)
time.sleep_ms(100)
Ostrzeżenie
Wejścia ADC płytki AE3 są odniesione do 1,8 V, a nie 3,3 V. Podanie surowego sygnału 3,3 V nasyci przetwornik i może uszkodzić pin — wyższe napięcia podziel zewnętrznie.
PWM¶
Pin |
Licznik czasu / kanał |
|---|---|
P0 |
TIM0 T1 |
P1 |
TIM0 T0 |
P2 |
TIM1 T1 |
P3 |
TIM1 T0 |
P4 |
TIM2 T1 |
P5 |
TIM2 T0 |
P6 |
TIM9 T0 (tylko B2B) |
P7 |
TIM9 T1 (tylko B2B) |
P8 |
TIM5 T0 (tylko B2B) |
P9 |
TIM5 T1 (tylko B2B) |
Steruj dowolnym z nich przez machine.PWM
from machine import Pin, PWM
pwm = PWM(Pin("P0"), freq=1_000, duty_u16=32768)
Programowe magistrale bit‑bang¶
machine.SoftI2C i machine.SoftSPI działają na dowolnym pinie GPIO, jeśli potrzebujesz dodatkowej magistrali.
Sensor termiczny (zewnętrzny)¶
Oprogramowanie układowe zawiera sterownik fir — sterownik sensora termicznego (fir == far infrared, daleka podczerwień) dla zewnętrznie podłączonej kamery termowizyjnej AMG8833 8 × 8. Podłącz moduł do magistrali I²C wymienionej poniżej, a następnie odczytuj ramki za pomocą 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())
Sterownik fir komunikuje się z sensorem wyłącznie przez I²C 1 — podłącz moduł do P4 (SCL) i P5 (SDA).
Pomiar czasu¶
time¶
Moduł time obejmuje blokujące opóźnienia, monotoniczne tiki oraz pomiar czasu, który upłynął:
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)
Wirtualne liczniki czasu¶
machine.Timer planuje okresowe lub jednorazowe wywołania zwrotne bez zajmowania sprzętowego slotu licznika czasu. Przekaż -1 jako id, aby użyć wirtualnego (programowego) licznika czasu:
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"))
Wartości okresu podawane są w milisekundach. Wywołaj deinit(), aby zatrzymać i zwolnić slot.
Zegar czasu rzeczywistego¶
machine.RTC utrzymuje czas zegarowy między resetami, w oparciu o 4 KB wbudowanej pamięci backup RAM, która przetrwa tryb deep sleep:
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 działa również w trybie deep sleep, więc możesz go wykorzystać jako źródło wybudzania dla machine.deepsleep().
Informacje o rozruchu i czasie działania¶
Okno bootloadera USB¶
Przy każdym włączeniu zasilania kamera uruchamia krótki bootloader (kilka sekund), który pozwala OpenMV IDE zaktualizować oprogramowanie układowe bez konieczności wchodzenia przez użytkownika w tryb DFU. Po upływie tego okna bootloader przekazuje sterowanie do boot.py, a następnie do main.py.
Działający skrypt może na żądanie ponownie wejść do bootloadera, wywołując machine.bootloader()
import machine
machine.bootloader()
System plików i kolejność rozruchu¶
Oprogramowanie układowe AE3 montuje przy rozruchu maksymalnie dwa systemy plików:
Wewnętrzna pamięć flash — zawsze montowana w
/flash. Domyślnie przechowujemain.pyiREADME.txt; tworzona przy pierwszym rozruchu.ROMFS — system plików tylko do odczytu, mapowany w pamięci, w
/rom, używany do dostarczania dużych zasobów danych (np. modeli AI), które korzystają z dostępu bez kopiowania. Montowany automatycznie przez MicroPython przy starcie, zanim uruchomi się jakikolwiek kod Pythona użytkownika.
Po zamontowaniu katalog roboczy jest ustawiany na /flash. Interpreter uruchamia następnie skrypty z tego katalogu:
boot.pyjest wykonywany przy każdym miękkim resecie (zimny rozruch,Ctrl‑Dz REPL lub gdy działający skrypt zakończy działanie).main.pyjest wykonywany tylko przy zimnym rozruchu, bezpośrednio poboot.py. Kolejne miękkie resety ponownie uruchamiająboot.py, ale przechodzą prosto do REPL — aby ponownie uruchomićmain.py, musisz w pełni zresetować płytkę.
Domyślny main.py dostarczany na świeżo wgranej płytce miga jedynie niebieskim kanałem diody RGB użytkownika jako sygnał aktywności (dwa krótkie błyski, krótka przerwa), dzięki czemu wiesz, że oprogramowanie układowe uruchomiło się poprawnie bez podłączonego hosta.
sys.path jest rozszerzany tak, aby obejmował oba systemy plików i ich podkatalogi lib/, więc importowalne moduły mogą znajdować się w /flash/lib lub /rom/lib.
Po podłączeniu przez USB /flash jest też widoczny na hoście jako napęd pamięci masowej USB, co pozwala edytować boot.py, main.py i wszelkie inne pliki bezpośrednio. Wysuń napęd przed resetem kamery, aby host zapisał buforowane zmiany.
Informacja
Ponieważ system operacyjny traktuje napęd jako pasywne urządzenie blokowe, pliki utworzone lub zmodyfikowane przez kod działający na OpenMV Cam nie pojawią się, dopóki host ponownie nie zamontuje napędu. Jeśli zarówno system operacyjny, jak i OpenMV Cam zapisują ten sam system plików w tym samym czasie, system operacyjny wygra i nadpisze zmiany wprowadzone przez kamerę.
Informacja
Czerwony kanał diody RGB użytkownika może na krótko się zaświecić, gdy host odczytuje lub zapisuje napęd pamięci masowej USB — to sterowany oprogramowaniem układowym wskaźnik aktywności, a nie usterka.
Rozmiary pamięci masowej¶
AE3 jest dostarczany z:
/flash— system plików FAT 8 MB, do odczytu/zapisu./romna rdzeniu HP — 24 MB systemu plików ROMFS tylko do odczytu, mapowanego w pamięci, na skrypty i dane, które rdzeń HP ładuje przy starcie./romna rdzeniu HE — 1 MB ROMFS tylko do odczytu, należący do rdzenia HE. Moduły i modele ML, które mają być dostępne dla zadań@openamp.async_remote, muszą być wbudowane w ten obraz, a nie w obraz HP.
Wskaźnik hard fault¶
Jeśli dioda RGB użytkownika szybko przełącza się przez wszystkie kolory — na tyle szybko, że zwykle wygląda jak migocząca biała dioda zamiast wyraźnych odcieni — oznacza to, że oprogramowanie układowe napotkało nieodwracalny hard fault. Aby przywrócić działanie, ponownie wgraj oprogramowanie układowe; jeśli to nie pomoże, płytka może być fizycznie uszkodzona.
Biblioteki programowe¶
Pełną listę modułów — w tym informację, które z nich są unikalne dla buildu AE3 — znajdziesz w indeksie bibliotek.