OpenMV AE3

OpenMV AE3는 Alif Ensemble E3를 중심으로 구성되어 있습니다. 이는 두 개의 온칩 NPU(400 MHz / 204 GOPS HP NPU + 160 MHz / 46 GOPS HE NPU)를 갖춘 듀얼 ARM Cortex‑M55 SoC(400 MHz HP 코어 + 160 MHz HE 코어)입니다. 이 보드는 NPU와 함께 PAG7936 1 MP 글로벌 셔터 센서, USB‑C 고속 인터페이스, Wi‑Fi, Bluetooth 5.1, LSM6DSM IMU, 마이크, 8×8 VL53L8CX 비행 시간(time‑of‑flight) 거리 측정기를 모두 30 × 30 mm 보드 안에 담고 있습니다.

OpenMV AE3

전체 데이터시트, 사진, 치수는 OpenMV AE3 제품 페이지 를 참조하세요.

주요 특징

  • Alif Ensemble E3 — Helium 128‑비트 SIMD를 갖춘 듀얼 ARM Cortex‑M55, 400 MHz HP 코어 + 160 MHz HE 코어(~640 / ~256 DMIPS, CoreMark 1748 / 752).

  • 듀얼 NPU: AI/ML을 위한 400 MHz / 204 GOPS HP NPU + 160 MHz / 46 GOPS HE NPU — 다른 작업과 함께 YOLO 객체 검출을 실행합니다.

  • 스케일링을 위한 하드웨어 2D GPU.

  • 13.5 MB 내부 SRAM5.5 MB 온칩 MRAM, 그리고 32 MB 외부 옥탈 플래시(100 MHz 8비트 DDR, 200 MB/s 읽기).

  • 온칩 RTC와 함께하는 4 KB 백업 RAM.

  • PAG7936 1 MP 컬러 글로벌 셔터 센서.

  • 온보드 IMU(LSM6DSM 가속도계 + 자이로스코프), 마이크, VL53L8CX 8×8 비행 시간(time‑of‑flight) 센서(최대 4 m).

  • EMI 필터링과 TVS 보호 기능을 갖춘 고속 USB-C(480 Mb/s), Wi-Fi a/b/g/n + Bluetooth 5.1(칩 안테나 또는 U.FL 옵션).

  • 10개의 사용자 I/O 핀 — 측면 헤더에 P0–P3, Qwiic 커넥터에 P4–P5, 뒷면 B2B 헤더에 P6–P9. 추가 디버그 및 복구 라인도 B2B 헤더로 연결되어 있습니다.

  • 모든 핀은 3.3 V 출력 / 3.3 V 허용, 핀당 25 mA, 인터럽트 지원. ADC 입력은 1.8 V 기준입니다.

  • 사용자 RGB LED, 사용자 버튼, 복구 스위치, Qwiic 커넥터.

  • 3.3 V에서 80 µA 딥 슬립(24 mA 유휴, 50–60 mA 활성).

경고

AE3의 I/O 핀은 5 V를 허용하지 않습니다. Arduino Mega와 같은 5 V MCU에 장치를 직접 연결하지 마세요. 5 V 신호에는 레벨 시프터를 사용하세요.

핀아웃

OpenMV AE3 PAG7936 핀아웃

핀 레퍼런스

AE3는 측면 헤더에 10개의 사용자 핀(P0–P9)을 노출합니다. JTAG 및 복구 라인을 포함한 추가 신호는 실드 및 캐리어 보드를 위해 보드 뒷면의 B2B(board‑to‑board) 헤더로 연결되어 있습니다.

핀 이름

기준

기능

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 (B2B 전용)

P7

1.8 V

I2C1 SCL / UART3 RTS / TIM9 T1 (B2B 전용)

P8

1.8 V

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

P9

1.8 V

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

P10

1.8 V

GPIO / JTAG TCK (B2B 전용)

P11

1.8 V

GPIO / JTAG TDO (B2B 전용)

P13

1.8 V

GPIO / JTAG TMS (B2B 전용)

P14

1.8 V

GPIO / JTAG TDI (B2B 전용)

RESET

3.3 V

보드를 리셋하려면 GND로 당기세요

SW

3.3 V

사용자 버튼(액티브 로우)

LED_RED

3.3 V

RGB LED 적색 채널(액티브 로우)

LED_GREEN

3.3 V

RGB LED 녹색 채널(액티브 로우)

LED_BLUE

3.3 V

RGB LED 청색 채널(액티브 로우)

참고

P0–P5는 측면 헤더에 있습니다(3.3 V 기준). P6–P9는 보드 뒷면의 B2B 헤더에만 노출되며 1.8 V 기준입니다. 1.8 V 기준 핀에 3.3 V를 인가하면 SoC가 손상됩니다. B2B 헤더에 연결되는 신호는 반드시 1.8 V여야 합니다.

전원 핀

  • 3.3V — AE3의 메인 전원 레일입니다. 동일한 3.3 V 레일이 GPIO 헤더 솔더 패드, Qwiic 커넥터, 보드 뒷면의 B2B 헤더에 노출됩니다.

  • 1.8VB2B 헤더출력 전용으로 노출됩니다. B2B 캐리어의 1.8 V 로직 주변장치에 전원을 공급하는 데 사용하세요. 보드 외부에서 이 핀을 구동하지 마세요.

  • GND — 공통 접지.

AE3에는 VIN 핀과 LiPo 충전기가 없습니다. 세 가지 경로 중 하나를 통해 전원을 공급할 수 있습니다:

  • USB‑C — 온보드 레귤레이터가 USB의 5 V를 3.3 V로 낮춰 3.3 V 레일에 주입합니다.

  • Qwiic 커넥터 — Qwiic 헤더에 안정화된 3.3 V 전원을 인가하여 Qwiic 모듈로 보드에 전원을 공급합니다.

  • GPIO 헤더 / B2B 3.3 V 패드 — I/O 헤더 또는 B2B 커넥터의 3.3 V 패드 중 하나에 안정화된 3.3 V 전원을 인가합니다.

USB 레귤레이터는 이상적인 다이오드(ideal diode)를 통해 레일에 전원을 공급하므로, USB가 여전히 연결된 상태에서도 Qwiic / GPIO / B2B 쪽의 외부 3.3 V 전원이 USB 레귤레이터를 역구동하지 않고 보드에 전원을 공급할 수 있습니다.

배터리 수명 추정기 를 사용하여 주어진 활성 / 딥 슬립 듀티 사이클에서 AE3가 배터리로 얼마나 오래 작동할지 모델링할 수 있습니다.

복구 및 디버그 핀

  • RESET — 보드를 리셋하려면 GND로 당기세요. 풀어주면 SoC가 정상적으로 부팅됩니다.

복구 스위치가 보드의 전면(카메라 쪽) 면 좌측 하단 모서리에 있습니다. 활성화되면 AE3의 SE UART를 USB로 강제 출력하여 OpenMV IDE가 온보드 부트로더를 재플래시할 수 있습니다. B2B 커넥터의 RECOVERY 핀을 로우로 당겨서 동일한 복구 모드를 원격으로 트리거할 수도 있습니다.

AE3는 SWD와 전체 JTAG 디버깅을 모두 지원합니다:

  • 보드 측면의 1.8 V SWD 헤더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 V 기준입니다. 연결하기 전에 디버그 어댑터가 1.8 V 로직으로 구성되어 있는지 확인하세요.

온보드 주변장치

LED

AE3에는 machine.LED 를 통해 소프트웨어로 제어할 수 있는 사용자 RGB LED가 하나 있습니다:

from machine import LED

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

사용자 버튼

AE3에는 사용자 버튼(SW)이 하나 있습니다:

from machine import Pin

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

보드를 딥 슬립 상태로 전환하고 SW 로 다시 깨우려면 machine.deepsleep() 만 호출하면 됩니다. 웨이크업 구성이 필요 없습니다. 버튼이 웨이크 입력에 직접 연결되어 있습니다:

import machine

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

SW 를 소프트 전원 스위치로 연결할 수도 있습니다. 상승(rising) 에지에서 트리거하세요. 사용자가 버튼을 놓으면 라인이 하이로 안정되므로 다음 누름이 명확하게 웨이크 이벤트가 됩니다:

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.

카메라 센서

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.IOCTL_SET_TRIGGERED_MODE 와 함께 csi.CSI.ioctl 을 통해 활성화하세요. 읽기 작업이 더 이상 다음 프레임의 적분과 파이프라인되지 않으므로 프레임 속도가 자유 실행 모드의 약 절반으로 떨어집니다:

cam.ioctl(csi.IOCTL_SET_TRIGGERED_MODE, True)

NPU

AE3의 두 온칩 NPU(400 MHz / 204 GOPS HP NPU + 160 MHz / 46 GOPS HE NPU)는 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에 패키징합니다. 메인 MicroPython 인스턴스, 카메라, HP NPU, USB 등을 실행하는 고성능(HP) 코어와, 훨씬 낮은 전력에서 자체적인 작은 MicroPython 인스턴스로 부팅되는 고효율(HE) 코어입니다. 두 코어는 Open-AMP / RPMsg 메시지 버스를 공유하므로, HP 코어는 HE 코어에 Python 함수를 디스패치하고 결과를 받아오면서 두 부분을 분리된 상태로 유지할 수 있습니다.

가장 간단한 진입점은 @openamp.async_remote 데코레이터입니다. 이는 Python 함수를 마샬링하여 HE 코어로 보내고, HE 코어가 이를 asyncio 작업으로 실행합니다. 작업을 등록한 후, HE 펌웨어의 플래시 주소로 openamp.RemoteProc 를 인스턴스화하고 rproc.start() 를 호출하여 두 번째 코어를 부팅합니다. 콜백이 없으면 데코레이트된 함수의 print() 출력이 기본 엔드포인트를 통해 HP 코어의 stdout으로 전달됩니다. “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)

양방향 메시징의 경우 데코레이터에 콜백을 전달하세요. 콜백은 HE 작업이 ept.send() 를 호출할 때마다 HP 코어에서 실행됩니다:

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 MHz, 46 GOPS)를 갖추고 있어, HP 코어의 HP NPU가 처리 중인 작업과 병렬로 두 번째 ML 모델을 실행할 수 있습니다. 유용한 분할 방식은 작고 항상 켜져 있는 트리거/분류기 모델을 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)

더 풍부한 분할을 원한다면, HP NPU에서 BlazeFace를 실행하면서 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 코어는 HP 쪽의 카메라/NPU 파이프라인과 경쟁시키고 싶지 않은 항상 켜진 또는 낮은 속도의 작업에 적합합니다. 작은 ML 추론, 마이크 또는 IMU 데이터에 대한 경량 DSP, 그리고 유사한 백그라운드 작업 등이 있습니다.

염두에 두어야 할 몇 가지 제약 사항:

  • HE 코어에서 주변장치를 구동할 때는 마이크와 IMU만 사용하세요. 이것들이 HE 쪽이 설계된 용도입니다. 각 주변장치는 한 번에 하나의 코어만 소유할 수 있으므로, HP 또는 HE 중 하나를 선택하고 스크립트가 동작하는 동안 그것을 유지하세요.

  • @openamp.async_remote 작업 본문은 500바이트 미만의 mpy 바이트코드로 마샬링되어야 합니다. 함수를 작게 유지하고 더 무거운 로직은 펌웨어에 프로즌되는 별도의 라이브러리 모듈로 분리하세요.

  • 디스패치된 함수 내부의 import는 HE 코어의 파일시스템에 존재하는 모듈만 볼 수 있습니다. HE 코어는 HP 코어의 /rom 과 분리된 자체 /rom ROMFS를 갖고 있으므로, HE에서 사용하려는 모듈과 ML 모델은 HP가 아닌 HE 쪽 ROMFS 이미지에 구워 넣어야 합니다.

마이크

온보드 마이크는 audio — 오디오 모듈 를 통해 캡처됩니다. 각 버퍼는 부호 있는 16비트 PCM bytearray 로 도착하므로, 빠른 DSP를 위해 ulab/numpy 로 손쉽게 전달할 수 있습니다. 간단한 음량 검출기 — 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)

비행 시간(time‑of‑flight) 센서

AE3는 프레임당 최대 64개의 거리 측정값을 반환하는 VL53L8CX 8×8 멀티존 비행 시간(time‑of‑flight) 센서를 탑재하고 있으며, 최대 측정 범위는 ~4 m입니다. 이는 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도 노출합니다. asyncio 친화적인 BLE를 위해 aioble — 비동기 BLE 을 사용하세요. 예를 들어 주변장치로 광고하고 중앙 장치가 연결되기를 기다립니다:

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 V CMOS이며 핀당 최대 25 mA를 싱크/소스할 수 있습니다.

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 V 기준이므로, UART3는 레벨 시프터나 B2B 캐리어를 통해서만 작동합니다. 3.3 V 로직을 직접 연결하지 마세요.

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 커넥터는 3.3 V에서 I2C2를 분기합니다.

I2C1I2C2machine.I2CTarget 을 통해 타깃(슬레이브) 모드로도 사용하여 다른 I²C 컨트롤러에 메모리 영역을 노출할 수 있습니다:

from machine import I2CTarget

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

참고

LPI2C 주변장치는 펌웨어에 노출되지 않습니다. 노출되더라도 타깃(슬레이브) 모드만 지원할 것이며, I2C1I2C2 가 이미 컨트롤러와 타깃 동작을 모두 다룹니다.

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는 P8P9(B2B 헤더 전용)에 두 개의 12비트 ADC 채널을 노출합니다. 두 입력 모두 1.8 V 기준입니다. read_u16 은 핀에서 0–1.8 V에 걸쳐 0–65535를 반환합니다:

from machine import ADC
import time

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

경고

AE3의 ADC 입력은 3.3 V가 아니라 1.8 V 기준입니다. 원시 3.3 V 신호를 인가하면 변환기가 포화되고 핀이 손상될 수 있습니다. 더 높은 전압은 외부에서 분압하세요.

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)

소프트웨어 비트뱅잉 버스

추가 버스가 필요하면 machine.SoftI2Cmachine.SoftSPI 가 모든 GPIO에서 작동합니다.

열 센서(보드 외부)

펌웨어에는 외부에 배선된 AMG8833 8 × 8 열화상 카메라용 fir — 열 센서 드라이버 (fir == far infrared, 원적외선) 드라이버가 포함되어 있습니다. 아래에 나열된 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 는 하드웨어 타이머 슬롯을 소비하지 않고 주기적 또는 일회성 콜백을 스케줄링합니다. 가상(소프트웨어) 타이머를 사용하려면 id로 -1 을 전달하세요:

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 KB의 온칩 백업 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 부트로더 창

전원이 켜질 때마다 카메라는 짧은 부트로더(몇 초)를 실행하여, 사용자가 DFU 모드로 진입할 필요 없이 OpenMV IDE가 펌웨어를 업데이트할 수 있도록 합니다. 창이 만료되면 부트로더는 boot.py 로, 그다음 main.py 로 제어를 넘깁니다.

실행 중인 스크립트는 machine.bootloader() 를 호출하여 필요할 때 부트로더로 다시 진입할 수 있습니다:

import machine

machine.bootloader()

파일시스템 및 부팅 순서

AE3 펌웨어는 부팅 시 최대 두 개의 파일시스템을 마운트합니다:

  • 내부 플래시 — 항상 /flash 에 마운트됩니다. 기본적으로 main.pyREADME.txt 를 보관하며, 최초 부팅 시 생성됩니다.

  • ROMFS — 제로 카피 액세스의 이점을 누리는 큰 데이터 자산(예: AI 모델)을 제공하는 데 사용되는 /rom 의 읽기 전용 메모리 매핑 파일시스템입니다. 사용자 Python이 실행되기 전 시작 시 MicroPython에 의해 자동으로 마운트됩니다.

마운트 후 작업 디렉터리는 /flash 로 설정됩니다. 그런 다음 인터프리터는 해당 디렉터리에서 스크립트를 실행합니다:

  • boot.py모든 소프트 리셋(콜드 부팅, REPL에서의 Ctrl‑D, 또는 실행 중인 스크립트가 반환될 때마다)에서 실행됩니다.

  • main.pyboot.py 직후 콜드 부팅 시에만 실행됩니다. 이후의 소프트 리셋은 boot.py 를 다시 실행하지만 곧바로 REPL로 진입합니다. main.py 를 다시 실행하려면 보드를 완전히 리셋해야 합니다.

새로 플래시된 보드에 기본 제공되는 main.py 는 사용자 RGB LED의 청색 채널을 하트비트로 깜박일 뿐이므로(짧은 펄스 두 번, 짧은 간격), 호스트가 연결되지 않은 상태에서도 펌웨어가 정상적으로 부팅되었는지 알 수 있습니다.

sys.path 는 두 파일시스템과 그 lib/ 하위 디렉터리를 모두 포함하도록 확장되므로, import 가능한 모듈은 /flash/lib 또는 /rom/lib 에 둘 수 있습니다.

USB로 연결되면 /flash 는 호스트에서 USB 대용량 저장 드라이브로도 열거되어 boot.py, main.py 및 기타 파일을 직접 편집할 수 있습니다. 호스트가 캐시된 쓰기를 플러시하도록 카메라를 리셋하기 전에 드라이브를 꺼내세요.

참고

OS는 드라이브를 수동 블록 장치로 취급하므로, OpenMV Cam에서 실행되는 코드가 생성하거나 수정한 파일은 호스트가 드라이브를 다시 마운트할 때까지 나타나지 않습니다. OS와 OpenMV Cam이 동시에 같은 파일시스템에 쓰면 OS가 우선하여 카메라가 변경한 내용을 덮어씁니다.

참고

호스트가 USB 대용량 저장 드라이브에서 읽거나 쓰는 동안 사용자 RGB LED의 적색 채널이 잠시 켜질 수 있습니다. 이는 펌웨어가 구동하는 활동 표시기이며 결함이 아닙니다.

저장 용량

AE3는 다음과 함께 제공됩니다:

  • /flash8 MB FAT 파일시스템, 읽기/쓰기.

  • HP 코어의 /rom — HP 코어가 시작 시 로드하는 스크립트와 데이터를 위한 24 MB 읽기 전용 메모리 매핑 ROMFS.

  • HE 코어의 /rom — HE 코어가 소유하는 1 MB 읽기 전용 ROMFS. @openamp.async_remote 작업에서 사용하려는 모듈과 ML 모델은 HP가 아니라 이 이미지에 구워 넣어야 합니다.

하드 폴트 표시기

사용자 RGB LED가 모든 색상을 빠르게 순환하면서 — 별개의 색조라기보다 반짝이는 흰색 LED처럼 보일 정도로 빠르게 — 펌웨어가 복구 불가능한 하드 폴트에 도달한 것입니다. 복구하려면 펌웨어를 재플래시하세요. 재플래시가 도움이 되지 않으면 보드가 물리적으로 손상되었을 수 있습니다.

소프트웨어 라이브러리

AE3 빌드에만 고유한 모듈을 포함한 전체 모듈 목록은 라이브러리 색인 을 참조하세요.