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