不能直接对flash地址用普通指针赋值,因为flash写入必须先擦除、受硬件保护且需特定时序,裸指针写操作不会触发flash控制器,多数mcu会触发hardfault或静默失败;真正写入必须调用厂商api(如hal_flash_unlock()+hal_flash_program()),指针仅用于传递目标地址,不可解引用写入。

为什么不能直接对Flash地址用普通指针赋值
嵌入式Flash不是RAM,写入前必须擦除、写入时有特定时序、还常受保护位限制。用 int *p = (int*)0x08000000 这类裸指针读可以,但一旦执行 *p = 0x1234,多数MCU会触发硬件异常(如HardFault)或静默失败——因为总线控制器根本不会把写请求转发给Flash控制器。
真正能改Flash的,只有厂商提供的Flash驱动函数,比如STM32的 HAL_FLASH_Unlock() + HAL_FLASH_Program(),它们内部封装了状态轮询、电压检查、锁定位操作等硬性流程。
如何安全地用指针配合Flash API写入数据
指针在这里只该用于“描述目标地址”,而不是“发起写操作”。典型做法是把Flash地址转为 uint32_t 或 const uint32_t* 类型传给API,避免类型误用:
-
uint32_t addr = 0x08004000;—— 明确表示这是地址数值,不参与解引用 - 调用前校验地址是否落在合法扇区(如STM32F4需对齐到
0x400边界),否则HAL_FLASH_Program()返回HAL_ERROR - 写入前必须确保该页已擦除:未擦除页写入单个字会失败,且可能拉低整页可靠性
- 禁止在Flash编程期间响应中断(尤其SysTick或NVIC抢占),否则可能卡死;
HAL_FLASH_Lock()后记得关全局中断再操作
用指针模拟Flash结构体时的常见坑
有人喜欢定义类似 typedef struct { uint32_t magic; uint16_t version; } config_t;,再用 config_t *cfg = (config_t*)0x08008000; 读取——这读可行,但写仍得走API。更大的问题是:
- 结构体成员对齐:编译器可能插入padding,导致
sizeof(config_t)≠ 实际存储长度,烧录时错位 - 未初始化Flash内容是
0xFF,magic字段若设为0xDEADBEEF,需确认该值在擦除后能被正确识别(有些芯片擦除后是0x00) - 指针指向地址若跨扇区(如0x08007FFF→0x08008000),一次擦除可能误删邻近配置
更稳妥的做法是用宏定义偏移,配合 memcpy 拆包/组包,而非依赖结构体指针直接映射。
调试时如何验证Flash写入结果
别信IDE的Memory View——它常缓存旧数据。真实验证要分三步:
- 调用
HAL_FLASH_Program()后检查返回值是否为HAL_OK,不是就查FLASH->SR寄存器的PGERR/WRPERR标志 - 写完立刻用
__DSB()+__ISB()刷新流水线,再从同一地址读回比对 - 断电重启后重新读取,确认数据未丢失(某些Flash需复位才能生效)
最隐蔽的问题是:调试器在SWD/JTAG连接状态下,部分芯片(如GD32)会自动禁用Flash编程,此时所有API都返回成功但实际没写入——必须断开调试器再测。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











