STM32H743 MicroPython移植实战:外扩QSPI Flash与SDRAM配置 📅 发布时间:2026/9/9 2:23:03 👁 浏览次数: 简介针对STM32H743高性能微控制器平台这份源码包为需要移植MicroPython并扩展大容量存储与内存的嵌入式开发者提供了一套可直接集成的板级配置方案。开发者只需将源码目录放入MicroPython官方源码的ports/stm32/boards路径下按提示编译即可生成支持32MB QSPI Flash与32MB SDRAM的固件适用于需要运行Python脚本同时缓存大量数据的AI边缘计算、人机交互或音视频处理场景。资源共5个文件包含C源文件、H头文件、Makefile配置片段及CSV引脚映射表分别承担外设驱动、板级宏定义、编译选项与引脚初始化逻辑压缩包仅4KB结构精简。该资源已在CSDN累积1356人学习适合具备STM32基础、希望借助MicroPython快速开发H743板载资源的开发者参考。通过这份源码可以熟悉板级BSP代码的定制流程节省自行移植Flash和SDRAM驱动的时间。1. 整体方案与移植思路拆解1.1 为什么选STM32H743这块MCU做这个项目之前我其实纠结过一阵子到底是继续用F407、F767还是直接上H743。最后选H743核心就两个字算力。STM32H743是Cortex-M7内核主频480MHz带双精度FPU和L1 Cache基础算力几乎是F4的3倍以上。Micropython本身是一套解释执行加运行时调度的东西解释器的字节码循环、内存分配、GC回收还有后期的LSP、闭包、异常机制硬件性能不够的话跑起来非常拖沓。H743在这个场景下才真正让Python代码有“顺畅”的感觉。另外H743内置2MB Flash和1MB RAM这在MCU里已经算“大别墅”了。但真要跑Micropython文件系统、用户脚本、字体库、图片资源一放进去2MB Flash依然不够用RAM也一样Python对象、帧缓冲、网络协议栈都很吃内存。所以这个项目的关键词不是“能不能跑”而是“怎么跑得爽、存得下”这就引出了外扩32MB Flash和32MB SDRAM的需求。1.2 Micropython移植的本质Micropython不是普通的库它本身就是一个完整的、可裁剪的操作系统式固件。它把Python代码逐行编译成字节码然后在自己的虚拟机上执行。移植工作本质上分两部分一是把MCU的启动代码、时钟树、外设驱动接入Micropython的硬件抽象层二是把外设功能注册成Python可调用的模块。STM32H743在Micropython官方仓库里已经有现成的移植基础官方NUCLEO-H743ZI开发板的配置可以直接参考。但如果你想用自己画的板子或者要外扩Flash和SDRAM就必须要自己做一套board配置。官方源码比很多人想象的要干净目录结构很清晰ports/stm32/boards/下每个子目录就是一块开发板的全部配置包含头文件、链接脚本、引脚映射表等。我们要做的就是把NUCLEO-H743ZI拿到改成自己板子的引脚和存储布局。1.3 外扩存储的选型逻辑Flash这块标题里写的SQPI其实就是QSPI也就是Quad SPI四线SPI接口。为什么不用并口Nor Flash因为引脚不够用STM32H743的FMC总线上挂着SDRAM再把并口Flash塞进去布线会变成噩梦。QSPI只需要6根引脚CLK、NCS和4根IO线就能读写出32MB速度还能跑到几十MHz完全能覆盖用户存储、Python脚本、资源文件这些需求。SDRAM选32MB是因为很多GUI场景LVGL、TFT显示缓冲、触摸屏交互需要大块的动态内存。SDRAM虽然比SRAM慢、延迟高但胜在容量大、价格低而且H743内置了FMC控制器专门驱动SDRAM硬件上几乎零成本唯一的挑战是时序配置。2. 外扩存储硬件设计与配置要点2.1 QSPI Flash接线与芯片选型QSPI Flash芯片我推荐用W25Q256JV或者GD25Q256两者引脚兼容性很好容量都是32MB256Mbit支持四线模式指令集也都兼容。接线比较关键以我用的H743板子为例QSPI接口挂在了QUADSPI外设的Bank1上CLKPB2NCSPB6IO0PD11IO1PD12IO2PE7IO3PE8当然不改代码直接用它们的话必须在board配置里把对应引脚声明清楚。如果不想看手册慢慢翻直接在CubeMX里配置QUADSPI图形界面会生成正确的引脚表你再照着填到pins.csv里就好。这里有个非常重要的点QSPI Flash在Micropython中主要承担文件系统的载体需要格式化成FAT或LittleFS文件系统。H743的QUADSPI控制器缓存非常有限读写操作如果走内存映射模式Memory-Mapped mode会很爽代码执行直接对Flash地址解引用但文件系统读写走的是间接模式Indirect mode这里要注意地址对齐我自己就吃过亏下面会细说。2.2 SDRAM接线与FMC时序SDRAM我这边用的是W9825G6KH32MB16位数据宽度该芯片兼容性好、资料多是很多H743板子的默认选择。SDRAM挂在FMC的Bank1由H743的FMC控制器统一管理。接线方面最关键的是时钟线SDCLK、片选SDNE、行/列选通SDNRAS、SDNCAS、写使能SDNWE以及16根数据线和若干地址线。初始化SDRAM时时序参数调不对上电初始化就会卡死或者HardFault。以100MHz的SDCLK为例我最终调好的参数是这样的参数项HAL字段参考值装载到激活延迟LoadToActiveDelay6退出自刷新延迟ExitSelfRefreshDelay6自刷新时间SelfRefreshTime6行周期延迟RowCycleDelay6写恢复时间WriteRecoveryTime2行预充电延迟RPDelay6行列选通延迟RCDDelay6这些参数值看起来有点“玄学”实际上就是SDRAM芯片手册里的时序要求换算过来的时钟周期数每一个都对应芯片内部的一个物理操作耗时。网上很多移植不成功的先例就是RowCycleDelay和RCDDelay填得太小导致SDRAM内部状态机还没准备好下一次访问就来了。2.3 PCB布局和电源建议既然是自己做板子布局上还是多说两句。QSPI Flash和SDRAM都建议靠近MCU放置SDRAM的数据线、地址线做等长处理时钟线最好单独包地。H743的VDD要特别注意480MHz主频下功耗不低模拟供电VDDA建议用磁珠隔离加上10uF和100nF去耦电容。Micropython跑起来之后如果板子突然发热大部分是内核电压被拉得太低造成的而不是程序问题。3. Micropython固件编译与移植实操3.1 编译环境搭建移植Micropython第一步是把工具链准备好我这里用的是Linux环境Windows下用WSL也一样。需要安装gcc-arm-none-eabi工具链版本建议10.3或更高太老的编译器对Cortex-M7的优化支持不好编译出来的固件可能跑出神秘问题。然后拉取官方仓库git clone https://github.com/micropython/micropython.git cd micropython git submodule update --initgit submodule update --init这步一定不能省Micropython依赖lib/目录下的几个第三方库比如berkeley-db、stm32lib不更新子模块会在编译时直接报错。拉取完成后先编译一个干净的官方NUCLEO-H743ZI固件验证工具链没问题cd ports/stm32 make BOARDNUCLEO_H743ZI submodules make BOARDNUCLEO_H743ZI -j8这一步能顺利产出build-NUCLEO_H743ZI/firmware.hex说明环境OK。接下来再开始改自己的板子。3.2 创建自定义Board在ports/stm32/boards/下复制一份NUCLEO_H743ZI目录重命名为H743_CUSTOM然后开始改四个核心文件mpconfigboard.h、mpconfigboard.mk、stm32h7xx_hal_conf.h、linker脚本。mpconfigboard.h里主要配置时钟和引脚。H743默认外部晶振如果是25MHz需要设置#define MICROPY_HW_MCU_NAME STM32H743 #define MICROPY_HW_CLK_USE_BOARD_XTAL (1) #define MICROPY_HW_XTAL_FREQ (25000000)接着是QSPI和SDRAM相关配置。QSPI部分6根引脚的宏定义都要写对#define MICROPY_HW_QSPI_BK1_CS (pin_PB6) #define MICROPY_HW_QSPI_BK1_IO0 (pin_PD11) #define MICROPY_HW_QSPI_BK1_IO1 (pin_PD12) #define MICROPY_HW_QSPI_BK1_IO2 (pin_PE7) #define MICROPY_HW_QSPI_BK1_IO3 (pin_PE8) #define MICROPY_HW_QSPI_FLASH_SIZE (32 * 1024 * 1024)MICROPY_HW_QSPI_FLASH_SIZE这个宏一定要和实际Flash大小一致因为固件会用它计算块设备的分区信息。SDRAM部分则通过HAL库初始化在stm32h7xx_hal_conf.h里确保HAL_SDRAM_MODULE_ENABLED和HAL_QUADSPI_MODULE_ENABLED两个宏开启。链接脚本也要同步修改。如果你的固件代码超过了芯片内部Flash容量限制或者希望把整个Micropython镜像做成“先搬一部分到外部Flash执行”的复杂布局这一步会变得很棘手。但常规做法还是保持链接脚本不变固件代码跑在内部2MB Flash里外部Flash只当文件系统使用。3.3 固件编译与烧写配置完成后重新编译make BOARDH743_CUSTOM -j8编译产物在build-H743_CUSTOM/下。烧写方式有两种最简单的是用STM32CubeProgrammer通过ST-LINK直接烧写firmware.hex。另外一种是用自带Bootloader走DFU升级需要先把firmware.dfu文件烧进去。不管哪种方式烧完后用串口连接板子的USART1打开任意串口终端波特率115200能看到Micropython的交互式命令行REPL就说明移植成功了。当然如果你不想自己编译官方源码也已经有很多现成的源码工程可以直接clone里面包含了QSPI和SDRAM的初始化模块、文件系统挂载脚本、SDRAM测试命令等省去不少重写底层的时间。但我个人建议还是至少亲自编译一遍这样后面遇到问题你知道去哪里找原因。3.4 源码中的关键模块解析代码层面QSPI和SDRAM的初始化分别集中在两个模块里。先说QSPI初始化流程大致是配置GPIO复用功能 - 初始化QUADSPI外设 - 发送读ID命令读取Flash的JEDEC ID确认通信正常 - 把Flash切换到四线模式 - 注册块设备。void qspi_flash_init(void) { // 启用外设时钟配置引脚复用 QUADSPI_InitTypeDef qspi_init {0}; qspi_init.ClockPrescaler 16; qspi_init.FifoThreshold 4; qspi_init.SampleShifting QUADSPI_SAMPLE_SHIFTING_HALFCLK; qspi_init.FlashSize 25; // 2^(251) 32MB按实际容量计算 HAL_QSPI_Init(hqspi, qspi_init); }FlashSize字段最容易出错。H743的手册里写的是“FlashSize FlashSize 1”比如32MB对应地址位宽为25位代码里写的是25。如果写错读取地址空间会直接装换出错表现为文件系统挂在但读写异常。SDRAM初始化部分则复杂很多。除了前面表格里的时序参数还需要配置SDRAM的工作模式寄存器Mode Register。这里有个坑W9825G6KH的突发长度设置为1CAS延迟设置为2或3具体由时序参数决定。如果CAS配置不对读数据时数据线上采到的值会整体往后挪一个CLK表现就是读出来的数据全是乱的但写进去是对的非常隐蔽。4. 常见问题与排查技巧实录4.1 上电后反复HardFault这个是最常见的而且经常出现在SDRAM初始化之后。排查方法很简单用ST-LINK连接STM32CubeProgrammer打开RDP寄存器页面看HardFault时PC指针在哪个地址。如果是卡在SDRAM初始化里大概率是时序参数不够宽裕。我遇到过一次RCDDelay设4能开机运行一会儿还是死最后加到6才稳定下来。另外有个细节H743的FMC SDRAM控制器会在上电后发出SDRAM初始化指令序列包括预充电PALL、自动刷新AUTO REFRESH和模式寄存器写入LOAD MODE REGISTER这个序列的时序是硬编码在FMC模块里的如果你的SDRAM对刷新间隔很敏感系统上电阶段容易出现偶发死机解决方式是适当调低SDCLK频率比如从100MHz减到80MHz。4.2 QSPI文件系统挂载不成功QSPI Flash挂载文件系统时报“OSError: [Errno 19] ENODEV”是典型问题。三个原因一是MICROPY_HW_QSPI_FLASH_SIZE大小写错这个宏定义会影响块设备的扇区数二是Flash芯片本身是全新的没有写入文件系统结构需要先格式化三是最隐蔽的QSPI和SDRAM的操作冲突。H743的QUADSPI和FMC都挂在同一个AXI总线上初始化顺序如果搞反可能出现一方把总线占住另一方超时。解决办法是把QSPI初始化放在SDRAM之前或者至少保证两者不在同一个临界区内同时操作。Micropython的GC垃圾回收一旦执行必须保证Flash和SDRAM不会访问冲突因为GC是无法交给Python控制的。格式化方面首次使用建议在REPL里手动执行一次全盘擦除和文件系统创建import os from machine import SPIFlash flash SPIFlash() os.VfsLfs2.mkfs(flash) os.mount(flash, /ext)4.3 SDRAM为什么不能直接并入GC堆很多人拿到32MB SDRAM后的第一反应是把它全部加入Micropython的堆内存让Python可以自由分配几百KB的列表。想法没问题但实际做要慎重。SDRAM比芯片内置SRAM慢得多GC扫描一整块32MB的内存时每次全量回收都可能卡顿几十毫秒这对实时性要求高的外设交互来说不可接受。更合理的做法是SDRAM只用来做显存、音频缓冲、帧缓冲等大块固定内存的存放地不要直接给Python解释器做堆内存。你可以通过C扩展模块在Python层拿到一个指向SDRAM地址的memoryview对象然后写像素、写音频数据buf memview_sdram() # 自定义C模块返回SDRAM地址的memoryview buf[0:1024] image_data这样既绕过了Python堆的GC压力又真正利用了SDRAM的大容量特性。如果你确实想测试SDRAM的读写速度可以在REPL里用machine.mem32直接解锁地址空间比如SDRAM映射在0xC0000000machine.mem32[0xC0000000] 0x12345678 print(hex(machine.mem32[0xC0000000]))如果这一步打印出来的不是0x12345678那SDRAM的初始化时序大概率还有问题。4.4 工具链版本导致的神秘崩溃编译Micropython还有一个非常容易踩坑的地方就是工具链版本。旧版gcc-arm-none-eabi比如9.x在编译H743的代码时优化级别开到-O2有概率生成错误的指令调度导致固件运行时偶发死机而且死机位置完全随机。最开始排查这个问题时我一度怀疑是SDRAM时序问题直到换成gcc-arm-none-eabi 10.3之后同样的代码稳定跑了好几天都没出事才确认是编译器背锅。所以建议第一工具链版本不要低于10.3第二mpconfigboard.mk里的优化级别如果没有特殊需求不要轻易去改它。你在网上看到别人说“开-O2跑不过去改-O0就好了”多半不是优化级别的问题而是编译器版本太老。最后再分享一个调试小技巧如果REPL能进但Python代码一跑就崩先把GC阈值调大一点#define MICROPY_GC_ALLOC_THRESHOLD (16 * 1024)比如上面这样的配置意思是堆内存累积分配超过16KB才触发GC。不过这个只是临时排查手段真正找到问题根源后还是要恢复默认值不然长时间运行内存占用会一直涨上去。移植这种外扩存储的方案说到底就是“内存布局 时序配置 工具链”的三方博弈哪个环节松动整机都会给你脸色看。本文还有配套的精品资源点击获取