Arduino Nicla Vision

Die Arduino Nicla Vision ist ein 22,86 × 22,86 mm großes Board für maschinelles Sehen, das um den STMicroelectronics STM32H747AII6 herum aufgebaut ist – ein Dual-Core-SoC, das einen Cortex-M7 mit 400 MHz mit einem Cortex-M4 mit 200 MHz kombiniert. Die OpenMV-Firmware läuft vollständig auf dem M7-Kern. Das Board kombiniert den MCU mit dem GC2145 2-MP-Farb-CMOS-Sensor, einer LSM6DSOX 6-Achsen-IMU, einem MP34DT06 MEMS-Mikrofon, einem VL53L1CB Time-of-Flight-Entfernungsmesser, Wi-Fi + Bluetooth LE 5.1 sowie einem Akkuladegerät / Fuel Gauge.

Arduino Nicla Vision

Vollständiges Datenblatt, Fotos und Abmessungen finden Sie auf der Arduino Nicla Vision Produktseite.

Höhepunkte

  • STMicroelectronics STM32H747AII6 Dual Cortex-M7 (400 MHz) + Cortex-M4 (200 MHz). Die OpenMV-Firmware läuft nur auf dem M7-Kern.

  • 2 MB internes Flash plus 16 MB externes QSPI-Flash (verwendet für die Anwendung + ROMFS).

  • 1 MB internes SRAM.

  • Hardware-JPEG-Encoder/Decoder.

  • GC2145 2-MP-Farb-CMOS-Sensor.

  • Onboard-IMU (LSM6DSOX Beschleunigungssensor + Gyroskop), MEMS-Mikrofon (MP34DT06JTR) und VL53L1CB Time-of-Flight-Entfernungsmesser (bis zu ~4 m).

  • Wi-Fi b/g/n (2,4 GHz) + Bluetooth LE 5.1 über das Murata 1DX (CYW4343W) Modul – verbindet sich über einen Onboard-U.FL-Anschluss mit der mitgelieferten Antenne.

  • High-Speed USB (480 Mb/s) über Micro USB durch eine externe ULPI-PHY (USB3320C).

  • 13 Benutzer-I/O-Pins an den Arduino-Randsteckleisten – vier digitale LPIOs (D0D3), drei 1,8-V-Analogeingänge (A0A2), das SCL/SDA I²C-Paar und das SCLK/CIPO/COPI/CS SPI-Quartett.

  • Akku-Unterstützung – Li-Po-Anschluss auf der Rückseite, BQ-artiges Ladegerät und MAX17262 Fuel Gauge über den internen PMIC-Bus.

  • 5-poliger ESLOV-Anschluss auf der Rückseite für lötfreie I²C-Erweiterung.

Warnung

Die digitalen Benutzer-Pins sind standardmäßig 3,3 V, werden jedoch über softwareprogrammierbare Pegelwandler (VDDIO_EXT) geführt, die auf 1,8 V umkonfiguriert werden können. Die Analogpins (A0–A2) sind ausschließlich 1,8 V – sie umgehen die Pegelwandler und sind direkt mit dem MCU verbunden. Das Anlegen von 3,3 V an A0–A2 beschädigt das SoC.

Pinbelegung

Arduino Nicla Vision Pinbelegung

Pin-Referenz

Dreizehn Benutzerpins werden an den Arduino-Randsteckleisten (J1 und J2) bereitgestellt. Zusätzliche Debug-, Wiederherstellungs- und PMIC-Signale werden zu Testpads auf der Rückseite des Boards geführt.

Pin-Name

Referenz

Funktion

D0

3,3 V

GPIO / LPIO0 (J1-1)

D1

3,3 V

LPUART1 TX / TIM1 CH2 / LPIO1 (J2-3)

D2

3,3 V

LPUART1 RX / TIM1 CH3 / LPIO2 (J2-4)

D3

3,3 V

GPIO / LPIO3 (J2-5)

A0

1,8 V

ADC1 Kanal 4 (J1-8)

A1

1,8 V

ADC2 Kanal 2 (J1-7)

A2

1,8 V

ADC3 Kanal 5 (J1-2)

SCL

3,3 V

I2C1 SCL / UART4 RX / TIM4 CH3 (J2-2)

SDA

3,3 V

I2C1 SDA / UART4 TX / TIM4 CH4 (J2-1)

SCLK

3,3 V

SPI4 SCK / TIM1 CH3N (J1-6)

CIPO

3,3 V

SPI4 MISO / TIM1 CH3 (J1-5)

COPI

3,3 V

SPI4 MOSI / TIM1 CH4 (J1-4)

CS

3,3 V

SPI4 NSS / TIM1 CH2 (J1-3)

RESET

3,3 V

auf GND ziehen (oder den Onboard-Schalter drücken), um das Board zurückzusetzen

LED_RED

3,3 V

RGB-LED Rotkanal (Low-aktiv)

LED_GREEN

3,3 V

RGB-LED Grünkanal (Low-aktiv)

LED_BLUE

3,3 V

RGB-LED Blaukanal (Low-aktiv)

Bemerkung

D0D3 und SCLK/CIPO/COPI/CS befinden sich hinter dem TXB0108 bidirektionalen Pegelwandler – dieser Baustein unterstützt nur Push-Pull-GPIO-Ansteuerung, sodass Open-Drain-Busverkehr (z. B. ein bit-banged 1-Wire oder I²C an diesen Pins) nicht funktioniert.

SCL/SDA befinden sich hinter einem separaten NTS0304-Wandler, der sowohl Push-Pull- als auch Open-Drain-Ansteuerung unterstützt, weshalb I²C 1 dort funktioniert.

Beide Wandler sind auf VDDIO_EXT referenziert (standardmäßig 3,3 V vom Onboard-PMIC), und ihre Treiberstärke ist im Vergleich zu einem direkten GPIO begrenzt – sie sind für Signalpegel und nicht für Leistungslasten ausgelegt.

Stromversorgungspins

Pins der Randsteckleiste:

  • VIN (J2-9) – Haupt-Systemschiene mit 3,6 – 5 V. Der PMIC bezieht hier seinen Eingang.

  • VDDIO_EXT (J2-7) – Ausgang der Pegelwandler-Schiene, 1,8 V oder 3,3 V (standardmäßig 3,3 V). Verwenden Sie diese, um externe 1,8-V- oder 3,3-V-Peripheriegeräte zu versorgen, die an die LPIO/SPI/I²C-Pins angeschlossen sind, damit sie denselben Logikpegel wie die Steckleisten sprechen.

  • VBAT (J3-2) – Li-Po-Akkueingang. Der Onboard-PMIC lädt die Zelle aus VIN und meldet den Ladezustand über das Fuel Gauge.

  • NTC (J3-1) – optionaler Li-Po-Thermistoreingang.

  • GND (J2-6) – gemeinsame Masse.

  • NC (J2-8) – nicht angeschlossen.

Testpads auf der Rückseite des Boards:

  • +3V3 – Haupt-3,3-V-Schiene.

  • D_P / D_N – USB-High-Speed-Datenpaar (nach PHY).

USB und der ESLOV-Anschluss speisen beide VIN über ein Paar LM66100 Ideal-Dioden (eine pro Quelle), sodass jede Versorgung das Board eigenständig betreiben kann und die beiden sich niemals gegenseitig rückspeisen. Wenn Sie VIN extern an J2-9 ansteuern, hat das Vorrang – die Dioden hören einfach auf, von USB / ESLOV zu leiten, sobald die externe Schiene höher ansteigt.

Das Board kann daher über jeden dieser Pfade mit Strom versorgt werden:

  • Micro USB – 5 V in VIN über die USB-seitige Ideal-Diode.

  • ESLOV-Anschluss – bis zu 5 V am VESLOV-Pin von J5, über die ESLOV-seitige Ideal-Diode in VIN geführt (siehe ESLOV-Anschluss).

  • VIN-Pin (J2-9) – eine geregelte 3,6 – 5 V Versorgung direkt anlegen.

  • Li-Po-Akku – an den J4-Akkuanschluss auf der Rückseite oder an die VBAT/GND/NTC-Pads an J3 / J2-6 anschließen. Schließen Sie nicht zwei Akkus gleichzeitig an.

ESLOV-Anschluss

J5 auf der Rückseite des Boards ist ein 5-poliger lötfreier ESLOV-Anschluss von Molex:

Pin

Name

Funktion

J5-1

VESLOV

Stromeingang (≤ 5 V) – über eine LM66100 Ideal-Diode mit VIN verodert

J5-2

INT

externer Interrupt-Eingang an PD9

J5-3

SCL_EXT

gemeinsam mit dem J2-SCL-Pad – derselbe I²C-1-Bus wie die Benutzersteckleiste

J5-4

SDA_EXT

gemeinsam mit dem J2-SDA-Pad – derselbe I²C-1-Bus wie die Benutzersteckleiste

J5-5

GND

gemeinsame Masse

ESLOVs SCL_EXT/SDA_EXT und J2s SCL/SDA sind dieselben Pins – ein I²C-1-Bus, der an zwei Anschlüssen bereitgestellt wird.

Tipp

Verwenden Sie den Akkulaufzeit-Schätzer, um zu modellieren, wie lange die Nicla Vision bei einem bestimmten Aktiv-/Deep-Sleep-Tastverhältnis mit einem Akku läuft.

Wiederherstellungs- und Debug-Pins

  • RESET – sowohl ein Taster auf der Oberseite des Boards als auch ein Pad (J3-4 / Testpad P5), das mit der NRST-Leitung des SoC verbunden ist. Auf GND ziehen, um zurückzusetzen.

Die Nicla Vision verwendet Arduinos Standard-Doppeltipp-Reset, um in Arduinos Bootloader zu gelangen – drücken Sie den Reset-Taster zweimal schnell hintereinander, und das Board meldet sich als DFU-Gerät an. OpenMV IDE nutzt diesen Modus, um die Firmware neu zu flashen.

Die STM32-SWD-Signale werden auf der Rückseite des Boards über eine Reihe von Testpads zwischen den beiden J2-Steckleisten bereitgestellt. Löten Sie eine 2,54 mm (100-mil) Steckleiste in diese ein, um einen ST-LINK- oder J-Link-Adapter anzuschließen:

  • P1 / P2 – interner PMIC I²C-Bus an PF0 (SDA) und PF1 (SCL). Dies ist machine.I2C(2) auf der Nicla Vision und führt den PMIC-, Fuel-Gauge- und ToF-Verkehr.

  • P3 – TMS / SWDIO (PA13)

  • P4 – TCK / SWCLK (PA14)

  • P5 – NRST

  • P6 – TDO / SWO (PB3)

  • P7 – +1V8-Schiene (die I/O-Versorgung des SoC – auch die richtige Referenz für den Debug-Adapter).

  • P8VOTP_PMICnur für die Werksprogrammierung. Muss unverbunden bleiben.

Alle Debug-Signale sind auf 1,8 V referenziert – der I/O-Ring des STM32H747 auf diesem Board läuft von der +1V8-Schiene. Stellen Sie Ihren Debug-Adapter auf 1,8-V-Logik ein, bevor Sie ihn anschließen.

Onboard-Peripheriegeräte

LEDs

Die Nicla Vision hat eine einzelne RGB-Benutzer-LED, die softwaregesteuert über machine.LED ansteuerbar ist:

from machine import LED

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

Eine separate DL2 CHARGE-LED an der Seite des Boards ist direkt mit dem CHGB-Ausgang des PMIC verdrahtet – sie leuchtet, während ein Li-Po-Akku über USB / ESLOV / VIN geladen wird, und ist nicht benutzersteuerbar.

Kamerasensor

Der GC2145 wird über das csi — Kamerasensoren-Modul angesteuert:

import csi

cam = csi.CSI()
cam.reset()
cam.pixformat(csi.RGB565)
cam.framesize(csi.QVGA)
cam.snapshot(time=2000)       # let auto‑exposure settle

while True:
    img = cam.snapshot()

Wenn Sie eine kleine Framegröße anfordern, schneidet der GC2145-Treiber ein proportional kleines Auslesefenster aus dem Sensor aus – standardmäßig ist das Herunterskalierungsverhältnis von Auslesung zu Ausgabe auf 3x begrenzt, um die Bildrate hochzuhalten. csi.IOCTL_SET_FOV_WIDE erhöht diese Grenze auf 5x, was bedeutet, dass der Treiber bei kleinen Auflösungen aus einem breiteren Bereich des Sensors liest. Das Ergebnis ist ein merklich breiteres Sichtfeld bei kleinen Framegrößen, auf Kosten von etwas Durchsatz:

cam.ioctl(csi.IOCTL_SET_FOV_WIDE, True)
cam.ioctl(csi.IOCTL_GET_FOV_WIDE)  # returns the current setting

M4-Kern

Der Cortex-M4-Kern wird über openamp für die Inter-Prozessor-Kommunikation bereitgestellt. Die OpenMV-Firmware läuft nur auf dem M7; der M4 hat keine eigene MicroPython-Laufzeit, sodass seine Nutzung bedeutet, ein separates C-Firmware-Image zu erstellen und es über openamp.RemoteProc aus dem Dateisystem zu laden. Vorgefertigte Beispiel-Firmware, die einen virtuellen UART-Endpunkt implementiert, ist im Repository openamp_vuart verfügbar – folgen Sie dessen README, um vuart.elf zu erstellen:

import openamp
import time

def ept_recv_callback(src_addr, data):
    print("Received:", data.decode())

ept = openamp.Endpoint("vuart-channel", callback=ept_recv_callback)

rproc = openamp.RemoteProc("vuart.elf")
rproc.start()

count = 0
while True:
    if ept.is_ready():
        ept.send("Hello World %d!" % count, timeout=1000)
        count += 1
    time.sleep_ms(1000)

In der Praxis sollte diese Unterstützung am besten als Demonstration der openamp-Schnittstelle betrachtet werden und nicht als funktionierende Dual-Core-Plattform – der M4 kann nicht unabhängig vom M7 zurückgesetzt werden, sodass das Stoppen des M4 einen vollständigen Systemneustart erzwingt.

Mikrofon

Das Onboard-MP34DT06JTR-PDM-Mikrofon wird über audio — Audio-Modul über das DFSDM-Peripheriegerät des STM32 erfasst. Jeder Puffer kommt als vorzeichenbehaftetes 16-Bit-PCM-bytearray an, bereit zur Einspeisung in ulab/numpy für DSP – zum Beispiel ein einfacher Lautstärkedetektor:

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

Der Onboard-LSM6DSOX-Beschleunigungssensor + Gyroskop wird über imu — IMU-Sensor bereitgestellt:

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)

Die IMU ist mit einem dedizierten internen SPI-Bus (SPI5) verdrahtet, sodass sie nicht mit dem an den Steckleisten herausgeführten Benutzer-SPI4 in Konflikt gerät.

Time-of-Flight-Entfernungsmesser

Der Onboard-ST-VL53L1CB-Time-of-Flight-Entfernungsmesser sitzt am internen PMIC-I²C-Bus (I²C 2). Verwenden Sie den eingefrorenen vl53l1x — VL53L1X ToF-Distanzsensor-Treiber-Treiber, um Abstandsmessungen bis zu ~4 m zu erhalten:

import time
from machine import I2C
import vl53l1x

bus = I2C(2)               # internal bus (PMIC / fuel gauge / ToF)
tof = vl53l1x.VL53L1X(bus)

while True:
    print("Distance:", tof.read(), "mm")
    time.sleep_ms(100)

Akku-Fuel-Gauge

Das Maxim MAX17262 ModelGauge m5 Fuel Gauge erfasst Spannung, Strom, Temperatur und Ladezustand des Li-Po-Akkus. Es sitzt an I²C 2 unter der Adresse 0x36.

Der MAX17262 verfügt über interne Strommessung, sodass das Stromregister direkt in Mikroampere ausgelesen wird, ohne einen externen Rsense-Faktor anwenden zu müssen. Das Auslesen des Fuel Gauge ist harmlos – es wird kein Treiber mitgeliefert, aber die im MAX17262-Datenblatt dokumentierten Register können direkt ausgelesen werden:

import time
import struct
from machine import I2C

FUEL_GAUGE = 0x36   # MAX17262

def read_reg(bus, addr, reg):
    return struct.unpack("<H", bus.readfrom_mem(addr, reg, 2))[0]

def read_signed(bus, addr, reg):
    v = read_reg(bus, addr, reg)
    return v - 0x10000 if v & 0x8000 else v

bus = I2C(2)

while True:
    # 0x05 RepCap — remaining capacity, raw × 0.5 mAh
    rep_cap   = read_reg(bus, FUEL_GAUGE, 0x05) * 0.5
    # 0x06 RepSOC — state of charge, raw / 256 %
    soc       = read_reg(bus, FUEL_GAUGE, 0x06) / 256
    # 0x08 Temp — die temperature, signed, raw / 256 °C
    temp      = read_signed(bus, FUEL_GAUGE, 0x08) / 256
    # 0x09 VCell — battery voltage, raw × 78.125 µV
    vcell     = read_reg(bus, FUEL_GAUGE, 0x09) * 78.125 / 1_000_000
    # 0x0A Current — signed, raw × 156.25 µA
    current   = read_signed(bus, FUEL_GAUGE, 0x0A) * 156.25 / 1000
    # 0x0B AvgCurrent — averaged current
    avg_curr  = read_signed(bus, FUEL_GAUGE, 0x0B) * 156.25 / 1000
    # 0x10 FullCapRep — learned full capacity, raw × 0.5 mAh
    full_cap  = read_reg(bus, FUEL_GAUGE, 0x10) * 0.5
    # 0x11 TTE — time-to-empty (valid while discharging), raw × 5.625 s
    tte_s     = read_reg(bus, FUEL_GAUGE, 0x11) * 5.625
    # 0x20 TTF — time-to-full   (valid while charging),  raw × 5.625 s
    ttf_s     = read_reg(bus, FUEL_GAUGE, 0x20) * 5.625
    # 0x17 Cycles — charge-cycle counter, 1% per LSB
    cycles    = read_reg(bus, FUEL_GAUGE, 0x17) / 100

    print("V:        %.3f V" % vcell)
    print("Capacity: %.1f / %.1f mAh (%.1f %%)" % (rep_cap, full_cap, soc))
    print("Temp:     %.1f C" % temp)
    print("Current:  %.1f mA  (avg %.1f mA)" % (current, avg_curr))
    print("TTE:      %.0f s   TTF: %.0f s" % (tte_s, ttf_s))
    print("Cycles:   %.2f" % cycles)
    print()
    time.sleep_ms(1000)

Current ist im vorzeichenbehafteten Zweierkomplement: positiv beim Laden, negativ beim Entladen. TTE ist nur aussagekräftig, wenn der Strom negativ ist; TTF nur, wenn der Strom positiv ist.

Power-Management-IC

Der NXP MC34PF1550A0EP PMIC verwaltet jeden Regler auf der Nicla Vision – die +3V3-Hauptschiene, die +1V8 SoC-Kern-/I/O-Schiene, VDDIO_EXT zu den Pegelwandlern und das Li-Po-Ladegerät. Er sitzt an I²C 2 unter der Adresse 0x08.

Warnung

Das Lesen von PMIC-Registern ist in Ordnung; das Schreiben in sie ist gefährlich. Eine fehlerhafte Konfiguration eines Abwärtsreglers oder einer Ladeeinstellung kann das Board, den Akku oder beides dauerhaft beschädigen. Behandeln Sie den PMIC als schreibgeschützt, es sei denn, Sie wissen genau, was Sie tun.

Das Nützlichste, was Ihnen der PMIC mitteilt und das Fuel Gauge nicht kann, ist die Lade-Zustandsmaschine – ob das Board derzeit über USB / ESLOV / VIN läuft, in welcher Phase des Ladezyklus sich der Li-Po befindet und ob das Ladegerät in einem thermischen oder Watchdog-Fehler ist. Die Ladegerät-Register liegen mit einem Offset von 0x80 im Haupt-I²C-Adressraum des PF1550 (siehe §22.2 des PF1550-Datenblatts), sodass beispielsweise CHG_INT_OK an der Ladegerät-Adresse 0x04 aus dem PMIC-Register 0x84 gelesen wird:

import time
from machine import I2C

PMIC = 0x08

# Charger state machine (low nibble of CHG_SNS, register 0x87)
CHG_STATES = {
    0x0: "precharge",
    0x1: "fast charge (constant current)",
    0x2: "fast charge (constant voltage)",
    0x3: "end of charge",
    0x4: "done",
    0x6: "timer fault",
    0x7: "thermistor suspend",
    0x8: "off — input invalid or charger disabled",
    0x9: "battery overvoltage",
    0xA: "thermal shutdown",
    0xC: "linear mode (not charging)",
}

bus = I2C(2)

while True:
    # 0x84 CHG_INT_OK — single-bit indicators
    ok = bus.readfrom_mem(PMIC, 0x84, 1)[0]
    vbus_ok = bool(ok & (1 << 5))   # bit 5 — VBUS valid (USB/ESLOV/VIN)
    bat_ok  = bool(ok & (1 << 2))   # bit 2 — battery OK
    chg_ok  = bool(ok & (1 << 3))   # bit 3 — charger actively charging
    thm_ok  = bool(ok & (1 << 7))   # bit 7 — thermistor in normal range

    # 0x87 CHG_SNS — charger state + thermal regulation flag
    chg_sns = bus.readfrom_mem(PMIC, 0x87, 1)[0]
    state   = CHG_STATES.get(chg_sns & 0x0F, "reserved")
    treg    = bool(chg_sns & (1 << 7))   # thermal regulation active

    print("VBUS valid:         ", vbus_ok)
    print("battery OK:         ", bat_ok)
    print("charger active:     ", chg_ok)
    print("thermistor normal:  ", thm_ok)
    print("thermal reg active: ", treg)
    print("state:              ", state)
    print()
    time.sleep_ms(1000)

Weitere schreibgeschützte Register, die im Datenblatt einen Blick wert sind (alle mit Ladegerät-Offset 0x80): 0x80 CHG_INT (gelatchte Ladegerät-Interrupts – Fehlerflags), 0x86 VBUS_SNS (der Multi-Bit-VBUS-Zustand einschließlich OVLO / UVLO / DPM) und 0x88 BATT_SNS (Akku-Vorhandensein und Überstromzustand).

Wi-Fi

Das Onboard-Murata 1DX (CYW4343W) wird über network — Netzwerkkonfiguration als Station-Schnittstelle bereitgestellt. Schließen Sie die mitgelieferte Antenne an den Onboard-U.FL-Anschluss an, bevor Sie das Funkmodul hochfahren:

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

Das gleiche Murata 1DX stellt auch Bluetooth LE 5.1 bereit. Verwenden Sie aioble — Async BLE für asyncio-freundliches BLE – zum Beispiel als Peripheriegerät werben und warten, bis sich ein Central verbindet:

import asyncio
import aioble

async def run():
    while True:
        conn = await aioble.advertise(250_000, name="Nicla-Vision")
        print("Connected:", conn.device)
        await conn.disconnected()

asyncio.run(run())

Bus-Referenz

GPIO

Verwenden Sie machine.Pin, um einen der bedruckten Pins zu lesen oder anzusteuern. Ausgänge sind 3,3 V CMOS (standardmäßig VDDIO_EXT), und die Pegelwandler begrenzen die Treiberstärke pro Pin auf wenige Milliampere – sie sind für Signalpegel und nicht für Leistungslasten ausgelegt.

from machine import Pin

out = Pin("D0", Pin.OUT)
out.on()
out.off()
out.value(1)

inp = Pin("D1", Pin.IN, Pin.PULL_UP)
print(inp.value())

Jeder Eingangspin kann auch einen Interrupt bei Flankenwechseln auslösen:

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

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

UART

Bus

TX

RX

UART4

SDA

SCL

from machine import UART

uart = UART(4, baudrate=115200)
uart.write("hello")
uart.read(5)

Bemerkung

UART4 teilt sich seine Pins mit I²C 1 – dieselben SDA/SCL-Pads führen beide Busse. Wählen Sie an diesen Pins UART oder I²C, nicht beides.

Der D1/D2-Aufdruck liest sich auch als UART_TX/UART_RX, aber in dieser Firmware sind diese Pins zu LPUART1 geführt, nicht zu machine.UART. machine.UART(1) selbst ist für den On-Chip-Bluetooth-Controller reserviert und an den Steckleisten nicht zugänglich.

I²C

Bus

SCL

SDA

I2C1

SCL

SDA

from machine import I2C

i2c = I2C(1, freq=400_000)
i2c.scan()
i2c.writeto(0x76, b"hi")

Die SCL/SDA-Pads an J2 und die SCL_EXT/SDA_EXT-Pins des ESLOV-Anschlusses landen auf demselben I²C-1-Bus – siehe ESLOV-Anschluss oben für die ESLOV-Pinbelegung.

Dieselbe Hardware kann auch im Target-(Slave-)Modus über machine.I2CTarget verwendet werden, um einer anderen I²C-Steuerung einen Speicherbereich bereitzustellen:

from machine import I2CTarget

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

SPI

Bus

MOSI

MISO

SCK

CS

SPI4

COPI

CIPO

SCLK

CS

from machine import SPI
from machine import Pin

spi = SPI(4, baudrate=10_000_000)
cs = Pin("CS", Pin.OUT, value=1)   # CS is not driven by the SPI peripheral

cs.value(0)
spi.write(b"hello")
cs.value(1)

ADC

Die Nicla Vision stellt drei 12-Bit-ADC-Kanäle an A0, A1 und A2 bereit. Alle drei sind auf 1,8 V referenziertread_u16 gibt 0–65535 über 0–1,8 V am Pin zurück:

from machine import ADC
import time

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

Warnung

Die ADC-Eingänge der Nicla Vision sind auf 1,8 V referenziert (und haben keinen Pegelwandler vor dem SoC). Das Anlegen eines 3,3-V-Signals sättigt den Wandler und kann den Pin beschädigen – teilen Sie höhere Spannungen extern herunter.

PWM

Pin

Timer / Kanal

D1

TIM1 CH2

D2

TIM1 CH3

SCL

TIM4 CH3, TIM16 CH1

SDA

TIM4 CH4, TIM17 CH1

SCLK

TIM1 CH3N

CIPO

TIM1 CH3

COPI

TIM1 CH4

CS

TIM1 CH2

Steuern Sie einen davon über machine.PWM an:

from machine import Pin, PWM

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

Bemerkung

Mehrere Pins teilen sich TIM1-Kanäle:

  • TIM1 CH2 ist an D1 und CS.

  • TIM1 CH3 ist an D2 und CIPO; SCLK gibt das invertierte Komplement (TIM1 CH3N) desselben Kanals aus.

  • TIM1 CH4 ist allein an COPI.

Wählen Sie einen Verbraucher pro Timer-Kanal. Die SPI-Quartett-Pins (SCLK/CIPO/COPI/CS) können außerdem nicht PWM-angesteuert werden, während machine.SPI(4) sie verwendet.

Software-bit-banged Busse

machine.SoftI2C und machine.SoftSPI funktionieren an jedem GPIO, falls Sie einen zusätzlichen Bus benötigen.

Thermalsensor (extern)

Die Firmware enthält den fir — Wärmesensortreiber (fir == far infrared)-Treiber für extern verdrahtete Wärmebildkameras:

  • MLX90621 – 16 × 4 IR-Array

  • MLX90640 – 32 × 24 IR-Array

  • MLX90641 – 16 × 12 IR-Array

  • AMG8833 – 8 × 8 IR-Array

Verdrahten Sie das Modul mit dem I²C-Bus des Boards und lesen Sie Einzelbilder mit 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())

Der fir-Treiber kommuniziert mit dem Sensor nur über I²C 1 – verdrahten Sie das Modul mit den bedruckten SCL / SDA-Pads.

Timing

time

Das time-Modul deckt blockierende Verzögerungen, monotone Ticks und die Messung verstrichener Zeit ab:

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)

Virtuelle Timer

machine.Timer plant periodische oder einmalige Callbacks, ohne einen Hardware-Timer-Slot zu belegen. Übergeben Sie -1 als id, um einen virtuellen (Software-)Timer zu verwenden:

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

Periodenwerte sind in Millisekunden. Rufen Sie deinit() auf, um den Slot zu stoppen und freizugeben.

Echtzeituhr

machine.RTC hält die Wanduhrzeit über Resets hinweg:

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

Watchdog

machine.WDT setzt das Board zurück, wenn die Anwendung hängt. Einmal gestartet, kann er nicht gestoppt oder umkonfiguriert werden – füttern Sie ihn periodisch innerhalb Ihrer Hauptschleife:

from machine import WDT

wdt = WDT(timeout=5_000)   # 5 second window
while True:
    # ...do work...
    wdt.feed()

Boot- und Laufzeitinformationen

Firmware-Update (DFU)

Die Nicla Vision verwendet Arduinos Standard-Doppeltipp-Reset, um in Arduinos Bootloader zu gelangen. Drücken Sie den Reset-Taster zweimal schnell hintereinander – das Board meldet sich über USB als DFU-Gerät neu an, und OpenMV IDE kann ein neues Firmware-Image flashen.

Ein laufendes Skript kann den Bootloader bei Bedarf erneut aufrufen, indem es machine.bootloader() aufruft:

import machine

machine.bootloader()

Dateisystem und Boot-Reihenfolge

Die Nicla-Vision-Firmware mountet beim Booten bis zu zwei Dateisysteme:

  • Internes Flash – immer unter /flash gemountet. Enthält standardmäßig main.py und README.txt; wird beim allerersten Boot erstellt.

  • ROMFS – schreibgeschütztes, speicherabgebildetes Dateisystem unter /rom, das von MicroPython beim Start automatisch gemountet wird.

Nach dem Mounten wird das Arbeitsverzeichnis auf /flash gesetzt. Der Interpreter führt dann Skripte aus diesem Verzeichnis aus:

  • boot.py wird bei jedem Soft-Reset ausgeführt (Kaltstart, Ctrl‑D aus der REPL oder immer dann, wenn das laufende Skript zurückkehrt).

  • main.py wird nur beim Kaltstart ausgeführt, unmittelbar nach boot.py. Nachfolgende Soft-Resets führen boot.py erneut aus, fallen aber direkt zur REPL durch – um main.py erneut auszuführen, müssen Sie das Board vollständig zurücksetzen.

Die standardmäßige main.py, die auf einem frisch geflashten Board ausgeliefert wird, lässt einfach den blauen Kanal der RGB-Benutzer-LED als Heartbeat blinken (zwei kurze Impulse, kurze Pause), sodass Sie erkennen können, dass die Firmware sauber gebootet ist, ohne dass ein Host angeschlossen ist.

sys.path wird erweitert, um beide Dateisysteme und ihre lib/-Unterverzeichnisse einzuschließen, sodass importierbare Module in /flash/lib oder /rom/lib liegen können.

Wenn über USB verbunden, meldet sich /flash auch als USB-Massenspeicherlaufwerk auf dem Host an, sodass Sie boot.py, main.py und alle anderen Dateien direkt bearbeiten können. Werfen Sie das Laufwerk aus, bevor Sie die Kamera zurücksetzen, damit der Host seine zwischengespeicherten Schreibvorgänge leert.

Bemerkung

Da das Betriebssystem das Laufwerk als passives Blockgerät behandelt, werden Dateien, die von auf der Kamera laufendem Code erstellt oder geändert werden, erst angezeigt, wenn der Host das Laufwerk neu mountet. Wenn sowohl das Betriebssystem als auch die Kamera gleichzeitig dasselbe Dateisystem beschreiben, gewinnt das Betriebssystem und überschreibt die von der Kamera vorgenommenen Änderungen. Verwenden Sie die SD-Karte für alle Daten, die das Skript zurückschreibt, und mounten Sie neu, bevor Sie diese Dateien vom Host lesen.

Bemerkung

Der rote Kanal der RGB-Benutzer-LED leuchtet möglicherweise kurz auf, während der Host vom USB-Massenspeicherlaufwerk liest oder darauf schreibt – dies ist eine firmwaregesteuerte Aktivitätsanzeige, kein Fehler.

Speichergrößen

Die Nicla Vision wird ausgeliefert mit:

  • /flash11 MB FAT-Dateisystem, lesen/schreiben.

  • /rom4 MB schreibgeschütztes, speicherabgebildetes ROMFS, verwendet zum Ausliefern von Skripten und ML-Modellen, die vom Zero-Copy-mmap-Zugriff profitieren.

Hard-Fault-Anzeige

Wenn die RGB-Benutzer-LED schnell alle Farben durchläuft – schnell genug, dass sie eher wie eine funkelnde weiße LED als wie einzelne Farbtöne aussieht – hat die Firmware einen nicht behebbaren Hard-Fault erlitten. Flashen Sie die Firmware neu, um sie wiederherzustellen; wenn das Neuflashen nicht hilft, ist das Board möglicherweise physisch beschädigt.

Software-Bibliotheken

Im Bibliotheksindex finden Sie die vollständige Liste der Module – einschließlich derjenigen, die einzigartig für den Nicla-Vision-Build sind.