4. Flash读写保护:读保护(RDP)级别与设置、写保护(WRP)配置、选项字节编程与修改、保护解除与全局擦除

Flash保护这个话题,说实话,很多工程师容易忽略。我早年做一款工业控制器时,就因为没设读保护,样机被客户“逆向”了个干净。从那以后,只要涉及产品量产,保护机制我一定会上。

Flash保护分两类:读保护(RDP) 和 写保护(WRP)。前者防别人读你的代码,后者防你自己(或异常程序)把关键区域擦写了。咱们一个一个说。

4.1 读保护(RDP)级别与设置

读保护,说白了就是给Flash上把锁。别人拿到芯片,想通过调试接口(SWD/JTAG)把固件读出来?没门。

STM32的RDP分三个级别,我习惯用这张表来对比:

级别 数值 保护状态 调试接口访问 从SRAM/System Memory启动
Level 0 0xAA 无保护 完全开放 允许
Level 1 0xBB(或其他非0xAA/0xCC值) 读保护开启 禁止读Flash,允许读写RAM/外设 允许,但Flash不可读
Level 2 0xCC 永久保护 完全禁止(调试接口永久失效) 禁止

Level 1 是最常用的。你想想看,产品出厂时设成Level 1,别人用J-Link连上,读Flash直接返回全0或全1,代码安全就有了基本保障。

Level 2 是个狠角色。一旦设了,调试接口永久锁死,连你自己都救不回来。我只有在产品完全定型、绝不打算做任何现场调试时才会用。嗯,这里要注意:Level 2不可逆,选之前想清楚。

核心要点: RDP Level 1 是量产标配。Level 2 慎用,除非你确定这辈子不再需要调试接口。

4.2 写保护(WRP)配置

写保护防的是谁?防的是程序跑飞后把Bootloader区域给擦了。我在一个OTA项目里就遇到过——升级程序有个bug,直接把引导区覆盖了,设备变砖。从那以后,Bootloader区域我一定上WRP。

WRP的原理是按页(或按扇区)设置保护。配置时你需要指定:

  • 起始地址(WRP_start)
  • 结束地址(WRP_end)
  • 保护使能位

以STM32F4为例,配置代码大致长这样:

// 假设保护扇区4到扇区7(地址范围0x08010000 ~ 0x0801FFFF)
FLASH_OB_WRPConfig(OB_WRP_Sector_4 | OB_WRP_Sector_5 | 
                   OB_WRP_Sector_6 | OB_WRP_Sector_7, ENABLE);
// 别忘了启动选项字节编程
FLASH_OB_Launch();

写保护生效后,任何对该区域的写/擦操作都会被硬件拒绝。你想想看,就算程序跑飞了,它也动不了你的Bootloader,这就是最后一道防线。

我的习惯: 把Bootloader和关键配置参数所在的扇区全部加上WRP。应用区可以不加,方便后续OTA升级。

4.3 选项字节编程与修改

RDP和WRP的配置,最终都存储在选项字节(Option Bytes)里。选项字节是Flash里一块独立的区域,专门存放芯片的配置信息。

修改选项字节的流程,我总结为三步:

  1. 解锁Flash控制器(FLASH_Unlock)
  2. 解锁选项字节(FLASH_OB_Unlock)
  3. 编程并启动(FLASH_OB_Program + FLASH_OB_Launch)

注意:选项字节的修改需要重新上电或系统复位才能生效。FLASH_OB_Launch() 函数内部会触发一个复位,别被它吓到,这是正常行为。

我曾经犯过一个错:修改完选项字节后,直接读RDP寄存器,发现值没变。后来才意识到——没复位!复位后再读,就对了。

警告: 选项字节编程过程中,芯片绝对不能断电。一旦掉电,选项字节可能处于未定义状态,轻则保护配置丢失,重则芯片锁死。建议在编程前先确保供电稳定。

4.4 保护解除与全局擦除

保护不是永久的(Level 2除外)。如果你想解除Level 1的读保护,或者修改WRP的区域,需要执行全局擦除。

流程是这样的:

  1. 将RDP设置为Level 0(写入0xAA)
  2. 芯片会自动执行Mass Erase(整片擦除)
  3. 擦除完成后,所有保护配置清零,芯片回到无保护状态

为什么要整片擦除?这是硬件设计的安全策略——防止你通过修改RDP来绕过保护。你想想看,如果允许不擦除就降级保护,那读保护还有什么意义?

代码实现:

// 解除读保护(Level 1 -> Level 0)
FLASH_Unlock();
FLASH_OB_Unlock();
FLASH_OB_RDPConfig(OB_RDP_Level_0);  // 写入0xAA
FLASH_OB_Launch();  // 触发复位,执行Mass Erase

执行完这段代码,芯片会擦除整个Flash(包括你的程序),然后复位。所以,解除保护意味着程序也没了,需要重新烧录。

避坑指南: 我曾经在生产线上遇到过一批芯片,RDP Level 1设好后,想通过调试器解除保护,结果一直报错。后来发现是调试器供电不稳,导致Mass Erase过程中断。解决办法:换了个带独立供电的调试器,一次成功。

4.5 知识结构总览

下面这张图,把Flash保护的整体逻辑串起来了。我建议你保存下来,调试时对照着看。

Flash读写保护知识结构图 Flash 读写保护知识结构 Flash 保护机制 读保护(RDP) 写保护(WRP) Level 0(无保护) Level 1(标准保护) Level 2(永久保护) 按页/扇区保护 保护Bootloader 选项字节(Option Bytes) 编程与修改流程 保护解除与全局擦除 核心逻辑:RDP防外部读取,WRP防内部误写,两者通过选项字节配置,解除保护需全局擦除

这张图把RDP、WRP、选项字节、保护解除的关系理清了。你调试时遇到保护相关的问题,先看这张图定位到哪个环节,再针对性排查。

调试小技巧: 如果调试器连不上芯片,先检查RDP级别。很多开发板出厂时RDP是Level 0,但如果你误操作设成了Level 1或Level 2,调试器就会“失联”。解决办法:用串口ISP模式(System Memory启动)重新配置选项字节。

Flash保护这块,说白了就是“防君子也防小人”。RDP防的是外部窃取,WRP防的是内部崩溃。两者配合使用,产品的固件安全才算有了基本保障。嗯,我建议你在项目初期就把保护方案定下来,别等到量产了再补,那时候改起来就麻烦了。