Arduino Portenta H7

La Arduino Portenta H7 es una placa de desarrollo industrial de 66 × 25 mm construida en torno al STMicroelectronics STM32H747XI — un SoC de doble núcleo que combina un Cortex‑M7 a 400 MHz con un Cortex‑M4 a 200 MHz. El firmware de OpenMV se ejecuta enteramente en el núcleo M7 y está diseñado para usarse con el Portenta Vision Shield (edición Ethernet o LoRa), que añade una cámara Himax HM01B0 / HM0360, micrófonos PDM duales y una ranura microSD a la Portenta H7 base.

Arduino Portenta H7

Para ver la hoja de datos completa, fotos y dimensiones, consulta la página del producto Arduino Portenta H7.

Aspectos destacados

  • STMicroelectronics STM32H747XI doble Cortex‑M7 (400 MHz) + Cortex‑M4 (200 MHz). El firmware de OpenMV se ejecuta únicamente en el núcleo M7; el núcleo M4 se expone a través de openamp para la comunicación entre procesadores.

  • 8 MB de SDRAM externa más 2 MB de memoria flash interna y 16 MB de memoria flash QSPI externa.

  • Codificador/decodificador JPEG por hardware.

  • Wi‑Fi b/g/n (2,4 GHz) + Bluetooth LE 5.1 a través del módulo Murata 1DX (CYW4343W) — se conecta a la antena suministrada mediante un conector U.FL integrado en la placa.

  • USB‑C de alta velocidad (480 Mb/s).

  • 22 pines de E/S de usuario en los conectores superiores de tipo Arduino MKR — D0–D14 (digitales) más A0–A6 (analógicos).

  • Dos conectores de alta densidad de 80 pines en la parte inferior exponen toda la estructura del STM32H747 — DCMI, DSI, Ethernet RMII, FDCAN, SDIO, SAI/I²S, UARTs, SPI/I²C/temporizadores adicionales, etc. Shields como el Vision Shield se acoplan a estos conectores.

  • JTAG / SWD disponibles en los conectores de alta densidad inferiores para depuración avanzada.

  • Soporte para batería — conector JST para Li‑Po de 3,7 V más cargador y monitor de batería integrados.

Distribución de pines

Distribución de pines de la Arduino Portenta H7

Referencia de pines

Se exponen 22 pines de usuario en los conectores superiores de tipo Arduino MKR — 15 digitales (D0-D14) más 7 analógicos (A0-A6). Hay muchos más pines del SoC disponibles a través de los conectores de alta densidad de 80 pines inferiores para trabajar con shields; consulta el PDF de distribución de pines completa de Arduino para ese mapeo.

Nombre de pin

Referencia

Función

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 (compartido con A3 / A5)

D9

3.3 V

SPI2 SCK

D10

3.3 V

SPI2 MISO (compartido 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 analógico)

A1

3.3 V

ADC12 IN1 (solo analógico)

A2

3.3 V

ADC123 IN12 (solo analógico; compartido con D10)

A3

3.3 V

ADC12 IN13 (solo analógico; compartido con D8)

A4

3.3 V

ADC123 IN12 (compartido con D10)

A5

3.3 V

ADC12 IN13 (compartido con D8)

A6

3.3 V

DAC1 OUT1 / ADC12 IN18

A7

3.3 V

TIM3 CH1 / ADC12 IN3 (no expuesto en los conectores)

D20

3.3 V

alias de D8 / A3 / A5

D21

3.3 V

alias de A6 — DAC1 OUT1

RESET

3.3 V

pulsa el interruptor integrado en la placa o conéctalo a GND para reiniciar

LED_RED

3.3 V

canal rojo del LED RGB (activo en bajo)

LED_GREEN

3.3 V

canal verde del LED RGB (activo en bajo)

LED_BLUE

3.3 V

canal azul del LED RGB (activo en bajo)

Nota

A0-A3 son pads solo analógicos en el STM32H747 sin función GPIO — trátalos únicamente como entradas ADC. A2/A4 y A3/A5 comparten sus pines físicos con D10 y D8 respectivamente, así que no puedes generar PWM ni usar SPI en ellos mientras los lees como analógicos. A7 se encuentra en los conectores de alta densidad inferiores.

Pines de alimentación

Pines del conector MKR:

  • VIN — riel principal del sistema hacia el PMIC integrado. Alimentado a través de un diodo desde el riel +5V, el pin VIN del MKR o los conectores de alta densidad de 80 pines inferiores.

  • +5V — riel de 5 V alimentado desde USB, el conector ESLOV o el propio pin +5V del MKR.

  • +3V3 — riel principal de 3,3 V (salida del regulador conmutado del PMIC).

  • AREF — referencia de tensión analógica para los pines ADC. Por defecto es 3,3 V; aplícale una tensión externa para usar una referencia diferente.

  • GND — tierra común.

Entrada de batería:

  • JST Li‑Po en la parte frontal de la placa acepta una celda Li‑Po de 3,7 V. El PMIC la carga siempre que +5V o VIN esté presente.

La Portenta H7 puede alimentarse a través de cualquiera de estas vías:

  • USB‑C — suministra 5 V al PMIC integrado.

  • Conector ESLOV — hasta 5 V en VESLOV (consulta Conector ESLOV).

  • Pin VIN — aplica directamente una fuente regulada de 5 V.

  • Batería Li‑Po — conéctala al JST de la parte frontal.

Conector ESLOV

En el lateral de la placa hay un conector ESLOV de 5 pines sin soldadura:

Pin

Nombre

Función

1

VESLOV

salida de alimentación de 5 V (mismo riel que el +5V del conector MKR)

2

INT

entrada de interrupción externa en D7

3

SCL_EXT

compartido con el pad D12 del conector MKR — mismo bus I²C 3 que el conector de usuario

4

SDA_EXT

compartido con el pad D11 del conector MKR — mismo bus I²C 3 que el conector de usuario

5

GND

tierra común

Los SCL_EXT/SDA_EXT de ESLOV y los D12/D11 del conector MKR son los mismos pines — un único bus I²C 3 expuesto en dos conectores.

Truco

Usa el estimador de duración de batería para modelar cuánto tiempo funcionará la Portenta H7 con una batería para un ciclo de trabajo activo / de sueño profundo determinado.

Pines de recuperación y depuración

  • RESET — es tanto un pin expuesto en el conector superior como un interruptor momentáneo en el lateral de la placa, conectado a la línea NRST del SoC. Conéctalo a GND o pulsa el botón para reiniciar.

La Portenta H7 usa el reinicio por doble pulsación estándar de Arduino para entrar en el gestor de arranque (bootloader) de Arduino. Pulsa rápidamente el botón de reinicio dos veces — la placa se vuelve a enumerar por USB como un dispositivo DFU y OpenMV IDE puede grabar una nueva imagen de firmware.

Las señales SWD del STM32 están expuestas en el conector de alta densidad J1 inferior:

  • J1‑73 — NRST

  • J1‑75 — SWDIO (PA13)

  • J1‑77 — SWCLK (PA14)

  • J1‑79 — SWO (PB3)

Conéctalas mediante una Portenta Breakout, el adaptador de depuración oficial de Arduino o una placa portadora personalizada con un conector de 1,27 mm. Todas las señales de depuración están referenciadas a 3,3 V.

Nota

Cuando el Portenta Vision Shield está acoplado, las mismas señales SWD/JTAG se enrutan hacia el conector JTAG de depuración ARM Cortex de 20 pines estándar del shield (paso de 1,27 mm / 0,05″).

Periféricos integrados

LEDs

La Portenta H7 tiene un único LED RGB de usuario, controlable por software a través de machine.LED:

from machine import LED

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

Un LED naranja de carga independiente, junto al JST de la batería, se ilumina cuando el cargador integrado está suministrando corriente a una Li‑Po conectada; no es controlable por el usuario.

Sensor de cámara (Vision Shield)

Con el Portenta Vision Shield (edición Ethernet o LoRa) acoplado, el sensor Himax se controla a través del módulo csi — sensores de cámara:

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

Se admiten dos revisiones del Vision Shield:

  • HM01B0 — 320 × 320 monocromo.

  • HM0360 — 640 × 480 monocromo.

Advertencia

Mientras la cámara del Vision Shield está inicializada, el firmware reclama los siguientes pines del conector MKR y no pueden usarse:

Pin MKR

Motivo

D1

TIM1 CH1 — reloj maestro de la cámara

D6

TIM1 CH1 (alt) — reloj maestro de la cámara

D11

I²C 3 SDA — compartido con la cámara; el bus es utilizable pero evita la dirección I²C del sensor (0x24)

D12

I²C 3 SCL — compartido con la cámara; el bus es utilizable pero evita la dirección I²C del sensor (0x24)

A6 / D21

DCMI HSYNC — también deshabilita el DAC

A7

DCMI PXCLK

Aprendizaje automático

ml — Aprendizaje automático ejecuta modelos TFLite cuantizados en el Cortex‑M7 con kernels CMSIS‑NN — lo bastante rápido para detectores compactos a unos pocos fotogramas por segundo. Los modelos del sistema de archivos de solo lectura /rom se cargan directamente desde la memoria flash sin copiarlos a la RAM. Aquí tienes un detector BlazeFace de 128×128 que superpone el rostro detectado y sus seis puntos de referencia en cada fotograma de la cámara 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")

Núcleo M4

El núcleo Cortex‑M4 se expone a través de openamp para la comunicación entre procesadores. El firmware de OpenMV se ejecuta únicamente en el M7; el M4 no tiene un entorno de ejecución MicroPython propio, así que usarlo implica compilar una imagen de firmware en C separada y cargarla desde el sistema de archivos mediante openamp.RemoteProc. Hay un firmware de ejemplo precompilado que implementa un endpoint UART virtual disponible en el repositorio openamp_vuart — sigue su README para compilar 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)

En la práctica, conviene tratar este soporte como una demostración de la interfaz openamp más que como una plataforma de doble núcleo funcional — el M4 no puede reiniciarse independientemente del M7, así que detener el M4 fuerza un reinicio completo del sistema.

Micrófono (Vision Shield)

El Vision Shield lleva micrófonos PDM duales capturados a través de audio — Módulo de audio mediante el periférico SAI4 del STM32. Cada búfer llega como PCM de 16 bits con signo en un bytearray, listo para alimentar a ulab/numpy para DSP — por ejemplo, un sencillo detector de volumen:

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

Pasa channels=2 a audio.init para recibir muestras intercaladas de ambos micrófonos.

Medidor de carga de batería

El medidor de carga ModelGauge m5 MAX17262 de Maxim realiza un seguimiento de la tensión, corriente, temperatura y estado de carga de la batería Li‑Po. Se ubica en el I²C 1 en la dirección 0x36.

El MAX17262 tiene detección de corriente interna, así que el registro de corriente se lee directamente en microamperios sin un factor Rsense externo que aplicar. Leer el medidor de carga es inofensivo — no se incluye ningún controlador, pero los registros documentados en la hoja de datos del MAX17262 pueden leerse directamente:

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 es complemento a dos con signo: positivo durante la carga, negativo durante la descarga. TTE solo tiene sentido cuando la corriente es negativa; TTF solo cuando la corriente es positiva.

Circuito integrado de gestión de energía

El PMIC PF1550 de NXP gestiona todos los reguladores de la Portenta H7 — el riel principal +3V3, el riel +1V8 del núcleo / E/S del SoC, y el cargador de la Li‑Po. Se ubica en el I²C 1 en la dirección 0x08.

Advertencia

Leer los registros del PMIC es seguro; escribir en ellos es peligroso. Configurar incorrectamente un regulador reductor o un ajuste del cargador puede dañar de forma permanente la placa, la batería, o ambas. Trata el PMIC como de solo lectura a menos que sepas exactamente lo que haces.

Lo más útil que el PMIC te indica y que el medidor de carga no puede es la máquina de estados del cargador — si la placa está funcionando actualmente con USB / ESLOV / VIN, en qué etapa del ciclo de carga se encuentra la Li‑Po, y si el cargador está en un fallo térmico o de watchdog. Los registros del cargador se ubican en un desplazamiento de 0x80 dentro del espacio de direcciones I²C principal del PF1550 (consulta la §22.2 de la hoja de datos del PF1550), así que, por ejemplo, CHG_INT_OK en la dirección de cargador 0x04 se lee del registro 0x84 del PMIC:

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)

Otros registros de solo lectura que vale la pena consultar en la hoja de datos (todos con desplazamiento de cargador 0x80): 0x80 CHG_INT (interrupciones del cargador retenidas — banderas de fallo), 0x86 VBUS_SNS (el estado VBUS multibit incluyendo OVLO / UVLO / DPM), y 0x88 BATT_SNS (presencia de la batería y estado de sobrecorriente).

Wi‑Fi

El Murata 1DX (CYW4343W) integrado se expone mediante network — configuración de red como una interfaz de estación. Conecta la antena suministrada al conector U.FL integrado en la placa antes de activar 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

El mismo Murata 1DX también expone Bluetooth LE 5.1. Usa aioble — BLE asíncrono para BLE compatible con asyncio — por ejemplo, anúnciate como periférico y espera a que un central se conecte:

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)

La edición LoRa del Vision Shield añade un módulo LoRaWAN Murata CMWX1ZZABZ conectado a la Portenta H7 por UART. El módulo lora envuelve el firmware de comandos AT y admite unión OTAA o ABP, enlace ascendente y enlace descendente:

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, etc. para regiones fuera de la UE, y cambia a lora.Lora.join_ABP() si tu servidor de red usa la activación ABP.

Advertencia

Mientras el módulo LoRa está en uso, el controlador reclama los siguientes pines del conector MKR como líneas de control para el Murata CMWX1ZZABZ — no pueden usarse:

Pin MKR

Motivo

D3

pin BOOT del módulo LoRa

D5

pin RST del módulo LoRa

Ethernet (Vision Shield)

La edición Ethernet del Vision Shield añade un conector RJ45 con magnéticos conectado al MAC Ethernet 10/100 del STM32H747 por RMII. Conecta un cable Ethernet y el PHY aparece como una interfaz LAN; DHCP se ejecuta automáticamente una vez que el enlace se activa:

import network
import time

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

Tarjeta microSD (Vision Shield)

Cuando se inserta una tarjeta, se monta automáticamente en /sdcard y es utilizable a través del sistema de archivos habitual:

import os

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

Referencia de buses

GPIO

Usa machine.Pin para leer o controlar cualquiera de los pines serigrafiados. Las salidas son CMOS de 3,3 V y pueden absorber/suministrar hasta 20 mA por pin (140 mA en total a través de todo el conector).

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

Cualquier pin de entrada también puede disparar una interrupción en las transiciones de borde:

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

Los pads D11/D12 del conector MKR y los pines SDA_EXT/SCL_EXT del conector ESLOV están en el mismo bus I²C 3 — consulta Conector ESLOV más arriba para la distribución de pines de ESLOV.

El mismo hardware también puede usarse en modo de destino (esclavo) a través de machine.I2CTarget para exponer una región de memoria a otro controlador 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

La Portenta H7 expone ocho canales ADC de 12 bits en A0–A7. Todos están referenciados a 3,3 Vread_u16 devuelve 0–65535 en el rango de 0–3,3 V en el 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

Se expone un único canal DAC de 12 bits en DAC1 (A6 / D21) a través de pyb.DAC:

from pyb import DAC

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

PWM

Pin

Temporizador / canal

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

Controla cualquiera de ellos mediante machine.PWM:

from machine import Pin, PWM

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

Nota

Varios pines comparten canales de temporizador:

  • TIM1 CH1 está en D1 y D6.

  • TIM1 CH2 está en D2 y D14.

  • TIM8 CH3N está en D0 y D1.

Elige un único consumidor por canal de temporizador.

Advertencia

TIM1 está reservado para el reloj maestro de la cámara cuando el Vision Shield se inicializa a través de csi — sensores de cámaraD1, D2, D6, D13 y D14 no pueden controlarse por PWM mientras la cámara está activa.

Buses bit‑banged por software

machine.SoftI2C y machine.SoftSPI funcionan en cualquier GPIO si necesitas un bus adicional.

Sensor térmico (externo)

El firmware incluye el controlador fir — controlador de sensor térmico (fir == infrarrojo lejano) para cámaras térmicas cableadas externamente:

  • MLX90621 — matriz IR de 16 × 4

  • MLX90640 — matriz IR de 32 × 24

  • MLX90641 — matriz IR de 16 × 12

  • AMG8833 — matriz IR de 8 × 8

Conecta el módulo al bus I²C de la placa y lee fotogramas 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())

El controlador fir solo se comunica con el sensor por I²C 3 — conecta el módulo a D12 (SCL) y D11 (SDA).

Temporización

time

El módulo time cubre retardos bloqueantes, tics monótonos y la medición de tiempo transcurrido:

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)

Temporizadores virtuales

machine.Timer programa funciones de retorno (callbacks) periódicas o de un solo disparo sin consumir una ranura de temporizador por hardware. Pasa -1 como id para usar un temporizador virtual (por 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"))

Los valores de periodo están en milisegundos. Llama a deinit() para detener y liberar la ranura.

Reloj de tiempo real

machine.RTC mantiene la hora del reloj de pared entre reinicios. El conector de alta densidad también expone un pad COINCELL que puede respaldar el RTC con una CR2032 ante una pérdida de alimentación:

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 reinicia la placa si la aplicación se cuelga. Una vez iniciado, no puede detenerse ni reconfigurarse — aliméntalo periódicamente dentro de tu bucle principal:

from machine import WDT

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

Información de arranque y de tiempo de ejecución

Actualización de firmware (DFU)

La Portenta H7 usa el reinicio por doble pulsación estándar de Arduino para entrar en el gestor de arranque (bootloader) de Arduino. Pulsa rápidamente el botón de reinicio dos veces — la placa se vuelve a enumerar por USB como un dispositivo DFU y OpenMV IDE puede grabar una nueva imagen de firmware.

Un script en ejecución puede volver a entrar en el gestor de arranque (bootloader) bajo demanda llamando a machine.bootloader():

import machine

machine.bootloader()

Sistema de archivos y orden de arranque

El firmware de la Portenta H7 monta hasta tres sistemas de archivos en el arranque:

  • Memoria flash interna — siempre montada en /flash. Contiene main.py y README.txt por defecto; se crea en el primer arranque.

  • Tarjeta microSD — si hay un Vision Shield acoplado y una tarjeta insertada, se monta en /sdcard.

  • ROMFS — sistema de archivos de solo lectura, mapeado en memoria, en /rom, montado automáticamente por MicroPython al inicio.

Tras el montaje, el directorio de trabajo se establece en /sdcard cuando la tarjeta está presente, de lo contrario en /flash. El intérprete ejecuta entonces los scripts desde ese directorio:

  • boot.py se ejecuta en cada reinicio suave (arranque en frío, Ctrl‑D desde el REPL, o cada vez que el script en ejecución retorna).

  • main.py se ejecuta solo en el arranque en frío, inmediatamente después de boot.py. Los reinicios suaves posteriores vuelven a ejecutar boot.py pero pasan directamente al REPL — para volver a ejecutar main.py tienes que reiniciar la placa por completo.

Colocar un boot.py o main.py en la tarjeta SD anula la copia de la memoria flash sin tocarla — ambos archivos se buscan en el directorio de arranque (/sdcard cuando la tarjeta está montada, de lo contrario /flash).

El main.py por defecto que viene en una placa recién grabada simplemente parpadea el canal azul del LED RGB de usuario como latido (dos pulsos cortos, breve pausa), para que puedas saber que el firmware arrancó correctamente sin ningún host conectado.

sys.path se extiende para incluir los tres sistemas de archivos y sus subdirectorios lib/, de modo que los módulos importables pueden residir en /flash/lib, /sdcard/lib o /rom/lib.

Para forzar al sistema a ignorar una tarjeta SD insertada (por ejemplo, para ejecutar el main.py de la flash incluso con una tarjeta presente), crea un archivo vacío llamado SKIPSD en la raíz de /flash.

Cuando está conectada por USB, el sistema de archivos de arranque (/sdcard si hay una tarjeta presente, de lo contrario /flash) también se enumera como una unidad de almacenamiento masivo USB en el host, lo que te permite editar boot.py, main.py y cualquier otro archivo directamente. Expulsa la unidad antes de reiniciar la placa para que el host vuelque sus escrituras en caché.

Nota

Como el sistema operativo trata la unidad como un dispositivo de bloques pasivo, los archivos creados o modificados por el código que se ejecuta en la cámara no aparecerán hasta que el host vuelva a montar la unidad. Si tanto el sistema operativo como la cámara escriben en el mismo sistema de archivos al mismo tiempo, el sistema operativo ganará y sobrescribirá los cambios hechos por la cámara. Usa la tarjeta SD para cualquier dato que el script escriba de vuelta, y vuelve a montar antes de leer esos archivos desde el host.

Nota

El canal rojo del LED RGB de usuario puede iluminarse brevemente mientras el host lee de la unidad de almacenamiento masivo USB o escribe en ella — esto es un indicador de actividad gestionado por el firmware, no un fallo.

Tamaños de almacenamiento

La Portenta H7 viene con:

  • /flash — sistema de archivos FAT de 11 MB, lectura/escritura.

  • /rom — ROMFS de solo lectura mapeado en memoria de 4 MB, usado para distribuir scripts y modelos de ML que se benefician del acceso mmap sin copia.

  • /sdcard — tamaño completo de la tarjeta microSD que esté insertada en un Vision Shield (cuando esté presente), lectura/escritura.

Indicador de fallo grave (hard fault)

Si el LED RGB de usuario está recorriendo rápidamente todos los colores — lo bastante rápido como para que tienda a parecer un LED blanco titilante en lugar de tonos distintos — el firmware ha sufrido un fallo grave irrecuperable. Vuelve a grabar el firmware para recuperarlo; si volver a grabarlo no ayuda, la placa puede estar dañada físicamente.

Bibliotecas de software

Consulta el índice de bibliotecas para ver la lista completa de módulos — incluyendo cuáles son exclusivos de la compilación para la Portenta H7.