STM32操作訪問flash,包括寫入資料到flash和從flash讀取資料
阿新 • • 發佈:2019-01-07
STM32中儲存區分為:隨機存取儲存器RAM和只讀儲存器ROM。
其中:
- RAM為常說的記憶體,比如手機的2G記憶體4G記憶體等,就是程式跑起來的時候所佔用的儲存空間,特點是掉電資料丟失。
- ROM為常說的硬碟,比如手機的64G和128G等,可以簡單的理解為硬碟的儲存空間,特點是掉電資料不丟失,所以又叫“非易失性儲存器件”。
- ROM又包含:EEPROM和flash。
畫個嵌入式產品儲存器件的思維導圖如下(如有什麼地方不對,懇請大神們進行指正):
作為ROM的一份子,flash的特點自然是掉電資料不丟失。但是,flash在STM32中比較重要,程式也是儲存在這個地方,所以輕易不讓使用者進行隨意的讀寫,以避免不必要的問題。
而這篇部落格就先簡單記錄一下flash的訪問流程和方法(讀和寫),具體原理以後理解深刻了再做補充。
1、STM32 FLASH操作流程
Flash操作已經屬於嵌入式裝置中很底層的操作了,直接對地址進行存取,簡單描述,Flash操作大致需要以下流程:
- 1、確定要寫入Flash的首地址(稍後介紹確定地址的方法)
- 2、解鎖Flash
- 3、對Flash進行操作(寫入資料)
- 4、對Flash重新上鎖
1.1 如何查詢並選定要寫入Flash十六進位制地址值的方法
要想選定安全的Flash地址進行讀寫,可以根據自己的STM32 MCU型號,查詢資料手冊,確定FLASH的地址區段,因為起始段會儲存程式碼,所以一定要避開起始段,以避免資料錯誤。(我一般是根據Flash大小計算Flash的最末尾地址,往前推一段地址空間,在這裡一般不會對程式碼中的資料產生覆蓋等影響)
我此次操作Flash使用的MCU是STM32103C8T6,所以以該型號MCU為例進行描述:
- 在資料手冊中,可以看到STM32103C8T6的flash起始地址是0x0800 0000(如下圖所示),而STM32103C8T6的Flash大小為64K,可以計算出STM32103C8T6的Flash地址範圍是:0x0800 0000——0x0800 FFFF(計算方法參考另一篇部落格:STM32記憶體大小與地址的對應關係以及計算方法)。這裡選取0x0800 F000作為讀寫操作的起始地址,對於C8T6這款MCU,操作這個起始地址應該算是很安全的範圍了。
2、Flash基本知識點
2.1 Flash容量
Flash根據容量大小可以分為以下三種:
- 1、小容量產品:Flash大小為1-32KB(STM32F10X_LD)
- 2、中容量產品:Flash大小為64-128KB(STM32F10X_MD)
- 3、大容量產品:Flash大小為256KB以上(STM32F10X_HD)
2.2 ST庫對Flash操作的支援
ST庫中對Flash操作主要提供了以下幾類操作API函式:
- 1、Flash解鎖、鎖定函式
- void FLASH_Unlock(void);//解鎖函式:在對Flash操作之前必須解鎖
- void FLASH_Lock(void);//鎖定函式:同理,操作完Flash之後必須重新上鎖
- 2、Flash寫操作函式
- FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data);//32位字寫入函式
- FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);//16位半字寫入函式
- FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data);//使用者選擇位元組寫入函式
注:這裡需要說明,32 位位元組寫入實際上是寫入的兩次 16 位資料,寫完第一次後地址+2,這與我們前面講解的 STM32 快閃記憶體的程式設計每次必須寫入 16 位並不矛盾。寫入 8位實際也是佔用的兩個地址了,跟寫入 16 位基本上沒啥區別。
- 3、Flash擦除函式
- FLASH_Status FLASH_ErasePage(uint32_t Page_Address);
- FLASH_Status FLASH_EraseAllPages(void);
- FLASH_Status FLASH_EraseOptionBytes(void);
- 4、獲取Flash狀態
- FLASH_Status FLASH_GetStatus(void);
獲取Flash狀態函式,主要是為了獲取Flash的狀態,以便於根據狀態對Flash進行操作。該函式返回值是通過列舉型別定義的,在程式碼中可以看到FLASH_Status型別定義如下(具體含義看註釋即可): - typedef enum
{
FLASH_BUSY = 1, //忙
FLASH_ERROR_PG, //程式設計錯誤
FLASH_ERROR_WRP, //防寫錯誤
FLASH_COMPLETE, //操作完成
FLASH_TIMEOUT //操作超時
}FLASH_Status;
- FLASH_Status FLASH_GetStatus(void);
- 5、等待操作完成函式
- FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout);
注:在執行快閃記憶體寫操作時,任何對快閃記憶體的讀操作都會鎖住匯流排,在寫操作完成後讀操作才能正確地進行;既在進行寫或擦除操作時,不能進行程式碼或資料的讀取操作。所以在每次操作之前,我們都要等待上一次操作完成這次操作才能開始。
- FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout);
3、OK,上乾貨,上程式碼
根據ST庫提供的上述函式,我們可以自己編寫Flash的讀寫操作程式碼如下:
3.1 先定義一個Flash操作的起始地址巨集定義和Flash狀態指示標誌位
#define STARTADDR 0x0800F000 //STM32F103C8T6適用
volatile FLASH_Status FLASHStatus = FLASH_BUSY; //Flash操作狀態變數
3.2 編寫各個讀寫函式
//////////////////////////////////////////////////////////////////////////////////
// Name: WriteFlashOneWord
//
// Function: 向內部Flash寫入32位資料
//
// Input: WriteAddress:資料要寫入的目標地址(偏移地址)
// WriteData: 寫入的資料
//////////////////////////////////////////////////////////////////////////////////
void WriteFlashOneWord(uint32_t WriteAddress, uint32_t WriteData)
{
FLASH_UnlockBank1();
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
FLASHStatus = 1; //清空狀態指示標誌位
FLASHStatus = FLASH_ErasePage(STARTADDR);
if(FLASHStatus == FLASH_COMPLETE)
{
FLASHStatus = 1; //清空狀態指示標誌位
FLASHStatus = FLASH_ProgramWord(STARTADDR+WriteAddress, WriteData); //flash.c 中API函式
}
FLASHStatus = 1; //清空狀態指示標誌位
FLASH_LockBank1();
}
//////////////////////////////////////////////////////////////////////////////////
// Name: WriteFlashData
//
// Function: 向內部Flash寫入資料
//
// Input: WriteAddress:資料要寫入的目標地址(偏移地址)
// data[]: 寫入的資料首地址
// num: 寫入資料的個數
//////////////////////////////////////////////////////////////////////////////////
void WriteFlashData(uint32_t WriteAddress, uint8_t data[], uint32_t num)
{
uint32_t i = 0;
uint16_t temp = 0;
FLASH_UnlockBank1(); //解鎖flash
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
FLASHStatus = 1; //清空狀態指示標誌位
FLASHStatus = FLASH_ErasePage(STARTADDR);//擦除整頁
if(FLASHStatus == FLASH_COMPLETE)//flash操作完成
{
FLASHStatus = 1; //清空狀態指示標誌位
for(i=0; i<num; i++)
{
temp = (uint16_t)data[i];
FLASHStatus = FLASH_ProgramHalfWord(STARTADDR+WriteAddress+i*2, temp);//寫入資料
}
}
FLASHStatus = 1; //清空狀態指示標誌位
FLASH_LockBank1(); //鎖定flash
}
//////////////////////////////////////////////////////////////////////////////////
// Name: ReadFlashNBtye
//
// Function: 從內部Flash讀取N位元組資料
//
// Input: ReadAddress:資料地址(偏移地址)
// ReadBuf:讀取到的資料存放位置指標
// ReadNum:讀取位元組個數
//
// Output: 讀取的位元組數
//////////////////////////////////////////////////////////////////////////////////
int ReadFlashNBtye(uint32_t ReadAddress, uint8_t *ReadBuf, int32_t ReadNum)
{
int DataNum = 0;
ReadAddress = (uint32_t)STARTADDR + ReadAddress;
while(DataNum < ReadNum)
{
*(ReadBuf + DataNum) = *(__IO uint8_t*) ReadAddress++;
DataNum++;
}
return DataNum;
}
//////////////////////////////////////////////////////////////////////////////////
// Name: ReadFlashData
//
// Function: 從內部Flash讀取num位元組資料
//
// Input: ReadAddress:資料地址(偏移地址)
// dest_Data: 讀取到的資料存放位置指標
// num: 讀取位元組個數
//////////////////////////////////////////////////////////////////////////////////
void ReadFlashData(uint32_t ReadAddress, uint8_t *dest_Data, uint32_t num)
{
int i = 0;
ReadAddress = (uint32_t)STARTADDR + ReadAddress;
while(i < num)
{
*(dest_Data+i) = *(__IO uint16_t*) ReadAddress;
ReadAddress += 2;
i++;
}
}