ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

STM32 FSMC挂载NOR Flash与FlashFS文件系统方案实战

2026/8/31 17:28:43 拓冰建站 浏览量
STM32 FSMC挂载NOR Flash与FlashFS文件系统方案实战 简介本资源是面向嵌入式开发工程师与STM32进阶学习者的完整FSMC驱动NOR Flash实战工程聚焦STM32F10x系列通过FSMC接口控制Spansion S29GL128128MbitNOR Flash芯片的核心实现解决固件存储、Bootloader开发及在线升级等关键场景下的硬件适配与时序调优问题。压缩包含192个文件以57个.h头文件和55个.c源文件构成主体逻辑涵盖FSMC初始化、NOR读写/擦除/校验、Numerical Algorithm Leveling时序自适应等辅以32个.s汇编文件支持底层时序控制以及Keil工程配置uvproj/uvopt/uvgui、编译输出axf/map/lnp和文档说明整体大小1.52MB。已有472人学习下载提供可直接编译运行的完整工程框架、详尽的FSMC寄存器配置范例、S29GL128命令序列实现及中断驱动的错误处理机制特别适合需要深入理解外部存储器接口设计与非易失性存储应用的开发者快速上手并二次开发。 搞嵌入式的人应该都遇到过这种场景主控跑着关键业务数据要存程序要跑还得能快速启动。SD卡太慢、SPI Flash容量和速度卡脖子这时候就需要一片并行NOR Flash而把STM32F10x的FSMC外设、S29GL128这片128Mbit的NOR Flash、再加上FlashFS文件系统组合起来就是一个很典型的“大容量存储 片上执行 文件管理”方案。我前段时间正好把这个方案完整跑通从硬件设计到FSMC时序、再到FlashFS移植踩了不少坑。这篇就把整个过程拆开讲清楚适合正在做类似存储方案、或者准备用FSMC挂NOR Flash的工程师参考。先交代一下这个项目的基本盘主控是STM32F103VE标准外设库3.5.0Flash芯片是Cypress现Infineon的S29GL128P容量16MB通过FSMC的Bank1区挂载在0x60000000起始地址上上层跑Keil MDK自带的RL-FlashFS文件系统。整套方案的目标是在无MMU的Cortex-M3上既能通过FSMC直接读NOR实现程序XIP又能把剩余空间当作文件系统用存配置参数和日志数据。1. 整体方案为什么是S29GL128 FSMC FlashFS1.1 这套组合解决什么问题NOR Flash和NAND Flash这对兄弟定位区别很明显。NAND容量大、价格低但坏块管理、擦写均衡都得自己伺候而且按页读取不适合直接跑代码。NOR Flash虽然单比特成本高、容量上不去但胜在随机读取快、读接口简单可以直接映射到MCU的地址空间实现XIP片上执行。S29GL128这片芯片是128Mbit换算下来16MB对于跑文件系统来说不算大但如果只是存设备参数、历史记录、字库、固件备份这个容量完全够用。加FSMC而不是用SPI核心诉求是速度和访问方式。STM32F10x的SPI最高18Mbps串行NOR再快也受限于协议开销而FSMC把NOR Flash映射成内存地址读操作就是一条LDR指令的事速度接近总线频率性能完全不是一个量级。另外FSMC把地址线、数据线、控制线全部引出接口是并行的CPU可以直接用指针访问这对上层文件系统来说非常友好不需要像SPI Flash那样做页缓存和命令转发。FlashFS在这套方案里解决的是“如何让裸Flash变成能用的存储空间”。没有文件系统16MB空间只能自己记偏移、管理扇区一个文件占几个扇区、哪个扇区被删除了全靠裸驱动维护改动一次字段布局就崩一次。FlashFS在NOR上做了扇区管理、擦写均衡、掉电保护文件操作通过标准fopen/fread/fwrite接口完成业务代码和存储介质解耦。这个组合本质上就是给STM32配了个“小型硬盘”FSMC是硬盘控制器S29GL128是硬盘盘片FlashFS是分区分文件系统。1.2 方案选型的几个关键考量选S29GL128而不选其他NOR主要看中三点。第一是容量的性价比。16MB在NOR Flash里算中等偏大再往上S29GL256是32MB价格接近翻倍往下8MB又不太够用。程序代码放一个256KB的分区剩下全给文件系统16MB刚好能腾挪开。第二是S29GL128的接口非常标准。3.3V供电16位数据总线地址线最多23根与STM32F10x的FSMC引脚几乎一一对应不需要电平转换。芯片内部按128KB扇区划分大扇区虽然擦除粒度粗但FlashFS会做层封装反而简化了管理。第三是供货和资料成熟度。S29GL系列是NOR Flash里的常青树手册清晰CFI查询、解锁命令、状态寄存器轮询这些时序都是标准操作网上工程案例也多遇到问题好排查。另外用它还有一个隐形好处——S29GL128支持同步突发读模式虽然这次没用上但未来想提升读性能硬件上也不需要改动。1.3 片内编号管理模块的设计思路项目标题里有个“numeralaev”那是我给片内编号管理模块起的代号。这个模块要解决的实际问题很简单Flash文件系统里记录的数据每一条都需要逻辑编号。裸Flash不知道“编号”是什么概念所以我做了一个薄层把所有文件记录转换成物理扇区加偏移的组合再维护一张编号到存储地址的映射表。每次写入新数据编号自增映射表记录它落在哪个扇区、扇区内的哪个页。启动时扫描Flash文件系统的分区表把映射表重建出来。这样上层业务访问数据时只按编号取值不需要关心底层存储细节。这个模块本身不复杂但它决定了文件系统和业务代码的边界文件系统只管字节流编号管理管索引。两者之间用固定的记录头协议对接。设计时留了一个关键决策——编号映射表不常驻RAM只在数据写入时更新对应的Flash区读取时按编号计算地址去文件里查。这样RAM占用很小也避免了频繁擦写导致Flash寿命快速消耗。2. 硬件连接与FSMC总线设计2.1 S29GL128的关键引脚与接线先理清S29GL128的引脚。芯片是56脚TSOP封装关键信号分四类地址线A0~A22、数据线DQ0~DQ15、控制线CE#、OE#、WE#、BYTE#、RESET#、WP#、状态线RY/BY#。STM32F103的FSMC有一套固定的引脚映射通过重映射功能可以放在不同GPIO组。我用的100脚LQFP封装FSMC引脚刚好够用接线原则是“同名控制线直连地址线偏移一位数据线一一对应”。这里最关键的坑就是地址线偏移。S29GL128内部按字寻址16位模式下地址线A0表示最低位的字地址而STM32的FSMC是字节寻址的内部地址总线的A0对应字节地址的最低bit。当FSMC配置成16位数据宽度时外部存储器的A0必须接到FSMC的A1A1接FSMC的A2依次类推。如果直接A0接A0访问0x60000000和0x60000002会命中同一个字整个文件系统就乱套了。这个错误在原理图阶段看不出来焊完板子点灯正常一跑内存测试就露馅。控制线方面CE#接FSMC_NOE读使能旁边的NE1片选FSMC_NE1对应Bank1第1区OE#接FSMC_NOEWE#接FSMC_NWERESET#接RC复位电路或直接拉到3.3VBYTE#接高电平锁定16位模式。RY/BY#是开漏输出没用到可以悬空如果想做写状态检测可以接一个上拉电阻到GPIO输入。2.2 FSMC Bank区地址映射计算STM32F10x的FSMC把外部存储器分成4个BankNOR/PSRAM用的Bank1又分成4个子区每个子区64MB。NE1片选对应0x60000000~0x63FFFFFF这段空间。S29GL128只有16MB挂在Bank1子区1时实际能用的地址范围是0x60000000到0x60FFFFFF。地址映射的换算要分两步。第一步确认FSMC看到的地址是字节地址从0x60000000开始每增加1对应一个字节第二步因为数据线是16位Flash内部字地址和FSMC字节地址的关系是Flash_A[22:0] FSMC_A[23:1]。也就是说FSMC地址0x60000000和0x60000001都指向Flash的第0个字只是读16位数据时低字节和高字节分别在DQ0~DQ7和DQ8~DQ15上。用代码访问时指针用uint16_t类型按数组访问就行比如volatile uint16_t *nor_base (uint16_t *)0x60000000;nor_base[0]就是Flash字地址0的数据。有一点要注意S29GL128内部有A0~A22共23根地址线对应8M个字正好等于16MB。如果换用32MB的S29GL256就得确认FSMC_A24有没有接到芯片的A23否则高地址空间访问不到。2.3 硬件设计上的几个细节首先是电源和去耦。S29GL128正常工作电流在读取时能到几十毫安编程和擦除时瞬态电流更大要求在VCC引脚附近放10uF钽电容加0.1uF陶瓷电容组合并且尽量靠近芯片引脚。FSMC总线翻转频率高数据线和地址线上串联33欧姆电阻能显著改善信号完整性尤其是PCB布线较长时。其次是复位时序。S29GL128的RESET#引脚低电平有效复位后芯片需要等待至少100ns才能接受命令。如果RESET#直接接RC复位电路要确保RC时间常数足够避免上电后立即访问Flash出现找不到设备的问题。最稳妥的做法是把RESET#接到MCU的GPIO软件复位后延时再访问。另一个容易忽略的是字节模式引脚BYTE#。这个引脚决定数据总线是8位还是16位。必须接死到VCC不能悬空。悬空时芯片内部可能有不确定电平导致数据线高8位不输出读出来全是错的。我遇到过两次这种问题最后都是拿万用表量BYTE#电压才定位到。3. FSMC时序配置与代码实现3.1 用标准外设库3.5.0初始化FSMCSTM32F10x标准外设库3.5.0里FSMC相关的初始化结构体是FSMC_NORSRAMInitTypeDef和FSMC_NORSRAMTimingInitTypeDef。虽然有HAL库可以用但这个老工程沿用标准库逻辑也更简洁直白。初始化流程分三步开GPIO时钟、配置GPIO复用功能、配置FSMC控制器。GPIO部分要开GPIOA、GPIOB、GPIOC、GPIOD、GPIOE的时钟同时开AFIO时钟如果用了重映射。以100脚的STM32F103VE为例FSMC用到的引脚非常多批量配置时可以用一个数组把引脚定义好循环赋值。每个引脚配置成复用推挽输出GPIO_Mode_AF_PP速率设成50MHz。接着是FSMC控制器配置核心代码如下FSMC_NORSRAMInitTypeDef fsmc_nor; FSMC_NORSRAMTimingInitTypeDef read_timing, write_timing; // 读时序 read_timing.FSMC_AddressSetupTime 1; read_timing.FSMC_AddressHoldTime 1; read_timing.FSMC_DataSetupTime 4; read_timing.FSMC_BusTurnAroundDuration 0; read_timing.FSMC_CLKDivision 1; read_timing.FSMC_DataLatency 0; read_timing.FSMC_AccessMode FSMC_AccessMode_A; // 写时序 write_timing.FSMC_AddressSetupTime 1; write_timing.FSMC_AddressHoldTime 1; write_timing.FSMC_DataSetupTime 3; write_timing.FSMC_BusTurnAroundDuration 0; write_timing.FSMC_CLKDivision 1; write_timing.FSMC_DataLatency 0; write_timing.FSMC_AccessMode FSMC_AccessMode_A; fsmc_nor.FSMC_Bank FSMC_Bank1_NORSRAM1; fsmc_nor.FSMC_DataAddressMux FSMC_DataAddressMux_Disable; fsmc_nor.FSMC_MemoryType FSMC_MemoryType_NOR; fsmc_nor.FSMC_MemoryDataWidth FSMC_MemoryDataWidth_16b; fsmc_nor.FSMC_BurstAccessMode FSMC_BurstAccessMode_Disable; fsmc_nor.FSMC_AsynchronousWait FSMC_AsynchronousWait_Disable; fsmc_nor.FSMC_WaitSignalPolarity FSMC_WaitSignalPolarity_Low; fsmc_nor.FSMC_WrapMode FSMC_WrapMode_Disable; fsmc_nor.FSMC_WaitSignalActive FSMC_WaitSignalActive_BeforeWaitState; fsmc_nor.FSMC_WriteOperation FSMC_WriteOperation_Enable; fsmc_nor.FSMC_WaitSignal FSMC_WaitSignal_Disable; fsmc_nor.FSMC_ExtendedMode FSMC_ExtendedMode_Disable; fsmc_nor.FSMC_WriteBurst FSMC_WriteBurst_Disable; fsmc_nor.FSMC_ReadWriteTimingStruct read_timing; fsmc_nor.FSMC_WriteTimingStruct write_timing; FSMC_NORSRAMInit(fsmc_nor); FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM1, ENABLE);配置完成后可以立刻做一个内存回读测试往0x60000000写几个特征值再读回来确认总线能正常工作。这一步建议单独做一个自检函数后面排查问题时直接用。3.2 读写时序参数怎么算时序是FSMC配置里最容易翻车的地方。STM32F10x的FSMC时序参数单位是HCLK周期F103默认72MHz时一个HCLK周期约13.9ns。S29GL128P的异步读访问时间tCE/tAA根据速度等级不同90ns和110ns两个版本比较常见。这里算一个最基础的同步验证读时序的总周期数 地址建立时间(ADDSET) 地址保持时间(ADDHLD) 数据建立时间(DATAST)再加上最后一个HCLK的采样点。72MHz下ADDSET1DATAST4代表地址建立耗时(11)*13.9ns不对FSMC的时序模型是HCLK沿驱动实际计算至少要看数据手册的时序图。但这套数值在绝大多数S29GL128配置里都是稳的因为实际总线速度被多个状态周期压到了10MHz以下。想从手册反推参数时要抓住最关键的一个约束数据建立时间DATAST乘以HCLK周期必须大于等于Flash输出有效数据到读周期结束的最小时间。S29GL128从地址有效开始最长90ns或110ns后数据有效FSMC会在读周期结尾采样数据总线。DATAST4意味着从地址有效算起数据要在约(141)*13.9ns83.4ns内准备好这个数有点紧所以我实际把DATAST调到了5做余量。要注意调DATAST不是越大越好太大会拖慢连续读性能。3.3 用指针还是用函数访问FSMC挂NOR之后读操作直接走指针解引用即可。写操作则必须通过命令序列进入编程模式不能直接往地址空间写数据就以为写进去了。这和SRAM不一样NOR Flash在未擦除状态下写入不了1必须先擦除再编程。底层驱动我的实现方式是读操作用__attribute__((optimize(O2)))内联函数返回*(volatile uint16_t *)(BASE (addr 1))其中addr是字地址写操作封装成命令发送函数先检查状态寄存器再操作。这里有个性能细节——FSMC读慢不是因为总线频率低而是Flash本身存取时间摆在那S29GL128同步模式理论上能跑到更高吞吐但STM32F10x的FSMC不支持同步突发读只有F2/F4才完整支持所以异步模式下的速度基本就是上限。4. S29GL128驱动从识别到擦写4.1 CFI查询与器件识别拿到芯片第一件事不是直接擦写而是识别。S29GL128支持CFICommon Flash Interface标准通过命令序列把芯片切进CFI查询模式读出一串ASCII码和参数表。识别流程如下向任意地址写入0x0098进入CFI查询模式。读地址0x0010确认是0x0051Q。连续读0x0011、0x0012确认0x0052R、0x0059Y这三位是CFI标志。读0x0027得到设备接口代码NOR一般是0x0002异步接口。读0x0043、0x0044得到参数区大小用于计算总容量。做完CFI查询再把芯片切到设备ID模式向0x555地址写0xAA、向0x2AA写0x55、再向0x555写0x90然后读0x0000得到制造商IDS29GL128是0x0001读0x0001得到设备ID具体值查手册我拿到的是0x227E对应128Mbit版本。确认ID无误后写0xF0回到读阵列模式。识别这块的代码最好做成独立的norflash_identify()函数打印或返回这些参数。因为S29GL128分了很多速度等级和扇区结构变体实际拿到的芯片不一定和手册默认值一致靠CFI读出来的参数最靠谱。4.2 扇区擦除与编程操作S29GL128的扇区擦除命令序列是标准的AMD/Spansion风格// 向0xAAA地址写入0xAA向0x555写入0x55再向0xAAA写入0x80 // 然后再次0xAA - 0xAAA, 0x55 - 0x555, 0x30 - 扇区地址这里地址单位是字地址注意我前面说的FSMC偏移——Flash的0xAAA字地址对应FSMC字节地址是0xAAA 1 0x1554再加上Bank1基址0x60000000实际写的位置是0x60001554。很多新手的错误就在这里直接拿SHIFT后的地址当成普通物理地址去算结果命令序列没有送达Flash内部命令状态机。擦除完成后要查询状态。标准做法是轮询DQ7数据位7。擦除过程中DQ7输出为0擦除完成后DQ7自动变成1表示操作结束。同时要留意DQ5如果DQ5拉高表示内部编程/擦除错误需要写复位命令恢复。擦除一个128KB扇区典型时间在1~2秒代码里不能死等最好加一个超时重试机制超时后回读CFI确认芯片没挂。编程操作相对简单先擦除目标扇区然后逐字写入。S29GL128支持写缓冲编程一次最多写入32个字64字节。理论上用写缓冲能显著缩短编程时间但前提是地址连续、数据量对齐。我的FlashFS底层驱动里文件系统写入本来就按块对齐所以把写缓冲编程做在了底层单次写入32字然后轮询DQ7等缓冲编程结束。实测连续写大文件时吞吐比逐字写快了不少。4.3 状态寄存器与超时处理NOR Flash这种并行接口没有专门的错误中断引脚全部靠状态位反馈。S29GL128在命令模式下DQg7、DQ6、DQ5这几个位分别表示操作完成、翻转位、错误标志。我的驱动模板是每次擦除或编程后调用norflash_wait_ready()函数函数主体先延时一小段时间比如1ms再读状态。判断逻辑如下DQ7 1 且数据稳定说明操作完成如果DQ6持续翻转且超过超时时间判定芯片忙死返回超时如果DQ5 1且DQ7 0说明内部操作异常需要发复位命令恢复。这个函数是所有上层操作的安全阀。FlashFS每次调用底层写接口都靠它兜底。没有这个超时机制芯片一旦进入异常状态整个文件系统就会一直阻塞在等待循环里看门狗都救不回来。还有一点值得提S29GL128在写命令序列时如果地址或数据非法芯片不会自动回到读阵列模式而是停在命令状态机里。所以所有的命令发送函数末尾最好统一复位到读阵列写0xF0或0xFF。这也是“芯片突然读回全FF”的经典原因之一。5. FlashFS文件系统移植5.1 RL-FlashFS与底层驱动的接口Keil MDK的RL-FlashFS现在叫File System组件自带NOR Flash介质驱动模板。在RTE配置里勾选File System选择NOR Flash驱动然后打开FS_Config_NOR_0.h配置文件这里面需要改的参数非常关键。RL-FlashFS对NOR的支持依赖于一套底层接口包括设备初始化、扇区擦除、页编程、读数据等。默认模板里会有一个硬件配置结构体需要按照S29GL128的实际参数填写设备容量是16MB扇区大小128KB页大小64字节。这里的“页”对应NOR Flash的编程粒度S29GL128的写缓冲是32字64字节所以配64字节作为页大小最合理。驱动接口实现时有几个函数必须和文件系统层的调用约定一致。比如扇区擦除函数传入的是扇区编号需要先乘以扇区大小换算成物理地址再交给norflash_erase_sector()页编程函数传入的是物理地址加缓冲区指针实际上把逻辑页地址映射到FSMC总线地址。5.2 FlashFS的分区与挂载FlashFS管理NOR时会占用整个设备空间也可以配置分区大小。在配置文件中设置好设备容量后挂载操作就一行代码f_mount(fs, , 0);挂载时FlashFS会扫描整个NOR介质检查有没有合法文件系统头部。如果是全新芯片头一次挂载会失败需要用f_fdisk或格式化函数初始化。格式化NOR的操作一定要慎重——它会整片擦除耗时可能几十秒。建议在量产时先在产线上格式化一次把文件系统镜像直接烧进NOR开机挂载就不需要再格式化了。FlashFS在NOR上的布局大致是起始扇区放引导区和文件系统元数据后续扇区按顺序划分成数据区每个扇区内部再做页级管理。这里的坑在于NOR的擦除粒度是128KB文件系统写一个小文件小则几字节大也不过几KB如果每次修改都触发整扇区擦除Flash寿命根本扛不住。FlashFS为此做了扇区日志和垃圾回收业务层只要保证写入频率合理不至于触发频繁回收就行。实际项目里我们的日志是累积到一定大小才落盘一次避免频繁小文件写入。5.3 文件读写与掉电安全FlashFS挂载完成后文件操作和标准C库几乎一致fopen/fwrite/fread/fclose。不过嵌入式环境要注意几个细节。第一打开文件时建议用只写或只读模式不要频繁打开同一个文件追加每次追加都要更新文件目录项开销不小。我的做法是数据先缓存在RAM中达到4KB或者定时10秒再一次性写入这样写放大最小。第二关键配置参数建议存成两个备份文件。NOR闪存的位翻转率虽然比NAND低但也不是零文件系统本身有ECC校验最好没有的话靠双备份恢复也够用。第三掉电保护。FlashFS在写入文件数据时会先写日志区再更新目录区设计上能防掉电导致的文件系统崩溃。但MCU如果直接断电恰好在擦除扇区中间那文件数据会丢失文件系统结构还能保住。要进一步提升可靠性可以在硬件上加大容量电容让MCU检测掉电后有足够时间flush文件并卸载文件系统。6. 常见问题与排查实录6.1 写入后读回全是0xFF这是NOR Flash新手最容易撞上的问题。第一次排查先看芯片有没有进入写保护状态S29GL128的WP#引脚如果拉低会保护最高地址扇区写入操作可能被硬件忽略。其次看命令序列的地址对不对特别是FSMC挂载后的地址偏移。拿逻辑分析仪抓FSMC_NWE和地址线波形对比S29GL128的数据手册命令表基本一眼就能看出问题。还有一个隐藏原因写操作直接作用在FSMC_Bank地址上如果这块地址被缓存了Cortex-M3内核没有D-CacheF103不会有这个问题或者是编译器优化后跳过了写操作会导致命令没发出去。解决办法是把NOR的基地址指针声明为volatile最好在写入命令序列后加一条内存屏障。6.2 文件系统挂载失败挂载失败的典型表现是f_mount返回错误码或者写入文件后断电重启文件读不出来。排查顺序如下确认底层驱动读ID是否正常排除硬件连接问题确认配置文件的设备容量、扇区大小和芯片实际参数一致S29GL128如果配成8MB分区FlashFS在擦除时就会算错扇区边界确认是否已经格式化。新片必须格式化格式化会整片擦除耗时较长别在中途断电驱动接口中页编程函数是否严格按64字节写入如果FlashFS按页调用驱动而驱动只写了前32字节文件内容就会缺一半。6.3 运行时偶发死机或数据错乱这是最头疼的问题大概率是FSMC时序余量不足。我遇到过一种情况常温下跑压力测试半天不挂环境温度一升高芯片存取时间变长DATAST4的时序就扛不住了偶发读回错误数据。解决思路是把DATAST增加2~3个HCLK牺牲一点点速度换稳定性。另外检查PCB走线地址线和数据线等长性不好也会偶发时序问题用示波器抓FSMC总线的过冲和下冲能辅助判断。如果用的是CH32V307这类国产MCU替换FSMC外设和F103基本兼容但时钟树配置不一样HCLK频率可能不同时序参数要重新换算。6.4 文件系统性能达不到预期FlashFS写大文件时如果吞吐率很低大概率是底层驱动没有使用写缓冲编程而是一个字一个字地写。S29GL128支持32字突发写利用起来能把写速度提升两到三倍。另外文件系统的缓存大小也可以调整RL-FlashFS在配置文件里可以加大缓冲减少小写入次数。我实测了一个100KB日志文件逐字写大概要7秒启用写缓冲后降到2.5秒左右差距非常明显。如果你也在做类似方案建议优先把写缓冲编程做扎实。7. 一些实际操作中的心得整套方案跑下来最大的体会有两个。一是FSMC NOR FlashFS这种组合硬件上看起来是一堆引脚和一根总线真正的工作量全在驱动适配和时序调试上。原理图改一版很轻松但时序参数不匹配的问题可能要跑几天压力测试才暴露出来。所以设计阶段就应该给时序留余量别卡着手册极限值配能省掉后面大量排障时间。二是文件系统的选型要提前想清楚。RL-FlashFS和Keil的工程集成度很高省事但它是闭源的出了问题只能靠文档和实验。如果你更倾向开源方案LittleFS也能跑在NOR上只是需要自己写一个基于FSMC的MTD层。两者我都跑过小规模测试FlashFS胜在集成快LittleFS胜在可控性强。具体选哪个取决于你对代码可维护性的要求。写缓冲编程和编号映射模块的配合是我们这套方案里最值得复用的部分。底层驱动把“擦/写”封装成原子操作编号映射表只管“哪条数据在哪个区块”上层业务代码从没关心过物理地址长什么样。这种分层思路无论以后是换Flash芯片还是换文件系统改动都不会波及业务代码。最后再分享一个小技巧量产时Flash芯片的擦写寿命请按照最差情况去规划。S29GL128虽然标称擦写循环10万次但文件系统的日志重写和垃圾回收会额外消耗擦写次数。我们现在的策略是编号映射区和日志区定时轮换避免某个扇区一直被反复擦写。跑了一年多的设备回读检查芯片状态依然健康。这套方案稳。本文还有配套的精品资源点击获取