实验:设置读写保护及解除
在本实验中我们将以实例讲解如何修改选项字节的配置,更改读保护级别、设置PCROP或写保护,最后把选项字节恢复默认值。
本实验要进行的操作比较特殊,在开发和调试的过程中都是在SRAM上进行的(使用SRAM启动方式)。例如,直接使用FLASH版本的程序进行调试时,如果该程序在运行后对扇区进行了写保护而没有解除的操作或者该解除操作不正常,此时将无法再给芯片的内部FLASH下载新程序,最终还是要使用SRAM自举的方式进行解除操作。所以在本实验中为便于修改选项字节的参数,我们统一使用SRAM版本的程序进行开发和学习,当SRAM版本调试正常后再改为FLASH版本。
关于在SRAM中调试代码的相关配置,请参考前面的章节。
注意:
若您在学习的过程中想亲自修改代码进行测试,请注意备份原工程代码。当芯片的FLASH被保护导致无法下载程序到FLASH时,可以下载本工程到芯片,并使用SRAM启动运行,即可恢复芯片至默认配置。但如果修改了读保护为级别2,采用任何方法都无法恢复!(除了这个配置,其它选项都可以大胆地修改测试。)
硬件设计
本实验在SRAM中调试代码,因此把BOOT0和BOOT1引脚都使用跳线帽连接到3.3V,使芯片从SRAM中启动。
软件设计
本实验的工程名称为“设置读写保护与解除”,学习时请打开该工程配合阅读,它是从“RAM调试—多彩流水灯”工程改写而来的。为了方便展示及移植,我们把操作内部FLASH相关的代码都编写到“internalFlash_reset.c”及“internalFlash_reset.h”文件中,这些文件是我们自己编写的,不属于标准库的内容,可根据您的喜好命名文件。
主要实验
- 学习配置扇区写保护;
- 学习配置PCROP保护;
- 学习配置读保护级别;
- 学习如何恢复选项字节到默认配置;
代码分析
配置扇区写保护
我们先以代码清单 44中的设置与解除写保护过程来学习如何配置选项字节。
代码清单 44 配置扇区写保护
1
2 #define FLASH_WRP_SECTORS (OB_WRP_Sector_0|OB_WRP_Sector_1)
3 __IO uint32_t SectorsWRPStatus = 0xFFF;
4
5 /**
6 * @brief WriteProtect_Test,普通的写保护配置
7 * @param 运行本函数后会给扇区FLASH_WRP_SECTORS进行写保护,再重复一次会进行解写保护
8 * @retval None
9 */
10 void WriteProtect_Test(void)
11 {
12 FLASH_Status status = FLASH_COMPLETE;
13 {
14 / 获取扇区的写保护状态 /
15 SectorsWRPStatus = FLASH_OB_GetWRP() & FLASH_WRP_SECTORS;
16
17 if (SectorsWRPStatus == 0x00)
18 {
19 / 扇区已被写保护,执行解保护过程/
20
21 / 使能访问OPTCR寄存器 /
22 FLASH_OB_Unlock();
23
24 / 设置对应的nWRP位,解除写保护 /
25 FLASH_OB_WRPConfig(FLASH_WRP_SECTORS, DISABLE);
26 status=FLASH_OB_Launch();
27 / 开始对选项字节进行编程 /
28 if (status != FLASH_COMPLETE)
29 {
30 FLASH_ERROR("对选项字节编程出错,解除写保护失败,status = %x",status);
31 / User can add here some code to deal with this error /
32 while (1)
33 {
34 }
35 }
36 / 禁止访问OPTCR寄存器 /
37 FLASH_OB_Lock();
38
39 / 获取扇区的写保护状态 /
40 SectorsWRPStatus = FLASH_OB_GetWRP() & FLASH_WRP_SECTORS;
41
42 / 检查是否配置成功 /
43 if (SectorsWRPStatus == FLASH_WRP_SECTORS)
44 {
45 FLASH_INFO("解除写保护成功!");
46 }
47 else
48 {
49 FLASH_ERROR("未解除写保护!");
50 }
51 }
52 else
53 { / 若扇区未被写保护,开启写保护配置 /
54
55 / 使能访问OPTCR寄存器 /
56 FLASH_OB_Unlock();
57
58 /使能 FLASH_WRP_SECTORS 扇区写保护 /
59 FLASH_OB_WRPConfig(FLASH_WRP_SECTORS, ENABLE);
60
61 status=FLASH_OB_Launch();
62 / 开始对选项字节进行编程 /
63 if (status != FLASH_COMPLETE)
64 {
65 FLASH_ERROR("对选项字节编程出错,设置写保护失败,status = %x",status);
66 while (1)
67 {
68 }
69 }
70 / 禁止访问OPTCR寄存器 /
71 FLASH_OB_Lock();
72
73 / 获取扇区的写保护状态 /
74 SectorsWRPStatus = FLASH_OB_GetWRP() & FLASH_WRP_SECTORS;
75
76 / 检查是否配置成功 /
77 if (SectorsWRPStatus == 0x00)
78 {
79 FLASH_INFO("设置写保护成功!");
80 }
81 else
82 {
83 FLASH_ERROR("设置写保护失败!");
84 }
85 }
86 }
87 }
本函数分成了两个部分,它根据目标扇区的状态进行操作,若原来扇区为非保护状态时就进行写保护,若为保护状态就解除保护。其主要操作过程如下:
- 调用FLASH_OB_GetWRP函数获取目标扇区的保护状态若扇区被写保护,则开始解除保护过程,否则开始设置写保护过程;
- 调用FLASH_OB_Unlock解锁选项字节的编程;
- 调用FLASH_OB_WRPConfig函数配置目标扇区关闭或打开写保护;
- 调用FLASH_OB_Launch函数把寄存器的配置写入到选项字节;
- 调用FLASH_OB_GetWRP函数检查是否配置成功;
- 调用FLASH_OB_Lock禁止修改选项字节。
配置PCROP保护
配置PCROP保护的过程与配置写保护过程稍有区别,见代码清单 45。
代码清单 45 配置PCROP保护(internalFlash_reset.c文件)
1
2 /**
3 * @brief SetPCROP,设置PCROP位,用于测试解锁
4 * @note 使用有问题的串口ISP下载软件,可能会导致PCROP位置1,
5 导致无法给芯片下载程序到FLASH,本函数用于把PCROP位置1,
6 模拟出无法下载程序到FLASH的环境,以便用于解锁的程序调试。
7 若不了解PCROP位的作用,请不要执行此函数!!
8 * @param None
9 * @retval None
10 */
11 void SetPCROP(void)
12 {
13
14 FLASH_Status status = FLASH_COMPLETE;
15
17
18 FLASH_INFO();
19 FLASH_INFO("正在设置PCROP保护,请耐心等待...");
20 //选项字节解锁
21 FLASH_OB_Unlock();
22
23 //设置为PCROP模式
24 FLASH_OB_PCROPSelectionConfig(OB_PcROP_Enable);
25 //设置扇区0进行PCROP保护
26 FLASH_OB_PCROPConfig(OB_PCROP_Sector_10,ENABLE);
27 //把寄存器设置写入到选项字节
28 status =FLASH_OB_Launch();
29
30 if (status != FLASH_COMPLETE)
31 {
32 FLASH_INFO("设置PCROP失败!");
33 }
34 else
35 {
36 FLASH_INFO("设置PCROP成功!");
37
38 }
39 //选项字节上锁
40 FLASH_OB_Lock();
41 }
该代码在解锁选项字节后,调用FLASH_OB_PCROPSelectionConfig函数把SPMOD寄存器位配置为PCROP模式,接着调用FLASH_OB_PCROPConfig函数配置目标保护扇区。
恢复选项字节为默认值
当芯片被设置为读写保护或PCROP保护时,这时给芯片的内部FLASH下载程序时,可能会出现图 46和图 47的擦除FLASH失败的错误提示。
图 46 擦除失败提示
图 47 擦除进度条卡在开始状态
只要不是把读保护配置成了级别2保护,都可以使用SRAM启动运行代码清单 46中的函数恢复选项字节为默认状态,使得FLASH下载能正常进行。
代码清单 46 恢复选项字节为默认值
1 // @brief OPTCR register byte 0 (Bits[7:0]) base address
2 #define OPTCR_BYTE0_ADDRESS ((uint32_t)0x40023C14)
3 //@brief OPTCR register byte 1 (Bits[15:8]) base address
4 #define OPTCR_BYTE1_ADDRESS ((uint32_t)0x40023C15)
5 //@brief OPTCR register byte 2 (Bits[23:16]) base address
6 #define OPTCR_BYTE2_ADDRESS ((uint32_t)0x40023C16)
7 // @brief OPTCR register byte 3 (Bits[31:24]) base address
8 #define OPTCR_BYTE3_ADDRESS ((uint32_t)0x40023C17)
9 // @brief OPTCR1 register byte 0 (Bits[7:0]) base address
10 #define OPTCR1_BYTE2_ADDRESS ((uint32_t)0x40023C1A)
11
12 /**
13 * @brief InternalFlash_Reset,恢复内部FLASH的默认配置
14 * @param None
15 * @retval None
16 */
17 int InternalFlash_Reset(void)
18 {
19 FLASH_Status status = FLASH_COMPLETE;
20
21 / 使能访问选项字节寄存器 /
22 FLASH_OB_Unlock();
23
24 / 擦除用户区域 (用户区域指程序本身没有使用的空间,可以自定义)*/
25 / 清除各种FLASH的标志位 /
26 FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR |
27 FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR|FLASH_FLAG_PGSERR);
28
29 FLASH_INFO("\r\n");
30 FLASH_INFO("正在准备恢复的条件,请耐心等待...");
31
32 //确保把读保护级别设置为LEVEL1,以便恢复PCROP寄存器位
33 //PCROP寄存器位从设置为0时,必须是读保护级别由LEVEL1转为LEVEL0时才有效,
34 //否则修改无效
35 FLASH_OB_RDPConfig(OB_RDP_Level_1);
36
37 status=FLASH_OB_Launch();
38
39 status = FLASH_WaitForLastOperation();
40
41 //设置为LEVEL0并恢复PCROP位
42
43 FLASH_INFO("\r\n");
44 FLASH_INFO("正在擦除内部FLASH的内容,请耐心等待...");
45
46 //关闭PCROP模式
47 FLASH_OB_PCROPSelectionConfig(OB_PcROP_Disable);
48 FLASH_OB_RDPConfig(OB_RDP_Level_0);
49
50 status =FLASH_OB_Launch();
51
52 //设置其它位为默认值
53 ((__IO uint32_t )(OPTCR_BYTE0_ADDRESS))=0x0FFFAAE9;
54 ((__IO uint16_t )(OPTCR1_BYTE2_ADDRESS))=0x0FFF;
55 status =FLASH_OB_Launch();
56 if (status != FLASH_COMPLETE)
57 {
58 FLASH_ERROR("恢复选项字节默认值失败,错误代码:status=%X",status);
59 }
60 else
61 {
62 FLASH_INFO("恢复选项字节默认值成功!");
63 }
64 //禁止访问
65 FLASH_OB_Lock();
66
67 return status;
68 }
这个函数进行了如下操作:
- 调用FLASH_OB_Unlock解锁选项字节的编程;
- 调用FLASH_ClearFlag函数清除所有FLASH异常状态标志;
- 调用FLASH_OB_RDPConfig函数设置为读保护级别1,以便后面能正常关闭PCROP模式;
- 调用FLASH_OB_Launch写入选项字节并等待读保护级别设置完毕;
- 调用FLASH_OB_PCROPSelectionConfig函数关闭PCROP模式;
- 调用FLASH_OB_RDPConfig函数把读保护级别降为0;
- 调用FLASH_OB_Launch定稿选项字节并等待降级完毕,由于这个过程需要擦除内部FLASH的内容,等待的时间会比较长;
- 直接操作寄存器,使用“((__IO uint32_t )(OPTCR_BYTE0_ADDRESS))=0x0FFFAAE9;”和“((__IO uint16_t )(OPTCR1_BYTE2_ADDRESS))=0x0FFF;”语句把OPTCR及OPTCR1寄存器与选项字节相关的位都恢复默认值;
- 调用FLASH_OB_Launch函数等待上述配置被写入到选项字节;
- 恢复选项字节为默认值操作完毕。
main函数
最后来看看本实验的main函数,见。代码清单 47。
代码清单 47 main函数
1 /**
2 * @brief 主函数
3 * @param 无
4 * @retval 无
5 */
6 int main(void)
7 {
8 / LED 端口初始化 /
9 LED_GPIO_Config();
10 Debug_USART_Config();
11 LED_BLUE;
12
13 FLASH_INFO("本程序将会被下载到STM32的内部SRAM运行。");
14 FLASH_INFO("下载程序前,请确认把实验板的BOOT0和BOOT1引脚都接到3.3V电源处!!");
15
16 FLASH_INFO("\r\n");
17 FLASH_INFO("----这是一个STM32芯片内部FLASH解锁程序----");
18 FLASH_INFO("程序会把芯片的内部FLASH选项字节恢复为默认值");
19
20
21 #if 0 //工程调试、演示时使用,正常解除时不需要运行此函数
22 SetPCROP(); //修改PCROP位,仿真芯片被锁无法下载程序到内部FLASH的环境
23 #endif
24
25 #if 0 //工程调试、演示时使用,正常解除时不需要运行此函数
26 WriteProtect_Test(); //修改写保护位,仿真芯片扇区被设置成写保护的的环境
27 #endif
28
30
31 /恢复选项字节到默认值,解除保护/
32 if(InternalFlash_Reset()==FLASH_COMPLETE)
33 {
34 FLASH_INFO("选项字节恢复成功,请把BOOT0和BOOT1引脚都连接到GND,");
35 FLASH_INFO("然后随便找一个普通的程序,下载程序到芯片的内部FLASH进行测试");
36 LED_GREEN;
37 }
38 else
39 {
40 FLASH_INFO("选项字节恢复成功失败,请复位重试");
41 LED_RED;
42 }
43
45
46 while (1);
47 }
在main函数中,主要是调用了InternalFlash_Reset函数把选项字节恢复成默认值,程序默认时没有调用SetPCROP和WriteProtect_Test函数设置写保护,若您想观察实验现象,可修改条件编译的宏,使它加入到编译中。
下载测试
把开发板的BOOT0和BOOT1引脚都使用跳线帽连接到3.3V电源处,使它以SRAM方式启动,然后用USB线连接开发板“USB TO UART”接口跟电脑,在电脑端打开串口调试助手,把编译好的程序下载到开发板并复位运行,在串口调试助手可看到调试信息。程序运行后,请耐心等待至开发板亮绿灯或串口调试信息提示恢复完毕再给开发板断电,否则由于恢复过程被中断,芯片内部FLASH会处于保护状态。
芯片内部FLASH处于保护状态时,可重新下载本程序到开发板以SRAM运行恢复默认配置。