尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

富士通FM3/FM4单片机Flash操作实战:从擦写到IAP升级

富士通FM3/FM4单片机Flash操作实战:从擦写到IAP升级 简介面向富士通MB95F318单片机开发者这是一套完整的Flash操作与EEPROM模拟示例工程。资源聚焦MB95F318片内Flash的编程、擦除及ISP/IAP应用适合正在学习该系列MCU或需要快速上手Flash固件更新的嵌入式工程师。压缩包共包含29个文件、约79KB以C源码、头文件、汇编启动文件为主同时附有.prj工程文件及.obj、.abs、.mhx等编译输出便于直接打开、编译与烧录验证。工程内提供Flash_EEPROM_Jetronic驱动模块与main测试入口可帮助理解块擦除、地址编程和校验等关键流程。目前已有469人学习对刚接触富士通8位机Flash开发的人群具有较好的参考价值。 做嵌入式开发这些年被问得最多的一个问题就是富士通单片机的Flash到底怎么操作尤其是FM3、FM4这两个系列很多刚接手项目的同事一上来就在片内Flash的擦写、烧录、保护上卡壳。其实富士通半导体并入瑞萨之后FM系列依然在汽车电子、工控仪表、白色家电里大量服役片内Flash的操作方式也延续了“先解锁、再擦写、后加锁”这套标准流程。这篇就把我实际调过的板子上的经验整理出来从Flash类型和分区讲到擦除、编程、校验、保护再到Bootloader和参数存储这么几个高频场景最后把常见的Flash下载失败、写不进去这类坑挨个排一遍。不管是刚接手富士通项目的新手还是想优化现有固件升级方案的工程师应该都能从这里找到能直接用的东西。1. 富士通单片机Flash操作到底是什么1.1 先认清你手头这颗芯片富士通单片机现在的主力是FM3、FM4这两个基于ARM内核的系列。FM3用的Cortex-M3FM4用的Cortex-M4F在瑞萨的产品手册里归入“FM族”。早期还有一些FR系列的老片子比如96F开头的那批Flash操作流程也大同小异但寄存器名和扇区大小会有差异。所以第一步不是急着写代码先确认手头芯片的具体型号找到对应的Hardware Manual硬件手册找到Flash Controller那一章老老实实把寄存器地址、命令字、状态位看一遍。有个很常见的误解就是把“片内Flash”和“外挂SPI Flash”混为一谈。片内Flash是MCU自带的一块非易失存储通过内部总线访问操作它需要走芯片指定的命令序列。而外挂SPI Flash比如W25Q64这类是通过SPI接口用标准的Read/Write/Erase命令去操作的完全不涉及片上Flash控制器。这两者的初始化、读写方式、延时逻辑都不一样写代码前一定要分清。1.2 哪些场景必须碰Flash片内Flash最常见的用途有三个。第一是代码存储编译好的固件就是烧在Code Flash里的这算是间接使用。第二是用户参数存储很多设备需要掉电保存校准值、配置项、运行状态FM系列的Data Flash就是干这个用的。第三是Bootloader和IAP升级程序自己在运行时把新的固件写入Flash的另一个区域然后跳转执行这是目前产品远程升级的主流方案。除了这三个产线校准数据写入、开机自检固件完整性校验、日志记录这些场景也都会用到Flash操作。说白了只要产品有“掉电不能丢数据”或者“程序需要在运行时自己更新自己”的需求Flash操作就是绕不开的一环。2. 摸清Flash底细再动手2.1 NOR Flash和NAND Flash的区别先讲个基础但这个基础特别重要。市面上的Flash主要分NOR和NAND两大类MCU片内用的几乎都是NOR Flash。对比项NOR FlashNAND Flash随机读取快支持XIP直接执行慢以页为单位读取写入方式按字/字节写入速度一般按页写入卷写效率高擦除单位扇区通常几KB到几十KB块通常128KB以上坏块管理一般不强制要求必须有坏块管理机制典型用途MCU片内存储、SPI Boot芯片U盘、SSD、大容量存储卡为什么MCU内部不用NAND核心原因是NOR Flash支持XIPExecute in Place也就是CPU可以直接在Flash上取指令执行。上电之后不用把代码搬到RAM里直接跑启动速度快、设计简单这对单片机系统来说太重要了。另外MCU的程序容量一般也就几十KB到几MBNOR擦写单位小、修改灵活正好匹配。NAND的优势在大容量和高卷写速度但坏块多、需要纠错算法、不能XIP适合做大容量数据存储和片内Flash的定位完全不同。2.2 富士通MCU的Flash地址布局以FM系列为例片内Flash一般划分成Code Flash和Data Flash两个区域。Code Flash用于存放程序通常从0x00000000开始Data Flash用于存放用户数据地址在另一个段上具体偏移看型号。区域用途典型特征Code Flash存放固件代码、只读常量扇区擦除8KB/32KB不等Data Flash存参数、日志、校准值独立于程序区可单独擦写安全/保护位防止意外擦写、防止代码被读出通常只能整体设置/解除很多人在操作时犯的第一个错就是直接拿Code Flash的地址当普通变量写。要知道片上Flash不是RAM不是随便一个指针赋值就能存进去的。必须调用Flash控制器的命令序列。而且不同型号的扇区大小、擦除时间、擦写次数上限都不同手册里写得很明确不查手册就写代码基本上是在赌运气。3. 核心细节擦除、编程、校验与保护3.1 动手前必须做好的三件准备工作第一次操作片内Flash我建议不要急着写命令序列先确认三件事。第一是供电Flash擦写瞬间电流比正常运行大不少供电不稳或者纹波偏大会导致擦写失败、数据错乱板子在电源引脚附近至少要有100nF的瓷片电容必要时加一个10uF以上的储能电容。第二是时钟不少富士通芯片要求Flash编程时系统主频不能太高或者在指定频率范围内写代码前最好确认主频配置好并且锁定为稳定模式。第三是解锁。解锁这个概念很多新手不理解。你可以把它当成保险柜钥匙钥匙没插到位柜门是打不开的。Flash也一样MCU为了避免程序跑飞后误擦Flash默认状态是锁定。要操作Flash必须先向控制寄存器写入一串特定的解锁序列比如某些芯片要求连续写两个固定值。这个序列写错了命令根本不执行少了一样不执行。习惯上写一个FLASH_Unlock函数操作前调用操作完再调FLASH_Lock把保险柜锁回去。3.2 擦除操作先擦后写是铁律Flash的一个物理特性决定了操作逻辑编程写只能把1变成0要把0变成1只能靠擦除。所以你想更新一段数据必须先擦除整个扇区再往里面写新数据。这就是“先擦后写”的由来。擦除的代码流程一般是这样的调用解锁函数让Flash控制器接收命令检查忙标志确保上一次操作已经结束写入扇区擦除命令并传入目标扇区地址等待忙标志清除也就是擦除完成读回该地址确认所有字节都是0xFF调用加锁函数简化下来就是void Flash_EraseSector(uint32_t addr) { FLASH_Unlock(); while (FLASH_IsBusy()); // 等待上一次操作完成 FLASH_CmdEraseSector(addr); // 发送扇区擦除命令 while (FLASH_IsBusy()); // 等待擦除完成 uint32_t *p (uint32_t *)addr; if (*p ! 0xFFFFFFFF) { // 擦除失败需要记录并处理 } FLASH_Lock(); }擦除时间一般在几十到几百毫秒级别不同型号差异很大。这个时间段内最忌讳掉电和复位一旦擦除中断轻则扇区数据变成不可预测的值重则产生坏块。所以正式产品上擦除前最好把“正在擦写”的状态记到RAM里而且通讯协议层要有握手确保设备不会在擦除过程中被外部命令打断。3.3 编程操作在RAM里执行是关键中的关键擦除完成之后就是写入数据。写入的最小单位一般是半字或字具体看型号。比如某些FM系列要求按32位字写入那么你写参数时就要做好对齐不能随意跨字操作。编程命令的流程和擦除类似也是解锁、发命令、等待完成、校验、加锁。但这里有个最大的坑我见过太多人栽在这里代码本身运行在Flash里然后你调用Flash编程函数去写同一个Flash这会造成总线冲突轻则写入失败重则程序跑飞。原因是CPU执行代码时需要从Flash取指令而Flash控制器正在被占用执行写操作指令和数据访问互相打架芯片直接挂掉。解决办法是把Flash擦写相关的函数搬到RAM里执行也就是常说的“在RAM中运行Flash驱动”。在Keil MDK环境里可以通过分散加载文件把指定函数放到RAM段。操作思路是把Flash驱动代码单独放在一个源文件里比如flash_driver.c在分散加载文件里给这个文件单独建一个执行区域定位到RAM编译时将这个区域标记为“可在运行时从ROM加载”也就是只加载代码到RAM不从Flash就地执行LR_IROM1 0x00000000 0x00080000 { ER_IROM1 0x00000000 0x00080000 { *.o (RESET, First) *(InRoot$$Sections) .ANY (RO) } RW_IRAM1 0x20000000 0x00030000 { *.o (RAM_CODE) ; 把Flash驱动放RAM .ANY (RW ZI) } }在源文件里给Flash擦写函数加上RAM区段的属性#if defined(__CC_ARM) #pragma arm section code RAM_CODE #endif void Flash_WriteWord(uint32_t addr, uint32_t data) { FLASH_Unlock(); while (FLASH_IsBusy()); FLASH_CmdProgram(addr, data); while (FLASH_IsBusy()); uint32_t check *((volatile uint32_t *)addr); if (check ! data) { // 写入失败处理 } FLASH_Lock(); } #if defined(__CC_ARM) #pragma arm section code #endif这段代码看着简单但如果不放到RAM里运行十有八九会在实际硬件上翻车。所以每次写Flash操作我都会先检查编译生成的map文件确认FlashDriver函数确实在RAM段而不是在Flash段。3.4 校验与加锁写完之后别忽略收尾写入完成后的读回校验不能省。Flash编程偶尔会因为电压波动、电磁干扰、时序余量不足等原因写入错误数据但状态标志显示成功。这时候严格的读回比较就能把问题拦截在早期。校验分两种。一种是全量读回把写入区域的每个字节读出来和目标数据逐字节比较适用于参数存储和固件写入。另一种是CRC校验适用于Bootloader升级这类大数据量场景写入整个固件后计算CRC和固件包里携带的CRC比对不一致就判定升级失败。后者效率高但前提是发送端和接收端的CRC算法一致别小看字节序这种细节。加锁同样重要。很多MCU的Flash控制寄存器在操作完后如果不重新锁定那么后续程序一旦跑飞乱写一通就有可能把Flash擦掉。养成“操作完就加锁”的习惯能在关键时候挽救一个产品。另外富士通系列里有的芯片还有保护位设置后调试器无法读取芯片内部代码这对商业产品防抄板非常有用。但注意保护位一旦设置以后再想用调试器编程就受限了需要先解除保护并全片擦除所以这个功能要留到产品量产的最后阶段再开启。4. 实操场景参数存储与IAP升级4.1 Data Flash当EEPROM用磨损均衡与掉电保护MCU片内Flash的一大用途就是替代外部EEPROM存参数。富士通FM系列的Data Flash就是为这个设计的可以直接按字写入不用像Code Flash那样每次都担心影响程序区。但Data Flash也有擦写寿命一般是1万到10万次。看起来很多可如果产品每隔几秒就写一次数据几个月就到寿命上限了。我在实际项目里吃过这个亏一个温控设备每次采集完温度就写日志运行三个月后Data Flash就出现擦写失败。后来改用磨损均衡算法才彻底解决。磨损均衡的思路很简单就是把存储区拆成多个扇区每次写入都换一个新扇区轮流使用。启动时扫描一遍找到“最新一次有效数据”所在的扇区然后从下一个扇区继续写。这样单片扇区的擦写次数被平均分散了。#define DATA_FLASH_PAGE_NUM 8 // 循环使用8个扇区 static uint32_t page_idx 0; void SaveParam(const Param_t *param) { // 从当前索引开始寻找一个空白扇区 uint32_t addr DATA_FLASH_BASE page_idx * DATA_FLASH_PAGE_SIZE; Flash_EraseSector(addr); Flash_WriteWord(addr 0, param-crc); Flash_WriteWord(addr 4, param-value1); Flash_WriteWord(addr 8, param-value2); page_idx (page_idx 1) % DATA_FLASH_PAGE_NUM; }掉电保护则是另一个必须考虑的点。如果在擦除旧数据、写入新数据的过程中突然断电可能造成旧数据丢失、新数据没写完的尴尬局面。我的做法是采用“双备份状态标志”的方案先在一个扇区写新数据全部写成功后再往标志位置写入“有效”标记启动时如果发现标志位无效就回退到上一个有效扇区。虽然多占一块空间但数据可靠性提升了一个量级。4.2 IAP升级Bootloader和App的Flash分区设计IAPIn-Application Programming是现在产品升级的主流手段。富士通MCU做IAP的思路没什么特殊核心是Flash分区分区起始地址用途Bootloader0x00000000负责启动、接收固件、校验、跳转App0x00004000示例用户功能代码固件暂存区0x00040000示例接收新固件后临时存放Data Flash独立地址保存升级标志、版本号升级流程一般是这样的Bootloader启动检查升级标志如果需要升级通过串口、CAN或以太网接收固件包每收到一包先写入固件暂存区同时计算CRC全部接收完成后校验整体CRC校验通过擦除App区把暂存区固件搬过去清除升级标志跳转到App这个流程里最容易被忽视的是中断向量表。App区不再是0x00000000所以App代码内部要重新设置中断向量表的起始位置。Cortex-M3/M4上典型写法是修改SCB-VTOR寄存器把它的值设为App的起始地址。#define APP_BASE 0x00004000 void JumpToApp(void) { uint32_t app_sp *((volatile uint32_t *)APP_BASE); uint32_t app_pc *((volatile uint32_t *)(APP_BASE 4)); __disable_irq(); // 修改中断向量表偏移 SCB-VTOR APP_BASE; // 设置MSP并跳转 __set_MSP(app_sp); ((void (*)(void))app_pc)(); }跳转后Bootloader就退出历史舞台了App享有全部外设。但有个细节跳转前把使用过的外设复位一遍尤其串口中断如果还开着App刚起跑就被一个莫名其妙的中断打断很容易出问题。我的习惯是__disable_irq之后把所有打开的外设时钟关掉再进行跳转。4.3 固件包格式的设计IAP升级过程中固件包格式看似不起眼实则影响后续所有逻辑。我常用的是“包头固件数据CRC”的结构typedef struct { uint32_t magic; // 固定魔数防止误识别 uint32_t version; // 版本号 uint32_t length; // 固件长度 uint32_t crc; // 固件CRC } FirmwareHeader;接收端解析包头后先校验magic和length是否合理再接收数据。很多升级失败案例都是因为收到一个错误的包头就贸然擦除App区结果固件数据不完整产品直接变砖。所以我把“先接收完整再校验校验通过才擦写”定成了一条铁律。5. 踩坑实录Flash下载失败与写不进数据的典型问题5.1 “Flash Download Failed - Target DLL has been cancelled”这个报错信息在调试器下载程序时出现频率极高尤其是FM3、FM4这类ARM内核的芯片。它本身不是Flash操作问题而是调试器无法正确访问目标板Flash导致的但几乎所有和Flash擦写相关的操作都容易被它卡住。可能原因表现解决办法Flash算法没选对下载到一半报错在Keil的Flash Download页面选择匹配芯片型号的算法下载频率太高偶尔能连上但下载失败把SWD/JTAG时钟降到1MHz甚至500kHzSWD引脚被复用连接正常但下载卡住检查用户程序是否把SWD引脚配置成了普通IO芯片被保护锁定连接后无法擦除执行全片擦除或解除保护供电不足或纹波大下载到一半断开用稳压源供电加储能电容Keil里的“Flash Download”配置如果选错了Algorithm或者Algorithm的地址范围和目标芯片不匹配就会出现Target DLL cancelled。这个错误排查时优先看逻辑分析仪或示波器上的复位引脚确认调试器有没有正常控制复位。如果没有复位动作多半是算法或时钟配置的问题。5.2 能连上调试器但程序跑飞这种情况最气人调试器能识别芯片也能下载但程序一运行就飞了或者擦写Flash时整个系统卡死。先说一个调试技巧很多人不知道Keil MDK里可以直接监控引脚电平。方法是进入调试模式后打开Peripherals窗口选择对应的GPIO外设就能实时看到每个引脚的电平状态如果用的是寄存器调试方式也可以在Watch窗口里添加GPIO的IDR寄存器和对应引脚位或者用内置的Logic Analyzer功能添加GPIO寄存器地址这样就能跟踪引脚的电平变化。排查Flash操作引起的跑飞时我先看引脚状态再在Flash擦写函数前后各打一个GPIO翻转用示波器判断是卡在擦除流程里还是跳转出了问题。代码跑飞的常见原因一个是Flash擦写函数没有放到RAM里执行导致总线冲突。另一个是擦写过程中看门狗没有喂芯片被看门狗复位而复位时Flash正处于写状态数据自然就乱了。解决办法是在长时间擦写期间暂停看门狗或者把擦写过程分解成多个短周期在每个周期之间喂狗。5.3 电源纹波导致擦写随机失败这是个非常隐蔽但很常见的问题。之前有一块板子全片擦除时经常失败十次里能失败三四次用调试器看状态寄存器又看不出异常。最后用示波器量VCC发现擦写瞬间电压有个明显跌落纹波将近200mV。后来在MCU电源脚旁边加了一个220uF电解电容和一个100nF陶瓷电容再测纹波降到了30mV以下擦写就再没失败过。Flash擦写瞬间的电流峰值比正常运行高不少如果供电链路设计得比较紧张就容易出这类随机问题。排查这种问题时别只盯着Flash配置先确认电源在擦写瞬间是稳定的。特别是样机阶段用USB供电或者劣质开关电源做实验最容易踩到这个坑。5.4 一个可靠的排查流程被Flash问题折磨过几次之后我总结了一套简单的排查流程现在每次遇到类似问题都按这个顺序来确认供电稳定示波器看VCC在擦写瞬间有没有明显跌落确认芯片型号对照手册检查Flash地址和扇区划分检查解锁序列是否完整命令字是否匹配确认擦写函数在RAM中执行而不是在Flash中执行检查擦写期间是否有关看门狗是否需要暂停逐个扇区做擦除测试确认单个扇区的可用性最后才怀疑芯片本身换一片新芯片对比测试绝大多数问题都出在第2到第4步尤其是第3和第4步。别小看这些琐碎的检查项它们能帮你省下大量时间。最后分享一点实际体会做富士通单片机开发Flash操作是基本功但真没必要把它想得太复杂。我最深的感受是这东西和“写文件”完全不同它更像操作一个必须按步骤来的物理设备先解锁再擦除然后写入最后校验加锁每一步都不能缺。只要把这套流程背熟再记住几个关键点——在RAM里执行驱动、先擦后写、掉电要有备份方案基本就稳了。特别建议正式量产前把Flash的分区策略、IAP的固件包格式、参数存储的磨损均衡方案都写成文档等产品出了问题再回头补代价太大。最后再分享一个小技巧开发板上最好保留一根串口线做成“救砖”方案万一Flash保护位之类设置错了还能通过硬件调试接口解除保护后全片擦除把芯片捞回来。本文还有配套的精品资源点击获取
返回列表