首页 / 资讯中心 / 文章详情

H743外扩QSPI Flash与SDRAM跑MicroPython

H743外扩QSPI Flash与SDRAM跑MicroPython ★ FEATURED ARTICLE
简介面向希望在STM32H743开发板上运行MicroPython的嵌入式开发者这套板级移植源码围绕外扩32M SQPI Flash与32M SDRAM的适配展开可用于搭建Python脚本快速验证环境也适合学习STM32H7系列在MicroPython下的板级支持组织方式对已有C语言与HAL基础、打算做硬件二次开发的读者同样友好。包内共5个文件压缩包约4KB由C语言板级初始化、两个H头文件、CSV引脚映射表和make编译配置组成分别承担外设初始化、宏与HAL参数定义、引脚分配及构建规则体量小但关键环节齐全。已有1367人学习。借助其中的存储与外设配置读者可对照理解SQPI Flash和SDRAM在板级支持包中的接入方式复现开发板固件构建并据此裁剪引脚、调整时钟与外设参数减少从零移植时的排查成本也可作为后续联网、传感采集等Python应用的前置硬件适配参考。1. 内部 2MB 撑不住以后一块 H743 板子的外扩存储改造值不值得做内部 Flash 只有 2MB一个带 network、ssl、ffi 的 micropython 固件编出来就能吃掉大半剩下那点空间连放几个 .py 脚本都紧张STM32H743 的 1MB 片内 SRAM 看着不少真跑起图像缓冲或者几十 KB 的 JSON 解析GC 一触发就开始掉帧。这套源码要解决的正是这两件事把 32M 的 SQPI flash 挂成 MicroPython 的根文件系统把 32M 的 SDRAM 并进堆区和缓冲区。它不是给纯新手点灯用的 demo是给已经能把 MicroPython 跑起来、但被存储卡住脖子的那批人准备的你得能看懂 board 目录结构、会写 mpconfigboard.h、知道 FMC 和 QSPI 控制器大概在干什么否则时序算错一格就是一块只会发热的板子。2. 存储映射与时钟树H743 跑 MicroPython 前必须先钉死的三件事H743 这颗芯片的存储布局比 F4 系列复杂得多不是把堆改大一点就能收工的。它有几块物理上完全不同的 RAMDTCM 128KB 紧贴内核、零等待、不经过 CacheAXI SRAM 512KB 挂在总线矩阵上还有 SRAM1~SRAM4 加起来三百多 KB。MicroPython 的堆默认放在哪块、栈放在哪块、DMA 缓冲区放在哪块直接决定了后面 SDRAM 接进来之后是锦上添花还是一地鸡毛。把这三件事按顺序钉死后面外扩 Flash 和 SDRAM 才有讨论的基础。2.1 先画清楚地址地图哪些区域能挂外设哪些不能动手改代码之前我会先在纸上把这几段地址抄一遍因为后面每一个宏、每一行链接脚本都要跟它们对齐区域起始地址容量典型用途能不能给 DMAITCM0x0000000064KB中断向量、极热代码否DTCM0x20000000128KB主栈、GC 根节点否AXI SRAM0x24000000512KB主堆、大缓冲是SRAM1~30x30000000288KB外设 DMA 缓冲是SRAM40x3800000064KB低功耗域缓冲是QSPI 映射窗口0x90000000最大 256MB外部 flash 内存映射读否FMC Bank50xC0000000最大 256MB外部 SDRAM否需过 MPU这张表里有两个点最容易翻车。第一DTCM 不能给 DMA 用很多人在 DTCM 上开一个数组喂给 SPI然后对着逻辑分析仪怀疑人生第二QSPI 和 FMC 都是映射窗口不是真内存访问延迟比片内高一个数量级把它们当普通 RAM 用之前必须过 MPU 配置属性。我一般会把主栈和 GC 的根对象留在 DTCM把大块 ByteArray、帧缓冲放到 SDRAM中间的 AXI SRAM 留给需要被 DMA 访问的缓冲。还有一个细节QSPI 映射窗口的地址是固定的 0x90000000但只有在使能内存映射模式之后才可读普通 SPI 模式下读这个地址会直接 BusFault。源码里外扩 flash 的驱动分成两条路径——一条走命令式读写用于擦写文件系统一条走映射读用于 XIP 执行或直接读资源两条路径的切换要保证当前没有正在进行的传输否则控制器状态机会卡死。2.2 时钟树QSPI 和 FMC 的内核时钟必须单独分配H743 跑到 400MHz 以上要开 VOS0 电压等级而 QSPI 和 FMC 的外设内核时钟并不是简单地从 HCLK 分频下来的它们各自有一个多路选择器。常见做法是PLL1 出 CPU 时钟PLL2 专门出 200MHz 给 FMC 和大部分外设PLL3 或者 PLL2R 分一路给 QSPI。这样做的原因是 FMC 的 SDRAM 时序全是按 FMC 时钟周期换算的一旦你为了省事把它挂在会动态变频的 HCLK 上调好的时序在降频时全部失效。板级目录里最小可用的骨架大概长这样// ports/stm32/boards/H743_EXT/mpconfigboard.h #define MICROPY_HW_BOARD_NAME H743-EXT-32M #define MICROPY_HW_MCU_NAME STM32H743 // 外部晶振板子上实际焊的是 25M写成别的值串口全是乱码 #define MICROPY_HW_HSE_VALUE (25000000) // 时钟CPU 480MHzFMC/QSPI 内核 200MHz #define MICROPY_HW_CLK_PLLM (5) // 25M / 5 5MHz 参考 #define MICROPY_HW_CLK_PLLN (192) // 5M * 192 960MHz VCO #define MICROPY_HW_CLK_PLLP (2) // 960 / 2 480MHz CPU #define MICROPY_HW_CLK_PLLQ (4) // 960 / 4 240MHz #define MICROPY_HW_CLK_PLLR (4) // 关掉用不到的模块先把固件压到 1.5MB 以内再谈别的 #define MICROPY_PY_NETWORK (0) #define MICROPY_PY_SSL (0) #define MICROPY_PY_BLUETOOTH (0)这几个宏看起来平平无奇实际每个都有对应的坑。MICROPY_HW_HSE_VALUE写错是最经典的编译一切正常烧进去串口吐出来全是问号因为 UART 的波特率是从错误的时钟推算出来的。PLLP设成 2 之后 CPU 是 480MHz但前提是电压等级已经是 VOS0如果板子的电源管理配置还停在 VOS1芯片会直接跑飞或者进 HardFault。至于关模块那几行纯粹是被 2MB 内部 Flash 逼的——先把固件体积压下来才有空间给后面的文件系统和脚本。改完这些编译命令是# 先同步子模块否则 littlefs / extmod 的头文件会缺 make -C ports/stm32 submodules # 板级目录名要和 boards/ 下的文件夹完全一致 make -C ports/stm32 BOARDH743_EXT -j8 # 产物在 build-H743_EXT/firmware.hex 和 firmware.dfu ls -lh ports/stm32/build-H743_EXT/firmware.*编译报错里最常见的是region RAM overflowed说明堆还是按默认大小算的得在 board 目录里加上MICROPY_HW_HEAP_...相关的覆盖另一种是undefined reference to qspi_...那基本是子模块没拉全。2.3 板级引脚表与 QSPI/SDRAM 的选型判断pins.csv这份文件决定了 MicroPython 里machine.Pin(PB6)能不能用格式是引脚名,端口,序号,复用功能。外扩 flash 的 CS、CLK、IO0~IO3 这六根线必须在这里出现否则你没法在 REPL 里手动拉 CS 做调试。我一般会把 SDRAM 的控制线也写进去虽然它们运行时被 FMC 独占但在排查到底有没有信号出来的时候能把引脚切回 GPIO 用万用表量一下电平比盯着示波器猜强得多。选型上有个判断标准值得说清楚如果板子上焊的是 32MB 容量的 QSPI NOR那它的擦除粒度大概率是 4KB 扇区 64KB 块。这个粒度和 littlefs 的 block size 必须匹配配成 4KB 是最稳的配大了浪费空间配小了每次 GC 都要擦一堆扇区写一个 100KB 的文件能卡你三秒。至于 SDRAM32MB 容量、16 位位宽的颗粒最常见行地址 13 位、列地址 9 位、4 个 bank这组参数决定了后面 FMC 的RowBitsNumber、ColumnBitsNumber、BankNumber三个字段怎么填填错的表现是只有一半地址能读写或者每次复位之后随机几个字节变成 0xFF。3. 32M SQPI Flash 落地从扇区驱动到 littlefs 挂载根目录外扩 flash 在 MicroPython 里不是挂上去就完事它有三种用法选错了后面所有工作都要重来。第一种是把它整个做成块设备直接当根文件系统import os之后/就在外部 flash 上第二种是不做文件系统只开内存映射用来放字体、模型、图片这类只读资源第三种是切成两半前一半给 littlefs后一半留给 raw 分区做参数存储或者固件备份。这份源码走的是第一种也是最适合板子要长期跑脚本的场景因为脚本和配置都能通过 REPL 直接改不用重新烧固件。3.1 三种接法的取舍为什么根文件系统该落在外部 flash内部 2MB Flash 里固件本身占掉 1.4~1.8MB能留给内部文件系统的往往只有几百 KB而这个内部文件系统还跟代码共享同一个 bank写文件时 CPU 取指会被总线仲裁拖慢。把根文件系统挪到外部 QSPI flash 之后内部 Flash 只负责放固件读写文件走独立的 QSPI 通道两边互不干扰这是最直接的好处。代价也很明确外部 flash 的随机读延迟比内部高很多import一个模块要经过 QSPI 控制器、可能还要过 Cache如果模块很多启动时间会肉眼可见地变长。我的处理办法是把启动时必须加载的模块预编译成 .mpy 塞进固件内部只把业务脚本放在外部文件系统上。这样启动时读的都是内部 flash只有真正跑业务逻辑才去碰外部存储。第二种只做映射读的方案适合资源型应用——比如你要从 flash 里直接读一张 800x480 的位图做显示映射模式下可以用指针直接访问不需要先读进 RAM。但这种模式下没办法执行写操作也不可能跑 littlefs所以它没法替代文件系统只能作为补充。3.2 块设备驱动与 littlefs 挂载扇区、块大小和缓存怎么配板级配置里跟外部 flash 相关的宏核心是这几个// 32MByte 256Mbit注意这里是 bit 数写错了文件系统大小直接对不上 #define MICROPY_HW_SPIFLASH_SIZE_BITS (256 * 1024 * 1024) // 四线模式下这几根线必须全部配好少一根会退化成单线速度掉到 1/4 #define MICROPY_HW_QSPI_CS (pin_B6) #define MICROPY_HW_QSPI_CLK (pin_B2) #define MICROPY_HW_QSPI_IO0 (pin_D11) #define MICROPY_HW_QSPI_IO1 (pin_D12) #define MICROPY_HW_QSPI_IO2 (pin_E2) #define MICROPY_HW_QSPI_IO3 (pin_D13) // 打开块设备作为根文件系统这一行决定 os.statvfs(/) 报的是内部还是外部 #define MICROPY_HW_BDEV_IS_SPIFLASH (1) // 扇区擦除 4KB跟芯片手册的 Sector Erase 对齐 #define MICROPY_HW_SPIFLASH_SECTOR_SIZE (4096)MICROPY_HW_SPIFLASH_SIZE_BITS这个宏名里带 BITS单位就是比特32M 字节的颗粒要写 256×1024×1024。我第一次移植时按字节写成了 32×1024×1024结果os.statvfs(/)报出来 4MB写满之后再写就报OSError: [Errno 28] ENOSPC查了半天才发现是单位问题。这种宏命名在不同版本的 ports/stm32 里改过几轮源码里如果搜不到就去ports/stm32/spibdev.c里看当前版本用的是哪一套别硬套网上的老配置。块设备层封装好之后文件系统的挂载是自动的烧进去第一次上电如果没有文件系统MicroPython 会自己格式化。想手动确认在 REPL 里跑import os # 看根文件系统容量和块大小确认挂在外部 flash 上 st os.statvfs(/) print(block size:, st[0]) print(total MB:, st[2] * st[0] // (1024 * 1024)) print(free MB:, st[3] * st[0] // (1024 * 1024)) # 实测一把擦写速度写入块越大越能看出 QSPI 的极限 import time data bx * 65536 t0 time.ticks_ms() with open(/test.bin, wb) as f: for _ in range(16): f.write(data) print(write 1MB:, time.ticks_diff(time.ticks_ms(), t0), ms)这段脚本里st[0]是块大小st[2]是总块数两个一乘才是容量。写入测试故意用了 64KB 的块循环 16 次而不是一次性写 1MB因为一次性写大 buffer 会先在 RAM 里拼好再下发测出来的是 RAM 带宽不是 flash 带宽分块写更接近真实业务场景也能顺带暴露 littlefs 在 GC 时的卡顿。3.3 编译期开关与版本差异宏名对不上时怎么找MicroPython 的 STM32 端口在不同版本之间外部 flash 相关宏的命名改过不止一次。有的版本用MICROPY_HW_SPIFLASH_*有的版本把它抽象成了通用的 bdev 接口board 目录下会多一个bdev.c自己做mp_blockdevice_...的注册。碰上这种情况判断方法很简单在ports/stm32/下搜MP_QSTR_加上你的设备名或者直接搜bdev看默认实现是怎么拿 board 宏的。我自己踩过一次照着一份老配置写好了MICROPY_HW_SPIFLASH_CS编译不报错运行起来os.statvfs(/)报的是内部 flash 的 512KB说明新版本压根没读这个宏走了默认的内部实现。解决方式是去看boards/下跟你芯片最接近的那个官方板目录抄它的mpconfigboard.mk里SRC_C那几行把自定义的 bdev 源文件加进编译列表。这个动作本身就是一次很好的学习——你能看到官方是怎么处理有些板子外扩、有些没有这种分支的。4. 32M SDRAM 落地FMC 时序换算、堆迁移与 Cache 一致性SDRAM 比 QSPI flash 难难在它没有擦写这种离散操作全是连续时序任何一个参数差一个周期表现都是随机的。有的板子上电跑十分钟才出错有的跑一次内存扫描就挂还有的只在温度低的时候挂——这类问题靠猜是猜不出来的只能把时序参数一项项按手册算清楚。这一章的顺序是先算时序再配 MPU最后才动堆顺序反了会多花两倍时间。4.1 FMC 时序参数换算从手册的 ns 到寄存器的周期数SDRAM 的时序参数在芯片手册里都是纳秒FMC 的寄存器填的是时钟周期数换算公式是寄存器值 ceil(纳秒 / 时钟周期) - 1。假设 FMC 内核时钟是 200MHz一个周期 5ns我通常按下面这张表填参数手册典型值换算过程寄存器值tRCD 行到列延迟18nsceil(18/5)-13tRP 预充电时间18nsceil(18/5)-13tWR 写恢复时间15nsceil(15/5)-12tRC 行周期60nsceil(60/5)-111tRFC 自刷新周期42nsceil(42/5)-18tXSR 退出自刷新72nsceil(72/5)-114tMRD 模式寄存器设置2 tCK直接填1除了这几个还有两个容易漏的刷新计数COUNT (64ms / 行数) × 时钟频率32MB 颗粒通常是 8192 行算下来(64/8192)ms × 200MHz ≈ 1562填 1562 或者保守一点填 1500以及 CAS 延迟常见颗粒支持 2 或 3填 3 更稳但慢一点填 2 需要确认颗粒在这个频率下确实支持。填进初始化代码大概是这样// SDRAM 初始化时序参数全部按上面的表换算得到 FMC_SDRAM_TimingTypeDef timing {0}; timing.LoadToActiveDelay 3; // tMRD timing.ExitSelfRefreshDelay 14; // tXSR timing.SelfRefreshTime 8; // tRFC timing.RowCycleDelay 11; // tRC timing.WriteRecoveryTime 2; // tWR timing.RPDelay 3; // tRP timing.RCDDelay 3; // tRCD // 刷新计数8192 行 / 64ms200MHz 下算出来约 1562 FMC_SDRAM_ProgramRefreshRate(FMC_Bank, 1562); // 上电后必须发一次预充电全部 bank 至少 8 次自动刷新否则内容全是随机值 // 这段顺序错了后面跑多久都是玄学注释里那句顺序错了就是玄学不是夸张。SDRAM 上电之后必须先发预充电命令把所有 bank 关掉再连发 8 次以上自动刷新然后才能写模式寄存器。少发几次刷新的表现是前几 KB 读写正常越往后越容易读到随机值而且每次上电出错的地址都不一样。我第一版就是因为刷新次数少写了几次跑了一个礼拜才定位到。4.2 堆迁移哪些数据该搬进 SDRAM哪些必须留在片内堆搬到 SDRAM 不是把起始地址改一改就完事。MicroPython 的 GC 是标记清除遍历的时候要频繁访问对象头如果所有对象都在 SDRAM 上每次 GC 的耗时能翻两三倍。我的做法是保留默认堆在 AXI SRAM另外在 SDRAM 上开一块大对象区专门给bytearray、帧缓冲、解析大 JSON 用。具体到代码board 目录里覆盖堆的边界// 主堆仍然放 AXI SRAM保证 GC 遍历速度 #define MICROPY_HW_HEAP_BEGIN ((void *)0x24000000) #define MICROPY_HW_HEAP_END ((void *)(0x24000000 480 * 1024)) // SDRAM 单独暴露给 Python 层用 machine.mem32 或者自定义模块访问 #define MICROPY_HW_SDRAM_BASE (0xC0000000) #define MICROPY_HW_SDRAM_SIZE (32 * 1024 * 1024) // 上电做一次全片读写测试出问题早点发现比跑一半挂掉强 #define MICROPY_HW_SDRAM_STARTUP_TEST (1)MICROPY_HW_SDRAM_STARTUP_TEST这个开关强烈建议打开。它会在启动时对整片 SDRAM 做一次写-读-校验代价是启动慢几百毫秒收益是时序配错时你立刻就知道而不是等到业务跑到一半才发现某块地址读出来是 0。量产固件里可以关掉但调试阶段别省这一步。SDRAM 暴露给 Python 的方式常见做法是写一个极简的 C 模块把基地址和长度包成memoryview或者干脆用machine.mem32按地址读写。前者安全、后者灵活我一般两个都留调试用后者业务用前者。4.3 Cache 与 MPUM7 上最容易被忽略的一致性坑Cortex-M7 有 D-Cache开了之后 SDRAM 的读写速度能差出三倍但代价是所有经过 DMA 的缓冲区都要手动维护一致性。MicroPython 本身不太用 DMA真正的问题出在你自己加的外设上——比如你用 SPI 加 DMA 往屏幕上刷一张存在 SDRAM 里的图CPU 写完图之后数据还在 Cache 里没落到 SDRAMDMA 读到的是旧内容屏幕上是花屏。这种问题在逻辑分析仪上什么都看不出来因为总线上的数据确实发出去了只是发的是错的。处理办法是给 SDRAM 区域配 MPU分两段给普通数据用的写回写分配区域和给 DMA 缓冲用的写穿透或者不缓存区域。写穿透会损失一点性能但省掉了每次传输前后手动 clean/invalidate 的麻烦对于刷新率不高的场景完全够用。如果确实要压榨性能那就得在每次 DMA 传输前后加上SCB_CleanDCache_by_Addr和SCB_InvalidateDCache_by_Addr而且要保证地址和长度按 32 字节对齐不对齐的话 invalidate 会把相邻的有用数据一起丢掉这个坑比花屏更难查。5. 避坑排查五个让 H743 外扩存储翻车的现场记录下面这五条全是我在这块板子上真实撞过的顺序按出现频率从高到低排。每条都按现象、原因、解决三段写遇到问题可以对着顺序一条条排除比漫无目的地翻代码快得多。5.1 上电就 HardFault连串口都没输出现象烧完固件复位之后没有任何串口输出用调试器挂上去看PC 停在一个跟存储地址有关的异常处理里。原因SDRAM 的控制器时钟还没使能初始化代码就已经往 0xC0000000 写数据了。H743 上 FMC 的时钟挂在 AHB3 上必须在SystemClock_Config之后、SDRAM 初始化之前显式开另一个常见原因是 MPU 没配访问外部存储区域默认属性是 Strongly-ordered任何非对齐访问都会触发异常。解决把 FMC 时钟使能放在时钟配置的最后一步SDRAM 初始化之前加一句__HAL_RCC_FMC_CLK_ENABLE()MPU 配置用HAL_MPU_ConfigRegion把 0xC0000000 起 32MB 设成 Normal、Non-shareable、Write-back并且这一段的DisableExec要设成允许执行否则放在这里的代码跑不了。5.2 文件系统能挂上写大文件之后目录损坏现象小文件读写都正常一旦写入超过几百 KB重启之后os.listdir(/)报错或者文件长度变成一个离谱的数字。原因MICROPY_HW_SPIFLASH_SECTOR_SIZE填的和颗粒实际的擦除粒度不一致。填成 64KB 的时候littlefs 以为一个块就是 64KB做 GC 时会按这个粒度搬数据但硬件擦除的最小单位是 4KB中间的映射关系全乱了。解决查颗粒手册的 Sector Erase 那一栏确认是 4KB 还是 64KB填最小的那个粒度。改完宏之后必须整片擦除一次再重新格式化因为旧的文件系统元数据是按错误块大小写的不清掉会一直报错。5.3 堆搬到 SDRAM 之后偶发数据错乱现象跑一段时间之后某个全局对象的值莫名变成 0或者字符串里出现乱码重启之后又正常。原因Cache 一致性。CPU 写的数据还在 D-Cache 里DMA 或者另一条总线上的外设读的是 SDRAM 里的旧值。另一种可能是 SDRAM 时序里的 tWR 或 tRP 填小了在高温或者批次不同的颗粒上刚好临界。解决先关 Cache 跑一遍如果问题消失就确认是一致性问题按 4.3 的 MPU 方案配如果关 Cache 还在错那就是时序问题把所有时序参数各加 1 个周期再试能稳定就说明原来那组是临界的别心疼那点性能。5.4 烧写成功但程序不跑调试器显示停在 0x90000000现象DFU 或者 SWD 烧写都提示成功复位之后程序不执行调试器读 PC 发现是 0x90000000 开头的地址。原因链接脚本里把一部分代码段放到了 QSPI 的映射区但启动时 QSPI 还没进入内存映射模式CPU 从那里取指直接取到无效数据。解决要么把代码全部放回内部 FlashQSPI 只做数据区要么在启动代码的最前面加一段从内部 Flash 运行的 QSPI 初始化初始化完再跳转到外部。后者对时序要求很高没有特殊需求不建议做。5.5 REPL 输出乱码或者干脆没输出现象串口工具能连上但收到的全是不可打印字符或者一个字节都没有。原因换了 HSE 频率或者改了 PLL 分频之后UART 的波特率计算基准变了但MICROPY_HW_HSE_VALUE没跟着改另一种是板子上的 USB 虚拟串口和物理串口同时使能固件默认输出走的是另一个。解决先把MICROPY_HW_HSE_VALUE和板上晶振核对一遍再看mpconfigboard.h里MICROPY_HW_UART_REPL和MICROPY_HW_USB_...的开关确保你接的那个口就是固件输出的那个口。这两个都对了还是乱码就把波特率降到 115200 试一次排除时钟本身的偏差。6. 验收与进阶三组基准数据判断这套移植到底成没成移植完不等于能用我一般会跑三组数据来验收每组都有明确的通过标准达不到就说明还有地方没配对。第一组是存储容量和稳定性import os, time # 容量核对32M 的 flash 格式化之后可用应该在 30MB 上下 st os.statvfs(/) print(flash total MB:, st[2] * st[0] // (1024 * 1024)) # 写满再删验证 GC 和目录结构没坏 chunk ba * 32768 t0 time.ticks_ms() for i in range(200): with open(/f%d.bin % i, wb) as f: f.write(chunk) print(write 6.4MB:, time.ticks_diff(time.ticks_ms(), t0), ms) for i in range(200): os.remove(/f%d.bin % i) print(free after clean:, os.statvfs(/)[3] * os.statvfs(/)[0] // (1024 * 1024))通过标准是写入过程中不报 ENOSPC删除之后可用空间回到接近初始值。如果删完空间回不来说明 littlefs 的 GC 没正常工作多半还是块大小配错了。第二组是 SDRAM 带宽直接反映时序和 Cache 配得对不对import time # 用 mem32 直接扫 SDRAM测纯写带宽 base 0xC0000000 n 1024 * 1024 # 1MB 个字 t0 time.ticks_us() for i in range(0, n, 4): import machine machine.mem32[base i] i dt time.ticks_diff(time.ticks_us(), t0) print(write 4MB:, dt / 1000, ms,, 4 * 1024 * 1024 / (dt / 1e6) / 1e6, MB/s)32 位总线、200MHz FMC 时钟下理论峰值是 400MB/s实测能到 150~250MB/s 就算正常低于 80MB/s 一般是没开 Cache 或者时序填得太保守。第三组是 GC 耗时这决定了板子能不能跑长时间业务import gc, micropython gc.collect() micropython.mem_info() # 反复分配释放看 GC 耗时是否稳定 import time t0 time.ticks_ms() for _ in range(200): a bytearray(4096) del a gc.collect() print(200 次 GC:, time.ticks_diff(time.ticks_ms(), t0), ms)单次 GC 超过 20ms 就值得优化了通常是因为堆里有大量小对象或者堆被放在了 SDRAM 上。进阶方向有两个一是固件裁剪把manifest.py里用不到的模块去掉把业务脚本预编译成 .mpy 打进固件启动速度能提升一倍以上二是把这套板级目录整理成模板新板子只要改 pins.csv 和时序表就能复用。我现在的习惯是从那以后每次拿到一块新板子不管多急都强制走一遍 SDRAM 全片读写、QSPI 擦写循环、REPL 连续输出这三件套全程不到五分钟但能省掉后面几天的返工。源码包里带的就是这套已经跑通的板级目录和时序表拿过去对着自己的颗粒改几个数就能编译希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站