Arduino Portenta H7

Arduino Portenta H7 是一款 66 × 25 mm 的工业级开发板,围绕 STMicroelectronics STM32H747XI 构建——这是一款双核 SoC,将一个 400 MHz 的 Cortex‑M7 与一个 200 MHz 的 Cortex‑M4 结合在一起。OpenMV 固件完全运行在 M7 核心上,设计用于配合 Portenta Vision Shield(以太网版或 LoRa 版)使用,该扩展板在基础 Portenta H7 上增加了 Himax HM01B0 / HM0360 摄像头、双 PDM 麦克风以及一个 microSD 卡槽。

Arduino Portenta H7

完整的数据手册、照片和尺寸信息请参阅 Arduino Portenta H7 产品页

亮点

  • STMicroelectronics STM32H747XI 双核 Cortex‑M7(400 MHz)+ Cortex‑M4(200 MHz)。OpenMV 固件仅运行在 M7 核心上;M4 核心通过 openamp 暴露出来用于核间通信。

  • 8 MB 外部 SDRAM 外加 2 MB 内部闪存16 MB 外部 QSPI 闪存

  • 硬件 JPEG 编码器/解码器

  • 通过 Murata 1DX(CYW4343W)模块提供 Wi‑Fi b/g/n(2.4 GHz)+ Bluetooth LE 5.1——通过板载 U.FL 连接器 连接到随附的天线。

  • 高速 USB‑C(480 Mb/s)。

  • Arduino MKR 风格顶部排针上的 22 个用户 I/O 引脚——D0–D14(数字)外加 A0–A6(模拟)。

  • 底部的 两个 80 针高密度连接器 暴露了完整的 STM32H747 引脚资源——DCMI、DSI、以太网 RMII、FDCAN、SDIO、SAI/I²S、UART、额外的 SPI/I²C/定时器等等。像 Vision Shield 这样的扩展板就插接到这些连接器上。

  • 底部 HD 连接器上引出了 JTAG / SWD,用于高级调试。

  • 电池支持——3.7 V 锂聚合物(Li‑Po)JST 连接器外加板载充电器和电池监控器。

引脚排布

Arduino Portenta H7 引脚排布

引脚参考

Arduino MKR 风格顶部边缘排针上暴露了 22 个用户引脚——15 个数字引脚(D0-D14)外加 7 个模拟引脚(A0-A6)。还有更多 SoC 引脚可通过底部 80 针高密度连接器 用于扩展板开发;该映射关系请参阅 Arduino 的 完整引脚排布 PDF

引脚名称

参考

功能

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(与 A3 / A5 共用)

D9

3.3 V

SPI2 SCK

D10

3.3 V

SPI2 MISO(与 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(仅模拟)

A1

3.3 V

ADC12 IN1(仅模拟)

A2

3.3 V

ADC123 IN12(仅模拟;与 D10 共用)

A3

3.3 V

ADC12 IN13(仅模拟;与 D8 共用)

A4

3.3 V

ADC123 IN12(与 D10 共用)

A5

3.3 V

ADC12 IN13(与 D8 共用)

A6

3.3 V

DAC1 OUT1 / ADC12 IN18

A7

3.3 V

TIM3 CH1 / ADC12 IN3(未引出到排针上)

D20

3.3 V

D8 / A3 / A5 的别名

D21

3.3 V

A6 的别名——DAC1 OUT1

RESET

3.3 V

按下板载开关或拉到 GND 即可复位

LED_RED

3.3 V

RGB LED 红色通道(低电平有效)

LED_GREEN

3.3 V

RGB LED 绿色通道(低电平有效)

LED_BLUE

3.3 V

RGB LED 蓝色通道(低电平有效)

备注

A0-A3 是 STM32H747 上的 纯模拟 焊盘,没有 GPIO 功能——只能将它们当作 ADC 输入使用。A2/A4A3/A5 分别与 D10D8 共用物理引脚,因此在将它们读取为模拟量的同时无法在这些引脚上驱动 PWM 或 SPI。A7 位于底部 HD 连接器上。

电源引脚

MKR 排针引脚:

  • VIN——进入板载 PMIC 的主系统电源轨。经过一个二极管由 +5V 电源轨、MKR 的 VIN 引脚或底部 80 针 HD 连接器供电。

  • +5V——5 V 电源轨,由 USB、ESLOV 连接器或 MKR 的 +5V 引脚本身供电。

  • +3V3——主 3.3 V 电源轨(PMIC 开关稳压器输出)。

  • AREF——ADC 引脚的模拟电压基准。默认为 3.3 V;可从外部驱动以使用不同的基准。

  • GND——公共地。

电池输入:

  • 板子前端的 Li‑Po JST 接受 3.7 V 锂聚合物电芯。只要存在 +5VVIN,PMIC 就会对其充电。

Portenta H7 可以通过以下任一路径供电:

  • USB‑C——向板载 PMIC 提供 5 V。

  • ESLOV 连接器——VESLOV 上最高 5 V(参见 ESLOV 连接器)。

  • VIN 引脚——直接驱动一个稳压的 5 V 电源。

  • Li‑Po 电池——连接到前端的 JST。

ESLOV 连接器

板子侧面有一个 5 针免焊 ESLOV 连接器:

引脚

名称

功能

1

VESLOV

5 V 电源输出(与 MKR 排针的 +5V 同一电源轨)

2

INT

D7 上的外部中断输入

3

SCL_EXT

与 MKR 排针的 D12 焊盘共用——与用户排针为同一条 I²C 3 总线

4

SDA_EXT

与 MKR 排针的 D11 焊盘共用——与用户排针为同一条 I²C 3 总线

5

GND

公共地

ESLOV 的 SCL_EXT/SDA_EXT 与 MKR 排针的 D12/D11 是同一组引脚——同一条 I²C 3 总线暴露在两个连接器上。

小技巧

使用 电池续航估算器 来推算 Portenta H7 在给定的活动 / 深度睡眠占空比下能用电池运行多久。

恢复与调试引脚

  • RESET——既是顶部排针上引出的一个引脚,也是板子侧面的一个瞬时开关,连接到 SoC 的 NRST 线。拉到 GND 或按下按钮即可复位。

Portenta H7 使用 Arduino 标准的 双击复位 来进入 Arduino 的引导加载程序。快速按两下复位按钮——板子会通过 USB 重新枚举为 DFU 设备,OpenMV IDE 即可烧录新的固件镜像。

STM32 的 SWD 信号引出在底部 HD 连接器 J1 上:

  • J1‑73——NRST

  • J1‑75——SWDIO(PA13)

  • J1‑77——SWCLK(PA14)

  • J1‑79——SWO(PB3)

通过 Portenta Breakout、官方 Arduino 调试适配器或带 1.27 mm 排针的自定义载板将它们连接起来。所有调试信号均为 3.3 V 基准

备注

当连接了 Portenta Vision Shield 时,相同的 SWD/JTAG 信号会被引到该扩展板上标准的 20 针 ARM Cortex Debug JTAG 排针(1.27 mm / 0.05″ 间距)。

板载外设

LED

Portenta H7 有一个用户 RGB LED,可通过 machine.LED 用软件控制:

from machine import LED

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

电池 JST 旁边有一个独立的橙色 充电 LED,当板载充电器正在向所连接的 Li‑Po 供电充电时会亮起;它不可由用户控制。

摄像头传感器(Vision Shield)

连接了 Portenta Vision Shield(以太网版或 LoRa 版)后,Himax 传感器通过 csi --- 摄像头传感器 模块驱动:

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

支持两个 Vision Shield 修订版本:

  • HM01B0——320 × 320 单色。

  • HM0360——640 × 480 单色。

警告

当 Vision Shield 摄像头被初始化后,以下 MKR 排针引脚会被固件占用,因而 无法使用

MKR 引脚

原因

D1

TIM1 CH1——摄像头主时钟

D6

TIM1 CH1(备用)——摄像头主时钟

D11

I²C 3 SDA——与摄像头共用;总线可用,但应避开传感器的 I²C 地址(0x24

D12

I²C 3 SCL——与摄像头共用;总线可用,但应避开传感器的 I²C 地址(0x24

A6 / D21

DCMI HSYNC——同时会禁用 DAC

A7

DCMI PXCLK

机器学习

ml --- 机器学习 使用 CMSIS‑NN 内核在 Cortex‑M7 上运行量化的 TFLite 模型——足够快,能以每秒几帧的速度运行紧凑型检测器。位于只读 /rom 文件系统上的模型可直接从闪存加载,无需复制到 RAM。下面是一个 128×128 的 BlazeFace 检测器,它在来自 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")

M4 核心

Cortex‑M4 核心通过 openamp 暴露出来用于核间通信。OpenMV 固件仅运行在 M7 上;M4 自身没有 MicroPython 运行时,因此使用它意味着要构建一个独立的 C 固件镜像,并通过 openamp.RemoteProc 从文件系统加载它。实现了一个虚拟 UART 端点的预构建示例固件可在 openamp_vuart 仓库中获取——按照其 README 构建 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)

实际上,最好将这项支持视为对 openamp 接口的演示,而非一个可用的双核平台——M4 无法独立于 M7 复位,因此停止 M4 会强制整个系统重启。

麦克风(Vision Shield)

Vision Shield 搭载 双 PDM 麦克风,通过 STM32 的 SAI4 外设由 audio --- 音频模块 采集。每个缓冲区以有符号 16 位 PCM bytearray 的形式到达,可直接送入 ulab/numpy 进行 DSP——例如一个简单的响度检测器:

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

audio.init 传入 channels=2 即可从两个麦克风接收交织采样。

电池电量计

Maxim MAX17262 ModelGauge m5 电量计跟踪 Li‑Po 电池的电压、电流、温度和荷电状态。它位于 I²C 1 总线上,地址为 0x36

MAX17262 具有 内部 电流检测,因此电流寄存器直接读出微安值,无需应用外部 Rsense 系数。读取电量计是无害的——虽然没有随附驱动,但 MAX17262 数据手册 中所记载的寄存器可以直接读取:

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 是有符号的二进制补码:充电时为正,放电时为负。TTE 仅在电流为负时有意义;TTF 仅在电流为正时有意义。

电源管理 IC

NXP PF1550 PMIC 管理 Portenta H7 上的每一个稳压器——+3V3 主电源轨、+1V8 SoC 核心 / I/O 电源轨以及 Li‑Po 充电器。它位于 I²C 1 总线上,地址为 0x08

警告

读取 PMIC 寄存器没问题;向它们写入则很危险。 错误配置降压稳压器或充电器设置可能会永久损坏板子、电池或两者。除非你完全清楚自己在做什么,否则请将 PMIC 视为只读。

PMIC 能告诉你而电量计无法提供的最有用信息是 充电器状态机——板子当前是靠 USB / ESLOV / VIN 运行、Li‑Po 处于充电周期的哪个阶段,以及充电器是否处于热故障或看门狗故障。充电器寄存器位于 PF1550 主 I²C 地址空间中 0x80 的偏移处(参见 PF1550 数据手册 的 §22.2),因此例如位于充电器地址 0x04CHG_INT_OK 是从 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)

数据手册中其他值得一看的只读寄存器(均位于充电器偏移 0x80 处):0x80 CHG_INT(锁存的充电器中断——故障标志)、0x86 VBUS_SNS(包括 OVLO / UVLO / DPM 在内的多位 VBUS 状态),以及 0x88 BATT_SNS(电池存在与否及过流状态)。

Wi‑Fi

板载的 Murata 1DX(CYW4343W)通过 network --- 网络配置 作为站点(station)接口暴露出来。在启动射频之前,请将随附的天线连接到板载 U.FL 连接器:

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

蓝牙

同一个 Murata 1DX 也暴露了 Bluetooth LE 5.1。使用 aioble --- 异步 BLE 实现对 asyncio 友好的 BLE——例如,作为外设进行广播并等待中心设备连接:

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)

Vision Shield 的 LoRa 版 增加了一个通过 UART 连接到 Portenta H7 的 Murata CMWX1ZZABZ LoRaWAN 模块。lora 模块封装了 AT 命令固件,支持 OTAA 或 ABP 入网、上行链路和下行链路:

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

对于非欧盟地区,请使用 BAND_US915 / BAND_AS923 / BAND_AU915 等;如果你的网络服务器使用 ABP 激活,则切换为 lora.Lora.join_ABP()

警告

在使用 LoRa 模块期间,驱动会占用以下 MKR 排针引脚作为 Murata CMWX1ZZABZ 的控制线——它们 无法使用

MKR 引脚

原因

D3

LoRa 模块 BOOT 引脚

D5

LoRa 模块 RST 引脚

以太网(Vision Shield)

Vision Shield 的 以太网版 增加了一个带变压器的 RJ45 插座,通过 RMII 连接到 STM32H747 的 10/100 以太网 MAC。插入以太网线后,PHY 会显示为一个 LAN 接口;链路一旦建立,DHCP 就会自动运行:

import network
import time

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

microSD 卡(Vision Shield)

插入卡后,它会自动挂载到 /sdcard,并可通过常规文件系统使用:

import os

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

总线参考

GPIO

使用 machine.Pin 来读取或驱动任意丝印标注的引脚。输出为 3.3 V CMOS,每个引脚可灌入/拉出最高 20 mA(整个排针总共 140 mA)。

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

任何输入引脚也都可以在边沿跳变时触发中断:

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

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

UART

总线

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

总线

SCL

SDA

I2C3

D12

D11

from machine import I2C

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

MKR 排针上的 D11/D12 焊盘和 ESLOV 连接器的 SDA_EXT/SCL_EXT 引脚落在同一条 I²C 3 总线上——ESLOV 引脚排布请参见上文 ESLOV 连接器

同一硬件也可以通过 machine.I2CTarget 以目标(从机)模式使用,向另一个 I²C 控制器暴露一块内存区域:

from machine import I2CTarget

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

SPI

总线

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

Portenta H7 在 A0–A7 上暴露了八个 12 位 ADC 通道。所有通道均为 3.3 V 基准——read_u16 在引脚电压 0–3.3 V 范围内返回 0–65535:

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

通过 pyb.DACDAC1A6 / D21)上暴露了一个 12 位 DAC 通道:

from pyb import DAC

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

PWM

引脚

定时器 / 通道

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

通过 machine.PWM 驱动其中任意一个:

from machine import Pin, PWM

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

备注

有几个引脚共用定时器通道:

  • TIM1 CH1D1 D6 上。

  • TIM1 CH2D2 D14 上。

  • TIM8 CH3ND0 D1 上。

每个定时器通道只选一个使用者。

警告

当 Vision Shield 通过 csi --- 摄像头传感器 初始化时,TIM1 会被保留用于 摄像头主时钟——摄像头处于活动状态时,D1D2D6D13D14 无法用于 PWM 驱动。

软件位操作(bit‑banged)总线

如果你需要额外的总线,machine.SoftI2Cmachine.SoftSPI 可以工作在任意 GPIO 上。

热成像传感器(板外)

固件包含 fir --- 热成像传感器驱动(fir == 远红外) 驱动,用于外接的热成像仪:

  • MLX90621——16 × 4 红外阵列

  • MLX90640——32 × 24 红外阵列

  • MLX90641——16 × 12 红外阵列

  • AMG8833——8 × 8 红外阵列

将模块连接到板子的 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 3 与传感器通信——请将模块连接到 D12(SCL)和 D11(SDA)。

时间

time

time 模块涵盖阻塞延时、单调计时(monotonic ticks)以及已用时间测量:

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 可在不占用硬件定时器槽位的情况下调度周期性或单次回调。将 -1 作为 id 传入即可使用虚拟(软件)定时器:

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 可在多次复位间保持挂钟时间。HD 连接器还暴露了一个 COINCELL 焊盘,可在断电期间用一颗 CR2032 为 RTC 供电:

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

看门狗

如果应用程序卡死,machine.WDT 会复位板子。它一旦启动就无法停止或重新配置——请在主循环中定期喂狗:

from machine import WDT

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

启动与运行时信息

固件更新(DFU)

Portenta H7 使用 Arduino 标准的 双击复位 来进入 Arduino 的引导加载程序。快速按两下复位按钮——板子会通过 USB 重新枚举为 DFU 设备,OpenMV IDE 即可烧录新的固件镜像。

正在运行的脚本可以通过调用 machine.bootloader() 按需重新进入引导加载程序:

import machine

machine.bootloader()

文件系统与启动顺序

Portenta H7 固件在启动时最多挂载三个文件系统:

  • 内部闪存——始终挂载在 /flash。默认存放 main.pyREADME.txt;在首次启动时创建。

  • microSD 卡——如果连接了 Vision Shield 且插入了卡,它会挂载在 /sdcard

  • ROMFS——位于 /rom 的只读、内存映射文件系统,由 MicroPython 在启动时自动挂载。

挂载完成后,当卡存在时工作目录会被设置为 /sdcard,否则为 /flash。然后解释器从该目录运行脚本:

  • boot.py每次 软复位时执行(冷启动、从 REPL 按 Ctrl‑D,或每当正在运行的脚本返回时)。

  • main.py 仅在冷启动时 执行,紧接在 boot.py 之后。后续的软复位会重新运行 boot.py 但随即直接进入 REPL——要重新运行 main.py,你必须完全复位板子。

boot.pymain.py 放到 SD 卡上会覆盖闪存中的副本而不触碰它——这两个文件都会在启动目录中查找(卡挂载时为 /sdcard,否则为 /flash)。

新烧录的板子上随附的默认 main.py 只是将用户 RGB LED 的 蓝色 通道作为心跳闪烁(两次短脉冲,短间隔),这样你无需连接任何主机就能判断固件是否干净地启动了。

sys.path 被扩展为包含全部三个文件系统及其 lib/ 子目录,因此可导入的模块可以放在 /flash/lib/sdcard/lib/rom/lib 中。

要强制系统忽略已插入的 SD 卡(例如即使在有卡的情况下也运行闪存中的 main.py),请在 /flash 根目录下创建一个名为 SKIPSD 的空文件。

通过 USB 连接时,启动文件系统(有卡则为 /sdcard,否则为 /flash)也会在主机上枚举为一个 USB 大容量存储驱动器,让你可以直接编辑 boot.pymain.py 以及任何其他文件。在复位板子之前请先弹出该驱动器,以便主机刷新其缓存的写入。

备注

由于操作系统将该驱动器视为被动块设备,在摄像头上运行的代码所创建或修改的文件,要等到主机重新挂载该驱动器后才会显示出来。如果操作系统和摄像头同时写入同一个文件系统,操作系统会胜出并覆盖摄像头所做的更改。脚本需要写回的任何数据都请使用 SD 卡,并在从主机读取这些文件前重新挂载。

备注

当主机正在从 USB 大容量存储驱动器读取或向其写入时,用户 RGB LED 的 红色 通道可能会短暂亮起——这是固件驱动的活动指示,而非故障。

存储容量

Portenta H7 出厂配置如下:

  • /flash——11 MB FAT 文件系统,可读写。

  • /rom——4 MB 只读内存映射 ROMFS,用于随附那些受益于零拷贝 mmap 访问的脚本和 ML 模型。

  • /sdcard——Vision Shield 中所插入 microSD 卡的全部容量(存在时),可读写。

硬故障指示

如果用户 RGB LED 正在快速循环切换所有颜色——快到看起来更像是一个 闪烁的白色 LED 而非不同的色调——则说明固件遇到了不可恢复的硬故障。重新烧录固件即可恢复;如果重新烧录无济于事,则板子可能已物理损坏。

软件库

完整的模块列表——包括哪些模块是 Portenta H7 构建版本独有的——请参阅 库索引