科技/江科大—35.FLASH閃存)
FLASH閃存程序現(xiàn)象1、讀寫內(nèi)部FLASH這個(gè)代碼的目的就是利用內(nèi)部flash程序存儲(chǔ)器的剩余空間來存儲(chǔ)一些掉電不丟失的參數(shù)。所以這里的程序是按下K1變換一下測(cè)試數(shù)據(jù)然后存儲(chǔ)到內(nèi)部FLASH按下K2把所有參數(shù)清0最后OIED顯示一下。這里OLED顯示Flag、Data 。Flag當(dāng)做標(biāo)志位內(nèi)容定義為A5A5。Flag作用判斷之前是不是存儲(chǔ)過數(shù)據(jù)如果存儲(chǔ)過直接讀取如沒有存儲(chǔ)過就先初始化。Data是要掉電存儲(chǔ)的數(shù)據(jù)。2讀取芯片ID在stm32里指定地址下存儲(chǔ)有原廠寫入的ID號(hào)使用指針的方式讀取就可以得到ID號(hào)。OLED可以看到有2個(gè)ID號(hào)第一個(gè)是F_SIZE表示芯片F(xiàn)LASH容量。0040表示FLASH容量單位是KB換成十進(jìn)制是64KB。第二個(gè)是U_ID是96位芯片唯一ID號(hào)每個(gè)芯片ID號(hào)都不一樣。STM32內(nèi)部FLASH閃存簡(jiǎn)介讀寫FLASH的用途利用程序存儲(chǔ)器的剩余空間來保存掉電不丟失的用戶數(shù)據(jù)通過在程序中編程IAP實(shí)現(xiàn)程序的自我更新第一個(gè)用途對(duì)于我們這個(gè)C8T6芯片來說它的程序存儲(chǔ)器容量是64K一般我們寫個(gè)簡(jiǎn)單的程序可能就只占前面的很小一部分空間剩下的大片空余空間我們就可以加以利用比如存儲(chǔ)一些我們自定義的數(shù)據(jù)而且可以充分利用資源。不過這里要注意我們?cè)谶x取存儲(chǔ)區(qū)域時(shí)一定不要覆蓋了原有的程序要不然程序自己把自己給破壞了一般存儲(chǔ)少量的參數(shù)我們就選最后幾頁存儲(chǔ)就行了、第二個(gè)用途通過在程序中編程IAP實(shí)現(xiàn)程序的自我更新。我們?cè)诖鎯?chǔ)用戶數(shù)據(jù)時(shí)要避開程序本身以免破壞程序但如果我們就非要修改程序本身這會(huì)發(fā)生什么呢在程序中編程利用程序來修改程序本身實(shí)現(xiàn)程序的自我更新這個(gè)在程序中編程就是IAP是用來實(shí)現(xiàn)程序升級(jí)的。在線編程 ICP這個(gè)JTAG SWD就是仿真器下載程序每次下載都是把整個(gè)程序完全更新掉那系統(tǒng)加載程序就是系統(tǒng)存儲(chǔ)器的BOOTLOADER也就是串口下載串口下載也是更新整個(gè)程序這就是我們一直在用的ICP下載方式。在程序中編程 IAP它可以使用微控制器支持的任意一種通信接口下載程序。實(shí)現(xiàn)過程比如這是整個(gè)程序存儲(chǔ)器我們首先需要自己寫一個(gè)BOOTLOADER程序并且存放在程序更新時(shí)不會(huì)覆蓋的地方然后需要更新程序時(shí)我們控制程序跳轉(zhuǎn)到這BOOTLOADER這里來在這里面我們就可以接收任意一種通訊接口傳過來的數(shù)據(jù)比如串口、USB、藍(lán)牙轉(zhuǎn)串口、WIFI轉(zhuǎn)串口等等這個(gè)傳過來的數(shù)據(jù)就是待更新的程序然后我們控制flash讀寫把收到的程序?qū)懭氲角懊娉绦蛘_\(yùn)行的地方寫完之后再控制程序跳轉(zhuǎn)回正常運(yùn)行的地方或者直接復(fù)位這樣程序就完成了自我升級(jí)。區(qū)別IAP就是和系統(tǒng)存儲(chǔ)器這個(gè)的BOOTLOADER一樣。系統(tǒng)存儲(chǔ)器的BOOTLOADER只能配置boot引腳觸發(fā)啟動(dòng)。而我們自己寫B(tài)OOTLOADER的話就可以想怎么收怎么收并且在整個(gè)升級(jí)過程程序都可以自主完成?;仡櫼幌麓鎯?chǔ)器影像閃存模塊組織C8T6芯片的flash閃存容量是64K屬于中容量產(chǎn)品。這里分為了三個(gè)塊第一個(gè)是主存儲(chǔ)器也是程序存儲(chǔ)器用來存放程序代碼的這是最主要也是容量最大的一塊。第二個(gè)是信息塊里面又可以分為啟動(dòng)程序代碼和用戶選擇字節(jié)其中啟動(dòng)程序代碼就是系統(tǒng)存儲(chǔ)器存放的是原廠寫入BOOTLOADER用于串口下載。第三塊是閃存存儲(chǔ)器接口寄存器這一塊的存儲(chǔ)器實(shí)際上并不屬于閃存地址都是40開頭的說明這個(gè)存儲(chǔ)器接口寄存器就是一個(gè)普通的外設(shè)和之前講的GPIO定時(shí)器、串口等等都是一個(gè)性質(zhì)的東西這些存儲(chǔ)器它們的存儲(chǔ)介質(zhì)也都是SRAM。這個(gè)閃存存儲(chǔ)器接口寄存器就上面主存儲(chǔ)器和信息塊的管理員閃存存儲(chǔ)器接口寄存器就是用來控制擦除和編程這個(gè)過程的。對(duì)于主存儲(chǔ)器這里對(duì)它進(jìn)行了分頁1024byte 0x400byte,進(jìn)制不同分頁是為了更好的管理閃存擦除和寫保護(hù)都是以頁為單位的。這點(diǎn)和之前W25Q64芯片的閃存一樣寫入前必須擦除擦除必須以最小單位進(jìn)行擦除后數(shù)據(jù)位全變?yōu)?數(shù)據(jù)只能1寫0不能0寫1擦除和寫入之后都需要等待忙這些都是一樣的。這里就比較簡(jiǎn)單了它只有一個(gè)基本單位就是頁每一頁的大小都是1K0到127總共128頁總量就是128K對(duì)于C8T6來說它只有64K所以C8T6的頁只有一半0~63總共64頁共64K然后看一下頁的地址范圍。所以地址只要以000、400、800、400結(jié)尾的都一定是頁的起始地址。FLASH基本結(jié)構(gòu)整個(gè)閃存分為程序存儲(chǔ)器、系統(tǒng)存儲(chǔ)器和選項(xiàng)字節(jié)三部分這里程序存儲(chǔ)器為以C8T6為例它是64K的所以總共只有64頁最后一頁的起始地址是0800FC00。左邊這里是閃存存儲(chǔ)器接口手冊(cè)里還有個(gè)名稱閃存編程和擦除控制器LPEC。然后閃存存儲(chǔ)器接口可以對(duì)程序存儲(chǔ)器進(jìn)行擦除和編程也可以對(duì)選項(xiàng)字節(jié)進(jìn)行擦除和編程系統(tǒng)存儲(chǔ)器是不能擦除和編程的。這個(gè)選項(xiàng)字節(jié)里面有很大一部分配置位其實(shí)是配置主程序存儲(chǔ)器的讀寫保護(hù)的所以右邊畫的寫入選項(xiàng)字節(jié)可以配置程序存儲(chǔ)器的讀寫保護(hù)。如何操作這個(gè)控制器FPEC來對(duì)程序存儲(chǔ)器和選項(xiàng)字節(jié)進(jìn)行擦除和編程。FLASH解鎖首先第一步是flash解鎖flash操作之前需要解鎖目的都是為了防止誤操作是通過在鍵寄存器寫入指定的鍵值來實(shí)現(xiàn)使用鍵寄存容器的好處就是更能防止誤操作每一個(gè)指令必須輸密碼才能完成叫密鑰寄存器。首先FPEC共有三個(gè)鍵值也就是三把開鎖的鑰匙R(shí)DPRT鍵是解除讀保護(hù)的密鑰值是0XA5實(shí)際上是隨便定義的只要你定義的不是很簡(jiǎn)單就行。解鎖第一個(gè)是復(fù)位后FPEC被保護(hù)不能寫入FLASH_CR也就是復(fù)位后flash默認(rèn)是鎖著的。在FLASH_KEYR鍵寄存器中先寫入KEY1再寫入KEY2解鎖這個(gè)鎖是KEYR寄存器所以這個(gè)有兩道鎖解鎖的話要先用K1鑰匙解再用K2鑰匙解最終才能解鎖成功所以非人為情況下基本不可能解鎖。然后就是錯(cuò)誤的操作序列會(huì)在下次復(fù)位前鎖死FPEC和FLASH_CR于是他發(fā)現(xiàn)有程序在嘗試撬鎖時(shí)一旦沒有先寫入KEY1再寫入KEY2整個(gè)模塊就會(huì)完全鎖死除非復(fù)位這是整個(gè)解鎖操作。加鎖我們操作完成之后要盡快把flash重新加鎖以防止意外情況加鎖的操作是設(shè)置FLASH_CR中的LOCK位鎖住FPEC和FLASH_CR就是控制寄存器里面有個(gè)LOCK位我們?cè)谶@一位寫1就能重新鎖住閃存。使用指針訪問存儲(chǔ)器uint16_t Data *((__IO uint16_t *)(0x08000000));第一步讀取給定存儲(chǔ)器器的地址比如0x08000000例讀取這個(gè)地址下的數(shù)據(jù)。第二步在地址前面加上強(qiáng)制類型轉(zhuǎn)換轉(zhuǎn)換成uint16_t * 。__IO 在stm32庫函數(shù)中這是宏定義。#define __IO volatile這個(gè)宏定義對(duì)應(yīng)C語言關(guān)鍵字 volatile。在uint16_t *數(shù)據(jù)類型前加volatile起一個(gè)保障作用目的是防止編譯器優(yōu)化。第三步使用*號(hào)指針取內(nèi)容把這個(gè)指針指向的存儲(chǔ)器取出來了這個(gè)值就是指定存儲(chǔ)器的值取出來可以把它賦值給自定義變量Data這樣就完成指定地址讀的任務(wù)。如果你這個(gè)地址寫的是SRAM的地址比如0X20000000那可以直接寫入了因?yàn)镾RAM在程序運(yùn)行時(shí)是可讀可寫的這是使用指針訪問存儲(chǔ)器的C語言代碼。其中讀取可以直接讀寫入需要解鎖并且執(zhí)行后面的流程。程序存儲(chǔ)器全擦除第一步是讀取lock位看一下芯片鎖沒鎖如果lock位1鎖住了就執(zhí)行解鎖過程。解鎖過程就是在KEYR寄存器先寫入KEY1再寫入KEY2如果lock位0當(dāng)前沒鎖住就不用解鎖了。這是流程圖里給的解鎖步驟如果鎖住了就解鎖如果沒鎖住就不用解鎖。但是在庫函數(shù)中并沒有這個(gè)判斷庫函數(shù)是直接執(zhí)行解鎖過程管你鎖沒鎖都執(zhí)行解鎖。第二步然后繼續(xù)解鎖之后首先置控制寄存器里的MER(Mass Erase)位為1然后再置STRT(Start)位為1。其中STRT為1是觸發(fā)條件STRT為1之后芯片開始干活然后現(xiàn)在看到MER位是1它就知道接下來要干的活就是全擦除這樣內(nèi)部電路就會(huì)自動(dòng)執(zhí)行全擦除的過程。第三步然后繼續(xù)擦除擦除過程開始后程序要執(zhí)行等待。判斷狀態(tài)寄存器的BSY(Busy)位是否為1BSY位表示芯片是否處于忙狀態(tài)BSY位為1表示芯片忙所以這里如果判斷BSY位等于1就跳轉(zhuǎn)回來繼續(xù)循環(huán)判斷直到BSY位0跳出循環(huán)。第四步讀出并驗(yàn)證所有頁的數(shù)據(jù)這個(gè)是測(cè)試程序才要做的如要全讀出來驗(yàn)證一下這個(gè)工作量太大了所以這里的最后一步就不管了正常情況下全刪除完成了我們默認(rèn)就成功了這是全擦除的流程。程序存儲(chǔ)器頁擦除第一步是解鎖的流程同上。第二步這個(gè)方框里的置控制寄存器的PER(Page Erase)位為1在FLASH_AR(Address Register)地址寄存器中選擇要擦除的頁最后置控制寄存器的STRT位為1置STRT位為1也是觸發(fā)條件芯片開始干活然后芯片看到PER等于1它就知道接下來要執(zhí)行頁擦除。然后閃存不止一頁頁擦除芯片就要知道要具體擦哪一頁所以它會(huì)繼續(xù)看AR寄存器的數(shù)據(jù)AR寄存器為提前寫入一個(gè)頁的起始地址這樣芯片就會(huì)把我們指定的一頁給擦除掉。第三步然后擦除開始之后我們也要等待BSY位最后讀出并驗(yàn)證數(shù)據(jù)。程序存儲(chǔ)器編程閃存的寫入閃存的寫入擦除之后我們就可以執(zhí)行寫入的流程了。另外說明一下STM32的閃存在寫入之前會(huì)檢查指定地址有沒有擦除如果沒有擦除就寫入STM32則不執(zhí)行寫入操作除非寫入的全是0這個(gè)數(shù)據(jù)是例外因?yàn)椴徊脸蛯懭肟赡軙?huì)寫入錯(cuò)誤。第一步是解鎖。第二步置控制寄存器的PG(Programming)位為1表示我們即將寫入數(shù)據(jù)。第三步在指定的地址寫入半字這一步我們需要*((__IO uint16_t *)(0x08000000)) 0x1234使用指針在指定地址寫入數(shù)據(jù)。注意寫入操作只能以半字的形式寫入在STM32中有幾個(gè)術(shù)語字、半字和字節(jié)其中字word就是32位數(shù)據(jù)半字half word就是16位數(shù)據(jù)字節(jié)byte就是8位數(shù)據(jù)。那這里只能以半字寫入意思就是只能以16位的形式寫入一次性寫入兩個(gè)字節(jié)如果你要寫入32位就分兩次完成。如果你只要寫入8位比較麻煩了如果你想單獨(dú)寫入一個(gè)字節(jié)還要保留另一個(gè)字節(jié)的原始數(shù)據(jù)的話那只能把整頁數(shù)據(jù)都讀到SRAM再隨意修改SRAM數(shù)據(jù)修改全部完成之后再把整頁都擦除最后再把整頁都寫回去。所以如果你想像SRAM一樣隨心所欲的讀寫那最好的辦法就先把閃存的一頁讀到SRAM中讀寫完成后再擦除一頁整體寫回去。第四步寫了半字之后芯片會(huì)處于忙狀態(tài)我們等待一下BUSY清0這樣寫入數(shù)據(jù)的過程就完成。那每執(zhí)行這樣一個(gè)流程只能寫入一個(gè)半字如果要寫出很多數(shù)據(jù)要不斷循環(huán)調(diào)用這個(gè)流程就可以。選項(xiàng)字節(jié)圖里的起始地址就是選項(xiàng)字節(jié)的起始地址1FFF800這塊的這些數(shù)據(jù)就前面這里這個(gè)表的這一行里面總共只有16個(gè)字節(jié)把這些存儲(chǔ)器給展開就這個(gè)圖。這里是對(duì)應(yīng)的16個(gè)字節(jié)其中有一半的名稱前面都帶了個(gè)N比如RDP和nRDPUSER和nUSER等意思是你在寫入RDP數(shù)據(jù)時(shí)要同時(shí)在NRDP寫入數(shù)據(jù)的反碼要在帶n的對(duì)應(yīng)的存儲(chǔ)器寫入反碼這樣寫入操作才是有效的。如果芯片檢測(cè)到這兩個(gè)存儲(chǔ)器不是反碼的關(guān)系那就代表數(shù)據(jù)無效有錯(cuò)誤對(duì)應(yīng)的功能就不執(zhí)行這是一個(gè)安全保障措施。但這個(gè)寫入反碼的過程硬件會(huì)自動(dòng)計(jì)算并寫入。那然后每個(gè)存儲(chǔ)器的功能去掉所有帶n的剩下八個(gè)字節(jié)存儲(chǔ)器第一個(gè)RDP(Read Protect)讀保護(hù)配置位解釋在RDP存儲(chǔ)器寫入RDPRT鍵然后解除讀保護(hù)如果RDP不是A5那閃存就是讀保護(hù)狀態(tài)無法通過調(diào)試器讀取程序避免程序被別人竊取。第二個(gè)字節(jié)USER可以配置硬件看門狗和進(jìn)入停機(jī)待機(jī)模式是否產(chǎn)生復(fù)位。第三個(gè)和第四個(gè)字節(jié)Data0/1這個(gè)在芯片中沒有定義功能用戶可自定義使用。最后四個(gè)字節(jié)WRP(WriteProtect)0、1、2、3這四個(gè)字節(jié)配置的是寫保護(hù)在中容量產(chǎn)品里是每一個(gè)位對(duì)應(yīng)保護(hù)四個(gè)存儲(chǔ)頁四個(gè)字節(jié)總共32位1bit對(duì)應(yīng)保護(hù)4頁總共保護(hù)32×4等于128頁正好對(duì)應(yīng)中容量量的最大128頁。小容量產(chǎn)品手冊(cè)2.5選項(xiàng)字節(jié)說明對(duì)于小容量產(chǎn)品也是每一位對(duì)應(yīng)保護(hù)四個(gè)存儲(chǔ)頁但小容量產(chǎn)品最大只有32K所以只需要一個(gè)字節(jié)WRP0就行4×832其他三個(gè)字節(jié)沒用到。大容量產(chǎn)品然而對(duì)于大容量產(chǎn)品每一個(gè)位只能保護(hù)兩個(gè)存儲(chǔ)頁這樣的話四個(gè)字節(jié)就不夠用了所以這里規(guī)定WRP3的最高位這一位直接把剩下的所有頁一起都保護(hù)了這是寫保護(hù)的定義。選項(xiàng)字節(jié)擦除選項(xiàng)字節(jié)擦除第一步其實(shí)也是解鎖閃存這里文字并沒有寫。第二步這里文字版的流程多了一步檢查FLASH_SR寄存器的BSY位以確認(rèn)沒有其他正在進(jìn)行的閃存操作這個(gè)實(shí)際上就是事前等待如果當(dāng)前已經(jīng)在忙了我先等一下。第三步解鎖FLASH_CR寄存器的OPTWRE(Option Write Enable)位這一步是選項(xiàng)字節(jié)的解鎖選項(xiàng)字節(jié)里面還有一個(gè)單獨(dú)的鎖在解鎖閃存后還需要再解鎖選項(xiàng)字節(jié)的鎖之后才能操作選項(xiàng)字節(jié)。解鎖選項(xiàng)字節(jié)看(前面閃存模塊組織圖)整個(gè)閃存的鎖是KEYR里面選項(xiàng)字節(jié)的小鎖是下面的OPTKEYR(Option Key Register)。解鎖這個(gè)小鎖也是類似的流程我們需要在OPTKEYR里先寫入KEY1再寫入KEY2這樣就能解鎖選項(xiàng)字節(jié)的小鎖了。第四步繼續(xù)解除小鎖之后設(shè)置FLASH_CR寄存器的OPTER(Option Erase)位為1表示即將擦除選項(xiàng)字節(jié)。第五步設(shè)置FLASH_CR寄存器的STRT位為1觸發(fā)芯片開始干活這樣芯片就會(huì)啟動(dòng)擦除選項(xiàng)字節(jié)的工作。第六步之后等待BUSY位變?yōu)?擦除選項(xiàng)字節(jié)就完成了。選項(xiàng)字節(jié)編程先檢測(cè)FLASH_SR寄存器的BSY然后解除小鎖之后設(shè)置FLASH_CR寄存器的OPTPG(Option Programming)位為1表示即將寫入選項(xiàng)字節(jié)再之后寫入要編程的半字到指定的地址這個(gè)是指針寫入操作最后等待忙這樣寫入選項(xiàng)字節(jié)就完成了。器件電子簽名電子簽名其實(shí)就是STM32的ID號(hào)它的存放區(qū)域是系統(tǒng)存儲(chǔ)器不僅有BOOTLOADER程序還有幾個(gè)字節(jié)的ID號(hào)系統(tǒng)存儲(chǔ)器起始地址是1FFFF000。第一個(gè)是閃存容量存儲(chǔ)器基地址是1FFFF7E0通過地址也可以確定它的位置就是系統(tǒng)存儲(chǔ)器這個(gè)存儲(chǔ)器的大小是16位它的值就是閃存的容量單位是KB。第二個(gè)是產(chǎn)品唯一身份標(biāo)識(shí)寄存器就是每個(gè)芯片的身份證號(hào)這個(gè)數(shù)據(jù)存放的基地址是1FFFF7E8大小是96位每一個(gè)芯片的這96位數(shù)據(jù)都是不一樣的。使用這個(gè)唯一ID號(hào)可以做一些加密的操作比如你想寫入一段程序只能在指定設(shè)備運(yùn)行那也可以在程序的多處加入ID號(hào)判斷如果不是指定設(shè)備的ID號(hào)就不執(zhí)行程序功能。這樣即使你的程序被盜在別的設(shè)備上也難以運(yùn)行這是STM32的電子簽名。閃存編程手冊(cè)在編程過程中任何讀寫閃存的操作都會(huì)使CPU暫停直到此閃存編程結(jié)束。這是讀寫內(nèi)部閃存存儲(chǔ)數(shù)據(jù)的一個(gè)弊端忙的時(shí)候代碼執(zhí)行會(huì)暫停因?yàn)閳?zhí)行代碼需要讀閃存閃存在忙沒法讀所以CPU也就沒法運(yùn)行了程序就會(huì)暫停。這會(huì)導(dǎo)致假如你使用內(nèi)部閃存存儲(chǔ)數(shù)據(jù)同時(shí)你的中斷代碼又在頻繁執(zhí)行這樣讀寫閃存的時(shí)候中斷代碼就無法執(zhí)行了這可能會(huì)導(dǎo)致中斷無法及時(shí)響應(yīng)。選項(xiàng)字節(jié)編程閃存控制寄存器FLASH_CR原文鏈接https://blog.csdn.net/m0_62005595/article/details/139897394