资讯详情

树莓派Pico存储体系全解析:BootROM、SRAM与QSPI Flash实战

📅 2026/9/10 4:31:43 | 华诺云谱 👁 阅读
树莓派Pico存储体系全解析:BootROM、SRAM与QSPI Flash实战
树莓派 Pico 严格来说只是一块几十块的开发板但它用到的存储技术却很典型BootROM、SRAM、外部 QSPI Flash 三套体系叠在一起缺一个都无法启动。很多人在点灯、PWM 控舵机的时候不会注意这些底层但一旦遇到烧录失败、程序跑飞、Flash 文件系统只读问题往往就藏在存储层的某个细节里。这篇文章就把 Pico 的 ROM、SRAM、Flash 彻底扒一遍尽量用说人话的方式讲清楚并附一个舵机控制器的完整案例让新手也能照着做。这篇内容适合两类人一类是刚拿到 Pico、想搞明白“程序到底存在哪里、上电后发生了什么”的初学者另一类是已经在用 MicroPython 或 C SDK 写项目、被各种诡异问题折磨过的实践者。我会把原理、地址、实操和坑都放在一起你可以跳着看但我建议从整体地图那节开始后面很多问题都能串起来。1. Pico 存储体系总览ROM、SRAM、Flash 到底谁管什么1.1 三兄弟的分工与地址地图Pico 使用的 RP2040 芯片内部并不是只有一颗“内存”它身上同时存在 ROM、SRAM、以及外部 Flash 三个存储层次各自承担完全不同的角色。先看官方内存映射里最关键的几段地址范围存储类型容量主要用途0x00000000 - 0x00003FFFBootROMROM16KB芯片出厂固化的引导代码0x10000000 - 0x10FFFFFFFlashXIP映射板载 2MB原版 Pico存放用户程序、只读数据、文件系统0x20000000 - 0x20041FFFSRAM264KB程序运行时变量、堆栈、数据0x40000000 起外设寄存器-GPIO、PWM、UART 等控制接口很多资料会把 0x00000000 说成“向量表开始的位置”这个说法容易混淆。在 RP2040 上0x00000000 用 BootROM 覆盖Cortex-M0 上电后默认从 0x00000000 读栈指针、从 0x00000004 读复位向量所以 BootROM 的第一件事就是接管 CPU然后才轮到你的程序。我们后面第二章会细讲这段引导过程。Flash 那部分也不是芯片内部的东西而是板子上的颗外部 NOR Flash 芯片通过 QSPI 总线接到 RP2040再被映射到 0x10000000 这个地址空间。CPU 可以直接在这个地址范围里取指执行这就是常说到的 XIPExecute In Place省去了把整个程序搬进 RAM 的步骤。1.2 为什么需要这么分从一次上电到 main()用一个生活中的类比来理解这三个存储角色BootROM 相当于电脑的 BIOS固定写在芯片内部出厂前就烧好了你改不了它。SRAM 相当于厨房的台面做菜的时候所有食材、锅碗瓢盆都要摊在台面上操作一关火台面就清空了。Flash 相当于冰箱和储物柜平时不用的东西都收纳在里面需要的时候拿到台面上用断电也不会丢。结合 Pico 的上电流程就更好理解Pico 上电CPU 从 BootROM 开始执行。BootROM 检查 BOOTSEL 按键状态以及 Flash 里有没有有效程序。如果 Flash 里已经有程序Flash 通过 XIP 映射到 0x10000000CPU 跳过去执行。用户程序启动后会把自己要用到的全局变量、栈、堆都建立在 SRAM 里。程序运行时如果需要读取常量表或者文件系统数据依然可以从 Flash 读取但如果要写入 Flash就得走专门的擦写流程不能像写内存那样直接赋值。这个分层设计不是 Pico 独有的几乎所有带外部存储的 MCU 都是这个思路。明白“程序在 Flash 里跑、变量在 SRAM 里放、引导逻辑在 ROM 里固化”这个三角关系后面看任何报错都能更快定位到问题所在。1.3 常见误区256KB 还是 264KBFlash 是不是只能存程序标题里写了 256KB这里必须先纠正一个很多人都会踩的误解RP2040 的 SRAM 官方标称是 264KB不是 256KB。它由 6 个 44KB 的 SRAM Bank 组成总容量 264KB所以你在 SDK 里看到PICO_RAM_SIZE相关的宏、或者在线文档里看到 264KB 是正常的。之所以很多人记成 256KB一是方便记忆二是部分资料把 USB DPRAM 和系统 SRAM 区分开来说。另一个常见误区是“Flash 只用来存程序”。在原版 Pico 的 MicroPython 固件里Flash 被分成了两块前面一块放 MicroPython 解释器固件后面一块做成了文件系统你往 Pico 里拖入的 .py 脚本、保存的配置文件、日志文件其实都存在这块 Flash 上。即使是 C/C SDK 项目Flash 里除了.text代码段之外还可以有只读数据、字库、配置块等。所以 Flash 是一个“程序 数据 文件系统”混合存储池别把它当成单纯只读的 ROM。2. BootROM 深挖上电后第一行代码是谁写的2.1 16KB BootROM 里固化了什么RP2040 内部这 16KB BootROM 是出厂时固化好的用户无法擦除。它里面包含了几块核心能力USB 引导加载器让 RP2040 能模拟成一个 U 盘也就是你按住 BOOTSEL 上电后看到的 RPI-RP2 盘符。UF2 文件解析器负责读取拖入 U 盘里的 .uf2 文件解析里面的地址和数据记录然后把数据写到对应的 Flash 地址。Flash 初始化与编程算法包括对 QSPI Flash 的识别、根据 JEDEC ID 选择合适的时序配置。GPIO 状态检测和启动源选择判断是从 Flash 启动、USB 启动还是 UART 启动。很多人在 Keil 或者 OpenOCD 里用 SWD 接口调试 Pico 时会看到FLASH DOWNLOAD FAILED - target dll has been cancelled这类报错。这往往不是因为程序写的有问题而是目标芯片根本就没处在正常调试模式或者调试器无法访问 BootROM 管理的启动状态。所以理解 BootROM 的职责对排查烧录问题很有帮助。2.2 UF2 拖拽背后的原理Pico 最友好的一个特性就是拖拽烧录按住 BOOTSEL 键再插 USB电脑上会出现一个 U 盘把 .uf2 文件拖进去就完成烧录了。但这个过程背后并不简单。UF2 文件不是普通的二进制文件而是被切分成了若干个 512 字节的块每个块都有固定格式的头部包含 Magic 数字、目标地址、数据长度、块序号等信息。BootROM 里的解析器会逐个读取这些块然后把有效数据写入块头指定的 Flash 地址。这里有个细节值得注意UF2 文件里的“目标地址”通常以 0x10000000 为基址比如你编出来的程序要烧到 Flash 偏移 0 处UF2 记录里写的可能是 0x10000000。BootROM 拿到这个地址后会自动换算成它自己对 Flash 的操作地址完成擦除和编程。整个拖拽过程结束后BootROM 会复位芯片让 CPU 从 Flash 正常启动。所以你在 Windows、macOS 或 Linux 下看到的 RPI-RP2 盘符并不是真正的 FAT32 文件系统而是 BootROM 模拟出来的只写接口。正因如此一些第三方工具可以生成直接在 RPI-RP2 盘符下使用的 UF2 文件而不需要额外安装驱动。2.3 BootROM 的隐藏技能不止 USB 启动大部分教程只讲按住 BOOTSEL 进 USB 下载模式但 RP2040 的 BootROM 还支持从 UART 启动和从 Flash 启动。在 RP2040 数据手册里BootROM 会检测 BOOTSEL 引脚GPIO15的状态以及其他启动源策略决定执行路径。如果 BOOTSEL 被按下进入 USB 引导模式如果 Flash 尾部存在有效启动标志则从 Flash 加载程序在特定条件下还可以走 UART 引导协议给无法用 USB 连接的环境提供了备选方案。对绝大多数用户来说UART 启动用的并不多但知道它的存在没有坏处当你遇到“板子插上电脑没有盘符、按 BOOTSEL 也没反应”的时候至少能意识到还有一条串口救砖的路可以探索。另外BootROM 里还有一组 ROM 函数表地址和签名是公开的。用户程序可以直接调用 ROM 里的 USB 栈、Flash 编程函数等。Pico SDK 里的rom_func_lookup就是干这个的。但这属于进阶玩法平时写业务代码很少用到了解即可。2.4 BootROM 与“救砖”思路很多人第一次把 Pico 玩坏是烧录了错误的 UF2 文件或者中途拔线。此时最容易出现的现象是程序跑不起来、Flash 里的内容被写坏、板子变得“没反应”。其实大部分情况不需要拆芯片BootROM 天然的独立性是救砖的关键。只要 BootROM 没坏你按住 BOOTSEL 再上电芯片就会绕过 Flash 直接进入 USB 引导模式。哪怕 Flash 里已经是一堆乱码BootROM 也能重新接收 UF2 并覆盖写入。所以“Pico 变砖”九成九都是假砖重新进 BOOTSEL 模式拖一遍固件就能恢复。实际操作中我建议备一根确认过数据传输能力的 USB 线因为 BOOTSEL 模式下如果线材只供电不通数据盘符根本不会出现最容易让人误以为板子废了。3. SRAM 实战264KB 如何安排才够用3.1 内存分布Bank 结构、堆栈地址RP2040 的 264KB SRAM 分成 6 个 Bank每个 44KB地址从 0x20000000 开始连续排列到 0x20041FFF。所有 Bank 都能被两个 Cortex-M0 核访问因此双核程序里的共享数据、核间通信内存都落在这片区域。从 C 程序的角度看SRAM 主要存放这几类东西.data段已初始化的全局变量启动代码会把初值从 Flash 复制过来。.bss段未初始化的全局变量启动代码会把这段清零。堆Heapmalloc、new出来的动态内存。栈Stack函数调用、局部变量、中断现场。启动用的向量表用户程序通常会把自己程序的向量表重定位到 SRAM以获得更快的更新速度这也是 RP2040 官方 SDK 的默认行为之一。3.2 什么时候 SRAM 会被“塞爆”一个实际案例264KB 听起来不小但在实际项目里真的不够豪横。最简单的一个例子如果你要做一块 320x240 的 RGB565 图像缓冲一帧就是 320 * 240 * 2 字节约 150KB占掉一半多 SRAM。再加上 DMA 描述符、网络协议栈缓冲、音频采样缓冲264KB 分分钟告急。另一个容易踩坑的是双核程序。Pico 的两个核默认各自维护一个栈如果在启动代码里给每个核栈分配得太大比如每个核 16KB那两块 32KB 就没了。再叠加 MicroPython 解释器运行时自己占用的内存留给用户脚本的 RAM 可能远小于 264KB。我见过一个实际场景有人用 Pico 做小型示波器需要同时采集双通道数据并进行 FFT。他把两个 2048 点的 float 数组直接声明成全局变量一个数组就是 8KB编译能过但一跑就 OOM。后来改成在采集完一帧后复用同一块缓冲区内存一下就省下来了。所以 SRAM 规划的核心不是“够不够”而是“能不能复用”。3.3 内存优化三板斧关于 SRAM 优化我总结三条最实用的经验第一把只读常量尽量留在 Flash。在 C/C 里用const修饰的大表比如正弦表、字库、配置文件模板默认会放在 Flash 的只读数据区通过 XIP 读取不需要占用 SRAM。不要图省事把它们复制到 RAM 里再访问除非你嫌 Flash 读取太慢。第二把时间关键型函数放到 SRAM 执行。Pico SDK 提供了__not_in_flash_func和__time_critical_func这两个宏可以把指定函数放到 SRAM 中执行。Flash 通过 QSPI 读取确实不如 SRAM 快如果某个函数对时序特别敏感比如 DSP 处理、PWM 波形切换把它搬到 SRAM 会有可见的提升。第三控制动态内存的分配与释放。MicroPython 自身有 GC但 GC 会不定期触发导致程序卡顿C/C 里malloc使用不当会产生碎片。一个好用但不优雅的办法是工程早期就确定好几个关键缓冲区的最大长度用静态数组代替malloc简单粗暴且更稳定。3.4 SRAM 和 DRAM 的一点背景聊到 SRAM难免会被拿来和 DRAM 对比。简单说SRAM 用触发器存储每一位数据不用周期性刷新读取速度快、接口简单所以非常适合做 MCU 内部的通用内存DRAM 靠电容存储电荷需要不断刷新密度更高、单位价格更便宜但接口复杂且延迟相对高所以主要用在 PC、手机这类大内存场景。Pico 选 SRAM 而不是 DRAM 原因很直接MCU 追求低功耗、低延迟、高可靠SRAM 不需要内存控制器上电即可用也没有刷新周期抢占总线的问题。你在调试 Pico 时如果看到变量莫名其妙丢失一般不怪 SRAM 本身而是栈溢出或者电源不稳导致复位别让 SRAM 背锅。4. Flash 深挖程序、配置和文件系统都住在哪4.1 为什么是 NOR QSPI而不是 NANDPico 板上的 Flash 是一颗串行 NOR Flash通过 QSPI 接口和 RP2040 连接。这里有一个很基础但重要的技术选型问题为什么 MCU 外部扩展普遍用 NOR而不是容量更大、价格更低的 NAND核心原因之一是 XIP。NOR Flash 可以按字节/字随机读取CPU 可以直接在映射地址上取指执行NAND Flash 的读取以页为单位且存在坏块管理问题不适合直接映射到地址空间上执行代码。你在网上搜“NOR Flash 和 NAND Flash 的区别”会看到各种答案但落到 Pico 的场景里最关键的就这一条能不能让 CPU 直接跑代码。NAND 更适合做海量数据存储比如 U 盘、SD 卡、eMMC 里的存储介质。Pico 如果想扩展大容量存储正确思路是外接 SD 卡或者 SPI Flash 文件系统而不是指望板载这颗 2MB Flash 变成大仓库。QSPI 则是接口层面的提升。四线 I/O 并行传输可以在同样时钟频率下把吞吐量提上去但相比 SRAM 依然慢不少所以 XIP 取指时命中缓存很重要。RP2040 的 XIP 子系统带有缓存就是优化这个瓶颈的。4.2 Flash 寿命擦写次数不是无限NOR Flash 的擦写寿命通常在 1 万到 10 万次级别具体数值看芯片数据手册。这里必须区分“读”和“写”读操作基本不影响寿命但写操作会磨损浮栅晶体管尤其擦除操作是整个 Flash 寿命损耗的主要来源。很多人写程序时习惯用 Flash 保存临时状态比如每隔几秒写一次计数值。这个做法非常危险。如果设备一天写 100 次一年就是 3.6 万次2MB Flash 的寿命可能两三年就顶不住了。正确的姿势是把 Flash 当作“配置存储”而不是“普通变量存储”只有在配置变化或者需要持久化的关键时刻才写一次。如果确实需要频繁记录数据可以做简单的磨损均衡把数据轮流写到多个扇区每个扇区头部放一个序列号读取时找到最新的一份。这个技术在简单项目里手写并不复杂但不建议新手上来就搞先用低频写入保证稳定性更重要。4.3 从代码角度看 Flash 读写Pico C SDK 提供了一组 Flash 操作接口#include pico/flash.h #include pico/sync.h #define FLASH_TARGET_OFFSET (1024 * 1024) uint8_t data[FLASH_PAGE_SIZE] {0x01, 0x02, 0x03}; uint32_t ints save_and_disable_interrupts(); flash_range_erase(FLASH_TARGET_OFFSET, FLASH_SECTOR_SIZE); flash_range_program(FLASH_TARGET_OFFSET, data, FLASH_PAGE_SIZE); restore_interrupts(ints);这里有几个必须注意的要点第一擦除和写入的地址必须按扇区对齐。常见的擦除单位是 4KB编程单位是 256B如果你给错了偏移轻则数据写错位置重则把程序区擦掉板子直接“变砖”。第二执行 Flash 写入期间CPU 不能从同一片 Flash 取指。因为擦写操作会和 XIP 读取冲突如果你把这段擦写代码放在 Flash 里运行大概率会卡死或触发总线上错误。Pico SDK 的底层实现会尽量处理但官方文档仍然建议把调用擦写函数的代码放到 RAM 中并且关闭中断。第三双核场景要特别小心。如果你在核 0 里执行 Flash 擦写核 1 还在跑代码同样可能从 Flash 取指导致总线冲突。规范做法是让两个核都进入一个空闲等待状态再执行 Flash 操作。官方也有对应的同步机制但很多新手第一次写双核 Flash 存储时都会在这里翻车。在 MicroPython 里Flash 读写被封装成了文件系统操作。你可以把配置写成 JSON 文件保存到根目录import json config {calib_min: 1638, calib_max: 8191} with open(config.json, w) as f: json.dump(config, f)MicroPython 会把文件系统挂载在 Flash 的某个分区里对用户透明。这个方式的好处是简单、安全坏处是你不知道底层具体写到了哪个扇区无法精确控制磨损分布。4.4 怎么用工具查询 Flash ID 和容量拿到一块 Pico首先确认板载 Flash 到底多大这是很多兼容板用户必须面对的问题。原版 Pico 一般是 2MB但也有 16MB 的扩展版甚至有人在某个买回来的板子上发现 Flash 型号和标称不符。在 C SDK 里读取 Flash ID 非常简单#include pico/flash.h uint32_t id flash_get_jedec_id();返回值是 3 个字节拼成的 JEDEC ID例如 Winbond W25Q16JV 通常返回 0xEF4015其中 0xEF 是厂商 ID0x40 表示 SPI NOR0x15 表示容量等级。如果你拿到的是 0xEF4018那对应的是 W25Q128也就是 16MB Flash。在系统层面也可以用picotool查看picotool info -f这个命令会显示 Flash 大小、固件信息等前提是 Pico 已经进入了 BOOTSEL 模式或者当前程序支持 picotool 访问。用这个方法识别“扩容盘”或者“假 Flash”是最直接的有时候你发现拖进去的程序运行到一半异常很可能就是 Flash 型号不匹配导致封装环境计算错误。5. 拿来就用舵机控制器里的存储协同作战5.1 需求设计前面讲了一堆原理现在用一个实际项目把 ROM、SRAM、Flash 串起来。这个项目是用 Pico 控制一个 SG90 舵机通过按钮切换角度同时把舵机的校准参数保存在 Flash 文件系统里实现“断电记忆”。为什么要用这个案例因为舵机控制正好覆盖了三个存储层次程序固件存在 Flash运行时角度变量和 PWM 寄存器状态存在 SRAM校准参数持久化又回到了 Flash。理解了这个小链路你就知道底层存储体系在日常项目里是怎么配合的。5.2 硬件与接线SG90 舵机有三根线棕色 GND、红色 VCC、橙色信号线。信号线不能直接接到 Pico 的 3.3V 引脚上驱动因为舵机信号虽然逻辑电平兼容 3.3V但电流需求通常在 100mA 以上Pico 的 GPIO 输出能力有限建议给舵机单独供电同时共地。推荐接线舵机引脚接线说明棕色线 GNDPico 的 GND 和外部电源 GND必须共地红色线 VCC外部 5V 电源正极不要直接吃 Pico 的 3.3V橙色线 SignalPico GPIO15直接接 GPIO 即可外部 5V 可以是一个 USB 电源或者两节锂电池注意电流至少 500mA 以上避免启动瞬间电压跌落导致 Pico 复位。5.3 MicroPython 代码实现舵机控制原理已经非常成熟SG90 需要 50Hz 的 PWM 信号高电平时间 0.5ms 对应 0 度2.5ms 对应 180 度。Pico 的 PWM 频率设为 50Hz 后用 16 位占空比寄存器控制0.5ms 对应 duty 值为 16382.5ms 对应 8191。from machine import Pin, PWM import json import utime SERVO_PIN 15 def load_calibration(): try: with open(servo_cal.json, r) as f: return json.load(f) except OSError: return {min_duty: 1638, max_duty: 8191} servo PWM(Pin(SERVO_PIN)) servo.freq(50) calib load_calibration() def set_angle(angle): angle max(0, min(180, angle)) duty calib[min_duty] (calib[max_duty] - calib[min_duty]) * angle // 180 servo.duty_u16(duty) for angle in range(0, 181, 30): set_angle(angle) utime.sleep_ms(500) # 重新校准把当前舵机实际角度对应的 duty 存下来 calib[min_duty] 1638 calib[max_duty] 8191 with open(servo_cal.json, w) as f: json.dump(calib, f) servo.deinit()这段代码里程序本身由 MicroPython 固件从 Flash 加载set_angle 函数里的变量和计算过程全部发生在 SRAM最后的校准值以 JSON 文件形式写回 Flash。如果换了一个舵机不用改代码直接改 JSON 里的 min_duty 和 max_duty 就能适配新的行程范围。5.4 代码里到底哪些东西待在哪很多人对“程序在 Flash 里运行”这个概念很模糊这里以这段 MicroPython 代码为例把每个部分的存储位置列清楚内容存储位置说明MicroPython 解释器固件Flash 低地址区域用户拖入的 UF2 固件servo_cal.json 文件Flash 文件系统分区断电后可保留的配置set_angle 的局部变量 dutySRAM 栈区函数执行时临时占用PWM 外设配置寄存器外设寄存器地址区不属于 SRAM但由 CPU 控制import 进来的模块代码Flash XIP 缓存MicroPython 模块也会从 Flash 读取从这个表可以看出一条完整的数据流Flash 提供“静态代码和长期数据”SRAM 提供“运行时的临时空间”BootROM 在中间负责把整个系统引导起来。每次上电BootROM 检查并启动 Flash 里的程序程序运行时把变量放在 SRAM需要保存数据时又写回 Flash。这套机制虽然简单但所有嵌入式系统都是这个模型的变体。6. 常见问题与排查速查6.1 烧录/仿真报错“FLASH DOWNLOAD FAILED - target dll has been cancelled”这个报错在 Keil、OpenOCD 环境下很常见尤其第一次调试 Pico 的人会碰到。现象是点击下载后烧录进度条很快失败提示 target dll 被取消。常见原因有三个第一Pico 没有进入调试模式。Pico 的 SWD 调试接口需要在目标板有程序运行或者 BOOTSEL 模式下才能访问如果你上电后芯片停留在不确定状态CMSIS-DAP 就连接不上。第二Flash 下载算法配置错误。Keil 烧录时需要指定目标 Flash 的下载算法比如 CMSIS-DAP 对应的 RP2040 Flash 算法。如果算法不匹配或者 Keil 自带的 Flash Loader 版本过旧就会报这个错。第三接线或 USB 供电问题。SWD 线虚接、杜邦线过长、供电不足都可能导致调试器在擦写 Flash 的中途和目标断开。排查思路先拔掉所有外部电路只留 Pico 和 USB 线按住 BOOTSEL 上电确认电脑能识别 U 盘盘符再用调试器连接看能否读到芯片 ID。如果 BOOTSEL 模式下依然失败大概率是调试器驱动或者 Keil 配置问题而不是 Pico 本身坏了。6.2 识别不到 RPI-RP2 或 Flash 容量异常按住 BOOTSEL 插上 USB电脑没出现盘符这是新手最常见的问题。第一优先排查 USB 线很多线只能充电不能传数据换一根确定能传数据的线立刻解决。第二排查 USB 接口台式机前置 USB 口容易出现供电不足换到后面板接口或者使用带供电的 USB HUB。Flash 容量异常的情况也很值得注意。比如你用picotool info -f看到板子上 Flash ID 对应的是 8MB但是编译固件时给文件系统划分的分区只有 2MB那就会出现“能启动但文件系统容量不对”的诡异现象。或者反过来你的板子 Flash 只有 2MB你硬塞了一个超过 2MB 的 UF2 文件BootROM 写入失败最终表现为“烧录没报错但程序跑不起来”。所以拿到新板子第一件事就是用 picotool 看一眼真实的 Flash ID 和容量顺手在板子上贴个标签。6.3 文件系统进入只读MicroPython 项目用到 Flash 文件系统时如果设备异常断电比如正在写文件时拔掉 USB下次启动可能会发现根目录文件系统变成了只读写入文件报 OSError。这个问题的根源是文件系统元数据不一致。MicroPython 的 LFS2 文件系统有一定容错能力但在写操作中间掉电仍然可能进入保护状态。修复方式是在 REPL 里强制重新格式化import os os.VfsLfs2.mkfs(bdev)注意格式化会清空 Flash 上的所有文件所以不到万不得已别乱执行。更稳妥的办法是平时写配置时先把新数据写入临时文件再用 os.replace 原子替换原文件减少中间状态损坏的概率。6.4 Flash 写入死机/卡死在 C SDK 项目里调用flash_range_erase或flash_range_program后程序卡死是存储底层最容易出现的“翻车现场”。直接原因通常是执行 Flash 操作时CPU 还想从 Flash 取指运行当前代码而 Flash 正被擦写流程占用总线冲突导致卡死。规避方法有三一是把擦写封装函数放到 RAM 中执行也就是用__not_in_flash_func修饰二是调用擦写前用save_and_disable_interrupts关掉中断防止中断回调又去读 Flash三是双核项目里先把另一个核踢进一段 RAM 里的自旋等待等擦写完成后再恢复运行。另外还要小心擦除范围覆盖了当前正在执行的代码区域。比如你的程序烧在 Flash 起始地址 0x10000000而你又调用了flash_range_erase(0, 4 * 1024)这等于把正在运行的程序从脚下抽走不死才怪。解决办法是把程序偏移到 Flash 高位运行或者保证你写数据用的扇区固定不占用代码区。6.5 内存不足 / Heap OOMMicroPython 环境下频繁创建大对象、字节数组可能弹出 MemoryError。C 环境下则是malloc返回 NULL 或者程序莫名奇妙进入 HardFault。排查方法分两步第一步看编译产物里的内存占用Pico SDK 构建结束后会打印 RAM 用量如果.data .bss 堆栈已经接近 264KB说明静态占用太高。第二步看运行时峰值可以在关键节点打印 Free heapMicroPython 里用gc.mem_free()查看C 里可以统计空闲堆空间。我个人在项目里最常用的一招是把大缓冲区统一生命周期管理。例如一个 64KB 的 DMA 缓冲只在采集阶段分配处理完立即释放而不是从头占到尾。很多时候 OOM 不是总量不够而是“大量内存被分配了却没人主动释放”调整一次生命周期就能看到明显改善。星期六的下午我又一次被同一块 Pico 折腾到天黑以为是自己写的 Flash 驱动有问题最后发现是 Flash ID 读错了型号和容量对不上烧录算法自然选不对。后来养成了习惯每块新板子到手先查 ID写任何持久化代码前先确认自己在哪个地址动刀。Pico 的存储体系一点都不神秘无非是 ROM 管启动、SRAM 管运行、Flash 管持久把这三者的边界和脾气摸清了后面碰到问题基本都是秒定位。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。