[特殊字符] 从零理解 Flash 写入:扇区、页、擦除、写入原理全解析

[特殊字符] 从零理解 Flash 写入:扇区、页、擦除、写入原理全解析

在嵌入式开发或 Linux 驱动开发中,你一定会遇到“Flash 写入”这一重要操作。但很多初学者在面对类似代码时经常一头雾水:

什么是扇区(Sector)?页(Page)?为什么写入前要擦除?为什么数据不能直接覆盖?什么叫“不能从 0 写回 1”?!

别担心,本文将带你全面理解 Flash 的基本结构和写入流程,解决你理解 Flash 写入代码时的所有障碍。

🧠 一、什么是 Flash?它是怎么组织的?

Flash(闪存)是一种非易失性存储器(断电后仍保留数据),广泛应用于 MCU、U 盘、固态硬盘等设备。

Flash 的结构由大到小如下:

┌───────────────┐

│ Flash 总容量 │ ← 如 512KB

├───────────────┤

│ 扇区(Sector)│ ← 通常为 4KB、8KB 或 64KB 等

├───────────────┤

│ 页(Page) │ ← 通常为 256B、512B

└───────────────┘

扇区是最小的“擦除单位”;页是最小的“编程(写入)单位”;字节 Byte是最小的“读取单位”。

Flash(闪存)是一种非易失性存储器(断电后仍保留数据),广泛应用于 MCU、U 盘、固态硬盘等设备。

Flash 的结构由大到小如下:

┌───────────────┐

│ Flash 总容量 │ ← 如 512KB

├───────────────┤

│ 扇区(Sector)│ ← 通常为 4KB、8KB 或 64KB 等

├───────────────┤

│ 页(Page) │ ← 通常为 256B、512B

└───────────────┘

扇区是最小的“擦除单位”;页是最小的“编程(写入)单位”;字节 Byte是最小的“读取单位”。

Flash(闪存)是一种非易失性存储器(断电后仍保留数据),广泛应用于 MCU、U 盘、固态硬盘等设备。

Flash 的结构由大到小如下:

┌───────────────┐

│ Flash 总容量 │ ← 如 512KB

├───────────────┤

│ 扇区(Sector)│ ← 通常为 4KB、8KB 或 64KB 等

├───────────────┤

│ 页(Page) │ ← 通常为 256B、512B

└───────────────┘

扇区是最小的“擦除单位”;页是最小的“编程(写入)单位”;字节 Byte是最小的“读取单位”。

❗ 二、Flash 的三个限制性原则(必须理解)

1️⃣ 写入前必须先擦除

Flash 不能直接覆盖旧数据,你必须先擦除目标区域(清空为 0xFF)再写入新数据。

2️⃣ 只能从 1 ➜ 0,不能从 0 ➜ 1

比如原来是 11111111(0xFF),可以写成 10101010(0xAA);

但如果原来是 10101010,你不能写回 11111111,只能擦除整个扇区。

3️⃣ 擦除粒度较大(按扇区)

哪怕你只想改一个字节,也必须擦除整个扇区,然后重新写入该字节 + 其它原有数据。

三、Flash 写入的完整流程

举例:你要写入 10 字节数据到地址 0x08005000

步骤一:解锁 Flash

很多芯片(如 STM32)默认 Flash 是锁定状态,需先调用 flash_unlock() 解锁。

步骤二:判断地址是否跨扇区

计算起始地址所在扇区是否能容纳全部数据,如果不能,需要分段处理多个扇区。

步骤三:读取扇区到缓存

由于写入前要擦除整个扇区,你需要把当前扇区的数据读出来保存。

步骤四:修改缓存中的对应区域

把你想写入的数据覆盖到缓存中对应的偏移位置。

步骤五:擦除该扇区

调用如 flash_sector_erase(addr) 擦除整片区域。

步骤六:写回整个缓存

使用 flash_program(addr, buf) 将整个缓冲区重新写入 Flash。

步骤七:锁定flash

写入完成后调用 flash_lock(),防止误操作。

💡 四、扇区 vs 页:区别与用途

项目

扇区(Sector)

页(Page)

功能

擦除单位

写入单位

粒度

大(4KB、64KB)

小(256B)

操作

擦除必须整扇区

可写入单页或多页

场景

擦除 Flash 空间

写入数据

注意:不是所有芯片都对页有显式操作,有些芯片只按字或半字写入,不暴露页概念。

📌 五、Flash 写入为什么复杂?

Flash 不是像 RAM 那样可以随意读写。你需要考虑:

地址对齐问题;页边界、扇区边界;写入效率(一次写多字);掉电保护(写一半掉电会导致数据异常);写入寿命(有限的写入次数,典型为 10 万次)。

因此,为了安全可靠,很多驱动写入逻辑都显得“绕”——它是为了兼容多种边界情况、避免数据损坏。

📁 六、实际代码中你会看到的关键词

术语

含义

flash_unlock()

解锁 Flash 写保护

flash_sector_erase()

擦除指定扇区

flash_program() / flash_write_nocheck()

写入数据(默认要求写入地址为 0xFF)

flash_read()

从 Flash 读取数据

SECTOR_SIZE

每个扇区的大小(通常为宏定义)

offset, remain

当前写入的位置与剩余空间计算

📁 七、应用实例!!!

error_status flash_write(uint32_t write_addr, uint8_t *p_buffer, uint16_t num_write)

{

uint32_t offset_addr;//flash偏移量

uint32_t sector_position;//片区偏移个数

uint16_t sector_offset;//片内偏移量

uint16_t sector_remain;//片区剩余量

uint16_t i;

flash_status_type status = FLASH_OPERATE_DONE;

flash_unlock();

offset_addr = write_addr - FLASH_BASE;//flash偏移量

sector_position = offset_addr / SECTOR_SIZE;////片区偏移个数,从 0 开始的索引编号

sector_offset = (offset_addr % SECTOR_SIZE);//片内偏移量

sector_remain = SECTOR_SIZE - sector_offset;////片区剩余量

if(num_write <= sector_remain)//判断片区剩余量是否足够写入需要的数据大小

sector_remain = num_write;//在主循环中表示,实际要写的字节数

while(1)

{

flash_read(sector_position * SECTOR_SIZE + FLASH_BASE, flash_buf, SECTOR_SIZE );//片区剩余量不够时读取出整个片区的数据到buf中

for(i = 0; i < sector_remain; i++)

{

if(flash_buf[sector_offset + i] != 0xFF)//判断片区剩余量后续地址是否有值

break;

}

if(i < sector_remain)//说明后面存在地址有值

{

status = flash_sector_erase(sector_position * SECTOR_SIZE + FLASH_BASE);//擦除整个片区

if((status == FLASH_PROGRAM_ERROR) || (status == FLASH_EPP_ERROR))//出现flash操作错误

flash_flag_clear(FLASH_PRGMERR_FLAG | FLASH_EPPERR_FLAG);//清除flash的相关错误标志位

else if(status == FLASH_OPERATE_TIMEOUT)//超时未完成

return ERROR;

status = flash_operation_wait_for(ERASE_TIMEOUT);//等待片区擦除完

if(status != FLASH_OPERATE_DONE)

return ERROR;

for(i = 0; i < sector_remain; i++)

{

flash_buf[i + sector_offset] = p_buffer[i];//将需要写入的数据写到buf中与偏移量对应的地方

}

if(flash_write_nocheck(sector_position * SECTOR_SIZE + FLASH_BASE, flash_buf, SECTOR_SIZE ) != SUCCESS)//然后将整个buf写到片区

return ERROR;

}

else//后面的地址没有值

{

if(flash_write_nocheck(write_addr, p_buffer, sector_remain) != SUCCESS)//直接写入

return ERROR;

}

if(num_write == sector_remain)

break;

else//片区剩余大小不够写入数据

{

sector_position++;//片区偏移个数加1

sector_offset = 0;//片区偏移量设为0

p_buffer += sector_remain;//传入数据数组偏移已经写入的值

write_addr += (sector_remain);//写入地址偏移

num_write -= sector_remain;//需要写入的数据个数减去已经写入的数据个数

if(num_write > (SECTOR_SIZE))

sector_remain = SECTOR_SIZE ;

else

sector_remain = num_write;

}

}

flash_lock();

return SUCCESS;

}

这段代码是一个典型的Flash写入函数,适用于如STM32或其他支持按扇区擦写的MCU。改函数设计考虑了Flash的以下特性:

Flash 写入前需要擦除;Flash 不能单字节直接覆盖写,写入前对应位置必须为 0xFF;Flash 按 扇区/页(Sector)擦除;写入可能跨多个扇区。

参数说明:

参数

含义

write_addr

要写入的 Flash 地址

p_buffer

指向要写入的数据的指针

num_write

要写入的数据字节数

🔁 函数总体流程概述

解锁 Flash 写保护;计算地址在哪个扇区、扇区内偏移多少;判断该扇区是否可直接写入;如不可直接写入,需读取整个扇区到缓存 → 修改 → 擦除 → 再整体写回;如果可以直接写入,则跳过擦除步骤;支持跨扇区写入;写入完成后锁定 Flash。

🧠 逐段解析代码逻辑

🔓 解锁 Flash

flash_unlock();

为了能写入或擦除 Flash,需要先解锁。

📍 地址定位

offset_addr = write_addr - FLASH_BASE;

sector_position = offset_addr / SECTOR_SIZE;

sector_offset = (offset_addr % SECTOR_SIZE);

sector_remain = SECTOR_SIZE - sector_offset;

offset_addr:写入地址距离 Flash 起始的偏移量;sector_position:当前地址位于第几个扇区;sector_offset:在扇区内部的偏移位置;sector_remain:当前扇区剩余可写空间(不一定足够写全部数据);

🧪 写入主循环

先读取整个扇区到缓存:

flash_read(sector_position * SECTOR_SIZE + FLASH_BASE, flash_buf, SECTOR_SIZE );

判断要写入的位置是否已被写过(非 0xFF):

for(i = 0; i < sector_remain; i++) {

if(flash_buf[sector_offset + i] != 0xFF)

break;

}

如果存在已写过区域,必须先擦除扇区

status = flash_operation_wait_for(ERASE_TIMEOUT);

// 错误处理

status = flash_sector_erase(...);

// 写入修改后的完整 buf

flash_buf[i + sector_offset] = p_buffer[i];

flash_write_nocheck(...);

如果可以直接写入:

flash_write_nocheck(write_addr, p_buffer, sector_remain);

🔁 判断是否写完

if(num_write == sector_remain)

break;

否则说明本扇区装不下所有数据,更新参数进入下一扇区:

sector_position++;

sector_offset = 0;

p_buffer += sector_remain;

write_addr += sector_remain;

num_write -= sector_remain;

🔒 最后加锁 Flash

flash_lock();

return SUCCESS;

总结:本函数的特点

特性

描述

安全性高

避免直接写入非 0xFF 区域,防止数据错写

支持跨扇区

自动处理跨越多个 Flash 扇区的数据写入

缓存保护

使用 flash_buf 读取整个扇区再写,防止非目标地址被擦除

可扩展性强

可用于 OTA 升级、BootLoader 写 Flash 等场景

✅ 总结

问题

结论

Flash 能像 RAM 一样随便写吗?

❌ 不能,必须先擦除

一个字节能单独擦除吗?

❌ 不行,只能按“扇区”擦除

擦除后 Flash 初始值是什么?

0xFF

可以从 0 写回 1 吗?

❌ 不行,只能 1 ➜ 0

为什么驱动中有这么多“判断剩余空间”的逻辑?

为了适配跨扇区、偏移写入等边界情况

相关文章

365用什么浏览器登录 网上订票怎么选择上下铺 动车票网上预订座位选择
365bet账号被限制 会员卡管理系统哪个好用?这几款就不错
365bet网络足球赌博 中兴Blade A4发布,宣传让人眼花缭乱,网友却说不值得
365bet账号被限制 欢迎访问中国建设银行网站

欢迎访问中国建设银行网站

🗓️ 06-29 👁️ 8405
365bet账号被限制 Python超详细一条龙版学习路线(2025年最新)
365bet网络足球赌博 Word文档中添加分割线的方法教程

Word文档中添加分割线的方法教程

🗓️ 07-08 👁️ 8611