第51章 設置FLASH的讀寫保護及解除
全套200集視頻教程和1000頁PDF教程請到秉火論壇下載:www.firebbs.cn
野火視頻教程優酷觀看網址:http://i.youku.com/firege
本章參考資料:《STM32F4xx 中文參考手冊》、《STM32F4xx規格書》、庫說明文檔《stm32f4xx_dsp_stdperiph_lib_um.chm》以及《Proprietary code read-out protection on microcontrollers》。
51.1 選項字節與讀寫保護
在實際發布的產品中,在STM32芯片的內部FLASH存儲了控制程序,如果不作任何保護措施的話,可以使用下載器直接把內部FLASH的內容讀取回來,得到bin或hex文件格式的代碼拷貝,別有用心的廠商即可利用該代碼文件山寨產品。為此,STM32芯片提供了多種方式保護內部FLASH的程序不被非法讀取,但在默認情況下該保護功能是不開啟的,若要開啟該功能,需要改寫內部FLASH選項字節(Option Bytes)中的配置。
51.1.1 選項字節的內容
選項字節是一段特殊的FLASH空間,STM32芯片會根據它的內容進行讀寫保護、復位電壓等配置,選項字節的構成見表 511。
表 511 選項字節的構成
地址 |
[63:16] |
[15:0] |
0x1FFF C000 |
保留 |
ROP 和用戶選項字節 (RDP & USER) |
0x1FFF C008 |
保留 |
扇區 0 到 11 的寫保護 nWRP 位 |
0x1FFE C000 |
保留 |
保留 |
0x1FFE C008 |
保留 |
扇區 12 到 23 的寫保護 nWRP 位 |
選項字節具體的數據位配置說明見表 512。
表 512 選項字節具體的數據位配置說明
選項字節(字,地址 0x1FFF C000) |
|
RDP:讀保護選項字節。 |
|
位 15:8 |
0xAA:級別 0,無保護 |
USER:用戶選項字節 |
|
位 7 |
nRST_STDBY |
位 6 |
nRST_STOP |
位 5 |
WDG_SW |
位 4 |
0x1:未使用 |
位 3:2 |
BOR_LEV: BOR 復位級別 |
位 1:0 |
0x1:未使用 |
選項字節(字,地址 0x1FFF C008) |
|
位15 |
SPMOD:選擇nWPRi位的模式 |
位14 |
DB1M:設置內部FLASH為1MB的產品的雙Bank模式 |
位 13:12 |
0xF:未使用 |
nWRP: Flash 寫保護選項字節。 |
|
位 11:0 |
nWRPi (i值為0-11,對應0-11扇區的保護設置): |
選項字節(字,地址 0x1FFE C000) |
|
位 15:0 |
0xFF:未使用 |
選項字節(字,地址 0x1FFE C008) |
|
位 15:12 |
0xF:未使用 |
nWRP: Flash 寫保護選項字節。 |
|
位 11:0 |
nWRPi: |
我們主要講解選項字節配置中的RDP位和PCROP位,它們分別用於配置讀保護級別及代碼讀出保護。
51.1.2 RDP讀保護級別
修改選項字節的RDP位的值可設置內部FLASH為以下保護級別:
0xAA:級別0,無保護
這是STM32的默認保護級別,它沒有任何讀保護,讀取內部FLASH及"備份SRAM"的內容都沒有任何限制。(注意這里說的"備份SRAM"是指STM32備份域的SRAM空間,不是指主SRAM,下同)
其它值:級別1,使能讀保護
把RDP配置成除0xAA或0xCC外的任意數值,都會使能級別1的讀保護。在這種保護下,若使用調試功能(使用下載器、仿真器)或者從內部SRAM自舉時都不能對內部FLASH及備份SRAM作任何訪問(讀寫、擦除都被禁止);而如果STM32是從內部FLASH自舉時,它允許對內部FLASH及備份SRAM的任意訪問。
也就是說,在級別1模式下,任何嘗試從外部訪問內部FLASH內容的操作都被禁止,例如無法通過下載器讀取它的內容,或編寫一個從內部SRAM啟動的程序,若該程序讀取內部FLASH,會被禁止。而如果是芯片自己訪問內部FLASH,是完全沒有問題的,例如前面的"讀寫內部FLASH"實驗中的代碼自己擦寫內部FLASH空間的內容,即使處於級別1的讀保護,也能正常擦寫。
當芯片處於級別1的時候,可以把選項字節的RDP位重新設置為0xAA,恢復級別0。在恢復到級別0前,芯片會自動擦除內部FLASH及備份SRAM的內容,即降級后原內部FLASH的代碼會丟失。在級別1時使用SRAM自舉的程序也可以訪問選項字節進行修改,所以如果原內部FLASH的代碼沒有解除讀保護的操作時,可以給它加載一個SRAM自舉的程序進行保護降級,后面我們將會進行這樣的實驗。
0xCC:級別2,禁止調試
把RDP配置成0xCC值時,會進入最高級別的讀保護,且設置后無法再降級,它會永久禁止用於調試的JTAG接口(相當於熔斷)。在該級別中,除了具有級別1的所有保護功能外,進一步禁止了從SRAM或系統存儲器的自舉(即平時使用的串口ISP下載功能也失效),JTAG調試相關的功能被禁止,選項字節也不能被修改。它僅支持從內部FLASH自舉時對內部FLASH及SRAM的訪問(讀寫、擦除)。
由於設置了級別2后無法降級,也無法通過JTAG、串口ISP等方式更新程序,所以使用這個級別的保護時一般會在程序中預留"后門"以更新應用程序,若程序中沒有預留后門,芯片就無法再更新應用程序了。所謂的"后門"是一種IAP程序(In Application Program),它通過某個通訊接口獲取將要更新的程序內容,然后利用內部FLASH擦寫操作把這些內容燒錄到自己的內部FLASH中,實現應用程序的更新。
不同級別下的訪問限制見圖 511。
圖 511 不同級別下的訪問限制
不同保護級別之間的狀態轉換見圖 512。
圖 512 不同級別間的狀態轉換
51.1.3 PCROP代碼讀出保護
在STM32F42xx及STM32F43xx系列的芯片中,除了可使用RDP對整片FLASH進行讀保護外,還有一個專用的代碼讀出保護功能(Proprietary code readout protection,下面簡稱PCROP),它可以為內部FLASH的某幾個指定扇區提供保護功能,所以它可以用於保護一些IP代碼,方便提供給另一方進行二次開發,見圖 513。
圖 513 PCROP保護功能
當SPMOD位設置為0時(默認值),nWRPi位用於指定要進行寫保護的扇區,這可以防止錯誤的指針操作導致FLASH 內容的改變,若扇區被寫保護,通過調試器也無法擦除該扇區的內容;當SPMOD位設置為1時,nWRPi位用於指定要進行PCROP保護的扇區。其中PCROP功能可以防止指定扇區的FLASH內容被讀出,而寫保護僅可以防止誤寫操作,不能被防止讀出。
當要關閉PCROP功能時,必須要使芯片從讀保護級別1降為級別0,同時對SPMOD位置0,才能正常關閉;若芯片原來的讀保護為級別0,且使能了PCROP保護,要關閉PCROP時也要先把讀保護級別設置為級別1,再在降級的同時設置SPMOD為0。
51.2 修改選項字節的過程
修改選項字節的內容可修改各種配置,但是,當應用程序運行時,無法直接通過選項字節的地址改寫它們的內容,例如,接使用指針操作地址0x1FFFC0000的修改是無效的。要改寫其內容時必須設置寄存器FLASH_OPTCR及FLASH_OPTCR1中的對應數據位,寄存器的與選項字節對應位置見圖 514及圖 515,詳細說明請查閱《STM32參考手冊》。
圖 514 FLASH_OPTCR寄存器說明(nWRP表示0-11扇區)
圖 515 FLASH_OPTCR1寄存器說明(nWRP表示12-23扇區)
默認情況下,FLASH_OPTCR寄存器中的第0位OPTLOCK值為1,它表示選項字節被上鎖,需要解鎖后才能進行修改,當寄存器的值設置完成后,對FLASH_OPTCR寄存器中的第1位OPTSTRT值設置為1,硬件就會擦除選項字節扇區的內容,並把FLASH_OPTCR/1寄存器中包含的值寫入到選項字節。
所以,修改選項字節的配置步驟如下:
(1) 解鎖,在 Flash 選項密鑰寄存器 (FLASH_OPTKEYR) 中寫入 OPTKEY1 = 0x0819 2A3B;接着在 Flash 選項密鑰寄存器 (FLASH_OPTKEYR) 中寫入 OPTKEY2 = 0x4C5D 6E7F。
(2) 檢查 FLASH_SR 寄存器中的 BSY 位,以確認當前未執行其它Flash 操作。
(3) 在 FLASH_OPTCR 和/或 FLASH_OPTCR1 寄存器中寫入選項字節值。
(4) 將 FLASH_OPTCR 寄存器中的選項啟動位 (OPTSTRT) 置 1。
(5) 等待 BSY 位清零,即寫入完成。
51.3 操作選項字節的庫函數
為簡化編程,STM32標准庫提供了一些庫函數,它們封裝了修改選項字節時操作寄存器的過程。
1. 選項字節解鎖、上鎖函數
對選項字節解鎖、上鎖的函數見代碼清單 511。
代碼清單 511選項字節解鎖、上鎖
1
2 #define FLASH_OPT_KEY1 ((uint32_t)0x08192A3B)
3 #define FLASH_OPT_KEY2 ((uint32_t)0x4C5D6E7F)
4
5 /**
6 * @brief Unlocks the FLASH Option Control Registers access.
7 * @param None
8 * @retval None
9 */
10 void FLASH_OB_Unlock(void)
11 {
12 if((FLASH->OPTCR & FLASH_OPTCR_OPTLOCK) != RESET)
13 {
14 /* Authorizes the Option Byte register programming */
15 FLASH->OPTKEYR = FLASH_OPT_KEY1;
16 FLASH->OPTKEYR = FLASH_OPT_KEY2;
17 }
18 }
19
20 /**
21 * @brief Locks the FLASH Option Control Registers access.
22 * @param None
23 * @retval None
24 */
25 void FLASH_OB_Lock(void)
26 {
27 /* Set the OPTLOCK Bit to lock the FLASH Option Byte Registers access */
28 FLASH->OPTCR |= FLASH_OPTCR_OPTLOCK;
29 }
解鎖的時候,它對FLASH_OPTCR寄存器寫入兩個解鎖參數,上鎖的時候,對FLASH_ OPTCR寄存器的FLASH_OPTCR_OPTLOCK位置1。
2. 設置讀保護級別
解鎖后設置選項字節寄存器的RDP位可調用FLASH_OB_RDPConfig完成,見代碼清單 512。
代碼清單 512 設置讀保護級別
1 /**
2 * @brief Sets the read protection level.
3 * @param OB_RDP: specifies the read protection level.
4 * This parameter can be one of the following values:
5 * @arg OB_RDP_Level_0: No protection
6 * @arg OB_RDP_Level_1: Read protection of the memory
7 * @arg OB_RDP_Level_2: Full chip protection
8 *
9 * /!\ Warning /!\ When enabling OB_RDP level 2 it's no more possible to go back to level 1 or 0
10 *
11 * @retval None
12 */
13 void FLASH_OB_RDPConfig(uint8_t OB_RDP)
14 {
15 FLASH_Status status = FLASH_COMPLETE;
16
17 /* Check the parameters */
18 assert_param(IS_OB_RDP(OB_RDP));
19
20 status = FLASH_WaitForLastOperation();
21
22 if(status == FLASH_COMPLETE)
23 {
24 *(__IO uint8_t*)OPTCR_BYTE1_ADDRESS = OB_RDP;
25
26 }
27 }
該函數根據輸入參數設置RDP寄存器位為相應的級別,其注釋警告了若配置成OB_RDP_Level_2會無法恢復。類似地,配置其它選項時也有相應的庫函數,如FLASH_OB_PCROP1Config、FLASH_OB_WRP1Config分別用於設置要進行PCROP保護或WRP保護(寫保護)的扇區。
3. 寫入選項字節
調用上一步驟中的函數配置寄存器后,還要調用代碼清單 513中的FLASH_OB_Launch函數把寄存器的內容寫入到選項字節中。
代碼清單 513 寫入選項字節
1 /**
2 * @brief Launch the option byte loading.
3 * @param None
4* @retval FLASH Status: The returned value can be: FLASH_BUSY, FLASH_ERROR_PROGRAM,
5 * FLASH_ERROR_WRP, FLASH_ERROR_OPERATION or FLASH_COMPLETE.
6 */
7 FLASH_Status FLASH_OB_Launch(void)
8 {
9 FLASH_Status status = FLASH_COMPLETE;
10
11 /* Set the OPTSTRT bit in OPTCR register */
12 *(__IO uint8_t *)OPTCR_BYTE0_ADDRESS |= FLASH_OPTCR_OPTSTRT;
13
14 /* Wait for last operation to be completed */
15 status = FLASH_WaitForLastOperation();
16
17 return status;
18 }
該函數設置FLASH_OPTCR_OPTSTRT位后調用了FLASH_WaitForLastOperation函數等待寫入完成,並返回寫入狀態,若操作正常,它會返回FLASH_COMPLETE。
51.4 實驗:設置讀寫保護及解除
在本實驗中我們將以實例講解如何修改選項字節的配置,更改讀保護級別、設置PCROP或寫保護,最后把選項字節恢復默認值。
本實驗要進行的操作比較特殊,在開發和調試的過程中都是在SRAM上進行的(使用SRAM啟動方式)。例如,直接使用FLASH版本的程序進行調試時,如果該程序在運行后對扇區進行了寫保護而沒有解除的操作或者該解除操作不正常,此時將無法再給芯片的內部FLASH下載新程序,最終還是要使用SRAM自舉的方式進行解除操作。所以在本實驗中為便於修改選項字節的參數,我們統一使用SRAM版本的程序進行開發和學習,當SRAM版本調試正常后再改為FLASH版本。
關於在SRAM中調試代碼的相關配置,請參考前面的章節。
注意:
若您在學習的過程中想親自修改代碼進行測試,請注意備份原工程代碼。當芯片的FLASH被保護導致無法下載程序到FLASH時,可以下載本工程到芯片,並使用SRAM啟動運行,即可恢復芯片至默認配置。但如果修改了讀保護為級別2,采用任何方法都無法恢復!(除了這個配置,其它選項都可以大膽地修改測試。)
51.4.1 硬件設計
本實驗在SRAM中調試代碼,因此把BOOT0和BOOT1引腳都使用跳線帽連接到3.3V,使芯片從SRAM中啟動。
51.4.2 軟件設計
本實驗的工程名稱為"設置讀寫保護與解除",學習時請打開該工程配合閱讀,它是從"RAM調試—多彩流水燈"工程改寫而來的。為了方便展示及移植,我們把操作內部FLASH相關的代碼都編寫到"internalFlash_reset.c"及"internalFlash_reset.h"文件中,這些文件是我們自己編寫的,不屬於標准庫的內容,可根據您的喜好命名文件。
1. 主要實驗
(1) 學習配置扇區寫保護;
(2) 學習配置PCROP保護;
(3) 學習配置讀保護級別;
(4) 學習如何恢復選項字節到默認配置;
2. 代碼分析
配置扇區寫保護
我們先以代碼清單 514中的設置與解除寫保護過程來學習如何配置選項字節。
代碼清單 514 配置扇區寫保護
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保護的過程與配置寫保護過程稍有區別,見代碼清單 515。
代碼清單 515 配置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下載程序時,可能會出現圖 516和圖 517的擦除FLASH失敗的錯誤提示。
圖 516 擦除失敗提示
圖 517 擦除進度條卡在開始狀態
只要不是把讀保護配置成了級別2保護,都可以使用SRAM啟動運行代碼清單 516中的函數恢復選項字節為默認狀態,使得FLASH下載能正常進行。
代碼清單 516 恢復選項字節為默認值
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函數,見。代碼清單 517。
代碼清單 517 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函數設置寫保護,若您想觀察實驗現象,可修改條件編譯的宏,使它加入到編譯中。
3. 下載測試
把開發板的BOOT0和BOOT1引腳都使用跳線帽連接到3.3V電源處,使它以SRAM方式啟動,然后用USB線連接開發板"USB TO UART"接口跟電腦,在電腦端打開串口調試助手,把編譯好的程序下載到開發板並復位運行,在串口調試助手可看到調試信息。程序運行后,請耐心等待至開發板亮綠燈或串口調試信息提示恢復完畢再給開發板斷電,否則由於恢復過程被中斷,芯片內部FLASH會處於保護狀態。
芯片內部FLASH處於保護狀態時,可重新下載本程序到開發板以SRAM運行恢復默認配置。
51.5 每課一問
1. 分別設置芯片為讀保護級別1、寫保護及PCROP保護,然后給芯片的內部FLASH下載程序,觀察實驗現象。