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 产品页。
亮点¶
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 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 |
|
D21 |
3.3 V |
|
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/A4 和 A3/A5 分别与 D10 和 D8 共用物理引脚,因此在将它们读取为模拟量的同时无法在这些引脚上驱动 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 锂聚合物电芯。只要存在
+5V或VIN,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 排针的 |
2 |
INT |
|
3 |
SCL_EXT |
与 MKR 排针的 |
4 |
SDA_EXT |
与 MKR 排针的 |
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——NRSTJ1‑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 引脚 |
原因 |
|---|---|
|
TIM1 CH1——摄像头主时钟 |
|
TIM1 CH1(备用)——摄像头主时钟 |
|
I²C 3 SDA——与摄像头共用;总线可用,但应避开传感器的 I²C 地址( |
|
I²C 3 SCL——与摄像头共用;总线可用,但应避开传感器的 I²C 地址( |
|
DCMI HSYNC——同时会禁用 DAC |
|
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),因此例如位于充电器地址 0x04 的 CHG_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 引脚 |
原因 |
|---|---|
|
LoRa 模块 BOOT 引脚 |
|
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.DAC,DAC1(A6 / 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 CH1 在
D1和D6上。TIM1 CH2 在
D2和D14上。TIM8 CH3N 在
D0和D1上。
每个定时器通道只选一个使用者。
警告
当 Vision Shield 通过 csi --- 摄像头传感器 初始化时,TIM1 会被保留用于 摄像头主时钟——摄像头处于活动状态时,D1、D2、D6、D13 和 D14 无法用于 PWM 驱动。
软件位操作(bit‑banged)总线¶
如果你需要额外的总线,machine.SoftI2C 和 machine.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.py和README.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.py 或 main.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.py、main.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 构建版本独有的——请参阅 库索引。