4. STM32 Flash编程模型

在STM32的量产烧录中,真正决定“能不能烧、烧多快、烧完能不能跑”的,并不是上位机软件,而是芯片内部的Flash编程模型。上位机发出的每一个擦除、编程、校验命令,最终都要落到Flash控制器和存储阵列上。理解页擦除、编程机制、选项字节以及写保护/读保护的作用,是设计稳定烧录流程的前提。

一、Flash存储阵列的组织方式

STM32的Flash并不是按字节随机写入的存储器,而是按“页(Page)”或“扇区(Sector)”划分的块结构。以常见的STM32F103系列为例,主存储区被划分为若干页,每页1KB或2KB;而STM32F4系列则按扇区划分,扇区大小从16KB到128KB不等。不同系列的页/扇区粒度不同,烧录算法必须与具体型号匹配。

Flash的物理特性决定了两个基本约束:

  • 写入只能把位从1变为0,不能从0变回1;
  • 擦除的最小单位是页/扇区,擦除后整页变为0xFF。

因此,任何一次“修改”Flash内容的操作,都必须遵循“先擦除、后编程”的顺序。如果目标地址所在页不是全0xFF状态,直接编程会导致写入数据与原有数据按位与,结果不可预期。

二、页擦除与编程机制

页擦除操作由Flash控制器执行。以STM32F1为例,典型流程是:解锁Flash(写入KEY1、KEY2到FLASH_KEYR)→ 设置FLASH_CR的PER位 → 选择页地址 → 置STRT位 → 等待BSY位清零。擦除完成后,目标页所有字节变为0xFF。

编程操作则以半字(16位)为单位。STM32F1要求每次写入一个半字,写入前需检查目标地址是否为0xFFFF。编程流程为:解锁 → 置FLASH_CR的PG位 → 向目标地址写入半字 → 等待BSY清零。若目标地址未被擦除,写入会失败并置PGERR标志。

// STM32F1 页擦除与半字编程示意(伪代码)
FLASH->KEYR = 0x45670123;
FLASH->KEYR = 0xCDEF89AB;

// 页擦除
FLASH->CR |= FLASH_CR_PER;
FLASH->AR = page_addr;
FLASH->CR |= FLASH_CR_STRT;
while (FLASH->SR & FLASH_SR_BSY);

// 半字编程
FLASH->CR |= FLASH_CR_PG;
*(volatile uint16_t*)addr = data;
while (FLASH->SR & FLASH_SR_BSY);

在量产烧录中,擦除和编程的粒度直接影响烧录时间。例如,一个64KB的固件,若按1KB页擦除,需要64次擦除;若按16KB扇区擦除,只需4次。但扇区擦除会擦掉整个扇区,若固件只占扇区一部分,其余部分也会被清空。因此,烧录算法需要根据固件大小和扇区布局,选择“整片擦除”还是“按需擦除”。

三、选项字节:芯片的“配置开关”

选项字节(Option Bytes)是Flash中一块独立区域,用于配置芯片的启动行为、看门狗、复位方式以及读写保护。它不随主存储区擦除而改变,但可以被修改。常见的选项字节字段包括:

  • RDP:读保护等级(0xAA表示无保护,其他值表示保护);
  • nWRP:写保护位,按扇区/页设置;
  • USER:看门狗、复位、启动模式等;
  • DATA0/DATA1:用户自定义数据。

选项字节的修改需要先解锁,写入新值,再触发选项字节加载。修改后通常需要复位芯片才能生效。在量产烧录中,选项字节的配置往往与固件烧录一起完成,例如:先烧录固件,再设置读保护等级,最后复位运行。

四、写保护与读保护对烧录流程的影响

写保护(WRP)和读保护(RDP)是量产烧录中最容易导致“烧不进去”或“读不出来”的两个因素。

写保护作用于主存储区的指定页/扇区。若目标区域被写保护,擦除和编程操作都会被硬件拒绝,FLASH_SR的WRPRTERR标志置位。此时烧录器会报“写保护错误”。解决方法是先解除写保护(修改选项字节),再执行擦除和编程。在量产中,通常会在烧录前统一解除写保护,烧录完成后再根据需要重新设置。

读保护则限制外部调试接口(SWD/JTAG)读取Flash内容。当RDP设置为保护等级1时,调试器无法读取Flash,但芯片仍可正常运行。若尝试通过调试器读取,会触发总线错误,甚至导致Flash被自动擦除(部分系列在解除读保护时会擦除主存储区)。因此,在量产烧录流程中,读保护的设置时机非常关键:必须在固件烧录并校验通过之后,再设置读保护,否则会导致后续无法读取或校验。

注意:部分STM32系列在从读保护等级1降回等级0时,会强制擦除整个主存储区。这意味着如果烧录后设置了读保护,后续想重新烧录,必须先解除保护并接受全片擦除。量产中应避免频繁切换读保护状态。

五、烧录流程中的关键判断

结合上述机制,一个稳定的量产烧录流程通常包含以下判断点:

  • 烧录前读取选项字节,确认当前写保护/读保护状态;
  • 若目标区域被写保护,先解除写保护;
  • 根据固件大小和扇区布局,选择擦除范围;
  • 执行擦除后,回读目标区域确认全0xFF;
  • 按半字/字编程,写入固件;
  • 回读校验,确认写入数据与源文件一致;
  • 校验通过后,按需设置读保护等级;
  • 复位芯片,进入运行模式。

其中,“擦除后回读确认”和“编程后回读校验”是两道关键防线。前者防止擦除不彻底导致编程失败,后者防止编程过程中出现位翻转或地址错位。在量产环境中,这两步通常由烧录器自动完成,但理解其背后的Flash机制,有助于在出现异常时快速定位问题。

STM32 Flash编程模型与烧录流程关键判断 主存储区(按页/扇区) Page 0 / Sector 0 Page 1 / Sector 1 Page 2 / Sector 2 … 写保护区域(WRP) 选项字节(Option Bytes) RDP:读保护等级 nWRP:写保护位 USER:启动/看门狗 DATA0/DATA1:用户数据 烧录流程关键判断 ① 读选项字节,查保护状态 ② 解除写保护(如需) ③ 擦除 → 回读确认0xFF ④ 编程 → 回读校验 ⑤ 设置读保护 → 复位运行 写保护与读保护对烧录的影响 写保护(WRP) • 目标页/扇区被保护 → 擦除/编程被拒绝 • FLASH_SR 的 WRPRTERR 置位 • 烧录器报“写保护错误” • 解决:先解除写保护,再擦除编程 读保护(RDP) • 等级1:调试器无法读取Flash • 解除保护可能触发全片擦除 • 设置时机:烧录校验通过之后 • 避免频繁切换,防止误擦除

总结来看,STM32的Flash编程模型可以概括为“页擦除、半字编程、选项字节配置、读写保护约束”四个层面。在量产烧录中,烧录器需要根据芯片型号的页/扇区结构、当前选项字节状态以及固件大小,动态决定擦除范围、编程顺序和保护设置时机。只有把这些机制理解清楚,才能在面对“烧录失败”“校验不通过”“芯片被锁死”等问题时,快速判断是擦除不彻底、写保护未解除,还是读保护设置过早导致的。