Arduino Portenta H7

L’Arduino Portenta H7 è una scheda di sviluppo industriale di 66 × 25 mm costruita attorno allo STMicroelectronics STM32H747XI — un SoC dual‑core che combina un Cortex‑M7 a 400 MHz con un Cortex‑M4 a 200 MHz. Il firmware OpenMV gira interamente sul core M7 ed è progettato per essere usato con il Portenta Vision Shield (edizione Ethernet o LoRa), che aggiunge alla base Portenta H7 una camera Himax HM01B0 / HM0360, due microfoni PDM e uno slot microSD.

Arduino Portenta H7

Per il datasheet completo, le foto e le dimensioni, vedere la pagina prodotto dell’Arduino Portenta H7.

Punti salienti

  • STMicroelectronics STM32H747XI dual Cortex‑M7 (400 MHz) + Cortex‑M4 (200 MHz). Il firmware OpenMV gira solo sul core M7; il core M4 è esposto tramite openamp per la comunicazione inter‑processore.

  • 8 MB di SDRAM esterna più 2 MB di flash interna e 16 MB di flash QSPI esterna.

  • Encoder/decoder JPEG hardware.

  • Wi‑Fi b/g/n (2,4 GHz) + Bluetooth LE 5.1 tramite il modulo Murata 1DX (CYW4343W) — si collega all’antenna fornita tramite un connettore U.FL integrato.

  • USB‑C ad alta velocità (480 Mb/s).

  • 22 pin I/O utente sui connettori superiori in stile Arduino MKR — D0–D14 (digitali) più A0–A6 (analogici).

  • Due connettori ad alta densità a 80 pin sul lato inferiore espongono l’intero fabric dello STM32H747 — DCMI, DSI, Ethernet RMII, FDCAN, SDIO, SAI/I²S, UART, ulteriori SPI/I²C/timer e così via. Shield come il Vision Shield si accoppiano a questi connettori.

  • JTAG / SWD disponibili sui connettori HD inferiori per il debug avanzato.

  • Supporto batteria — connettore JST per Li‑Po da 3,7 V più caricabatterie e monitor della batteria integrati.

Pinout

Pinout dell'Arduino Portenta H7

Riferimento dei pin

22 pin utente sono esposti sui connettori del bordo superiore in stile Arduino MKR — 15 digitali (D0-D14) più 7 analogici (A0-A6). Molti altri pin del SoC sono disponibili tramite i connettori ad alta densità a 80 pin inferiori per il lavoro con gli shield; vedere il PDF del pinout completo di Arduino per quella mappatura.

Nome pin

Riferimento

Funzione

D0

3,3 V

TIM8 CH3N

D1

3,3 V

TIM1 CH1 / SPI5 NSS

D2

3,3 V

TIM1 CH2 / SPI5 MISO

D3

3,3 V

GPIO

D4

3,3 V

TIM3 CH2 / TIM8 CH2 / USART6 RX

D5

3,3 V

TIM3 CH1 / TIM8 CH1 / USART6 TX

D6

3,3 V

TIM1 CH1 / I2C3 SCL

D7

3,3 V

TIM5 CH4 / SPI2 NSS

D8

3,3 V

SPI2 MOSI (condiviso con A3 / A5)

D9

3,3 V

SPI2 SCK

D10

3,3 V

SPI2 MISO (condiviso con A2 / A4)

D11

3,3 V

I2C3 SDA

D12

3,3 V

I2C3 SCL

D13

3,3 V

USART1 RX / TIM1 CH3

D14

3,3 V

USART1 TX / TIM1 CH2

A0

3,3 V

ADC12 IN0 (solo analogico)

A1

3,3 V

ADC12 IN1 (solo analogico)

A2

3,3 V

ADC123 IN12 (solo analogico; condiviso con D10)

A3

3,3 V

ADC12 IN13 (solo analogico; condiviso con D8)

A4

3,3 V

ADC123 IN12 (condiviso con D10)

A5

3,3 V

ADC12 IN13 (condiviso con D8)

A6

3,3 V

DAC1 OUT1 / ADC12 IN18

A7

3,3 V

TIM3 CH1 / ADC12 IN3 (non esposto sui connettori)

D20

3,3 V

alias di D8 / A3 / A5

D21

3,3 V

alias di A6 — DAC1 OUT1

RESET

3,3 V

premere l’interruttore integrato o tirare a GND per il reset

LED_RED

3,3 V

canale rosso del LED RGB (attivo basso)

LED_GREEN

3,3 V

canale verde del LED RGB (attivo basso)

LED_BLUE

3,3 V

canale blu del LED RGB (attivo basso)

Nota

A0-A3 sono pad solo analogici sullo STM32H747 senza funzione GPIO — trattali esclusivamente come ingressi ADC. A2/A4 e A3/A5 condividono i loro pin fisici rispettivamente con D10 e D8, quindi non puoi pilotare PWM o SPI su di essi mentre li leggi come analogici. A7 si trova sui connettori HD inferiori.

Pin di alimentazione

Pin del connettore MKR:

  • VIN — rail principale del sistema verso il PMIC integrato. Alimentato tramite un diodo dal rail +5V, dal pin VIN MKR o dai connettori HD a 80 pin inferiori.

  • +5V — rail a 5 V alimentato da USB, dal connettore ESLOV o dal pin +5V MKR stesso.

  • +3V3 — rail principale a 3,3 V (uscita del regolatore switching del PMIC).

  • AREF — riferimento di tensione analogico per i pin ADC. Predefinito a 3,3 V; pilotalo dall’esterno per usare un riferimento diverso.

  • GND — massa comune.

Ingresso batteria:

  • JST Li‑Po sul fronte della scheda accetta una cella Li‑Po da 3,7 V. Il PMIC la carica ogni volta che +5V o VIN è presente.

Il Portenta H7 può essere alimentato attraverso una qualsiasi di queste vie:

  • USB‑C — fornisce 5 V al PMIC integrato.

  • Connettore ESLOV — fino a 5 V su VESLOV (vedere Connettore ESLOV).

  • Pin VIN — fornisci direttamente un’alimentazione regolata a 5 V.

  • Batteria Li‑Po — collega al JST sul fronte.

Connettore ESLOV

Sul lato della scheda si trova un connettore ESLOV a 5 pin senza saldatura:

Pin

Nome

Funzione

1

VESLOV

uscita di alimentazione a 5 V (stesso rail del +5V del connettore MKR)

2

INT

ingresso di interrupt esterno su D7

3

SCL_EXT

condiviso con il pad D12 del connettore MKR — stesso bus I²C 3 del connettore utente

4

SDA_EXT

condiviso con il pad D11 del connettore MKR — stesso bus I²C 3 del connettore utente

5

GND

massa comune

I pin SCL_EXT/SDA_EXT dell’ESLOV e i pin D12/D11 del connettore MKR sono gli stessi pin — un unico bus I²C 3 esposto su due connettori.

Suggerimento

Usa lo stimatore della durata della batteria per modellare per quanto tempo il Portenta H7 funzionerà a batteria per un dato duty cycle attivo / deep-sleep.

Pin di recovery e debug

  • RESET — sia un pin esposto sul connettore superiore sia un interruttore momentaneo sul lato della scheda, collegati alla linea NRST del SoC. Tira a GND o premi il pulsante per il reset.

Il Portenta H7 usa il doppio tap di reset standard di Arduino per entrare nel bootloader di Arduino. Premi rapidamente due volte il pulsante di reset — la scheda si rienumera via USB come dispositivo DFU e OpenMV IDE può flashare una nuova immagine firmware.

I segnali SWD dello STM32 sono esposti sul connettore HD J1 inferiore:

  • J1‑73 — NRST

  • J1‑75 — SWDIO (PA13)

  • J1‑77 — SWCLK (PA14)

  • J1‑79 — SWO (PB3)

Collegali tramite un Portenta Breakout, l’adattatore di debug ufficiale Arduino o un carrier personalizzato con un connettore da 1,27 mm. Tutti i segnali di debug sono riferiti a 3,3 V.

Nota

Quando è collegato il Portenta Vision Shield, gli stessi segnali SWD/JTAG vengono instradati verso lo standard connettore JTAG di debug ARM Cortex a 20 pin sullo shield (passo da 1,27 mm / 0,05″).

Periferiche integrate

LED

Il Portenta H7 ha un singolo LED RGB utente, controllabile via software tramite machine.LED

from machine import LED

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

Un LED arancione di carica separato accanto al JST della batteria si accende quando il caricabatterie integrato eroga corrente verso una Li‑Po collegata; non è controllabile dall’utente.

Sensore camera (Vision Shield)

Con il Portenta Vision Shield (edizione Ethernet o LoRa) collegato, il sensore Himax viene pilotato tramite il modulo csi — sensori camera

import csi

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

while True:
    img = cam.snapshot()

Sono supportate due revisioni del Vision Shield:

  • HM01B0 — 320 × 320 monocromatico.

  • HM0360 — 640 × 480 monocromatico.

Avvertimento

Mentre la camera del Vision Shield è inizializzata, i seguenti pin del connettore MKR sono riservati dal firmware e non possono essere usati:

Pin MKR

Motivo

D1

TIM1 CH1 — clock master della camera

D6

TIM1 CH1 (alt) — clock master della camera

D11

I²C 3 SDA — condiviso con la camera; il bus è utilizzabile ma evita l’indirizzo I²C del sensore (0x24)

D12

I²C 3 SCL — condiviso con la camera; il bus è utilizzabile ma evita l’indirizzo I²C del sensore (0x24)

A6 / D21

DCMI HSYNC — disabilita anche il DAC

A7

DCMI PXCLK

Machine learning

ml — Machine Learning esegue modelli TFLite quantizzati sul Cortex‑M7 con kernel CMSIS‑NN — abbastanza veloci per rilevatori compatti a qualche frame al secondo. I modelli sul filesystem di sola lettura /rom vengono caricati direttamente dalla flash senza copiarli in RAM. Ecco un rilevatore BlazeFace 128×128 che sovrappone il volto rilevato e i suoi sei landmark su ogni frame proveniente dalla camera del Vision Shield:

import csi
import time
import ml
from ml.postprocessing.mediapipe import BlazeFace

# Initialize the sensor.
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.GRAYSCALE)
csi0.framesize(csi.QVGA)
csi0.window((240, 240))

# Load built-in face detection model
model = ml.Model("/rom/blazeface_front_128.tflite", postprocess=BlazeFace(threshold=0.4))
print(model)

clock = time.clock()
while True:
    clock.tick()
    img = csi0.snapshot()

    # faces is a list of ((x, y, w, h), score, keypoints) tuples
    for r, score, keypoints in model.predict([img]):
        ml.utils.draw_predictions(img, [r], ("face",), ((0, 0, 255),), format=None)

        # keypoints is a ndarray of shape (6, 2)
        ml.utils.draw_keypoints(img, keypoints, color=(255, 0, 0))

    print(clock.fps(), "fps")

Core M4

Il core Cortex‑M4 è esposto tramite openamp per la comunicazione inter‑processore. Il firmware OpenMV gira solo sull’M7; l’M4 non ha un proprio runtime MicroPython, quindi usarlo significa compilare una separata immagine firmware C e caricarla dal filesystem tramite openamp.RemoteProc. Un firmware di esempio precompilato che implementa un endpoint UART virtuale è disponibile nel repository openamp_vuart — segui il suo README per compilare vuart.elf

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 pratica questo supporto è da considerare al meglio come una dimostrazione dell’interfaccia openamp piuttosto che come una piattaforma dual‑core funzionante — l’M4 non può essere resettato indipendentemente dall’M7, quindi fermare l’M4 forza un riavvio completo del sistema.

Microfono (Vision Shield)

Il Vision Shield monta due microfoni PDM acquisiti tramite audio — Modulo Audio attraverso la periferica SAI4 dello STM32. Ogni buffer arriva come PCM bytearray a 16 bit con segno, pronto per essere passato a ulab/numpy per il DSP — ad esempio, un semplice rilevatore di volume:

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

Passa channels=2 a audio.init per ricevere campioni interlacciati da entrambi i microfoni.

Indicatore di carica della batteria

Il fuel gauge Maxim MAX17262 ModelGauge m5 traccia la tensione, la corrente, la temperatura e lo stato di carica della batteria Li‑Po. Si trova sull”I²C 1 all’indirizzo 0x36.

Il MAX17262 ha il rilevamento della corrente interno, quindi il registro della corrente fornisce direttamente il valore in microampere senza alcun fattore Rsense esterno da applicare. Leggere il fuel gauge è innocuo — non viene fornito alcun driver, ma i registri documentati nel datasheet del MAX17262 possono essere letti direttamente:

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

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 è in complemento a due con segno: positivo durante la carica, negativo durante la scarica. TTE è significativo solo quando la corrente è negativa; TTF solo quando la corrente è positiva.

IC di gestione dell’alimentazione

Il PMIC NXP PF1550 gestisce ogni regolatore del Portenta H7 — il rail principale +3V3, il rail core / I/O del SoC +1V8 e il caricabatterie Li‑Po. Si trova sull”I²C 1 all’indirizzo 0x08.

Avvertimento

Leggere i registri del PMIC va bene; scriverci è pericoloso. Configurare in modo errato un regolatore buck o un’impostazione del caricabatterie può danneggiare permanentemente la scheda, la batteria o entrambi. Tratta il PMIC come di sola lettura a meno che tu non sappia esattamente cosa stai facendo.

La cosa più utile che il PMIC ti dice e che il fuel gauge non può è la macchina a stati del caricabatterie — se la scheda sta attualmente funzionando su USB / ESLOV / VIN, in quale fase del ciclo di carica si trova la Li‑Po e se il caricabatterie è in errore termico o di watchdog. I registri del caricabatterie risiedono a un offset di 0x80 nello spazio di indirizzamento I²C principale del PF1550 (vedere §22.2 del datasheet del PF1550), quindi ad esempio CHG_INT_OK all’indirizzo del caricabatterie 0x04 viene letto dal registro PMIC 0x84

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

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

Altri registri di sola lettura che vale la pena consultare nel datasheet (tutti all’offset caricabatterie 0x80): 0x80 CHG_INT (interrupt latch del caricabatterie — flag di errore), 0x86 VBUS_SNS (lo stato VBUS multi‑bit incluso OVLO / UVLO / DPM) e 0x88 BATT_SNS (presenza della batteria e stato di sovracorrente).

Wi‑Fi

Il Murata 1DX (CYW4343W) integrato è esposto tramite network — configurazione di rete come interfaccia station. Collega l’antenna fornita al connettore U.FL integrato prima di attivare la radio:

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

Lo stesso Murata 1DX espone anche il Bluetooth LE 5.1. Usa aioble — BLE asincrono per il BLE compatibile con asyncio — ad esempio, fai l’advertising come periferica e attendi che un central si connetta:

import asyncio
import aioble

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

asyncio.run(run())

LoRa (Vision Shield)

L”edizione LoRa del Vision Shield aggiunge un modulo LoRaWAN Murata CMWX1ZZABZ collegato al Portenta H7 tramite UART. Il modulo lora incapsula il firmware a comandi AT e supporta join OTAA o ABP, uplink e downlink:

from lora import Lora
from lora import BAND_EU868
from lora import LoraErrorTimeout

lora = Lora(band=BAND_EU868, poll_ms=60000)
print("Device EUI:", lora.get_device_eui())

appEui = "1234567890123456"
appKey = "12345678901234567890123456789012"

try:
    lora.join_OTAA(appEui, appKey)
except LoraErrorTimeout as e:
    print("Join timed out — try moving near a window:", e)

lora.set_port(3)
lora.send_data("HeLoRA world!", True)

while True:
    if lora.available():
        data = lora.receive_data()
        if data:
            print("Port:", data["port"], "Data:", data["data"])
    lora.poll()

Usa BAND_US915 / BAND_AS923 / BAND_AU915 ecc. per le regioni non UE, e passa a lora.Lora.join_ABP() se il tuo network server usa l’attivazione ABP.

Avvertimento

Mentre il modulo LoRa è in uso, il driver riserva i seguenti pin del connettore MKR come linee di controllo per il Murata CMWX1ZZABZ — non possono essere usati:

Pin MKR

Motivo

D3

pin BOOT del modulo LoRa

D5

pin RST del modulo LoRa

Ethernet (Vision Shield)

L”edizione Ethernet del Vision Shield aggiunge una presa RJ45 con magnetics collegata al MAC Ethernet 10/100 dello STM32H747 tramite RMII. Collega un cavo Ethernet e la PHY appare come interfaccia LAN; il DHCP parte automaticamente non appena il link si attiva:

import network
import time

lan = network.LAN()
lan.active(True)
while not lan.isconnected():
    time.sleep(1)
print("Ethernet IP:", lan.ipconfig("addr4")[0])

Scheda microSD (Vision Shield)

Quando viene inserita una scheda, questa viene montata automaticamente su /sdcard ed è utilizzabile tramite il normale filesystem:

import os

for entry in os.listdir("/sdcard"):
    print(entry)

Riferimento dei bus

GPIO

Usa machine.Pin per leggere o pilotare uno qualsiasi dei pin serigrafati. Le uscite sono CMOS a 3,3 V e possono assorbire/erogare fino a 20 mA per pin (140 mA totali sull’intero connettore).

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

Qualsiasi pin di ingresso può anche generare un interrupt sulle transizioni di fronte:

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

UART1

D14

D13

UART6

D5

D4

from machine import UART

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

I²C

Bus

SCL

SDA

I2C3

D12

D11

from machine import I2C

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

I pad D11/D12 sul connettore MKR e i pin SDA_EXT/SCL_EXT del connettore ESLOV si trovano sullo stesso bus I²C 3 — vedere Connettore ESLOV sopra per il pinout dell’ESLOV.

Lo stesso hardware può anche essere usato in modalità target (slave) tramite machine.I2CTarget per esporre una regione di memoria a un altro controller I²C:

from machine import I2CTarget

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

SPI

Bus

MOSI

MISO

SCK

CS

SPI2

D8

D10

D9

D7

from machine import SPI
from machine import Pin

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

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

ADC

Il Portenta H7 espone otto canali ADC a 12 bit su A0–A7. Tutti sono riferiti a 3,3 Vread_u16 restituisce 0–65535 su 0–3,3 V al pin:

from machine import ADC
import time

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

DAC

Un singolo canale DAC a 12 bit è esposto su DAC1 (A6 / D21) tramite pyb.DAC

from pyb import DAC

dac = DAC("DAC1")
dac.write(int(0.5 * 255))   # 8‑bit output, ~1.65 V

PWM

Pin

Timer / canale

D0

TIM8 CH3N

D1

TIM1 CH1, TIM8 CH3N

D2

TIM1 CH2, TIM8 CH2N

D4

TIM3 CH2, TIM8 CH2

D5

TIM3 CH1, TIM8 CH1

D6

TIM1 CH1

D7

TIM5 CH4

D13

TIM1 CH3

D14

TIM1 CH2

A7

TIM3 CH1

Pilota uno qualsiasi di essi tramite machine.PWM

from machine import Pin, PWM

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

Nota

Diversi pin condividono i canali del timer:

  • TIM1 CH1 è su D1 e D6.

  • TIM1 CH2 è su D2 e D14.

  • TIM8 CH3N è su D0 e D1.

Scegli un solo consumatore per canale del timer.

Avvertimento

TIM1 è riservato al clock master della camera quando il Vision Shield è inizializzato tramite csi — sensori cameraD1, D2, D6, D13 e D14 non possono essere pilotati in PWM mentre la camera è attiva.

Bus software bit‑banged

machine.SoftI2C e machine.SoftSPI funzionano su qualsiasi GPIO se ti serve un bus aggiuntivo.

Sensore termico (esterno alla scheda)

Il firmware include il driver fir — driver del sensore termico (fir == far infrared) per le termocamere collegate esternamente:

  • MLX90621 — array IR 16 × 4

  • MLX90640 — array IR 32 × 24

  • MLX90641 — array IR 16 × 12

  • AMG8833 — array IR 8 × 8

Collega il modulo al bus I²C della scheda e leggi i frame con 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())

Il driver fir comunica con il sensore solo tramite I²C 3 — collega il modulo a D12 (SCL) e D11 (SDA).

Temporizzazione

time

Il modulo time copre i ritardi bloccanti, i tick monotoni e la misura del tempo trascorso:

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)

Timer virtuali

machine.Timer pianifica callback periodiche o singole senza consumare uno slot di timer hardware. Passa -1 come id per usare un timer virtuale (software):

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

I valori del periodo sono in millisecondi. Chiama deinit() per fermare e rilasciare lo slot.

Orologio in tempo reale

machine.RTC mantiene l’orario reale attraverso i reset. Il connettore HD espone anche un pad COINCELL che può alimentare l’RTC da una CR2032 in caso di mancanza di alimentazione:

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 resetta la scheda se l’applicazione si blocca. Una volta avviato non può essere fermato o riconfigurato — alimentalo periodicamente all’interno del tuo loop principale:

from machine import WDT

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

Informazioni di boot e runtime

Aggiornamento del firmware (DFU)

Il Portenta H7 usa il doppio tap di reset standard di Arduino per entrare nel bootloader di Arduino. Premi rapidamente due volte il pulsante di reset — la scheda si rienumera via USB come dispositivo DFU e OpenMV IDE può flashare una nuova immagine firmware.

Uno script in esecuzione può rientrare nel bootloader su richiesta chiamando machine.bootloader()

import machine

machine.bootloader()

Filesystem e ordine di boot

Il firmware del Portenta H7 monta fino a tre filesystem all’avvio:

  • Flash interna — sempre montata su /flash. Contiene per impostazione predefinita main.py e README.txt; creata al primissimo avvio.

  • Scheda microSD — se è collegato un Vision Shield ed è inserita una scheda, viene montata su /sdcard.

  • ROMFS — filesystem di sola lettura mappato in memoria su /rom, montato automaticamente da MicroPython all’avvio.

Dopo il montaggio, la directory di lavoro è impostata su /sdcard quando la scheda è presente, altrimenti su /flash. L’interprete esegue quindi gli script da quella directory:

  • boot.py viene eseguito a ogni soft reset (cold boot, Ctrl‑D dal REPL o ogni volta che lo script in esecuzione termina).

  • main.py viene eseguito solo al cold boot, immediatamente dopo boot.py. I soft reset successivi rieseguono boot.py ma passano direttamente al REPL — per rieseguire main.py devi resettare completamente la scheda.

Posizionare un boot.py o un main.py sulla scheda SD sovrascrive la copia in flash senza toccarla — entrambi i file vengono cercati nella directory di boot (/sdcard quando la scheda è montata, altrimenti /flash).

Il main.py predefinito fornito su una scheda appena flashata si limita a far lampeggiare il canale blu del LED RGB utente come heartbeat (due brevi impulsi, breve pausa), così puoi capire che il firmware si è avviato correttamente senza alcun host collegato.

sys.path viene esteso per includere tutti e tre i filesystem e le loro sottodirectory lib/, così i moduli importabili possono risiedere in /flash/lib, /sdcard/lib o /rom/lib.

Per forzare il sistema a ignorare una scheda SD inserita (ad esempio per eseguire il main.py della flash anche con una scheda presente), crea un file vuoto chiamato SKIPSD nella radice di /flash.

Quando è connesso via USB, il filesystem di boot (/sdcard se è presente una scheda, altrimenti /flash) si enumera anche come unità di archiviazione di massa USB sull’host, permettendoti di modificare direttamente boot.py, main.py e qualsiasi altro file. Espelli l’unità prima di resettare la scheda così l’host scarica le sue scritture in cache.

Nota

Poiché il sistema operativo tratta l’unità come un dispositivo a blocchi passivo, i file creati o modificati dal codice in esecuzione sulla camera non appariranno finché l’host non rimonta l’unità. Se sia il sistema operativo sia la camera scrivono lo stesso filesystem nello stesso momento, il sistema operativo vincerà e sovrascriverà le modifiche fatte dalla camera. Usa la scheda SD per qualsiasi dato che lo script riscrive, e rimonta prima di leggere quei file dall’host.

Nota

Il canale rosso del LED RGB utente può accendersi brevemente mentre l’host legge da o scrive sull’unità di archiviazione di massa USB — questo è un indicatore di attività pilotato dal firmware, non un guasto.

Dimensioni di archiviazione

Il Portenta H7 viene fornito con:

  • /flash — filesystem FAT da 11 MB, lettura/scrittura.

  • /rom — ROMFS mappato in memoria di sola lettura da 4 MB, usato per fornire script e modelli ML che traggono vantaggio dall’accesso mmap zero-copy.

  • /sdcard — dimensione completa di qualsiasi scheda microSD inserita in un Vision Shield (quando presente), lettura/scrittura.

Indicatore di hard‑fault

Se il LED RGB utente sta ciclando rapidamente attraverso tutti i colori — abbastanza velocemente da sembrare un LED bianco scintillante piuttosto che tonalità distinte — il firmware ha riscontrato un hard fault irrecuperabile. Riflasha il firmware per ripristinare; se il riflash non aiuta, la scheda potrebbe essere fisicamente danneggiata.

Librerie software

Vedere l”indice della libreria per l’elenco completo dei moduli — incluso quali sono unici della build del Portenta H7.