容量:64KB
起始地址 结束地址
0x08000000 ┌────────────────────┐
│ 主存储器 (64KB) │ ← 用户程序+数据
│ 页 0~63 (1KB/页) │
0x08010000 ├────────────────────┤
│ │
0x1FFFF000 ├────────────────────┤
│ 系统存储器 (2KB) │ ← Bootloader (出厂固件)
0x1FFFF800 ├────────────────────┤
│ 选项字节 (16B) │ ← 读保护/看门狗配置
0x1FFFF810 └────────────────────┘
| 区域 | 地址范围 | 大小 | 说明 |
|---|---|---|---|
| 主存储器 | 0x0800 0000 ~ 0x0800 FFFF | 64KB | 用户代码和数据 |
| 系统存储器 | 0x1FFF F000 ~ 0x1FFF F7FF | 2KB | 系统 Bootloader |
| 选项字节 | 0x1FFF F800 ~ 0x1FFF F80F | 16B | 配置位 |
| 唯一 ID | 0x1FFF F7E8 ~ 0x1FFF F7F0 | 12B | 芯片唯一序列号 |
页大小:1KB (STM32F103C8T6)
读 (任意时刻)
│
Flash 存储单元: 浮栅晶体管
│ 初始状态 = 1 (已擦除)
│
写操作 (只能 1→0):
┌──────────────┐
│ 1 → 0 ✅ │ ← 写入 0 可以
│ 0 → 1 ❌ │ ← 写入 1 不行!必须先擦除
└──────────────┘
│
擦除操作 (恢复为全 1):
页擦除 (1KB) 或 整片擦除
关键结论:
FLASH_Unlock()
│
├── 写 KEY1 = 0x45670123 到 FLASH_KEYR
└── 写 KEY2 = 0xCDEF89AB 到 FLASH_KEYR
│
▼
检查 FLASH_SR_BSY (忙)
│
├── 忙 → 等待
│
▼
FLASH_ErasePage(addr) ← 擦除指定页 (1KB)
│
▼
FLASH_ProgramHalfWord/Word(addr, data) ← 编程 (16/32 位)
│
▼
FLASH_Lock()
// 唯一 ID 地址 (96 位 = 12 字节)
// X/Y: wafer X/Y 坐标, W: wafer 编号, LOT: 批次号
uint32_t* pUID = (uint32_t*)0x1FFFF7E8;
uint32_t uid0 = pUID[0]; // LOT 号 (低位)
uint32_t uid1 = pUID[1]; // LOT 号 (高位) + W 编号
uint32_t uid2 = pUID[2]; // X + Y 坐标
| 函数 | 说明 |
|---|---|
void FLASH_Unlock(void) |
解锁 Flash 控制器 |
void FLASH_Lock(void) |
锁定 Flash 控制器 |
FLASH_Status FLASH_ErasePage(uint32_t Page_Address) |
擦除指定页 (1KB) |
FLASH_Status FLASH_EraseAllPages(void) |
擦除所有页 |
FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data) |
16 位编程 |
FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data) |
32 位编程 |
FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data) |
编程选项字节 |
FLASH_Status FLASH_EraseOptionBytes(void) |
擦除选项字节 |
void FLASH_ITConfig(uint32_t FLASH_IT, FunctionalState NewState) |
中断配置 |
void FLASH_SetLatency(uint32_t FLASH_Latency) |
设置等待周期 |
FLASH_Status FLASH_GetStatus(void) |
获取状态 |
FlagStatus FLASH_GetFlagStatus(uint32_t FLASH_FLAG) |
获取标志 |
void FLASH_ClearFlag(uint32_t FLASH_FLAG) |
清除标志 |
FLASH_Status 返回值:
| 状态 | 说明 |
|---|---|
FLASH_BUSY |
忙 (BSY=1) |
FLASH_COMPLETE |
操作完成 |
FLASH_ERROR_PG |
编程错误 |
FLASH_ERROR_WRP |
写保护错误 |
FLASH_TIMEOUT |
超时 |
FLASH_FLAG:
| 标志 | 说明 |
|---|---|
FLASH_FLAG_BSY |
忙标志 |
FLASH_FLAG_PGERR |
编程错误 |
FLASH_FLAG_WRPRTERR |
写保护错误 |
FLASH_FLAG_EOP |
操作结束 |
FLASH_Latency (Flash 等待周期):
| 系统时钟频率 | 等待周期 |
|---|---|
| 0~24MHz | 0 (FLASH_Latency_0) |
| 24~48MHz | 1 (FLASH_Latency_1) |
| 48~72MHz | 2 (FLASH_Latency_2) |
/*------------------------------------------------
* FLASH_Example.c
* 保存/加载用户配置参数到 Flash 最后一页
*------------------------------------------------*/
#include "stm32f10x.h"
#include <stdint.h>
#include <string.h>
/* 存储位置:Flash 最后一页 (64KB - 1KB = 63KB 偏移) */
#define CONFIG_FLASH_ADDR 0x0800FC00 // 第 63 页起始 (0x08000000 + 63*1024)
/* 用户配置结构体 (不超过 1KB) */
typedef struct {
uint16_t magic; // 校验魔数 0x55AA
uint16_t baudRate; // 波特率
uint8_t channel; // 通道号
uint8_t reserved[21]; // 保留 (对齐)
} UserConfig;
static UserConfig g_config;
/* ---- 从 Flash 读取配置 ---- */
void Config_Load(void)
{
uint16_t* pFlash = (uint16_t*)CONFIG_FLASH_ADDR;
uint16_t* pDest = (uint16_t*)&g_config;
uint32_t i;
for (i = 0; i < sizeof(UserConfig) / 2; i++)
pDest[i] = pFlash[i];
if (g_config.magic != 0x55AA)
{
/* 无效配置,恢复默认 */
g_config.magic = 0x55AA;
g_config.baudRate = 115200;
g_config.channel = 1;
}
}
/* ---- 保存配置到 Flash ---- */
void Config_Save(void)
{
uint16_t* pSrc = (uint16_t*)&g_config;
uint32_t i;
g_config.magic = 0x55AA;
FLASH_Unlock();
/* 擦除整个页 */
FLASH_ErasePage(CONFIG_FLASH_ADDR);
/* 逐 16 位写入 */
for (i = 0; i < sizeof(UserConfig) / 2; i++)
{
FLASH_ProgramHalfWord(CONFIG_FLASH_ADDR + i * 2, pSrc[i]);
}
FLASH_Lock();
}
/* ---- 写验证 ---- */
uint8_t Config_Verify(void)
{
uint16_t* pFlash = (uint16_t*)CONFIG_FLASH_ADDR;
uint16_t* pDest = (uint16_t*)&g_config;
uint32_t i;
for (i = 0; i < sizeof(UserConfig) / 2; i++)
{
if (pFlash[i] != pDest[i])
return 0; // 验证失败
}
return 1; // 验证通过
}
int main(void)
{
Config_Load();
/* 修改配置 */
g_config.baudRate = 9600;
g_config.channel = 2;
Config_Save();
if (Config_Verify())
{
/* 保存成功 */
}
while (1);
}
/*------------------------------------------------
* UID_Example.c
*------------------------------------------------*/
#include "stm32f10x.h"
typedef struct {
uint32_t lotNumLow; // LOT 号低 32 位
uint32_t lotNumHigh; // LOT 号高 16 位 + Wafer 编号
uint32_t coordXY; // X 坐标 + Y 坐标
} ChipUID;
void ReadChipUID(ChipUID* uid)
{
uint32_t* pUID = (uint32_t*)0x1FFFF7E8;
uid->lotNumLow = pUID[0];
uid->lotNumHigh = pUID[1];
uid->coordXY = pUID[2];
}
int main(void)
{
ChipUID uid;
ReadChipUID(&uid);
/* uid 可用于软件加密、设备识别等 */
while (1);
}
/*------------------------------------------------
* FLASH_ErrorHandling.c
*------------------------------------------------*/
#include "stm32f10x.h"
FLASH_Status Flash_ProgramSafe(uint32_t addr, uint16_t data)
{
FLASH_Status status;
status = FLASH_ProgramHalfWord(addr, data);
if (status != FLASH_COMPLETE)
{
/* 错误处理 */
if (status == FLASH_ERROR_WRP)
{
/* 写保护错误:检查该页是否被保护 */
}
else if (status == FLASH_ERROR_PG)
{
/* 编程错误:地址未擦除或对齐问题 */
}
}
return status;
}
int main(void)
{
FLASH_Unlock();
FLASH_ErasePage(0x0800FC00);
if (Flash_ProgramSafe(0x0800FC00, 0x1234) == FLASH_COMPLETE)
{
/* 编程成功 */
}
FLASH_Lock();
while (1);
}
擦除以页为单位 (1KB)
编程必须 16 位对齐
FLASH_ProgramHalfWord 要求地址是半字对齐 (偶数地址)FLASH_ProgramWord 要求地址是字对齐 (4 的倍数)不要在 Flash 中执行程序时擦写同一页
Flash 等待周期
FLASH_SetLatency(FLASH_Latency_2) (2 个等待周期)SystemInit() 中自动配置,手动改主频后也要重新设置写保护
FLASH_ERROR_WRP,需检查选项字节中的写保护位FLASH_EraseOptionBytes() 清除保护Flash 擦写寿命
操作后锁定 Flash
FLASH_Lock() 防止误操作唯一 ID 只读
0x1FFFF7E8 是只读区域,不能擦写读 Flash 直接用指针
*(uint16_t*)addr 或 *(uint32_t*)addr系统存储器与选项字节
0x1FFFF000) 是出厂 Bootloader,不可擦写0x1FFFF800) 可用 FLASH_ProgramOptionByteData 修改(需谨慎)