news 2026/3/1 8:16:38

系统学习erase前必须知道的存储基础知识

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
系统学习erase前必须知道的存储基础知识

深入理解Flash中的erase操作:从物理原理到工程实践

你有没有遇到过这样的问题?在做固件升级时,明明只改了几百字节的数据,结果整个系统卡住好几百毫秒;或者日志写了一阵子,突然发现Flash某些区域再也写不进去了——最后排查发现是“坏块”导致的?

这些问题的背后,往往都藏着一个被忽视的关键动作:erase(擦除)

在嵌入式开发中,很多人习惯性地把Flash当成可以随意读写的存储器,就像操作内存一样。但事实并非如此。Flash有它自己的脾气和规则,而其中最核心、最容易踩坑的一条就是:想写,先擦。

本文将带你从底层物理结构讲起,彻底搞清楚为什么必须先擦后写?erase到底发生了什么?它对性能、寿命和系统设计意味着什么?最终让你在实际项目中不再“凭感觉”调用erase_block(),而是真正“懂它”。


一、别再把它当RAM了:Flash的本质差异

我们都知道,SRAM或DRAM支持任意地址的直接覆写。你想把某个字节从0xFF改成0x55?没问题,一条指令搞定。

但Flash不行。

为什么不能“覆盖写”?

这要从它的存储单元说起。

Flash的基本单位是一个叫做浮栅晶体管(Floating Gate Transistor)的器件。这个晶体管的“栅极”被一层绝缘层包裹着,电子一旦进去就很难出来——这就是它能“断电保存数据”的原因。

  • 当浮栅里有电子→ 阈值电压升高 → 判定为逻辑“0”
  • 当浮栅里没有电子→ 阈值电压正常 → 判定为逻辑“1”

关键来了:

你可以通过编程(Program)往浮栅注入电子,实现“1 → 0”
但无法通过普通写操作把电子“抽出来”,也就是不能“0 → 1”

所以,如果你想在一个原本是“0”的位上写“1”,唯一的办法是:先整体擦除这块区域,把所有位都恢复成“1”状态,然后再重新写入你需要的“0”位置。

这就引出了那句每个嵌入式工程师都应该刻在脑子里的话:

🔥Flash只能将“1”变成“0”,不能将“0”变成“1”。要变回“1”,必须整块擦除。


二、擦的是“页”还是“块”?粒度决定一切

我们知道写入是以“页”为单位(常见4KB、8KB),但你知道吗?擦除的最小单位是“块”(Block),通常是64KB甚至更大。

这意味着:

即使你只想修改一页里的几个字节,也必须:
1. 把整个块(比如64KB)的内容读到RAM;
2. 在RAM中修改目标页;
3. 找一个新的、已经擦好的块;
4. 把更新后的所有页写过去;
5. 最后把旧块标记为无效,并安排后续擦除。

听起来很麻烦?没错,这正是许多高性能文件系统(如LittleFS、SPIFFS、YAFFS)存在的意义。

典型参数对比(以Micron MT29F系列为例)

参数
页面大小(Page Size)4 KB
块大小(Block Size)256 pages = 1 MB
擦除时间(Typical)2~3 ms
编程时间(Per Page)~300 μs
耐久性(P/E Cycles)100,000 次(SLC模式)

看到没?一次擦除的时间相当于连续写几十个页面!而且每擦一次,就在消耗这块Flash的生命。

这也解释了为什么频繁的日志记录如果不加管理,很容易把某几个块“累死”——它们被反复擦写,而其他块却几乎没动过。


三、erase到底做了什么?不只是“清零”那么简单

很多人以为“erase”就是把数据全变成0xFF,其实不然。它是实实在在的物理过程

内部发生了什么?

擦除操作利用的是Fowler-Nordheim隧穿效应。简单来说:

  • 控制栅接地(0V)
  • 衬底施加高正电压(约20V)
  • 强电场迫使浮栅中的电子穿过绝缘层逸出
  • 所有单元恢复到高电平状态(即“1”)

这个过程需要芯片内部产生高压,因此耗时较长,且功耗瞬间上升。期间Flash处于“忙”状态,无法响应任何读写请求。

状态机控制:主机只能等待

Flash芯片内部有一个状态机来管理 erase 流程。典型流程如下:

  1. 主机发送WRITE ENABLE
  2. 发送ERASE SETUP命令 + 地址
  3. 触发PROGRAM/ERASE CONFIRM命令
  4. 芯片启动内部高压电路,进入 busy 状态
  5. 主机轮询状态寄存器(Status Register),直到BUSY BIT == 0
// 示例:等待擦除完成 do { status = read_status_register(); delay_us(100); } while (status & 0x01); // BUSY bit

⚠️ 注意:如果在这期间强行访问Flash(例如中断服务程序尝试读取配置),会导致命令失败甚至数据损坏。


四、“先擦后写”不是建议,是铁律

让我们来看一段真实代码场景:

// 错误示范:假设flash_page_write会自动处理擦除 flash_page_write(0x0008_0000, new_data, 4096);

你以为这一行就能写入一页数据,但如果目标块还没擦过呢?

结果可能是:
- 写操作静默失败
- 只有部分bit被正确写入(因为原数据中有“0”)
- 数据看似写入成功,实则下次读取异常

正确的做法永远是:

if (!is_block_erased(target_block_addr)) { flash_erase_block(target_block_addr); // 显式擦除 } flash_page_write(target_page_addr, data, size);

记住:Flash不会替你做任何事。你不擦,它就不让你写。


五、实战陷阱与应对策略

1. 写放大(Write Amplification):小改动引发大开销

你只是更新了4KB的配置信息,却不得不擦除1MB的块?这就是典型的写放大问题。

更糟的是,如果你用了垃圾回收机制,可能还要搬动其他有效页,进一步增加实际写入量。

📌对策
- 使用日志结构设计:新数据总是追加写入,避免原地更新
- 实现磨损均衡(Wear Leveling):轮流使用不同块,防止单点老化
- 设置预留块池(Over-provisioning):留出备用块用于GC和替换

2. 擦除阻塞系统:UI卡顿、通信超时

一次擦除动辄几毫秒,在实时性要求高的系统中简直是灾难。

📌对策
-异步化处理:把擦除任务交给后台线程或低优先级任务
-分片执行:将多个擦除拆成微任务,在空闲循环中逐步完成
-预擦除机制:提前擦好一些备用块,需要用时直接分配

3. 掉电风险:擦到一半断电怎么办?

Flash最怕的就是“半途而废”。如果在擦除过程中断电,可能导致:
- 块状态不确定
- 元数据损坏
- 整个文件系统无法挂载

📌防护手段
- 关键元数据加CRC 校验
- 使用原子更新协议(如双备份+切换标志)
- 实现掉电检测 + 恢复机制:重启后检查未完成事务并修复


六、OTA升级中的erase实战案例

来看一个常见的 OTA 场景。

存储布局设计

[0x0000_0000] Bootloader [0x0001_0000] Current Firmware ← 当前运行的固件 [0x0008_0000] Staging Area ← 新固件下载区 [0x000F_0000] Config & Logs

升级流程中的erase时机

  1. 接收新固件包,按页写入 Staging Area
  2. 每次写之前检查所在块是否已擦除
    - 否 → 调用erase_block()
    - 是 → 直接写入
  3. 完整性校验通过后,设置“升级标志”
  4. 下次启动由 Bootloader 检测标志,切换运行区
  5. 原 Firmware 区被标记为无效,等待 GC 回收并擦除

关键设计点

永不覆盖正在运行的代码:确保即使升级失败也能回退
延迟擦除旧固件区:等到新固件验证通过后再释放空间
擦除失败重试机制:最多3次,失败则标记为坏块
电源监控联动:电压低于阈值时暂停擦除操作

这样既保证了安全性,又提升了用户体验——用户不会因为“正在擦除”而看到设备长时间无响应。


七、高级技巧:如何让erase更聪明?

1. 动态统计擦除次数

给每个块维护一个 erase count 计数器,记录其已被擦除多少次。结合磨损均衡算法,优先选择擦得少的块进行写入。

struct block_info { uint32_t start_addr; uint16_t erase_count; uint8_t status; // FREE / USED / DIRTY / BAD };

可用于现场故障诊断,也能预警潜在寿命耗尽风险。

2. 合并擦除请求

如果有多个相邻块都需要擦除,尽量合并成批量操作。虽然Flash本身不支持“多块同时擦”,但减少命令交互次数仍可提升效率。

3. 利用硬件特性加速

某些高端Flash支持quad IO + high-performance mode,可在 busy 期间继续接收命令缓冲(如Status Polling via DTR),间接提高吞吐率。


写在最后:别让erase成为你的盲区

在嵌入式系统中,对Flash的理解深度,决定了你能走多远。

很多开发者只关注“怎么写进去”,却忽略了“能不能写”、“什么时候能写”、“写了之后还能活多久”这些更本质的问题。

而这一切的答案,都藏在那个看似简单的函数调用背后:

flash_erase_block(address);

它不是一句无关痛痒的操作,而是触发了一场微观世界的物理风暴——高压生成、电子迁移、状态切换……稍有不慎,轻则性能下降,重则数据丢失、设备变砖。

所以,请不要再把它当作理所当然的动作。下一次你在写 Flash 驱动、实现日志模块、设计 OTA 方案时,不妨停下来问自己几个问题:

  • 我的目标区域真的已经擦过了吗?
  • 这次擦除会影响系统响应吗?
  • 这个块是不是已经快到寿命极限了?
  • 如果现在断电,我的数据还能恢复吗?

当你开始思考这些问题的时候,你就不再是“调API的人”,而是真正掌控系统的工程师了。

如果你也在做相关开发,欢迎留言分享你的经验和踩过的坑。我们一起把这块“硬骨头”啃透。

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/28 18:39:18

OpenWrt Argon主题终极配置指南:从安装到优化的完整解决方案

OpenWrt Argon主题终极配置指南:从安装到优化的完整解决方案 【免费下载链接】luci-theme-argon Argon is a clean and tidy OpenWrt LuCI theme that allows users to customize their login interface with images or videos. It also supports automatic and man…

作者头像 李华
网站建设 2026/3/1 4:50:45

Keil串口通信调试:新手必备的实战入门篇

Keil串口调试实战:从零点亮“开发者之眼”你有没有过这样的经历?代码烧进STM32,板子上电,LED不闪、屏幕无显,程序像掉进了黑洞——完全不知道它跑到了哪里。这时候,最朴素也最有效的救星是什么?…

作者头像 李华
网站建设 2026/2/26 23:28:42

ComfyUI-Manager终极优化指南:让低配设备也能流畅运行AI创作

ComfyUI-Manager终极优化指南:让低配设备也能流畅运行AI创作 【免费下载链接】ComfyUI-Manager 项目地址: https://gitcode.com/gh_mirrors/co/ComfyUI-Manager 还在为老旧电脑运行ComfyUI时的卡顿、崩溃而烦恼吗?本文将从实战角度出发&#xff…

作者头像 李华
网站建设 2026/3/1 0:50:29

近视率持续攀升:儿童近视防控,家长不可忽视的必修课

近年来,我国儿童青少年近视率呈逐年上升趋势,低龄化、重度化特征愈发明显——教育部发布的监测数据显示,部分地区小学生近视率已突破40%,初中生超70%,近视问题正以惊人的速度侵蚀着下一代的视觉健康。儿童近视绝非简单…

作者头像 李华
网站建设 2026/2/25 17:03:16

核心要点:proteus仿真时钟源配置方法

一文搞懂Proteus仿真中的时钟配置:从晶振到代码的完整闭环你有没有遇到过这种情况?在Proteus里画好了电路、写好了程序,点击仿真却“纹丝不动”——LED不闪、串口没输出、调试器卡在启动文件。翻来覆去检查代码逻辑,结果问题根本不…

作者头像 李华
网站建设 2026/2/27 18:06:01

初学者必看的Multisim下载与配置手把手教程

从零开始:手把手教你搞定 Multisim 安装与配置,轻松开启电路仿真之旅 你是不是也曾在搜索引擎里反复输入“ multisim下载 ”,却在一堆广告、破解包和英文官网之间迷失方向? 刚入门电子设计的你,是否被老师一句“用…

作者头像 李华