简介:面向富士通MB95F318等8位微控制器的嵌入式开发者,这份资源围绕Flash存储器的操作展开,覆盖编程、擦除、验证及ISP/IAP应用场景,适合需要实现固件更新与在板调试的工程师或学习者使用。压缩包共29个文件,以C语言源码、头文件、汇编启动文件为主,另含工程配置与编译输出文件(如prj、wsp、abs、mhx等),整体约79KB,便于查看完整工程结构。内容包含Flash与EEPROM读写相关实现、中断向量配置及启动代码,可帮助理解MB95F318的Flash操作流程和代码组织方式。已有469人学习下载,适合作为单片机Flash开发的入门参考或工程模板。 富士通单片机这个名字,很多老工程师一听就知道,说的就是FM3/FM4系列,核心是基于ARM Cortex-M3/M4的MCU。说实话,这系列芯片性能不算差,但网上资料少,尤其涉及Flash操作的部分,官方手册写得又臭又长,寄存器看一眼就头大。我做项目用过FM3系列的MB9BF系列,也调过FM4的Flash读写,踩过不少坑,所以这篇博客想把富士通单片机Flash操作的底层逻辑、代码流程、常见问题一次讲清楚。不管你是刚接触富士通,还是已经用了一段时间被Flash下载、擦除、掉电保存折腾得头疼,这篇都有参考价值。
1. 整体设计思路:先想清楚Flash里放什么,再动手写代码
1.1 富士通单片机Flash在系统里扮演什么角色
富士通MCU的片内Flash,主要分两块:一块是代码存储区,也就是跑程序的地方;另一块是数据存储区,也叫User Flash或者Data Flash,专门用来存参数、校准值、运行日志这类掉电不能丢的数据。很多新手上来就把所有Flash当成一个整体,直接在代码区写数据,这是最容易出问题的地方。
拿FM3系列来说,代码Flash通常有256KB到1MB不等,Data Flash一般是几KB到几十KB。Data Flash和代码Flash的擦写方式不一样,擦除粒度也不同。代码Flash按Sector擦除,一个Sector可能是8KB、16KB或者32KB;Data Flash则按块(Block)擦除,一块可能是64字节或者512字节。这个差异直接决定了你的数据结构怎么设计——如果频繁改一个小参数,却把整个Sector擦掉重写,损耗和耗时都受不了。
1.2 方案选型:片内Flash够用吗,要不要外挂SPI Flash
我见过不少项目,明明片内Data Flash还剩一大半,却硬要外挂一颗SPI Flash。不是说不行,但成本、PCB面积、驱动代码量都会上来。富士通MCU片内的Data Flash,寿命一般是1万到10万次擦写,如果你的产品一天擦写几次,用十年绰绰有余。只有当数据量大到片内装不下,或者需要频繁追加日志、做文件系统的时候,才有必要外挂NOR Flash或者NAND Flash。
外挂存储选NOR还是NAND,也是老生常谈:NOR读快、随机访问友好、可以直接XIP执行代码,适合放固件和配置;NAND容量大、按页读写、有坏块问题,适合做大容量数据缓存。但在富士通MCU这个平台上,如果你只是存几个参数,我强烈建议先用片内Data Flash,省事、安全、代码量小。我之前一个项目就是把10个校准系数放在Data Flash里,断电保存、上电读取,整段驱动代码不到200行,稳定跑了两年。
1.3 为什么官方库不是万能药
富士通(现在属于英飞凌)官方提供过Flash驱动库,但说实话,库的封装层次多,出了问题不好排查。而且很多二手开发板厂商给的例程是从别的芯片移植过来的,寄存器映射对不上,直接编译会报错。我的习惯是:官方库用来参考流程,但核心的擦、写、读三个函数自己手写,直接操作寄存器,这样出了问题能一眼定位。后面我会给出基于FM3系列寄存器的实现思路,FM4系列大同小异,寄存器名稍有变化。
2. Flash操作核心原理:编程、擦除、状态轮询的逻辑
2.1 为什么Flash要先擦后写,还能按字节写吗
Flash存储器的物理特性决定了它只能把1写成0,不能把0写成1。所以写数据之前,必须先把目标区域擦除成全0xFF,然后再写入数据。这也是“先擦后写”这句口诀的由来。擦除操作的最小单位是Sector或者Block,不能只擦一个字节。如果你只想改一个字节的数据,编程时仍然要按最小编程单位来——富士通MCU的Flash一般支持按字(16位)或者半字编程,但底层还是要先确保目标区域是全0xFF。
这里有个常见的思维误区:很多人把Flash当EEPROM用,认为随便写个地址就能存数据。实际上,如果你在已经写过数据的地址上再写,结果是不可预测的,数据会错乱。正确做法是:先备份整块数据,擦除Sector,再一次性写回。
2.2 寄存器级操作流程:解锁、命令、轮询
富士通MCU的Flash控制器,操作流程大致固定:初始化时钟和电源,然后解锁Flash控制寄存器(防止误写),写入擦除/编程命令,等待操作完成,最后锁回寄存器。核心寄存器一般包括Flash状态寄存器(如FSR)、命令寄存器(如FCR)、地址寄存器(如FAR)和数据寄存器(如FDR)。
我以FM3系列的擦除为例,说明一下寄存器操作逻辑:
#define FLASH_FCR (*(volatile uint32_t *)0x40000000) #define FLASH_FSR (*(volatile uint32_t *)0x40000004) #define FLASH_FAR (*(volatile uint32_t *)0x40000008) #define FLASH_FDR (*(volatile uint32_t *)0x4000000C) void fm_flash_sector_erase(uint32_t sector_addr) { // 1. 解锁Flash控制寄存器 FLASH_FCR = 0x00000001; // 2. 写擦除命令(不同芯片命令字不同,0x03是FM3的Sector Erase命令) FLASH_FAR = sector_addr; FLASH_FCR = 0x03; // 3. 等待擦除完成,轮询状态寄存器的RDY位 while ((FLASH_FSR & 0x02) == 0) { // 可加超时判断,防止死等 } // 4. 锁定Flash控制寄存器 FLASH_FCR = 0x00000000; }这段代码里的偏移地址和命令字,在不同的富士通型号上会有出入,移植时必须对照你所用芯片的参考手册核对。状态寄存器的RDY位表示Flash忙/闲状态,操作期间轮询它,直到变回空闲才算完成。注意:擦除期间不能访问Flash代码区,所以这段擦除代码要么放在RAM里执行,要么确保擦除期间不触发中断,否则芯片会直接死机或产生总线错误。
2.3 编程操作:按字写还是按页写
编程操作写入的最小单位因型号而异。FM3系列的编程支持按半字(16位)编程,FM4系列有的支持按行编程。操作流程和擦除类似:解锁、写命令、送地址、送数据、等待完成。
void fm_flash_program_halfword(uint32_t addr, uint16_t data) { // 等待Flash空闲 while ((FLASH_FSR & 0x02) == 0); // 解锁 FLASH_FCR = 0x00000001; // 写编程命令 FLASH_FAR = addr; FLASH_FDR = data; FLASH_FCR = 0x01; // 半字编程命令 // 等待完成 while ((FLASH_FSR & 0x02) == 0); // 锁定 FLASH_FCR = 0x00000000; }很多人问我,能不能连续编程多个半字,减少开锁关锁的时间?可以,富士通的部分系列支持连续编程模式,但你得确认芯片手册里是否允许批量编程命令,以及地址是否要求连续。如果不支持连续编程,老老实实一个一个写,最多就是耗时多一点,数据安全最重要。
3. 实操过程:从工程配置到读写完整实现
3.1 工程配置:Flash驱动放RAM,关中断是必须的吗
前面提到擦写Flash期间不能执行Flash上的代码,这就要求Flash驱动函数必须放在RAM里运行。在Keil MDK中,可以用__attribute__((section(".ARM.__at_0x20000000")))这类方式把函数放到RAM指定地址,或者用__ramfunc关键字。FM3系列的开发环境大多是IAR或Keil,IAR里用__ramfunc就非常方便:
__ramfunc void fm_flash_sector_erase(uint32_t sector_addr) { // 同上 }另外,擦写Flash期间必须关中断或者关看门狗,否则中断服务函数里的Flash访问会出错。我的一般做法是:在擦除/编程入口处关总中断,操作完成后再打开。如果中断里有重要的实时响应需求,那就要把Flash操作放到一个独立任务里,并且和中断抢占做好互斥。实测下来,FM3的Sector擦除时间大概是几十毫秒,关中断几十毫秒在大多数控制应用里可以接受,但如果你的系统对中断延迟极其敏感,就得换方案。
3.2 数据管理:扇区划分、备份、掉电保护
片内Data Flash就那么大,怎么用很讲究。我习惯把Flash空间划分为三个区域:参数区A、参数区B、日志区。参数区A和B交替使用,每次写入时先写B,写完再把A擦除,这样即使中途掉电,至少有一份已知正确的数据。
为什么这样做?因为Flash擦除过程中掉电,整个Sector可能变成不可预知的状态,单一备份无法保证安全。双备份加标志位校验,是嵌入式存储最保守也最有效的方案。我写过一个简单的状态机:
- 上电先读A区和B区的标志字节,判断哪份完整。
- 如果两份都不完整,用默认参数启动,并把默认参数写回A区。
- 更新参数时,先擦B区,写入新参数和CRC校验值,再把A区擦除写入同样的新参数。
这样做的好处是,无论掉电发生在哪一步,总有一份可用数据。CRC校验用最普通的CRC16就行,别省这个步骤,否则数据错一个字节可能到运行时才发现,那才叫痛苦。
3.3 写入性能与寿命:擦写次数、磨损均衡怎么算
Data Flash寿命按擦写次数算,一般1万到10万次,听起来不多,但如果每秒钟写一次,一天就是86400次,几天就报废了。所以写入频率要有意识地控制,不能无脑往Flash里写。我见过一个温控项目,传感器每100ms上报一次温度,工程师直接把温度写进Flash,结果一周后Flash就挂了。后来改成每5分钟写一次,加上只在温度变化超过0.5℃时才写,Flash寿命立刻够用十年。
如果确实需要高频记录数据,那就得做磨损均衡。简单说就是把写入地址在扇区内轮转,每个地址都写一遍后再擦除重来。磨损均衡的开销是代码复杂度上升,但能显著延长Flash寿命。富士通MCU的资源够跑这种算法,几百行代码的事,值得做。
3.4 读取校验:写完就读,别相信一次写入就成功
Flash写入偶尔会失败,原因是电压波动、噪声、温度异常等。所以写入之后一定要回读校验。最经济的方法:写完最后一个字之后,把整个扇区的数据逐字读出,和源数据比对,不一致就重新擦写。回读校验会增加几百毫秒的耗时,但对可靠性要求高的设备完全值得。
我在一个车载项目里做过统计:100万次擦写操作中,大约有20多次首次写入失败,重试一次后全部成功。这个比例不高,但如果没有校验逻辑,那20多次就是20多次的隐性故障。
4. 常见问题与排查:Flash下载失败、擦写跑飞、J-Link连不上
4.1 下载阶段的“Flash Download Failed”速查表
很多人第一次接触富士通MCU,就卡在下载这一步。最常见的是Keil报Flash Download failed - Target DLL has been cancelled,或者IAR报类似错误。这通常不是代码问题,而是配置问题。
| 现象 | 大概率原因 | 解决办法 |
|---|---|---|
| Flash Download failed - Target DLL has been cancelled | J-Link驱动版本太老,或Flash算法不匹配 | 更新J-Link驱动,在Keil里重新选择对应芯片的Flash Algorithm |
| Flash download failed Cortex-M3 | 芯片型号选择错误,或Flash起始地址不对 | 核对芯片Flash起始地址,FM3一般是0x00000000,FM4也一样,但大小要选对 |
| 下载时校验失败 | RAM中Flash算法空间不足,或目标板供电不稳 | 增大RAM for Algorithm,检查电源,降低JTAG/SWD速率 |
| J-Link可以识别芯片但无法擦除 | 芯片被读保护锁住 | 使用J-Flash的全片擦除功能,或按芯片手册解锁步骤解除保护 |
| 用SWD下载但目标板没反应 | BOOT引脚或复位电路问题 | 检查NRST上拉电容,断电重试 |
我踩过最坑的一次是:芯片之前跑过一段写保护代码,把自己Sector区域加了写保护,之后任何下载都失败,J-Link连ID都能读出来,就是擦不掉。最后没办法,用J-Flash的全片擦除,再把Boot引脚拉高强制进入ROM boot模式,才救回来。所以Flash写保护功能要慎用,尤其产品开发阶段,少设置这类“保险丝”。
4.2 运行阶段:擦写期间程序跑飞、看门狗复位
程序在擦写Flash时死机,绝大多数是因为在Flash操作期间执行了Flash上的代码,或者看门狗没有被喂。前面说了,要么把驱动放RAM,要么在擦写前关中断、关看门狗。我推荐做法是两者都做:驱动放RAM是底线,关看门狗是保险。如果擦除一个Sector需要50ms,而你的看门狗喂狗周期是1秒,理论上不用关,但为了安全,还是临时关一下,操作完恢复喂狗。
另外,如果程序里用了RTOS,多任务环境下擦写Flash期间,任务调度器发起的上下文切换会访问Flash,也可能导致总线错误。所以擦写Flash必须加互斥锁,或者直接挂起所有任务,等Flash操作完成再恢复调度。FM3的Flash控制器设计得比较保守,操作期间对总线的任何Flash访问都可能触发HardFault,这一点比很多新片子要敏感,务必注意。
4.3 数据校验失败的排查思路
写完数据,回读却发现不对,先从这几个方向排查:地址是否越界,尤其写到了代码区;数据长度是否超过扇区;编程命令的字长是否和芯片支持的最小编程单位一致;擦除操作是否真的执行成功。我调试时会用调试器的Memory窗口直接查看Flash对应地址的内容,比代码打印快多了。
再有一个隐蔽问题:如果使用了编译器优化,Flash驱动代码可能被优化掉部分关键操作。我建议Flash相关函数全部加上#pragma optimize = off之类的禁用优化标记,或者直接看反汇编确认关键寄存器写操作都在。富士通MCU的Flash寄存器操作顺序非常敏感,编译器优化有时会改变时序,导致偶发失败。
4.4 我的避坑清单
做富士通MCU Flash开发这几年,我给自己整理了一份清单,每次验证新板子都会过一遍:
- 先确认芯片型号的Flash大小和扇区边界,用脚本或表格列出所有Sector的起始地址。
- 任何对Flash的操作入口,统一封装成API,禁止业务代码直接操作寄存器。
- 写入的数据结构固定头部(魔数、版本号、长度、CRC),便于后期升级兼容。
- 上电启动时做Flash完整性检查,发现数据无效就进入恢复流程,不要带病运行。
- 下载器算法配置和芯片型号严格匹配,新开发板第一次下载前先做全片擦除。
这套流程帮我避免了很多低级故障,也节省了大量调试时间。
5. 写在最后:一点个人体会
富士通单片机本身的Flash使用逻辑,和市面上大部分Cortex-M单片机是相通的,入门难不在寄存器,而在于你愿不愿意仔细读手册和做冗余设计。我见过太多人把“能写进去、能读出来”当作完成,结果批量生产时掉电损坏、数据错乱,这才回过头来补做备份和校验。Flash操作这件事,做扎实了,产品能稳定跑很多年;做糊弄了,跑着跑着数据就没了,这种故障最难排查。
如果你刚开始用富士通MCU做Flash操作,我的建议很简单:第一,先花半天时间把参考手册的Flash章节读透,标记出命令字和状态位的含义;第二,把双备份加CRC这套机制先加上,哪怕项目不需要,至少心里有底;第三,所有Flash操作都放RAM执行,这个习惯能救你很多次。等这几个基本功练好,你会发现富士通单片机的Flash操作并不神秘,跟玩STM32也没什么两样。
本文还有配套的精品资源,点击获取