[STM32 - 野火] - - - 固件库学习笔记 - - - 十五.设置FLASH的读写保护及解除

一、选项字节与读写保护

1.1 为什么要设置读写保护

防止内部FLASH中的程序被非法读取。

在实际发布的产品中,STM32芯片的内部FLASH存储了控制程序。然而,如果不对内部FLASH采取任何保护措施,用户可以使用下载器直接读取其内容,并将其导出为bin或hex文件格式的代码副本。这可能导致别有用心的厂商利用该方法复制产品。为了防止这种情况,STM32芯片提供了多种方式来保护内部FLASH中的程序,防止其被非法读取。但在默认情况下,这些保护功能是未开启的。若要启用这些保护功能,需要修改内部FLASH选项字节(Option Bytes)中的相关配置。

1.2 选项字节的内容

选项字节是一段特殊的 FLASH 空间, STM32 芯片会根据它的内容进行读写保护配置,选项字节的构成如下图:

STM32F103 系列芯片的选项字节有 8 个配置项,即上表中的 USER、 RDP、 DATA0/1 及 WRP0/1/2/3,而表中带 n 的同类项是该项的反码,即 nUSER 的值等于 (~USER)、 nRDP 的值等于 (~RDP), STM32利用反码来确保选项字节内容的正确性。

选项字节的 8 个配置项具体的数据位配置说明如下:

当nRST_STDBY设置为0时:只要成功执行进入待机模式指令,器件就会复位,而非进入待机模式。

1.2.1 RDP

设置读保护:修改选项字节的 RDP 位的值可设置内部 FLASH 为以下保护级别:

  • 0xA5:无保护;

    这是 STM32 的默认保护级别。在STM32芯片的默认保护级别下,内部FLASH没有任何读保护措施,读取其内容没有任何限制。这意味着第三方可以使用调试器等工具轻松获取芯片FLASH中存储的程序,并将其以bin或hex格式下载到另一块STM32芯片中。结合PCB抄板技术,他们可以轻易地复制出相同的产品。

  • 其它值:使能读保护;

    将RDP(Read Protection Level,读保护级别)配置为除0xA5以外的任意数值,都会启用读保护功能。在这种情况下,如果通过调试功能(例如使用下载器或仿真器)或者从内部SRAM启动,将无法对内部FLASH进行任何访问(包括读取、写入和擦除操作)。然而,如果STM32是从内部FLASH启动的,它仍然允许对内部FLASH进行任意访问。

    换句话说,任何试图从外部访问内部FLASH内容的操作都会被禁止。例如,无法通过下载器读取其内容,也无法编写一个从内部SRAM启动的程序来读取内部FLASH,因为这种操作会被阻止。相反,如果芯片是从内部FLASH启动的程序本身访问内部FLASH(例如程序中包含对内部FLASH某个地址的读取操作),则可以正常进行,不会受到限制。

当STM32芯片被设置为读保护后,内部FLASH的前4KB空间会被强制加上写保护。这意味着,即使是从FLASH启动的程序,也无法对这4KB空间进行擦除或写入操作。然而,对于前4KB以外的FLASH空间,读保护不会限制对其的擦除或写入操作。利用这一特性,可以编写IAP(In-Application Programming,应用程序内编程)代码来更新FLASH中的程序。

IAP的工作原理是通过某种通信接口(如UART、I2C、SPI等)接收外部设备发送的程序更新数据,然后利用内部FLASH的擦写功能将这些数据烧录到FLASH的指定区域(通常是前4KB之外的区域),从而实现应用程序的更新。这种原理类似于串口ISP(In-System Programming,系统内编程)程序下载功能。不过,ISP的接收数据和更新代码由ST公司提供,并存放在系统的存储区域;而IAP代码由用户自行编写,存放在用户自定义的FLASH区域,并且通信方式可以根据用户需求定制,只要能够接收数据即可。


解除读保护:当需要解除芯片的读保护时,要把选项字节的 RDP 位重新设置为 0xA5。

当解除读保护时,芯片会自动触发擦除主FLASH存储器的全部内容。这意味着解除保护后,原内部FLASH中的代码会丢失,从而防止降级后原内容被读取。

这种机制是为了确保在解除保护后,原有的程序代码不会被非法读取或复制。

如果设置了读保护后在代码中没有解除读保护,可以通过把(解除读保护的)代码写入到RAM中,在RAM中运行代码,进而解除读保护。


芯片被配置成读保护后根据不同的使用情况,访问权限如下图:

RAM自举:程序存储在STM32的内部SRAM中,并从SRAM中启动执行;

ROM自举:程序存储在STM32的系统存储器(System Memory)中,并从该区域启动。

1.2.2 Datax

Data字段是供用户自定义使用的存储区域,主要用于存储一些需要在掉电后仍能保留的配置信息或状态标志。

作用:

  • 1、存储存储用户自定义数据:

    • Data字段通常包含两个字节(Data0和Data1),位于选项字节的特定地址(例如0x1FFF F804和0x1FFF F806)。这些字节可以存储用户定义的任意数据。

    • 例如,可以用于存储固件版本号、设备配置参数或标志位,这些数据在设备掉电后仍能保留。

  • 2、用于固件升级:

    • 在固件升级过程中,Data字段可以用来存储升级标志位或状态信息,帮助设备在重启后判断是否需要继续执行升级操作。
  • 3、灵活的配置用途:

    • 由于Data字段的内容由用户定义,因此可以根据具体应用场景灵活使用。例如,可以用于存储设备的唯一标识符、校准参数或其他重要信息。

1.2.3 WRP

使用选项字节的 WRP0/1/2/3 可以设置主 FLASH 的写保护,防止它存储的程序内容被修改。

设置写保护

写保护的配置通常以4KB为单位进行设置。除了WRP3的最后一位较为特殊外,每个WRP选项字节的每一位都对应一个4KB的FLASH区域,用于控制该区域的写访问权限。

将对应的WRP位设置为0,即可对该4KB区域启用写保护。启用写保护后,被保护的主FLASH区域中的内容将无法通过任何方式被擦除或写入。

例如WRP0设置为11111110,那么页0、页1就不能写;为11111101,那么页2、页3就不能写。

需要注意的是,写保护仅限制写操作,不影响读操作的权限,读操作的权限则由前面介绍的读保护设置决定。

设置写保护

解除写保护的过程是写保护设置的逆操作。将对应的WRP位设置为1,即可解除对应4KB区域的写保护。

解除写保护后,主FLASH中的内容不会像解除读保护那样丢失,而是会保持原样,不会发生任何改变。

二、修改选项字节的过程

根据前面的说明,修改选项字节的内容可以修改读写保护的配置。然而,选项字节复位后的默认状态是==可读但被写保护==的。这与FLASH_CR寄存器的访问限制类似。

因此,要修改选项字节,需要先对FLASH_OPTKEYR寄存器写入解锁编码。由于修改选项字节时也需要访问FLASH_CR寄存器,因此还需要对FLASH_KEYR寄存器写入解锁编码。

修改选项字节的整个过程总结如下

  • 1、解除 FLASH_CR 寄存器的访问限制。

    • 先往 FPEC 键寄存器(FLASH_KEYR) 中写入 KEY1 = 0x45670123。

    • 再往 FPEC 键寄存器(FLASH_KEYR) 中写入 KEY2 = 0xCDEF89AB。

  • 2、解除对选项字节的访问限制。

    • 先往 闪存 OPTKEY 寄存器(FLASH_OPTKEYR) 中写入 KEY1 = 0x45670123。

    • 再往 闪存 OPTKEY 寄存器(FLASH_OPTKEYR) 中写入 KEY2 = 0xCDEF89AB。

  • 3、配置 FLASH_CR 的 OPTPG 位,准备修改选项字节。

  • 4、直接使用指针操作修改选项字节的内容,根据需要修改 RDP、 WRP 等内容。

  • 5、对于读保护的解除,由于它会擦除 FLASH 的内容,所以需要检测状态寄存器标志位以确认FLASH 擦除操作完成。

  • 6、若是设置读保护及其解除,需要给芯片重新上电复位 ,以使新配置的选项字节生效;对于设置写保护及其解除,需要给芯片进行系统复位,以使新配置的选项字节生效。

    • 解除读保护后需要给系统上电复位,如果是使用系统复位(NVIC_SystemReset)会出现不可预知的情况。

三、实验:设置读写保护及解除

c 复制代码
// flash_rdprotect.c文件
#include "flash_rdprotect.h"

void Write_Protect(void)
{
	FLASH_Unlock();
	
	FLASH_EraseOptionBytes();	// 写之前要先擦除
	
	FLASH_EnableWriteProtection(FLASH_WRProt_AllPages);
	
	NVIC_SystemReset();			// 产生系统复位
}

void Write_Protect_Disable(void)
{
	FLASH_Unlock();
	
	FLASH_EraseOptionBytes();
	
	FLASH_EnableWriteProtection(0x00000000);
	
	NVIC_SystemReset();			// 产生系统复位
}

void Read_Protect(void)
{
	FLASH_Unlock();
	
	FLASH_EraseOptionBytes();
	
	FLASH_ReadOutProtection(ENABLE);
}

void Read_Protect_Disable(void)
{
	FLASH_Unlock();
	
	FLASH_EraseOptionBytes();
	
	FLASH_ReadOutProtection(DISABLE);
}
  • 为什么设置写保护写的值是0xffffffff、解除写保护写的值是0x00000000:

    FLASH_EnableWriteProtection函数中会对传进来的参数进行取反,将WRPx中的位设置为1则是不实施写保护。

c 复制代码
// flash_rdprotect.h文件
#ifndef __FLASH_RDPROTECT_H
#define __FLASH_RDPROTECT_H

#include "stm32f10x.h"

#define WRITE_PROTECT

void Write_Protect(void);
void Write_Protect_Disable(void);
void Read_Protect(void);
void Read_Protect_Disable(void);
	
#endif /* __FLASH_RDPROTECT_H */								
c 复制代码
// main.c文件
#include "stm32f10x.h"
#include "bsp_key.h"
#include "usart.h"
#include "flash_rdprotect.h"

int main(void)
{
	KEY1_GPIO_Config();
	KEY2_GPIO_Config();
	
	USART_Config();
	
	#ifdef WRITE_PROTECT
	printf("写保护状态:0x%08x \r\n", FLASH_GetWriteProtectionOptionByte());
	#else
	printf("写保护状态:0x%x \r\n", FLASH_GetReadOutProtectionStatus());
	#endif
	
	
	while(1)
	{
		if(KEY_Scan(KEY1_PROT, KEY1_Pin) == KEY_ON)
		{
			#ifdef WRITE_PROTECT
			printf("即将实施写保护 \r\n");
			
			Write_Protect();
			#else
			printf("即将实施读保护 \r\n");
			
			Read_Protect();
			#endif
		}
		
		if(KEY_Scan(KEY2_PROT, KEY2_Pin) == KEY_ON)
		{
			#ifdef WRITE_PROTECT
			printf("即将解除写保护 \r\n");
			
			Write_Protect_Disable();
			#else
			printf("即将解除读保护 \r\n");
			
			Read_Protect_Disable();
			#endif
		}
	}
	
}
  • FLASH_EraseOptionBytes()函数会将16个字节都擦除。如果想只擦除某一个字节:将WRP全部清除掉再重新配置。

  • FLASH_GetWriteProtectionOptionByte() 函数的返回值类型为 uint64_t,表示的是 WRP0~3 的值。当未设置写保护时,返回值为 0xFFFFFFFF;而当所有区域均设置为写保护时,返回值为 0x00000000。

四、将代码修改为RAM自举

  • 1、添加"Project Targets";

  • 2、在C/C++中添加宏VECT_TAB_SRAM;

此宏在SystemInit函数中。
注意,两个宏之间用","分开。

  • 3、打开.sct文件;

  • 4、(根据实际需要)调整ROM空间大小;

修改前的空间大小。

修改后的空间大小。

  • 5、下载器配置;
  • 6、添加ini文件;
  • 七、将Project Targets更改为新建的Targets。
相关推荐
Zevalin爱灰灰22 分钟前
编程技巧(基于STM32)第一章 定时器实现非阻塞式程序——按键控制LED灯闪烁模式
stm32·单片机·嵌入式硬件
饮长安千年月2 小时前
Linksys WRT54G路由器溢出漏洞分析–运行环境修复
网络·物联网·学习·安全·机器学习
红花与香菇2____2 小时前
【学习笔记】Cadence电子设计全流程(二)原理图库的创建与设计(上)
笔记·嵌入式硬件·学习·pcb设计·cadence·pcb工艺
苏慕TRYACE4 小时前
RT-Thread+STM32L475VET6实现红外遥控实验
stm32·单片机·嵌入式硬件·rt-thread
一天八小时4 小时前
Docker学习进阶
学习·docker·容器
前端没钱4 小时前
前端需要学习 Docker 吗?
前端·学习·docker
拥有一颗学徒的心4 小时前
鸿蒙第三方库MMKV源码学习笔记
笔记·学习·性能优化·harmonyos
车端域控测试工程师5 小时前
【ISO 14229-1:2023 UDS诊断(ECU复位0x11服务)测试用例CAPL代码全解析⑰】
经验分享·学习·汽车·测试用例·capl
车端域控测试工程师5 小时前
【ISO 14229-1:2023 UDS诊断(ECU复位0x11服务)测试用例CAPL代码全解析⑪】
经验分享·学习·汽车·测试用例·capl
yyqzjw8 小时前
【STM32】外部时钟|红外反射光电开关
stm32·单片机·嵌入式硬件