stm32专题二十九:Flash 读写保护

设置Flash的读写保护,其实就是操作内部Flash的选项字节。

选项字节在内部Flash的主存储页之后,由于是Flash,不能像内存RAM一样随意写入。由于Flash的写入特性,只能将 1 写成 0,而如果要确保写入数据的绝对正确,则需要先擦除再写入。如果直接对Flash写入,则只能确保写入 0 值是正确的。

选项字节,可以认为是掉电不会丢失的寄存器(Flash空间)。就是用Flash介质来存储配置,要修改选项字节,跟修改Flash一样。

RDP 读保护字节描述(可以通过j-link或st-link读Flash 加密等):

RDP配置方式:

2个数据字节 Data0 Data1

3 设置写保护

RDP 读保护

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

读保护的解除,必须要上电复位才可以。

WRP 写保护

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

1 设置写保护

       写保护的配置一般以 4K 字节为单位,除 WRP3 的最后一位比较特殊外,每个WRP 选项字节的一位用于控制 4K 字节的写访问权限, 把对应 WRP 的位置 0 即可把它匹配的空间加入写保护。被设置成写保护后,主 FLASH 中的内容使用任何方式都不能被擦除和写入,写保护不会影响读访问权限,读访问权限完全由前面介绍的读保护设置限制

2 解除写保护

       解除写保护是逆过程,把对应 WRP 的位置 1 即可把它匹配的空间解除写保护。解除写保护后,主 FLASH 中的内容不会像解读保护那样丢失,它会被原样保留。

修改选项字节的过程

Flash_CR寄存器,操作选项字节的位描述:

 

操作选项字节的固件库函数:

结构体和宏定义:

/** 
  * @brief Option Bytes Registers
  */
  
typedef struct
{
  __IO uint16_t RDP;
  __IO uint16_t USER;
  __IO uint16_t Data0;
  __IO uint16_t Data1;
  __IO uint16_t WRP0;
  __IO uint16_t WRP1;
  __IO uint16_t WRP2;
  __IO uint16_t WRP3;
} OB_TypeDef;

#define OB                  ((OB_TypeDef *) OB_BASE) 
#define OB_BASE               ((uint32_t)0x1FFFF800)    /*!< Flash Option Bytes base address */

注意,这里的RDP定义成16位,但实际上的RDP只有8位。这里是把RDP和nRDP连续两个寄存器合起来。当向选项字节RDP写入时,stm32会把nRDP会自动取反并写入。

设置Flash写保护使能函数:

/* 宏定义,对应WRP寄存器的相应位 */
#define FLASH_WRProt_Pages0to1         ((uint32_t)0x00000001) 
#define FLASH_WRProt_Pages2to3         ((uint32_t)0x00000002) 
#define FLASH_WRProt_Pages4to5         ((uint32_t)0x00000004) 
#define FLASH_WRProt_Pages6to7         ((uint32_t)0x00000008) 
#define FLASH_WRProt_Pages8to9         ((uint32_t)0x00000010) 

/* FLASH Mask 掩码,用于取出页码的对应位 */
#define RDPRT_Mask               ((uint32_t)0x00000002)
#define WRP0_Mask                ((uint32_t)0x000000FF)
#define WRP1_Mask                ((uint32_t)0x0000FF00)
#define WRP2_Mask                ((uint32_t)0x00FF0000)
#define WRP3_Mask                ((uint32_t)0xFF000000)

/* 因为是 0 写保护,所以要对Flash页码来取反 */
FLASH_Pages = (uint32_t)(~FLASH_Pages);

/* 取出对应的WPR寄存器要写入的值,如果该页不写保护,则应该是0XFF */
WRP0_Data = (uint16_t)(FLASH_Pages & WRP0_Mask);
WRP1_Data = (uint16_t)((FLASH_Pages & WRP1_Mask) >> 8);
WRP2_Data = (uint16_t)((FLASH_Pages & WRP2_Mask) >> 16);
WRP3_Data = (uint16_t)((FLASH_Pages & WRP3_Mask) >> 24);
/* 没有先擦除选项字节,而是直接修改WRP寄存器的对应位,但是会存在一个问题,
   Flash只能把1改写成0,因此写保护是能生效的。但是,如果想要解除某页的写保护,
   无法使用这个函数,只能擦除选项字节 */
FLASH_EnableWriteProtection
/* 写保护所有页,直接0XFFFF FFFF,则取反后所有的位全为0,那么所有的WRP寄存器全部写入0 */
/* 解除写保护所有页,要先全部擦除选项字节,再0X0000 0000,取反后全为FFFF FFFF,那么所有
   WPR寄存器全为1,都不设置写保护  */

注意,这里有对CR寄存器进行操作,因此要先对Flash进行解锁。

设置读保护FLASH_ReadOutProtection函数:

1

 

注意:在设置Flash读写保护时,一定要预留一个接口(串口 按键等都可以),来解除Flash的读写保护,不然很有可能导致芯片报废了。另外,由于Flash的特性,在操作Flash之前,最好先进行擦除操作,不然可能会设置出错。

示例:利用按键功能,来设置 / 解除 读写保护

写保护例程:

**
  * @brief  反转写保护的配置,用于演示
	          若芯片处于写保护状态,则解除,
						若不是写保护状态,则设置成写保护
  * @param  无
  * @retval 无
  */
void WriteProtect_Toggle(void)
{
	/* 获取写保护寄存器的值进行判断,寄存器位为0表示有保护,为1表示无保护 */
	/*  若不等于0xFFFFFFFF,则说明有部分页被写保护了 */
	if(FLASH_GetWriteProtectionOptionByte() != 0xFFFFFFFF )
	{
			FLASH_DEBUG("芯片处于写保护状态,即将执行解保护过程...");
			
			//解除对FLASH_CR寄存器的访问限制
			FLASH_Unlock();

			/* 擦除所有选项字节的内容,操作之前先进行擦除 */
			FLASH_EraseOptionBytes();

			/* 对所有页解除,这一句不加,应该也可以解锁写保护,因为选项字节被擦除至全为1 */
			FLASH_EnableWriteProtection(0x00000000);
			
			FLASH_DEBUG("配置完成,芯片将自动复位加载新配置,复位后芯片会解除写保护状态\r\n");

			/* 复位芯片,以使选项字节生效 */
			NVIC_SystemReset();
	}
	else //无写保护
	{
			FLASH_DEBUG("芯片处于无写保护状态,即将执行写保护过程...");
			
			//解除对FLASH_CR寄存器的访问限制
			FLASH_Unlock();

			/* 先擦除所有选项字节的内容,防止因为原有的写保护导致无法写入新的保护配置 */
			FLASH_EraseOptionBytes();

			/* 对所有页进行写保护 */
			FLASH_EnableWriteProtection(FLASH_WRProt_AllPages);

			FLASH_DEBUG("配置完成,芯片将自动复位加载新配置,复位后芯片会处于写保护状态\r\n");

			/* 复位芯片,以使选项字节生效 */
			NVIC_SystemReset();		
	}

}

读保护例程:

/**
  * @brief  反转读保护的配置,用于演示
	          若芯片处于读保护状态,则解除,
						若不是读保护状态,则设置成读保护
  * @param  无
  * @retval 无
  */
void ReadProtect_Toggle(void)
{
	if(FLASH_GetReadOutProtectionStatus () == SET )
	{
		FLASH_DEBUG("芯片处于读保护状态\r\n");
		
		//解除对FLASH_CR寄存器的访问限制
		FLASH_Unlock();
		
		FLASH_DEBUG("即将解除读保护,解除读保护会把FLASH的所有内容清空");
		FLASH_DEBUG("由于解除后程序被清空,所以后面不会有任何提示输出");
		FLASH_DEBUG("等待20秒后即可给芯片下载新的程序...\r\n");
		
		FLASH_ReadOutProtection (DISABLE);		

	//即使在此处加入printf串口调试也不会执行的,因为存储程序的整片FLASH都已被擦除。
	FLASH_DEBUG("由于FLASH程序被清空,所以本代码不会被执行,串口不会有本语句输出(SRAM调试模式下例外)\r\n");

	}
	else
	{
		FLASH_DEBUG("芯片处于无读保护状态,即将执行读保护过程...\r\n");
		
		//解除对FLASH_CR寄存器的访问限制
		FLASH_Unlock();				

		FLASH_ReadOutProtection (ENABLE);
		
		printf("芯片已被设置为读保护,上电复位后生效(必须重新给开发板上电,只按复位键无效)\r\n");
		printf("处于保护状态下无法正常下载新程序,必须要先解除保护状态再下载\r\n");

	}
}

这些函数,本质上都是操作寄存器。实施起来其实并不复杂,但是不敢轻易尝试(即使我确定解除程序正确),因为一旦失败,可能芯片就报废了,永远无法写入,很矛盾。所以,使用J-Flash或者ST-Link来设置保护比较稳妥

附加:

读保护等级:

在使用 hal 库来设置FLASH读保护时,看到有一个读保护等级的配置如下:

typedef struct
{
  uint32_t OptionType;  /*!< OptionType: Option byte to be configured.
                             This parameter can be a value of @ref FLASHEx_OB_Type */

  uint32_t WRPState;    /*!< WRPState: Write protection activation or deactivation.
                             This parameter can be a value of @ref FLASHEx_OB_WRP_State */

  uint32_t WRPPage;     /*!< WRPPage: specifies the page(s) to be write protected
                             This parameter can be a value of @ref FLASHEx_OB_Write_Protection */

  uint8_t RDPLevel;     /*!< RDPLevel: Set the read protection level..
                             This parameter can be a value of @ref FLASHEx_OB_Read_Protection */

  uint8_t USERConfig;   /*!< USERConfig: Program the FLASH User Option Byte: 
                             IWDG / STOP / STDBY / BOOT1 / VDDA_ANALOG / SRAM_PARITY
                             This parameter can be a combination of @ref FLASHEx_OB_IWatchdog, @ref FLASHEx_OB_nRST_STOP,
                             @ref FLASHEx_OB_nRST_STDBY, @ref FLASHEx_OB_BOOT1, @ref FLASHEx_OB_VDDA_Analog_Monitoring and
                             @ref FLASHEx_OB_RAM_Parity_Check_Enable */

  uint32_t DATAAddress; /*!< DATAAddress: Address of the option byte DATA to be programmed
                             This parameter can be a value of @ref FLASHEx_OB_Data_Address */
  
  uint8_t DATAData;     /*!< DATAData: Data to be stored in the option byte DATA
                             This parameter must be a number between Min_Data = 0x00 and Max_Data = 0xFF */  
} FLASH_OBProgramInitTypeDef;

可以看到,这里有一个RDPLevel的结构体成员,然后会调用下面这个函数来写入RDP寄存器:

/**
  * @brief  Set the read protection level.
  * @param  ReadProtectLevel specifies the read protection level.
  *         This parameter can be one of the following values:
  *            @arg @ref OB_RDP_LEVEL_0 No protection
  *            @arg @ref OB_RDP_LEVEL_1 Read protection of the memory
  *            @arg @ref OB_RDP_LEVEL_2 Full chip protection
  * @note   Warning: When enabling OB_RDP level 2 it's no more possible to go back to level 1 or 0
  * @retval HAL status
  */
static HAL_StatusTypeDef FLASH_OB_RDP_LevelConfig(uint8_t ReadProtectLevel)
{
  HAL_StatusTypeDef status = HAL_OK;
  
  /* Check the parameters */
  assert_param(IS_OB_RDP_LEVEL(ReadProtectLevel));
  
  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);
  
  if(status == HAL_OK)
  { 
    /* Clean the error context */
    pFlash.ErrorCode = HAL_FLASH_ERROR_NONE;
    
    /* If the previous operation is completed, proceed to erase the option bytes */
    SET_BIT(FLASH->CR, FLASH_CR_OPTER);
    SET_BIT(FLASH->CR, FLASH_CR_STRT);

    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);

    /* If the erase operation is completed, disable the OPTER Bit */
    CLEAR_BIT(FLASH->CR, FLASH_CR_OPTER);

    if(status == HAL_OK)
    {
      /* Enable the Option Bytes Programming operation */
      SET_BIT(FLASH->CR, FLASH_CR_OPTPG);
      
      WRITE_REG(OB->RDP, ReadProtectLevel);
      
      /* Wait for last operation to be completed */
      status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE); 
      
      /* if the program operation is completed, disable the OPTPG Bit */
      CLEAR_BIT(FLASH->CR, FLASH_CR_OPTPG);
    }
  }
  
  return status;
}

我翻阅了F0和F1系列的HAL库和flash编程手册,发现F1系列没有读保护等级的概念,而F0系列有点不同:

下面是HAL库的描述:

F0系列
/** @defgroup FLASHEx_OB_Read_Protection Option Byte Read Protection
  * @{
  */
#define OB_RDP_LEVEL_0             ((uint8_t)0xAAU)
#define OB_RDP_LEVEL_1             ((uint8_t)0xBBU)
#define OB_RDP_LEVEL_2             ((uint8_t)0xCCU) /*!< Warning: When enabling read protection level 2 
                                                      it's no more possible to go back to level 1 or 0 */

F1系列
/** @defgroup FLASHEx_OB_Read_Protection Option Byte Read Protection
  * @{
  */
#define OB_RDP_LEVEL_0            ((uint8_t)0xA5)
#define OB_RDP_LEVEL_1            ((uint8_t)0x00)

然后是参考手册的描述,F1的不再赘述,F0的如下所示:

 

  • 9
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值