FLASH — 内部 Flash 读写
难度: ⭐⭐(理解 Flash 特性即可)
固件库: AT32F403A_407_Firmware_Library V2.2.2
源文件:project/at_start_f403a/examples/flash/flash_write_read/src/main.c
这个实验是干嘛的?
MCU 的内部 Flash 主要用来存储程序代码。但它有一个"隐藏技能":你可以在程序运行时,把 Flash 的空闲区域当作 EEPROM 使用,存储掉电不丢失的用户数据。
有什么用?
- 存储校准参数(ADC 偏移、温度补偿系数)
- 存储用户设置(亮度、音量、WiFi 密码)
- 存储运行计数(开机次数、累计运行时间)
- 存储故障日志(最后一条错误信息)
省掉了外部 EEPROM 芯片,省成本、省引脚、省 PCB 空间。
本实验在 Flash 地址 0x08002800(偏移 10KB)处写入 3000 个 16 位数据,然后读回比对。
硬件准备
不需要任何外部硬件! 操作的是 MCU 内部 Flash。
Flash 的特性(必须了解!)
Flash vs RAM vs EEPROM
| 特性 | Flash | RAM | EEPROM |
|---|---|---|---|
| 掉电保存 | 是 | 否 | 是 |
| 读取速度 | 快 | 最快 | 快 |
| 写入速度 | 慢(ms 级) | 最快 | 慢(ms 级) |
| 擦写次数 | 1 万~10 万次 | 无限 | 100 万次 |
| 最小擦除单位 | 扇区(2KB/4KB) | 无 | 字节 |
| 写入限制 | 必须先擦后写 | 无 | 无 |
Flash 的铁律
- 只能把 1 写成 0,不能把 0 写成 1
- 必须先擦除(把所有位变成 1 = 0xFF),才能写入
- 擦除按扇区,最小 2KB(AT32F403A)
- 写入按半字(16 位)
地址规划
AT32F403A 的 Flash 从 0x08000000 开始:
|
|
警告:写入地址不能和程序代码重叠,否则会把程序擦掉,MCU 变砖!
完整代码
|
|
代码分段讲解
1. 写入地址选择
|
|
0x08000000是 Flash 的起始地址- 偏移 10KB 是为了避开程序代码区
- 你必须确保这个地址不和程序代码重叠!
如何确认?看 Keil 编译后的 .map 文件,找到程序占用的最大地址,写入地址必须大于它。
2. flash_write 内部做了什么
flash_write() 函数内部的流程:
|
|
3. flash_read 做了什么
|
|
Flash 读取非常简单——直接从内存地址读就行。Flash 就是内存映射的,读取和读 RAM 一样。
4. 为什么用 uint16_t?
AT32F403A 的 Flash 最小写入单位是半字(16 位)。所以数据类型用 uint16_t,不是 uint8_t。
实验现象
- 3 个 LED 全部亮起 → Flash 读写成功
- LED 不亮 → 写入失败或数据不一致
实际应用:存储校准参数
|
|
常见问题
Q: 写入后程序跑飞了?
- 写入地址和程序代码重叠了!
- 检查
.map文件,确保写入地址在程序占用区域之外 - 建议用 Flash 的最后几个扇区存储数据
Q: 写入次数有限制吗?
- Flash 擦写次数约 1 万~10 万次
- 如果需要频繁写入,用 RAM 缓存 + 定期保存策略
- 或者用外部 EEPROM(100 万次)
Q: 写入速度很慢?
- Flash 写入是 ms 级的(每半字约 10~50us)
- 3000 个半字 ≈ 150ms
- 写入期间 CPU 会被阻塞(Flash 总线占用)
Q: 想存储字符串/结构体?
- 强制转换为
uint16_t*指针 - 长度要对齐到半字(2 字节)
- 建议加 CRC 校验,防止意外断电导致数据损坏
小结
| 概念 | 说明 |
|---|---|
| 内部 Flash | MCU 自带的程序存储器,也能存用户数据 |
| 先擦后写 | Flash 的铁律,只能 1→0 |
| 扇区擦除 | 最小擦除单位 2KB |
| 半字写入 | 最小写入单位 16 位 |
| 地址规划 | 不能覆盖程序代码! |
| CRC 校验 | 防止数据损坏 |
学了 Flash 读写能做什么?
内部 Flash 是最省钱的"掉电存储方案":
数据存储
| 项目 | 原理 | 难度 |
|---|---|---|
| 校准参数 | 出厂校准一次,永久保存 | ⭐ |
| 用户设置 | 亮度、音量、WiFi 密码等 | ⭐ |
| 运行日志 | 开机次数、累计时间、故障记录 | ⭐⭐ |
| 数据记录 | 温度/湿度历史数据(Flash 空间够的话) | ⭐⭐ |
固件升级(IAP)
| 项目 | 原理 | 难度 |
|---|---|---|
| IAP 自升级 | 从串口/USB 接收新固件,写入 Flash,跳转执行 | ⭐⭐⭐⭐ |
| A/B 双分区 | 两个程序分区,一个运行一个升级,防变砖 | ⭐⭐⭐⭐⭐ |
| Bootloader | 最小程序,负责加载和跳转应用 | ⭐⭐⭐ |
与外部 Flash 的对比
| 方案 | 优点 | 缺点 |
|---|---|---|
| 内部 Flash | 免费、无外部接线、速度快 | 容量小、擦写次数少、不能边写边读 |
| 外部 SPI Flash | 容量大(几 MB~几百 MB)、擦写次数多 | 需要 SPI 接线、占引脚 |
| 外部 EEPROM | 逐字节读写、擦写次数多(100 万次) | 容量小(几 KB)、需要 I2C 接线 |
核心思路:内部 Flash 是最简单的掉电存储方案——不需要额外硬件,不需要接线,直接写地址就行。适合存储少量关键数据(校准参数、用户设置)。如果数据量大或写入频繁,再考虑外部 Flash 或 EEPROM。
🎉 恭喜! 你已经完成了 AT32 入门系列的 8 个实验:GPIO → USART → ADC → SPI → I2C → TIM → DMA → FLASH。这些是嵌入式开发最核心的基础,掌握了它们,你就能独立开发各种嵌入式项目了!