【嵌入式外设精学】Day17|Flash:页擦写、参数保存、掉电安全
今天解决:为什么不能像 RAM 随便写?参数怎样存才抗掉电?
目录
1. 问题
- 存参数偶发坏
- 写得很勤后来整片变砖
2. 原理
2.1 擦和写是两回事
| 擦 | 页/扇区 | 整块变“空白” |
| 写 | 字/半字 | 往空白里写 |
改一个字节,往往也要先擦整页再写回。
2.2 寿命有限
每块能擦写的次数有限(常见万级到十万级,看器件)。 不能每 10ms 存一次 PID。
2.3 掉电安全(双份 A/B)
A 页 / B 页,都带“序号 + 校验”
保存:写到“不是当前有效”的那一份
上电:两份都读,取校验过且序号大的
这样写到一半掉电,旧那份仍可用。
3. 代码示例
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#define MAGIC 0x5041524Du
typedef struct {
float kp, ki, kd;
} par_t;
typedef struct {
uint32_t magic;
uint16_t ver;
uint16_t seq;
par_t p;
uint32_t crc;
} blob_t;
/* 平台相关:擦页、写、读、CRC */
void flash_erase_page(uint32_t page);
void flash_write(uint32_t addr, const void *d, uint32_t n);
void flash_read(uint32_t addr, void *d, uint32_t n);
uint32_t page_addr(uint32_t page);
uint32_t crc32_calc(const uint8_t *d, uint32_t n);
static void seal(blob_t *b)
{
b->magic = MAGIC;
b->crc = crc32_calc((const uint8_t *)b, (uint32_t)(sizeof(*b) – 4u));
}
static bool ok(const blob_t *b)
{
return b->magic == MAGIC &&
b->crc == crc32_calc((const uint8_t *)b, (uint32_t)(sizeof(*b) – 4u));
}
int param_save(uint16_t *seq, const par_t *in, uint32_t page)
{
blob_t b;
memset(&b, 0, sizeof(b));
b.seq = (uint16_t)(*seq + 1u);
b.p = *in;
seal(&b);
flash_erase_page(page);
flash_write(page_addr(page), &b, sizeof(b));
*seq = b.seq;
return 0;
}
bool param_load(par_t *out, uint16_t *seq, uint32_t page_a, uint32_t page_b)
{
blob_t a, b;
flash_read(page_addr(page_a), &a, sizeof(a));
flash_read(page_addr(page_b), &b, sizeof(b));
if (ok(&a) && ok(&b)) {
const blob_t *sel = ((int16_t)(a.seq – b.seq) > 0) ? &a : &b;
*out = sel->p; *seq = sel->seq;
return true;
}
if (ok(&a)) { *out = a.p; *seq = a.seq; return true; }
if (ok(&b)) { *out = b.p; *seq = b.seq; return true; }
return false;
}
保存时机:按钮“保存参数”、关机前、模式切换——不要高频定时写。
4. 常见误区
| Flash 当 EEPROM 随便改 | 要擦写策略和寿命意识 |
| 只存一份 | 掉电可能半新半旧 |
| 不加校验 | 坏数据当参数用 |
| 运行中频繁写 | 寿命和掉电风险都大 |
5. 自测
6. 答案
作者:shuifanyu 标签:Flash 掉电 参数存储
网硕互联帮助中心




评论前必须登录!
注册