ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

STM32H750外挂QSPI Flash的MDK下载算法(FLM)制作详解

STM32H750外挂QSPI Flash的MDK下载算法(FLM)制作详解 1. 为什么要手写QSPI下载算法从串口升级的痛点说起1.1 场景痛点H750只有128KB内部FlashApp无处安放做STM32H750项目的人大概率都经历过同一个尴尬这颗芯片性能是够猛主频480MHz带硬件JPEG编解码器、带TFT-LCD控制器、带一堆高级外设但偏偏内部Flash只给128KB。放个轻量级RTOS加个UI框架再塞两版图像资源Flash就红了。要是产品还得做远程升级Bootloader加App双分区128KB根本不够分。所以现在H750方案绕着绕去最后基本都会落到外挂一颗QSPI Flash上。最常用的就是华邦的W25Q324MB容量8脚封装单颗不到两块钱读速度还不慢。H750自带的QUADSPI外设能把这颗Flash直接映射到0x90000000地址支持XIP就地执行理想情况下代码和数据都能放在外面。硬件方案是顺了烧录方案却成了新麻烦。常规做法是写一个Bootloader放内部Flash启动后用串口接收固件再写入W25Q32。听起来没毛病实际用起来特别磨人调试阶段每天要烧几十次固件重复做拔串口线、按复位键、进Boot模式、打开上位机、选文件、等进度条、断点重启这一套流程。运气好一次过运气不好传输中断、Flash擦写失败还得重新来。最要命的是Bootloader本身如果有Bug或者串口驱动偶发异常板子直接变砖只能重新接SWD把Bootloader刷回来。所以很多人问我能不能让MDK像烧内部Flash那样点一下download程序直接进W25Q32可以做。关键就是给MDK做一个针对W25Q32的下载算法也就是FLM文件。1.2 下载算法的工作原理一个跑在RAM里的临时烧录器MDK的Flash下载算法本质上是一个编译出来的小工具扩展名是.flm。它包含一段面向特定Flash芯片的烧录驱动以及一份设备参数表。MDK在下载程序时并不会直接拿调试器去操作Flash而是先通过调试接口把这段驱动代码加载到单片机RAM里然后让CPU去执行这段代码由它通过QSPI外设完成Flash的擦除、写入和校验。你可以把它理解成“临时Bootloader”平时不存在MDK烧录时才被灌输到RAM里干完活就消失完全不影响应用代码。MDK对下载算法暴露的接口是固定的核心函数定义在FlashOS.h里几个必须实现的如下Init初始化QSPI外设和Flash芯片失败返回非0值UnInit反初始化比如退出QSPI外设EraseSector擦除指定扇区EraseChip整片擦除可选ProgramPage写入一段数据通常是一页Verify回读校验可选但强烈建议保留MDK烧录的过程是先调Init再根据固件地址擦除对应扇区然后以页为单位调ProgramPage写入最后调Verify回读比对。所有步骤对用户不可见界面上就是那个进度条。我们做W25Q32的下载算法实际上就是实现这几个函数在函数里用QSPI命令操作Flash编译成FLM文件放进MDK的Flash算法目录MDK就能在下拉列表里看到我们这颗“W25Q32”。1.3 为什么MDK默认认不出W25Q32Keil官方自带的Flash算法覆盖了大量MCU内部Flash但W25Q32这类外挂SPI/QSPI Flash基本都在默认列表之外。道理不难理解外部Flash的数据引脚接在哪个GPIO、时钟分频是多少、CS用哪根线每个板子都不一样MDK不可能给你预设一套万能驱动。所以W25Q32的下载算法从一开始就注定要自己写。还有一点更要提醒网上能搜到不少现成的H750 QSPI算法FLM文件下载下来往Keil目录一丢就能用看起来省事但隐患很大。别人板子上的QSPI引脚映射和你的大概率不一样FLM里写死的GPIO初始化、QSPI外设配置放在你的板子上很容易Verify Failed或者卡死。哪怕同一个芯片型号不同开发板引脚都可能不同。所以自己编译FLM确认引脚和参数才是稳妥做法。2. 制作下载算法前的准备工具选型与工程结构剖析2.1 软硬件清单与版本选择准备材料不复杂大致如下一块带W25Q32的STM32H750开发板 QSPI Flash型号可以换W25Q64只需要改几个参数调试器ST-Link V2或者J-Link都行FLM调试阶段需要频繁下载MDK 5.30以上版本我这边用的是5.37版本稳定很重要STM32CubeH7固件包主要取HAL库源码尤其是stm32h7xx_hal_qspi.cW25Q32数据手册查命令码、JEDEC ID、时序参数用Keil安装目录下的Flash算法模板工程我这里特别想强调MDK版本统一。FLM文件格式本身没有强版本绑定但实际使用中高版本MDK编译出来的FLM放到老版本Keil里偶尔会加载报错。如果你在办公室和家里各有一台开发电脑最好让两边的MDK保持同一版本不然会碰到“代码在我电脑上能烧在同事电脑上就No Flash Device”的怪问题。2.2 分清几种Flash接口SPI、QuadSPI、OSPI的区别动手写代码前先把接口概念理清楚。很多人看到W25Q32带“QSPI”字样以为它用的是H750的OCTOSPI外设这是新手最容易犯的错。普通SPI四根线SCK、CS、MOSI、MISO同一时刻数据只能一根线进出Quad SPI在普通SPI基础上增加两根数据线合计四根数据线IO0到IO3读和写都可以四线并行OSPI八根数据线对应STM32H7系列里的OCTOSPI外设W25Q32是Quad SPI接口用的是H750的QUADSPI外设不是OCTOSPI。购买Flash芯片时也要注意别买成了QSPI NOR之外的型号W25Q32这颗具体型号是W25Q32JVSIQ之类的变体接口都兼容。还有一个关键细节W25Q32支持一个0x38命令进入QPI模式进去之后连指令都走四线传输。这个模式对下载算法来说没有实际意义反而会增加复杂度。我们做算法时通常只用“指令单线、地址单线、数据按需四线”这种方式也就是常说的1-1-4模式稳定而且兼容性最好。2.3 理解Flash算法工程的三个关键文件一个标准Keil Flash算法工程核心就三个文件文件作用FlashOS.h定义算法接口、FlashDevice结构体、函数原型FlashDev.c填充设备的描述信息起始地址、容量、页大小、扇区大小FlashPrg.c实现Init、EraseSector、ProgramPage等实际驱动函数FlashDev.c里的结构体相当于一张“设备注册表”MDK靠它判断这颗Flash能被识别成什么样。FlashPrg.c是核心驱动所有QSPI命令都在这里发送。编译时Flash算法工程会通过特殊的分散加载配置把全部代码分配到RAM地址空间。也就是说FLM运行期间不依赖任何Flash存储器所有指令和只读数据都待在RAM里。这也是为什么FLM里不能随便引用放在Flash地址的常量或调用半主机函数后面踩坑部分会详细说。3. 手把手实现W25Q32的FLM下载算法3.1 从模板创建Flash算法工程Keil自带的Flash算法模板通常在以下目录C:\Keil_v5\ARM\Flash\_Template里面包含了FlashOS.h、FlashDev.c、FlashPrg.c和现成的工程文件。用MDK打开模板工程另存为比如W25Q32_QSPI.uvprojx然后开始改代码。如果这个目录在你安装的MDK版本里不存在也没关系从ARM/Flash目录下随便复制一个STM32H7系列的算法工程删掉原有FlashPrg.c和FlashDev.c内容换成我们的代码即可。打开工程后第一件事是检查Options for Target的Output标签页确认“Create Flash Loader Algorithm”这个复选框已经被勾选。这个选项决定了MDK最终生成的是FLM文件而不是普通axf文件。新建的空工程默认不会勾选漏了这一步编译产物烧不进去。3.2 修改FlashDev.c把W25Q32的参数填进去直接给出可用的结构体内容#include FlashOS.h struct FlashDevice const FlashDevice { FLASH_DRV_VERS, // 驱动版本号保持默认 W25Q32 QSPI 0x90000000, // 设备名称会显示在MDK算法列表里 EXTSPI, // 设备类型外部SPI 0x90000000, // 设备起始地址对应QSPI内存映射地址 0x00400000, // 设备大小4MB 256, // 页大小W25Q32的页是256字节 0, // 保留字段填0 0xFF, // 擦除后的默认值 2000, // 页编程超时时间单位ms 3000, // 扇区擦除超时时间单位ms { { 0x1000, 0x1000 }, // 扇区大小4KB { SECTOR_END } // 结束标记 } };字母上容易被坑的几个点单独拿出来说设备大小是0x400000等于4MBW25Q32的32Mbit除以8很多人会填成0x40000那就是256KB肯定不对页大小填256。W25Q32的标准页编程一次最多256字节。也有人把页大小填4096这样MDK会一次性传给ProgramPage一整个扇区你的Program函数内部就必须做切割循环否则数据会丢。像我这样直接填256逻辑最省心超时值给了2000ms和3000ms实际上W25Q32的页编程典型时间是不到10ms扇区擦除典型时间45到400ms之所以给这么大余量是为了防止芯片老化、供电不稳或者高温环境下的异常情况。3.3 编写FlashPrg.cInit、UnInit、EraseSector、ProgramPage#include FlashOS.h #include stm32h7xx_hal.h static QSPI_HandleTypeDef hqspi; /* QSPI GPIO初始化引脚必须按照你板子的原理图修改 */ static void QSPI_MspInit_My(void) { GPIO_InitTypeDef GPIO_InitStruct {0}; __HAL_RCC_QUADSPI_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOE_CLK_ENABLE(); GPIO_InitStruct.Mode GPIO_MODE_AF_PP; GPIO_InitStruct.Pull GPIO_NOPULL; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate GPIO_AF10_QUADSPI; /* 下面这组引脚来自我手头板子仅供参考 */ GPIO_InitStruct.Pin GPIO_PIN_2; /* QUADSPI_CLK */ HAL_GPIO_Init(GPIOB, GPIO_InitStruct); GPIO_InitStruct.Pin GPIO_PIN_10; /* QUADSPI_BK1_IO0 */ HAL_GPIO_Init(GPIOE, GPIO_InitStruct); GPIO_InitStruct.Pin GPIO_PIN_11; /* QUADSPI_BK1_CS */ HAL_GPIO_Init(GPIOE, GPIO_InitStruct); GPIO_InitStruct.Pin GPIO_PIN_12; /* QUADSPI_BK1_IO1 */ HAL_GPIO_Init(GPIOE, GPIO_InitStruct); GPIO_InitStruct.Pin GPIO_PIN_13; /* QUADSPI_BK1_IO2 */ HAL_GPIO_Init(GPIOE, GPIO_InitStruct); GPIO_InitStruct.Pin GPIO_PIN_14; /* QUADSPI_BK1_IO3 */ HAL_GPIO_Init(GPIOE, GPIO_InitStruct); }再写Init函数int Init(unsigned long adr, unsigned long clk, unsigned long fnc) { uint8_t jedec[3]; QSPI_MspInit_My(); hqspi.Instance QUADSPI; hqspi.Init.ClockPrescaler 2; hqspi.Init.FifoThreshold 4; hqspi.Init.SampleShifting QSPI_SAMPLE_SHIFTING_HALFCLK; hqspi.Init.FlashSize 21; /* 2^22 4MB */ hqspi.Init.ChipSelectHighTime QSPI_CS_HIGH_TIME_5_CYCLE; hqspi.Init.ClockMode QSPI_CLOCK_MODE_0; hqspi.Init.FlashID QSPI_FLASH_ID_1; hqspi.Init.DualFlash QSPI_DUALFLASH_DISABLE; HAL_QSPI_Init(hqspi); /* 软复位Flash清除以往可能残留的模式 */ QSPI_SendCommand(0x66, 0, 0); QSPI_SendCommand(0x99, 0, 0); /* 读取JEDEC IDW25Q32响应 EF 40 16 */ QSPI_ReadID(jedec); if (jedec[0] ! 0xEF || jedec[1] ! 0x40 || jedec[2] ! 0x16) { return 1; /* INIT失败 */ } return 0; /* 成功 */ }这里几个关键点必须说明。FLM的Init函数返回值约定是0代表成功非0代表失败MDK会直接报Init error。如果芯片不在位、SPI引脚接错、ID读不对返回1能帮你快速定位问题。EraseSector和ProgramPage实现如下int EraseSector(unsigned long adr) { uint32_t flash_addr adr - 0x90000000; QSPI_WriteEnable(); QSPI_SendAddrCommand(0x20, flash_addr, 0); /* Sector Erase, 4KB */ QSPI_WaitBusy(); return 0; } int ProgramPage(unsigned long adr, unsigned long sz, unsigned char *buf) { uint32_t flash_addr adr - 0x90000000; QSPI_WriteEnable(); QSPI_SendAddrCommand(0x02, flash_addr, sz); /* Page Program */ HAL_QSPI_Transmit(hqspi, buf, sz, 1000); QSPI_WaitBusy(); return 0; }我写的ProgramPage默认一次最多256字节因为FlashDev结构体页大小已经填了256MDK会严格按这个值调用。如果你出于某种原因把页大小改大这里必须加循环切割。UnInit就比较简单int UnInit(unsigned long fnc) { /* 建议不要在这里DeInit QSPI否则后续校验可能失败 */ return 0; }这是我自己的习惯UnInit里不做任何反初始化动作。原因在于MDK在下载完成后可能会通过算法继续读Flash做校验你把QSPI外设关了回读失败直接报Verify Failed。让QSPI保持可用状态反而是最安全的做法。3.4 QSPI底层命令的HAL封装与关键时序底层命令封装的代码骨架如下static void QSPI_WriteEnable(void) { QSPI_CommandTypeDef cmd {0}; cmd.Instruction 0x06; cmd.InstructionMode QSPI_INSTRUCTION_1_LINE; cmd.AddressMode QSPI_ADDRESS_NONE; cmd.DataMode QSPI_DATA_NONE; HAL_QSPI_Command(hqspi, cmd, 100); } static void QSPI_SendAddrCommand(uint8_t instruction, uint32_t addr, uint32_t nbData) { QSPI_CommandTypeDef cmd {0}; cmd.Instruction instruction; cmd.InstructionMode QSPI_INSTRUCTION_1_LINE; cmd.AddressMode QSPI_ADDRESS_24_BITS; cmd.AddressSize QSPI_ADDRESS_24_BITS; cmd.Address addr; cmd.DataMode (nbData 0) ? QSPI_DATA_1_LINE : QSPI_DATA_NONE; cmd.NbData nbData; HAL_QSPI_Command(hqspi, cmd, 100); } static void QSPI_ReadID(uint8_t *id) { QSPI_CommandTypeDef cmd {0}; cmd.Instruction 0x9F; cmd.InstructionMode QSPI_INSTRUCTION_1_LINE; cmd.AddressMode QSPI_ADDRESS_NONE; cmd.DataMode QSPI_DATA_1_LINE; cmd.NbData 3; HAL_QSPI_Command(hqspi, cmd, 100); HAL_QSPI_Receive(hqspi, id, 3, 100); } static void QSPI_WaitBusy(void) { uint8_t status 0; QSPI_CommandTypeDef cmd {0}; do { cmd.Instruction 0x05; /* Read Status Register */ cmd.InstructionMode QSPI_INSTRUCTION_1_LINE; cmd.AddressMode QSPI_ADDRESS_NONE; cmd.DataMode QSPI_DATA_1_LINE; cmd.NbData 1; HAL_QSPI_Command(hqspi, cmd, 100); HAL_QSPI_Receive(hqspi, status, 1, 100); } while (status 0x01); }几个容易理解错的细节W25Q32所有指令默认单线发送所以InstructionMode统一用QSPI_INSTRUCTION_1_LINE。只有当你想在数据传输阶段启用四线模式时才把DataMode改成QSPI_DATA_4_LINESQSPI_WaitBusy不能省。W25Q32的擦除和页编程都是“发出命令后芯片自己忙”你必须读状态寄存器0x05等BIT0变成0才算结束。省略这一步紧接着发下一条写命令时芯片可能还在忙命令被直接丢弃后果就是数据错乱或者缺失FlashSize21是由公式容量 2^(FlashSize1)算出来的W25Q32是4MB2^22所以FlashSize21。填错一位地址空间就偏差一倍另外说明我上面这组代码是HAL库风格。如果你用的是从网上下载的寄存器版模板思路完全一样只是把HAL_QSPI_Command换成直接操作QUADSPI-CCR、QUADSPI-AR、QUADSPI-DLR这些寄存器。寄存器版更精简也不容易被库里的超时机制影响但可读性差一些。两者选一个不必纠结。4. 编译FLM并在MDK工程里启用从FLM到一键下载4.1 FLM工程编译配置与注意事项算法源码改完后编译前建议花两分钟把工程设置逐个过一遍Output标签页确认“Create Flash Loader Algorithm”已勾选C/C标签页优化级别选O0或者最多O1。下载算法里的时序查询和状态轮询一旦被编译器自作聪明优化掉一部分很容易出现烧录到一半卡住的问题C/C标签页预处理符号里加STM32H750xx具体大小写以你的HAL库版本为准不然头文件匹配不到Linker标签页分散加载文件保持模板默认。不要自己手写Keil模板工程里已经配置好了“代码全部放RAM”的策略编码方面建议源文件保存为UTF-8 Signature格式。MDK老版本的默认编码是GB2312如果你之前在MDK里写过带中文注释的代码切到UTF-8工程后显示是乱码不影响编译但很影响心情。更重要的还是团队协作不同编码的文件提交到Git里每次切换分支都可能产生大量无意义的编码差异提前统一会省很多事。编译成功后会生成一个W25Q32_QSPI.FLM文件。通常我习惯直接把它复制到C:\Keil_v5\ARM\Flash目录这样在MDK的算法列表里可以直接看到设备名。如果你不想动Keil安装目录也可以把FLM放到工程目录在Add按键弹出的对话框里直接选择路径效果一样。有一个经验MDK在你修改了FLM文件之后如果Options窗口一直开着新算法不一定立刻刷新出来。建议先关掉Options再重新打开或者干脆重启一下MDK比你反复点刷新有效得多。4.2 在目标工程中配置QSPI下载算法现在打开你真正要烧录的App工程进入Options for Target的Utilities标签页点Settings进Flash Download把刚做好的W25Q32算法添加进来。添加后需要确认两件事Start Address填0x90000000Size填0x00400000这时候最容易忽略的一个点是MDK并不是看你添加了哪个算法就自动用哪个而是根据目标文件里的加载地址去匹配算法。如果你的App编译后加载地址还是在0x08000000也就是内部Flash区域MDK用到的依然是内部Flash算法你添加的QSPI算法根本不会被触发。所以想让程序下载到W25Q32必须回到App工程的Target标签页把IROM1的Start地址改成0x90000000Size改成0x00400000。这一步做完MDK从镜像文件里看到加载地址是0x90000000才会自动调用W25Q32算法。4.3 目标工程链接地址设置把App放到0x90000000把App链接到0x90000000听起来就是改个IROM1地址实际还有几个坑向量表VTOR。程序启动后CPU从0x90000000读取栈指针和复位向量但中断向量表的位置需要软件设置。很多App会把SCB-VTOR设置成0x90000000如果漏了这一步中断一触发就会跑飞启动方式。STM32H750复位后默认从内部Flash启动它不会主动去读QSPI。你想让App直接跑在外部Flash要么配置BOOT引脚和选项字节支持外部启动要么内部Flash放一个小Bootloader上电初始化QSPI后跳转到0x90000000XIP性能。从QSPI Flash直接执行代码速度一定比内部Flash慢。对性能敏感的项目建议只把字库、图片、配置文件这类非时间关键数据放QSPIApp主体和耗时算法还是放内部Flash这里我也想纠正一个思路QSPI下载算法不一定要配合XIP使用。它可以单纯作为“往0x90000000写数据”的驱动。比如你做了一个H750方案内部Flash跑代码外部Flash专门存资源那就可以在Flash Download列表里同时挂内部Flash算法和W25Q32算法分别对应0x08000000和0x90000000两段地址MDK一次性把两部分内容都烧进去省时省力。4.4 常见问题Flash算法没有出现在列表里Add列表里看不到刚放进去的FLM通常三个原因FLM文件没有真正复制到Keil的Flash目录或者扩展名不对注意是.flm不是.axfMDK没有重启新FLM没被扫描到Flash Download页面的算法文件路径被某些第三方工具改过遇到这种情况最直接的排查方式就是在Add窗口里手动选路径。能选到文件且没有报错说明算法本身没问题剩下的就是路径识别问题用习惯就好。5. 实操踩坑实录下载失败与卡死的排查指南5.1 常见错误速查表我把这段时间调试QSPI下载算法遇到的典型报错整理成一个表方便你遇到问题时直接对照现象最常见原因解决方向Flash Download failed - No Flash DeviceFLM没配置或路径有问题检查Flash Download算法列表Could not load file xxx.FLMFLM损坏或版本兼容问题重新编译FLM或统一MDK版本Verify Failed写入数据和回读不一致检查引脚、FlashSize、页大小Erase Failed擦除命令没发出去或忙等待超时检查WriteEnable和Busy轮询Programming Failed at 0x90000000目标加载地址和算法起始地址不匹配确认App的IROM1起始地址卡死在InitQSPI初始化失败或Flash没有正确复位检查时钟配置、ID读取、引脚复用下载成功但运行异常向量表或启动模式问题检查VTOR、BOOT引脚或Bootloader跳转5.2 案例一Verify Failed——FlashSize和JEDEC ID的坑我最早做H750的QSPI算法时图省事直接用了一个网上流传的“通用STM32H7 QSPI算法”。烧录的时候每次都跑到最后才报Verify Failed进度条几乎走满然后红色报错。折腾了很久才发现那份算法模板默认FlashSize填的是64对应W25Q64的8MB容量而我开发板上是W25Q32只有4MB。MDK把0x90000000到0x90100000之间的数据当成有效写入范围可物理Flash实际到0x90400000就结束了写入越界校验自然失败。QSPI的FlashSize不是字节数而是“地址位数减一”计算公式是容量 2^(FlashSize1)。W25Q32容量4MB2^22所以FlashSize21。填成22或者20地址映射都会出错。还有一次ID读出来全是0xFF是因为D2和D3两个引脚在板子上被复用成了其他功能硬件上并没有连接到W25Q32。排查方法很简单先用一个裸机工程只做QSPI初始化和读ID确认引脚都没问题再回过来调FLM。别一上来就在FLM里找Bug调试效率会高很多。5.3 案例二下载过程中卡死——XIP模式残留与Busy等待下载到一半进度条卡住MTK报超时这个场景多发生于板子上电后Bootloader已经进入了XIP模式。什么叫XIP模式残留就是Bootloader把W25Q32映射到了0x90000000并且CPU正在执行里头的代码。你通过MDK连接调试器把FLM算法加载到RAM后算法去操作QSPI但此时Flash可能正处于被连续读取的状态内部状态机卡在某个读序列里你发的写命令芯片根本不响应于是死等。解决办法是在Init函数里先对芯片做一次软复位发送66h再发送99h强制W25Q32回到默认状态退出任何非正常模式。注意这两条命令必须按顺序发不能只发99h。紧接着还有一个坑W25Q32的擦除和编程都需要“写使能”但写完命令后必须等内部忙标志清除也就是调用QSPI_WaitBusy。我见过有同行觉得WaitBusy浪费时间命令发完直接进入下一页编程结果就是偶尔成功偶尔失败写进去的数据一半对一半错特别难排查。像这种异步操作Busy轮询是保命动作不能省。5.4 案例三烧进去不运行——BOOT启动与内存映射最让人崩溃的情况发生在最后MDK提示下载成功进度条绿色一切正常但板子复位后程序没跑。原因要从H750的启动流程说起芯片复位后默认从内部Flash执行它不会自动去QSPI找代码。想让程序从外部Flash启动要么设置BOOT引脚和选项字节让芯片支持外部启动要么在内部Flash放一个Bootloader上电后初始化QSPI确认数据有效后跳转到0x90000000。从产品稳定性的角度我更推荐Bootloader方案。Bootloader可以只占16KB内部Flash完全够用启动后先初始化QSPI再检查0x90000000处的栈指针和复位向量是否合理通过校验再跳转。这样远程升级、回滚、完整性校验全都放Bootloader里做QSPI下载算法只负责把固件烧进去各司其职。如果你现阶段只是调试裸机程序不想搞Bootloader那还有一个临时办法开发时把App放内部Flash字库图片资源放QSPI用QSPI算法单独烧资源App里通过映射地址读取。这种方式不要求外部启动最省事。6. 进阶与效率提升让H750外挂Flash开发更省心6.1 校验提速开启四线快速读烧写完的校验环节默认读Flash方式是单线速度不算快但对于几MB的固件也能感觉到明显的进度条等待。想提速可以在Verify流程里启用W25Q32的四线快速读命令0xEB。配置方法是在发送读命令时把DataMode设置成四线cmd.Instruction 0xEB; /* Quad Output Fast Read */ cmd.InstructionMode QSPI_INSTRUCTION_1_LINE; cmd.AddressMode QSPI_ADDRESS_24_BITS; cmd.AddressSize QSPI_ADDRESS_24_BITS; cmd.DataMode QSPI_DATA_4_LINES; cmd.DummyCycles 6; /* W25Q32手册规定0xEB命令需要6个dummy周期 */ cmd.NbData len;不同Flash型号对DummyCycles的定义不同W25Q32在0xEB命令下是6个dummy cycleW25Q64同样是6但换成其他厂商的芯片必须重新查手册。这个参数错了读出来的数据要么偏移要么全错。四线读提速带来的收益在烧录大资源文件时非常明显相当于校验时间缩短一半以上。6.2 用一套HAL库同时维护Bootloader和下载算法如果你同时维护Bootloader、App和这个下载算法建议把QSPI底层驱动抽成一份独立源码三处共用。我踩过最尴尬的坑是Bootloader里用的QSPI初始化和FLM算法里复制的代码不完全一致结果Bootloader能正常跑MDK下载也提示成功但App在跳转后死机。后来把驱动统一抽成一个qspi_w25q.c文件三处共用问题直接消失。这里要补充一句FLM工程编译时只把驱动文件加进去就够了不要引入业务模块。如果算法工程里混进了别的头文件依赖编译体积会膨胀而且可能带上一些需要在Flash里读取的只读常量导致加载到RAM后直接HardFault。6.3 算法文件的后处理与团队协作FLM算法源码和成品文件建议都提交到Git仓库。有人觉得FLM是编译产物不该入库这个观点在普通应用工程里没错但在下载算法这里不完全适用。原因很简单并不是所有同事都装了带Flash算法模板的完整MDK给他们源码也编译不了。源码加FLM一起提交别人拿到仓库就能直接给板子烧片这是最省协作成本的方式。FLM文件本身只有几KB到几十KB放到Git里占不了多少空间却能让整个团队少走很多弯路。最后再分享一个小技巧FLM文件其实可以用文本编辑器打开搜索字符串。如果你某天忘了当初把设备名写成了什么直接打开FLM搜“W25Q32”就能看到Keil加载算法时也正是凭这个字符串在列表里显示设备名称。遇到算法加载异常时这个技巧能帮你快速确认加载的到底是哪个文件。
返回列表