本文主要介紹STM32多種的內部Flash讀寫方式和讀寫長文件的功能函數怎樣編寫。閱讀完本文可以使你能夠正常的完成Flash讀寫操作。
介紹
STM32 FLASH
不同型號的 STM32,其 FLASH 容量也有所不同,最小的只有 16K 字節,最大的則達到了1024K 字節。本次實驗選用的STM32 開發板是F103ZET6,其 FLASH 容量為 512K 字節,屬于大容量產品(另外還有中容量和小容量產品),大容量產品的閃存模塊組織如圖 所示:
STM32 的閃存模塊由:主存儲器、信息塊和閃存存儲器接口寄存器等 3 部分組成。
主存儲器,該部分用來存放代碼和數據常數(如 const 類型的數據)。對于大容量產品,其被劃分為 256 頁,每頁 2K 字節。注意,小容量和中容量產品則每頁只有 1K 字節。從上圖可以看出主存儲器的起始地址就是0X08000000, B0、B1 都接 GND 的時候,就是從 0X08000000開始運行代碼的。
信息塊,該部分分為 2 個小部分,其中啟動程序代碼,是用來存儲 ST 自帶的啟動程序,用于串口下載代碼,當 B0 接 V3.3,B1 接 GND 的時候,運行的就是這部分代碼。用戶選擇字節,則一般用于配置寫保護、讀保護等功能。
閃存存儲器接口寄存器,該部分用于控制閃存讀寫等,是整個閃存模塊的控制機構。對主存儲器和信息塊的寫入由內嵌的閃存編程/擦除控制器(FPEC)管理;編程與擦除的高電壓由內部產生。
在執行閃存寫操作時,任何對閃存的讀操作都會鎖住總線,在寫操作完成后讀操作才能正確地進行;既在進行寫或擦除操作時,不能進行代碼或數據的讀取操作。
閃存的編程和擦除
STM32 的閃存編程是由 FPEC(閃存編程和擦除控制器)模塊處理的,這個模塊包含 7 個
32 位寄存器,他們分別是:
FPEC 鍵寄存器(FLASH_KEYR)
選擇字節鍵寄存器(FLASH_OPTKEYR)
閃存控制寄存器(FLASH_CR)
閃存狀態寄存器(FLASH_SR)
閃存地址寄存器(FLASH_AR)
選擇字節寄存器(FLASH_OBR)
寫保護寄存器(FLASH_WRPR)
STM32 復位后,FPEC 模塊是被保護的,不能寫入 FLASH_CR 寄存器;通過寫入特定的序列到 FLASH_KEYR 寄存器可以打開 FPEC 模塊,只有在寫保護被解除后,我們才能操作相關寄存器。
STM32 閃存的編程每次必須寫入 16 位(不能單純的寫入 8 位數據哦!),當 FLASH_CR 寄存器的 PG 位為’1’時,在一個閃存地址寫入一個半字將啟動一次編程;寫入任何非半字的數據,FPEC 都會產生總線錯誤。在編程過程中(BSY 位為’1’),任何讀寫閃存的操作都會使 CPU暫停,直到此次閃存編程結束。
同樣,STM32 的 FLASH 在編程的時候,也必須要求其寫入地址的 FLASH 是被擦除了的(也就是其值必須是 0XFFFF),否則無法寫入,在FLASH_SR 寄存器的 PGERR 位將得到一個警告。
STM23 的 FLASH 寫入過程如圖所示。
STM32的Flash寫入順序如下:
檢查 FLASH_CR 的 LOCK 是否解鎖,如果沒有則先解鎖
檢查 FLASH_SR 寄存器的 BSY 位,以確認沒有其他正在進行的編程操作
設置 FLASH_CR 寄存器的 PG 位為’1’
在指定的地址寫入要編程的半字
等待 BSY 位變為’0’ - 讀出寫入的地址并驗證數據
Flash讀寫的標準庫函數
解鎖函數:void FLASH_Unlock(void);
對 FLASH 進行寫操作前必須先解鎖,解鎖操作也就是必須在 FLASH_KEYR 寄存器寫入特定的序列,固件庫函數實現很簡單:只需要直接調用 FLASH_Unlock();即可。
鎖定函數:void FLASH_Lock(void);
有解鎖當然就有上鎖,為了保護Flash,讀寫和擦除全部需要的Flash后需要上鎖,只需要調用:FLASH_Lock();
寫操作函數:
固件庫提供了三個 FLASH 寫函數:
FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data);FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data);
顧名思義分別為:FLASH_ProgramWord 為 32 位字寫入函數,其他分別為 16 位半字寫入和用戶選擇字節寫入函數。這里需要說明,32 位字節寫入實際上是寫入的兩次 16 位數據,寫完第一次后地址+2,這與我們前面講解的 STM32 閃存的編程每次必須寫入 16 位并不矛盾。寫入 8位實際也是占用的兩個地址了,跟寫入 16 位基本上沒啥區別。
4. 獲取 FLASH 狀態
主要是用的函數是:FLASH_Status FLASH_GetStatus(void);
返回值是通過枚舉類型定義的,分別為:
FLASH_BUSY = 1,//忙
FLASH_ERROR_PG,//編程錯誤
FLASH_ERROR_WRP,//寫保護錯誤
FLASH_COMPLETE,//操作完成
FLASH_TIMEOUT//操作超時
5. 等待操作完成函數
在執行閃存寫操作時,任何對閃存的讀操作都會鎖住總線,在寫操作完成后讀操作才能正確地進行;既在進行寫或擦除操作時,不能進行代碼或數據的讀取操作。
所以在每次操作之前,我們都要等待上一次操作完成這次操作才能開始。使用的函數是:FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout)入口參數為等待時間,返回值是 FLASH 的狀態,這個很容易理解,這個函數本身我們在固件庫中使用得不多,但是在固件庫函數體中間可以多次看到。
6. 讀 FLASH 特定地址數據函數
有寫就必定有讀,而讀取 FLASH 指定地址的半字的函數固件庫并沒有給出來,這里我們自己寫的一個函數。
u16 STMFLASH_ReadHalfWord(u32 faddr){return *(vu16*)faddr; }
軟件設計
FLASH的讀取
直接讀取某一地址的內容
因為讀取FLASH并不需要解鎖,我們可以直接用指針指向所讀的地址,之后讀取此地址的內容即可。
p = (uint32_t *)(0x08008000);printf("\r\n讀取內部FLASH該地址存儲的內容為:0x%x",*p);
此程序就是先將0x08008000賦給指針變量P,之后將P指向地址的內容以16進制的格式輸出出來。
讀取選定位置的選定大小的內容
首先我們編寫一個函數,用以讀取指定地址的半字(16位數據)。
u16 STMFLASH_ReadHalfWord(u32 faddr){ return *(vu16*)faddr; }
從指定地址開始讀出指定長度的數據
LReadAddr:起始地址
pBuffer:數據指針
NumToWrite:半字(16位)數
void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead) { u16 i; for(i=0;i當我們想讀取FLASH內容時,只需要直接調用上面的函數即可。
STMFLASH_Read(FLASH_ADDR,Temporary_storage,size);這里FLASH_ADDR是我們要讀取的起始地址,Temporary_storage是
16位的指針變量,存放我們讀取到的內容, size是我們要讀取的大小,值得注意的是,size是半字大小,也就是有多少個兩個字節。比如我們要讀取100個字節,size就可以填50。FLASH的寫入
直接使用標準庫寫入
首先需要先解鎖
FLASH_Unlock();寫入前需要擦除當前頁,對擦除有不理解的可以看我的另一篇文章:基于STM32的Flash擦除方式
FLASH_ErasePage(0x08000000+2*1024*5);之后可以調用固件庫函數,進行寫入。例如向地址 0x08000000+210245 至 0x08000000+210246 地址寫入數據
FLASH_ProgramWord(0x08000000+2*1024*5,0x01234567);寫入之后,不要忘了上鎖。
FLASH_Lock();寫入選定位置的選定大小的內容
我們首先編寫一個不檢查的寫入的函數。
WriteAddr:起始地址,pBuffer:數據指針,NumToWrite:半字(16位)數 。void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite) { u16 i; for(i=0;i之后編寫函數,實現從指定地址寫入指定大小的指定內容。
void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite) { u32 secpos; //扇區地址 u16 secoff; //扇區內偏移地址(16位字計算) u16 secremain; //扇區內剩余地址(16位字計算) u16 i; u32 offaddr; //去掉0X08000000后的地址 if(WriteAddr=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return;//非法地址 FLASH_Unlock(); //解鎖 offaddr=WriteAddr-STM32_FLASH_BASE; //實際偏移地址. secpos=offaddr/STM_SECTOR_SIZE; //扇區地址 0~127 for STM32F103RBT6 secoff=(offaddr%STM_SECTOR_SIZE)/2; //在扇區內的偏移(2個字節為基本單位.) secremain=STM_SECTOR_SIZE/2-secoff; //扇區剩余空間大小 if(NumToWrite<=secremain)secremain=NumToWrite;//不大于該扇區范圍 while(1) { STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//讀出整個扇區的內容 for(i=0;i (STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;//下一個扇區還是寫不完 else secremain=NumToWrite;//下一個扇區可以寫完了 } }; FLASH_Lock();//上鎖} 使用時,我們只需要當我們功能需要寫入Flash時,調用此函數即可。
STMFLASH_Write(FLASH_ADDR,flg_false,size);//將標志位置為更改為0x00此語句實現從FLASH_ADDR地址寫入size大小的Temporary_storage數據。
-
寄存器
+關注
關注
31文章
5357瀏覽量
120623 -
FlaSh
+關注
關注
10文章
1638瀏覽量
148180 -
STM32
+關注
關注
2270文章
10906瀏覽量
356531
發布評論請先 登錄
相關推薦
評論