[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)会出现不可预知的情况。
  • 三、实验:设置读写保护及解除

    // 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则是不实施写保护。

  • // 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 */								
    
    // 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。

  • 作者:CSDN_PBB

    物联沃分享整理
    物联沃-IOTWORD物联网 » [STM32 – 野火] – – – 固件库学习笔记 – – – 十五.设置FLASH的读写保护及解除

    发表回复