OpenMV AE3

OpenMV AE3 är byggd kring Alif Ensemble E3 — en SoC med dubbla ARM Cortex‑M55 (400 MHz HP‑kärna + 160 MHz HE‑kärna) och två NPU:er på chipet (400 MHz / 204 GOPS HP NPU + 160 MHz / 46 GOPS HE NPU). Kortet parar NPU:erna med PAG7936 1 MP global‑shutter‑sensorn, USB‑C med hög hastighet, Wi‑Fi, Bluetooth 5.1, en LSM6DSM IMU, en mikrofon och en 8×8 VL53L8CX time‑of‑flight‑avståndsmätare, allt på ett kort på 30 × 30 mm.

OpenMV AE3

För fullständigt datablad, foton och mått, se produktsidan för OpenMV AE3.

Höjdpunkter

  • Alif Ensemble E3 — dubbla ARM Cortex‑M55 med Helium 128‑bitars SIMD, 400 MHz HP‑kärna + 160 MHz HE‑kärna (~640 / ~256 DMIPS, CoreMark 1748 / 752).

  • Dubbla NPU:er: 400 MHz / 204 GOPS HP NPU + 160 MHz / 46 GOPS HE NPU för AI/ML — kör YOLO‑objektdetektering parallellt med andra arbetsbelastningar.

  • Hårdvarubaserad 2D GPU för skalning.

  • 13,5 MB internt SRAM plus 5,5 MB MRAM på chipet och 32 MB externt octal flashminne (100 MHz 8‑bitars DDR, 200 MB/s läsning).

  • 4 KB backup‑RAM med RTC:n på chipet.

  • PAG7936 1 MP global‑shutter‑sensor i färg.

  • Inbyggd IMU (LSM6DSM accelerometer + gyroskop), mikrofon och VL53L8CX 8×8 time‑of‑flight‑sensor (upp till 4 m).

  • USB‑C med hög hastighet (480 Mb/s) med EMI‑filtrering och TVS‑skydd, Wi‑Fi a/b/g/n + Bluetooth 5.1 (chipantenn eller U.FL som tillval).

  • 10 användar‑I/O‑stift — P0–P3 på sidostiftlisterna, P4–P5 på Qwiic‑kontakten och P6–P9 på B2B‑stiftlisten på baksidan. Ytterligare felsöknings‑ och återställningslinjer är också dragna till B2B‑stiftlisten.

  • Alla stift har 3,3 V utgång / tål 3,3 V, 25 mA per stift och stöder avbrott. ADC‑ingångar är referensade till 1,8 V.

  • Användar‑RGB‑LED, användarknapp, återställningsbrytare, Qwiic‑kontakt.

  • 80 µA djupsömn vid 3,3 V (24 mA i vila, 50–60 mA aktiv).

Varning

AE3:ans I/O‑stift tål inte 5 V. Anslut inte enheten direkt till en 5 V‑MCU som Arduino Mega — använd en nivåomvandlare för alla 5 V‑signaler.

Stiftutläggning

Stiftutläggning för OpenMV AE3 PAG7936

Stiftreferens

AE3:an exponerar 10 användarstift på sidostiftlisterna (P0–P9). Ytterligare signaler — inklusive JTAG och återställningslinjen — är dragna till en B2B‑stiftlist (board‑to‑board) på baksidan av kortet för shields och bärarkort.

Stiftnamn

Referens

Funktion

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 (endast B2B)

P7

1,8 V

I2C1 SCL / UART3 RTS / TIM9 T1 (endast B2B)

P8

1,8 V

I3C SDA / UART3 RX / TIM5 T0 / ADC ch S10 (endast B2B)

P9

1,8 V

I3C SCL / UART3 TX / TIM5 T1 / ADC ch S11 (endast B2B)

P10

1,8 V

GPIO / JTAG TCK (endast B2B)

P11

1,8 V

GPIO / JTAG TDO (endast B2B)

P13

1,8 V

GPIO / JTAG TMS (endast B2B)

P14

1,8 V

GPIO / JTAG TDI (endast B2B)

RESET

3,3 V

dra till GND för att återställa kortet

SW

3,3 V

användarknapp (aktiv låg)

LED_RED

3,3 V

RGB‑LED röd kanal (aktiv låg)

LED_GREEN

3,3 V

RGB‑LED grön kanal (aktiv låg)

LED_BLUE

3,3 V

RGB‑LED blå kanal (aktiv låg)

Anteckning

P0–P5 sitter på sidostiftlisterna (referensade till 3,3 V); P6–P9 exponeras endast på B2B‑stiftlisten på baksidan av kortet och är referensade till 1,8 V. Att driva 3,3 V in i ett stift referensat till 1,8 V skadar SoC:en — se till att varje signal som ansluts till B2B‑stiftlisten är på 1,8 V.

Strömstift

  • 3.3V — AE3:ans huvudströmskena. Samma 3,3 V‑skena exponeras på lödöarna för GPIO‑stiftlisten, Qwiic‑kontakten och B2B‑stiftlisten på baksidan av kortet.

  • 1.8V — exponeras på B2B‑stiftlisten endast som utgång. Använd den för att driva kringutrustning med 1,8 V‑logik på ett B2B‑bärarkort; driv den inte utifrån kortet.

  • GND — gemensam jord.

AE3:an har inget VIN‑stift och ingen LiPo‑laddare. Den kan matas via någon av tre vägar:

  • USB‑C — den inbyggda regulatorn sänker 5 V från USB till 3,3 V och matar in det på 3,3 V‑skenan.

  • Qwiic‑kontakt — mata in en reglerad 3,3 V‑försörjning i Qwiic‑stiftlisten för att driva kortet från en Qwiic‑modul.

  • GPIO‑stiftlist / B2B 3,3 V‑öar — mata in en reglerad 3,3 V‑försörjning i någon av 3,3 V‑öarna på I/O‑stiftlisten eller B2B‑kontakten.

USB‑regulatorn matar skenan genom en ideal diod, så externa 3,3 V‑försörjningar på Qwiic‑/GPIO‑/B2B‑sidan kan driva kortet även medan USB fortfarande är anslutet utan att bakmata USB‑regulatorn.

Tips

Använd batterilivslängdsberäknaren för att modellera hur länge AE3:an kan köra på ett batteri för en given aktiv/djupsömn‑arbetscykel.

Återställnings‑ och felsökningsstift

  • RESET — dra till GND för att återställa kortet. När den släpps startar SoC:en upp normalt.

Det finns en återställningsbrytare på kortets framsida (kamerasidan), i nedre vänstra hörnet. När den är aktiverad tvingar den ut AE3:ans SE UART över USB så att OpenMV IDE kan flasha om den inbyggda startladdaren. Samma återställningsläge kan utlösas på distans genom att dra RECOVERY‑stiftet på B2B‑kontakten lågt.

AE3:an stöder felsökning med både SWD och fullständig JTAG:

  • 1,8 V SWD‑stiftlisten på kortets sida är avsedd för en Tag-Connect ECV3-06-CTX‑kabel och bryter ut de fyra SWD‑signalerna (TCK / TMS / TDO / RSTN) plus GND.

  • B2B‑stiftlisten på baksidan av kortet exponerar samma felsökningsstift (P10 = TCK, P11 = TDO, P13 = TMS, P14 = TDI) plus system‑RSTN och en separat JTAG RSTN. Dessa stift kan användas antingen för SWD (TCK + TMS) eller fullständig JTAG; JTAG RSTN‑linjen behövs bara i fullständigt JTAG‑läge.

Alla felsökningssignaler är referensade till 1,8 V — se till att din felsökningsadapter är konfigurerad för 1,8 V‑logik innan du ansluter.

Inbyggd kringutrustning

LED:ar

AE3:an har en enda användar‑RGB‑LED som kan styras med programvara via machine.LED

from machine import LED

LED("LED_RED").on()
LED("LED_GREEN").on()
LED("LED_BLUE").on()

Användarknapp

AE3:an har en enda användarknapp (SW):

from machine import Pin

sw = Pin("SW", Pin.IN)
print(sw.value())

För att försätta kortet i djupsömn och låta SW väcka det igen, anropa bara machine.deepsleep() — ingen väckningskonfiguration behövs, knappen är direkt kopplad till en väckningsingång:

import machine

machine.deepsleep()   # press SW to wake the board

Du kan också koppla SW som en mjuk strömbrytare. Utlös på den stigande flanken — linjen stabiliseras högt efter att användaren släpper knappen, så nästa tryckning är otvetydigt en väckningshändelse:

import machine
from machine import Pin

def power_off(_):
    machine.deepsleep()

Pin("SW", Pin.IN).irq(power_off, Pin.IRQ_RISING)

# ...rest of the application runs here. Press SW once to sleep,
# press it again to wake.

Kamerasensor

PAG7936 drivs genom modulen csi — kamerasensorer

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 stöder triggat läge — pixelintegrationen ställer in sig exakt efter varje csi.CSI.snapshot‑anrop i stället för den fritt löpande bildklockan, vilket är användbart för att synkronisera infångning med en extern händelse eller en annan sensor. Aktivera det via csi.CSI.ioctl med csi.IOCTL_SET_TRIGGERED_MODE. Bildfrekvensen sjunker till ungefär hälften av fritt löpande läge eftersom utläsningen inte längre överlappar med nästa bildrutas integration:

cam.ioctl(csi.IOCTL_SET_TRIGGERED_MODE, True)

NPU

AE3:ans två NPU:er på chipet (400 MHz / 204 GOPS HP NPU + 160 MHz / 46 GOPS HE NPU) exponeras genom modulen ml — Maskininlärning. Modeller som lagras på det skrivskyddade /rom‑filsystemet laddas direkt från flashminnet utan att kopieras till RAM, så även stora detektorer ryms bekvämt jämte den live‑bildbufferten. Kör en YOLOv8‑detektor på varje bildruta och rita prediktionerna ovanpå live‑bilden:

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‑kärna

AE3:an packar två Cortex‑M55‑kärnor i en MCU: högprestandakärnan (HP) som kör den huvudsakliga MicroPython‑instansen, kameran, HP NPU:n, USB och så vidare; och högeffektivitetskärnan (HE) som ligger på mycket lägre effekt och startar upp i en egen liten MicroPython‑instans. Båda kärnorna delar en Open-AMP / RPMsg‑meddelandebuss, så HP‑kärnan kan skicka Python‑funktioner till HE‑kärnan, få tillbaka resultat och hålla de två halvorna frikopplade.

Den enklaste ingångspunkten är dekoratorn @openamp.async_remote. Den paketerar en Python‑funktion, skickar den till HE‑kärnan, och HE‑kärnan kör den som en asyncio‑uppgift. Efter att uppgifter registrerats, instansiera openamp.RemoteProc med HE‑programvarans flashadress och anropa rproc.start() för att starta den andra kärnan. Utan något återanrop vidarebefordras den dekorerade funktionens print()‑utdata över standardändpunkten till HP‑kärnans stdout — praktiskt för ett ”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)

För dubbelriktad meddelandehantering, skicka ett återanrop till dekoratorn. Återanropet körs på HP‑kärnan varje gång HE‑uppgiften anropar 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‑kärnan har sin egen HE NPU (160 MHz, 46 GOPS), så den kan köra en andra ML‑modell parallellt med vad än HP‑kärnans HP NPU är upptagen med. En användbar uppdelning är att lägga en liten alltid‑på trigger‑/klassificeringsmodell på HE‑sidan och låta HP‑kärnan reagera bara när något intressant flaggas — nyckelordsigenkänning från den inbyggda mikrofonen passar bra eftersom den är kontinuerlig, lågbandbredds och HE‑kärnan stannar på mycket lägre effekt än HP. Den frysta hjälpmodulen ml.apps.MicroSpeech känner igen ”Yes” och ”No” direkt — säg orden högt och tydligt i den inbyggda mikrofonen för att utlösa detektering:

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)

För en rikare uppdelning, kör BlazeFace på HP NPU:n medan HE‑kärnan hanterar nyckelordsigenkänning i bakgrunden — HP‑loopen lägger det senast hörda nyckelordet som överlägg på kamerabildrutan:

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‑kärnan lämpar sig väl för alltid‑på‑ eller låghastighetsarbetsbelastningar som du inte vill ska konkurrera med kamera‑/NPU‑pipelinen på HP‑sidan — liten ML‑inferens, lättviktig DSP på mikrofon‑ eller IMU‑data och liknande bakgrundsjobb.

Några begränsningar att hålla i minnet:

  • Håll dig till mikrofonen och IMU:n när du driver kringutrustning från HE‑kärnan — det är vad HE‑sidan är konstruerad för. Varje kringenhet kan bara ägas av en kärna åt gången, så välj HP eller HE för den och håll dig till det under skriptets livstid.

  • Varje @openamp.async_remote‑uppgiftskropp måste paketeras till under 500 byte mpy‑bytekod — håll funktionen liten och bryt ut tyngre logik i separata biblioteksmoduler som fryses in i programvaran.

  • Importer inuti den utskickade funktionen ser bara moduler som finns på HE‑kärnans filsystem. HE‑kärnan har sitt eget /rom ROMFS — separat från HP‑kärnans /rom — så moduler och ML‑modeller du vill ha tillgängliga på HE måste bakas in i HE‑sidans ROMFS‑avbild, inte HP‑sidans.

Mikrofon

Den inbyggda mikrofonen fångas in genom audio — Ljudmodul. Varje buffert kommer som en signerad 16‑bitars PCM bytearray, vilket gör det trivialt att mata in den i ulab/numpy för snabb DSP. En enkel ljudstyrkedetektor — skriv ut när RMS‑volymen passerar ett tröskelvärde:

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

Den inbyggda LSM6DSM accelerometern + gyroskopet exponeras genom imu — imu-sensor

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)

Time‑of‑flight‑sensor

AE3:an bär en VL53L8CX 8×8 multi‑zon time‑of‑flight‑sensor som returnerar upp till 64 avståndsmätningar per bildruta, med ett maximalt avstånd på ~4 m. Den exponeras genom modulen tof — drivrutin för time-of-flight-sensor — anropa tof.init() för att starta sensorn och tof.read_depth() för att hämta en djupbildruta som en platt lista med millimetermätningar (en per zon):

import tof

tof.init()
while True:
    depth, depth_min, depth_max = tof.read_depth()
    print("min:", depth_min, "mm  max:", depth_max, "mm")

Djuparrayen kan också ritas över en färgbildruta från huvudsensorn — tof.draw_depth() målar den på en befintlig image.Image, medan tof.snapshot() returnerar en nyrenderad djupbild:

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

Den inbyggda CYW43439 exponeras via network — nätverkskonfiguration som ett stationsgränssnitt. Efter anslutning returnerar ipconfig("addr4") paret (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

Samma CYW43439 exponerar även Bluetooth 5.1. Använd aioble — Asynkron BLE för asyncio‑vänlig BLE — annonsera till exempel som en kringenhet och vänta på att en central ansluter:

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())

Bussreferens

GPIO

Använd machine.Pin för att läsa eller driva något av de silktryckta stiften. Utgångar är 3,3 V CMOS och kan sänka/källa upp till 25 mA per stift.

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())

Varje ingångsstift kan också utlösa ett avbrott vid flankövergångar:

def handler(pin):
    print("triggered:", pin)

Pin("P1", Pin.IN, Pin.PULL_UP).irq(
    handler, Pin.IRQ_FALLING | Pin.IRQ_RISING,
)

UART

Buss

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 är den enda bussen med hårdvarubaserad flödeskontroll. Eftersom P6–P9 sitter på B2B‑stiftlisten och är referensade till 1,8 V, fungerar UART3 bara genom en nivåomvandlare eller ett B2B‑bärarkort — anslut inte 3,3 V‑logik till den direkt.

I²C

Buss

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")

Den inbyggda Qwiic‑kontakten bryter ut I2C23,3 V.

I2C1 och I2C2 kan också användas i target‑läge (slav) genom machine.I2CTarget för att exponera en minnesregion för en annan I²C‑styrenhet:

from machine import I2CTarget

buf = bytearray(32)
target = I2CTarget(1, addr=0x42, mem=buf)

Anteckning

LPI2C‑kringenheten exponeras inte i programvaran. Den skulle bara stödja target‑läge (slav) om den exponerades, och I2C1 och I2C2 täcker redan både styrenhets‑ och target‑drift.

SPI

Buss

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 exponerar två 12‑bitars ADC‑kanaler på P8 och P9 (endast B2B‑stiftlist). Båda ingångarna är referensade till 1,8 Vread_u16 returnerar 0–65535 över 0–1,8 V vid stiftet:

from machine import ADC
import time

adc = ADC("P8")
while True:
    voltage = adc.read_u16() * 1.8 / 65535
    print(voltage)
    time.sleep_ms(100)

Varning

AE3:ans ADC‑ingångar är referensade till 1,8 V, inte 3,3 V. Att driva in en rå 3,3 V‑signal mättar omvandlaren och kan skada stiftet — dela ner högre spänningar externt.

PWM

Stift

Timer / kanal

P0

TIM0 T1

P1

TIM0 T0

P2

TIM1 T1

P3

TIM1 T0

P4

TIM2 T1

P5

TIM2 T0

P6

TIM9 T0 (endast B2B)

P7

TIM9 T1 (endast B2B)

P8

TIM5 T0 (endast B2B)

P9

TIM5 T1 (endast B2B)

Driv någon av dem via machine.PWM

from machine import Pin, PWM

pwm = PWM(Pin("P0"), freq=1_000, duty_u16=32768)

Mjukvarustyrda bit‑bangade bussar

machine.SoftI2C och machine.SoftSPI fungerar på vilken GPIO som helst om du behöver en extra buss.

Termisk sensor (extern)

Programvaran inkluderar drivrutinen fir — drivrutin för värmesensor (fir == far infrared) för en externt kopplad AMG8833 8 × 8 termisk avbildare. Anslut modulen till I²C‑bussen som listas nedan, och läs sedan bildrutor med 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‑drivrutinen kommunicerar med sensorn endast över I²C 1 — koppla modulen till P4 (SCL) och P5 (SDA).

Tidtagning

time

Modulen time täcker blockerande fördröjningar, monotona tick och mätning av förfluten tid:

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)

Virtuella timrar

machine.Timer schemalägger periodiska eller engångsåteranrop utan att förbruka en hårdvarutimer‑plats. Skicka -1 som id för att använda en virtuell (mjukvarustyrd) timer:

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"))

Periodvärden anges i millisekunder. Anropa deinit() för att stoppa och frigöra platsen.

Realtidsklocka

machine.RTC håller väggklockstid över omstarter, uppbackad av 4 KB backup‑RAM på chipet som överlever djupsömn:

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:n körs också genom djupsömn, så du kan använda den som väckningskälla för machine.deepsleep().

Start‑ och körningsinformation

USB‑startladdarfönster

Vid varje uppstart kör kameran en kort startladdare (några sekunder) som låter OpenMV IDE uppdatera programvaran utan att användaren behöver gå in i DFU‑läge. När fönstret löper ut lämnar startladdaren över till boot.py och sedan main.py.

Ett körande skript kan gå in i startladdaren igen på begäran genom att anropa machine.bootloader()

import machine

machine.bootloader()

Filsystem och startordning

AE3‑programvaran monterar upp till två filsystem vid start:

  • Internt flashminne — alltid monterat på /flash. Innehåller main.py och README.txt som standard; skapas vid allra första starten.

  • ROMFS — skrivskyddat, minnesmappat filsystem på /rom som används för att leverera stora datatillgångar (t.ex. AI‑modeller) som drar nytta av nollkopieringsåtkomst. Monteras automatiskt av MicroPython vid start, innan någon användar‑Python körs.

Efter monteringen sätts arbetskatalogen till /flash. Tolken kör sedan skript från den katalogen:

  • boot.py körs vid varje mjuk återställning (kallstart, Ctrl‑D från REPL, eller när det körande skriptet returnerar).

  • main.py körs endast vid kallstart, omedelbart efter boot.py. Efterföljande mjuka återställningar kör boot.py igen men hoppar direkt till REPL — för att köra main.py igen måste du återställa kortet helt.

Standard‑main.py som levereras på ett nyflashat kort blinkar bara användar‑RGB‑LED:ns blå kanal som ett hjärtslag (två korta pulser, kort paus), så att du kan se att programvaran startade rent utan någon värd ansluten.

sys.path utökas till att inkludera båda filsystemen och deras lib/‑underkataloger, så importerbara moduler kan ligga i /flash/lib eller /rom/lib.

När den är ansluten över USB enumereras /flash också som en USB‑masslagringsenhet på värden, vilket låter dig redigera boot.py, main.py och alla andra filer direkt. Mata ut enheten innan du återställer kameran så att värden tömmer sina cachade skrivningar.

Anteckning

Eftersom operativsystemet behandlar enheten som en passiv blockenhet visas inte filer som skapats eller ändrats av kod som körs på OpenMV Cam förrän värden monterar om enheten. Om både operativsystemet och OpenMV Cam skriver till samma filsystem samtidigt vinner operativsystemet och skriver över ändringar som gjorts av kameran.

Anteckning

Användar‑RGB‑LED:ns röda kanal kan kortvarigt lysa upp medan värden läser från eller skriver till USB‑masslagringsenheten — detta är en programvarudriven aktivitetsindikator, inte ett fel.

Lagringsstorlekar

AE3:an levereras med:

  • /flash8 MB FAT‑filsystem, läs/skriv.

  • /rom på HP‑kärnan — 24 MB skrivskyddat minnesmappat ROMFS för skript och data som HP‑kärnan laddar vid start.

  • /rom på HE‑kärnan — 1 MB skrivskyddat ROMFS som ägs av HE‑kärnan. Moduler och ML‑modeller du vill ha tillgängliga för @openamp.async_remote‑uppgifter måste bakas in i denna avbild, inte HP‑sidans.

Hard‑fault‑indikator

Om användar‑RGB‑LED:n snabbt cyklar genom alla färger — så snabbt att den tenderar att se ut som en tindrande vit LED snarare än distinkta nyanser — har programvaran råkat ut för ett oåterställbart hard fault. Flasha om programvaran för att återhämta dig; om omflashning inte hjälper kan kortet vara fysiskt skadat.

Programvarubibliotek

Se biblioteksindexet för den fullständiga listan över moduler — inklusive vilka som är unika för AE3‑bygget.