STM系列 Flash操作
STM系列的Flash操作方法及誤操作筆記
1、flash操作方法
1.1 對stm8的flash操作
#define BaseCfgaddr (uint32*)0x4000
/*
*函式名稱 : WriteAddrToFlash
*函式功能 : 資料寫入Flash
*輸入引數 :
*輸出引數 : 無
*返 回 值 : 0或1
*其它說明 : 無
* */
uint8 SYS_WriteDataToFlash(uint8* Data,uint32* addr,uint16Len)
{
if(!Data)
return 0;
/*操作EEPROM,先進行解鎖*/
FLASH_DUKR = 0xAE; /* 注意這裡不能斷點除錯,否則會造成內部不同步,FLASH解鎖失敗*/
FLASH_DUKR = 0x56;
FLASH_CR2 = 0x00;
FLASH_NCR2 = 0xFF;
if(!(FLASH_IAPSR & 0x08)) /* 檢測對應的位是否解鎖 */
return 0;
memcpy(addr,Data,Len);
FLASH_IAPSR = (ST_UINT8)(~0x08); /* 重新上鎖 */
return 1;
}
按位元組操作即每次寫入一位
SYS_WriteDataToFlash(Buf,BaseCfgaddr,WriteCfgSize);
1.2 stm32f的flash操作。
使用stm32f0k4
/*
*函式名稱 : WriteAddrToFlash
*函式功能 : 資料寫入Flash
*輸入引數 :
*輸出引數 : 無
*返 回 值 : 0或1
*其它說明 :
寫入一個配置,每次寫只用某一頁開始寫,不可一頁中從起始地址寫之後又從中間地址寫入。
寫第二個模組配置時,空間配置從下一頁開始寫。否則會擦除之前的頁,寫之前會先擦除該頁。
* */
uint8 Sys_WriteDataToFlash(uint8* pData,uint32 Address,uint32 Len)
{
if(!pData)
return 0;
uint32_t SLen = 0;
uint32_t Wdata;
FLASH_Unlock();
FLASH_ErasePage(Address); // 寫之前擦除要寫入的整頁
FLASH_ClearFlag(FLASH_FLAG_BSY|FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPERR);
/* Clear pending flags (if any) */
do
{
memcpy(&Wdata, pData, 4);//
FLASH_ProgramWord(Address,Wdata);
Address += 4;
pData += 4;
SLen += 4;
}while(SLen<Len);
FLASH_Lock();
return 1;
}
/*
*函式名稱 : Sys_ReadFlashToBuf
*函式功能 : 從flash中讀出資料到buf檢視
*輸入引數 :
*輸出引數 : 無
*返 回 值 :
*其它說明 :
* */
void Sys_ReadFlashToBuf(uint8* buf,uint32 addr,uint16 len)
{
while(len--)
{
*buf = *(uint8*)addr;
addr++;
buf++;
}
}
32位機一個字為4個位元組,庫函式提供兩種形式,一種是半字寫入另一種是一個字寫入,以上使用的是一個字寫入的方式
實際操作過程中是將一個結構體內容寫入flash,先將該結構體以位元組對其,將該結構體指向buf儲存,再將該buf按位元組寫入flash。
讀取時將內容讀到buf,在將該buf轉為結構體,即可準確提取出結構體中定義的值
2、操作中所犯錯誤及注意點
1、記憶體地址
錯誤:從0x08000000基地址開始操作flash,一執行讀寫flash,程式就卡死。
要操作flash的地址,如果程式是儲存在main flash memory中開始執行,進入使用者程式,我們操作flash就不能從flash基地址開始操作,這部分已經被應用程式佔用。
其操作flash地址值要大於本程式碼所佔用FLASH的大小+0x08000000
使用者程式程式碼佔用flash大小可從編譯時候看出來
8 394 bytes of readonly code memory
84 bytes of readonly data memory
2 828 bytes of readwrite data memory
#define FLASH_BASE_ADDR (uint32_t)(0x08000000u)
#define FLASH_STARTADDR_PAGE11 (FLASH_BASE_ADDR + 1024 * 11)
計算可寫在第幾頁,最好的方法是從最後一頁開始寫,根據手冊瞭解flash大小,頁數,勿超出flash大小,否則易出現HardFault_Handler錯誤
2、將字轉換為按位元組寫入
錯誤: 寫入的資料與實際的資料值不同
從cotex-m0的庫函式來看,對flash的操作時以字或者半字來操作。實際使用操作多按1個位元組操作,造成不對齊。
uint32_t Wdata;
memcpy(&Wdata, pData, 4);//
FLASH_ProgramWord(Address,Wdata);
Address += 4;
pData += 4;
固按以上操作,將4個位元組轉換為按字寫入,統一了使用的所有工程,再將地址按四個位元組偏移。
3、巨集定義地址
錯誤:flash讀取時資料出錯,內容指向錯誤。
SYS_BASE_CFG_T *pCfg = (SYS_BASE_CFG_T *) FLASH_STARTADDR_PAGE11;
操作是將flash頁11的地址讀取出來,指向轉為結構體SYS_BASE_CFG_T 形式讀取資料
但在讀取就出錯,程式執行到該句即出現記憶體操作錯誤中斷產生
void HardFault_Handler(void)
{
while (1)
{
}
}
從執行引數上看,當執行到該句,地址指向並不是預先設計好的11頁地址
#define FLASH_STARTADDR_PAGE11 FLASH_BASE_ADDR + 1024 * 11
由於巨集定義加操作沒有加括號,導致實際偏移值出現錯誤。巨集定義式子為防錯誤一定要加括號
#define FLASH_STARTADDR_PAGE11 (FLASH_BASE_ADDR + 1024 * 11)
4、HardFault_Handler
該中斷產生的一般條件是記憶體非法操作,包括陣列越界、記憶體溢位、指標地址錯誤、野指標存在
在stm8中當記憶體非法操作時mcu內部會產生復位,從復位源可檢視為非法操作符復位
5、頁大小
flash操作擦除時可以以頁為單位擦除或者以整片flash擦除。
在使用flash時,如果需要寫入兩組不同的資料並且在一頁以內,建議直接使用一頁一組資料,由於記憶體夠大而且防止誤操作到其他組資料。
M3或者M0核心大容量與小容量的flash的頁大小有所區別,小容量以1024位元組 1k為一頁,大容量以2048位元組2k 為一頁。所以操作具體記憶體時以
(FLASH_BASE_ADDR + 1024 * 11)形式,更直觀看出資料位置,便於IDE除錯檢視。
對結構體的flash寫入寫出
uint16 ReadToFlash(uint8 *pCfg)
{
CfgInfo.pBaseCfg = (BASE_CFG_T *)pCfg;
return sizeof(BASE_CFG_T);
}
void ReadAllCfg(void)
{
uint8 *pRead = (uint8*)FLASH_STARTADDR_PAGE11; // 轉化為unsigned char 按位元組操作 32位一次偏移4位元組
uint32 CfgLen = 0;
CfgLen = ReadToFlash(pRead );
pRead += CfgLen;
CfgInfo.SysReadCfgSize += CfgLen;
}
uint16 WriteToFlash(uint8 *pCfg)
{
CfgInfo.pBaseCfg = (BASE_CFG_T *)pCfg;
CfgInfo.pBaseCfg->WriteFlashFlag = CFG_FLASH_FLAG;
CfgInfo.pBaseCfg->FeedWatDogTime = 200;
return sizeof(BASE_CFG_T);
}
static void WriteAllCfg(void)
{
uint8 *pWrite = CfgInfo.CfgBuf;
uint32 CfgLen = 0;
CfgLen = WriteToFlash(pWrite );
pWrite += CfgLen;
CfgInfo.SysWriteCfgSize += CfgLen;
Sys_WriteDataToFlash(CfgInfo.CfgBuf,FLASH_STARTADDR_PAGE11,CfgInfo.SysWriteCfgSize);
Sys_ReadAllCfg();
}
void Sys_CfgInit(void)
{
BASE_CFG_T *pCfg = (BASE_CFG_T *) FLASH_STARTADDR_PAGE11;
/*在要寫入的一個buf的第一位作為寫入標誌位,程式下次執行到這兒就不再重寫,而只是讀取這個flash的內容,實現配置的形式操作。*/
if(pCfg->WriteFlashFlag != CFG_FLASH_FLAG)
{
/* 寫配置到FLASH */
WriteAllCfg();
}
else
{
ReadAllCfg();
memcpy(CfgInfo.CfgBuf, (char *)FLASH_STARTADDR_PAGE11, CfgInfo.SysReadCfgSize);
}
}
typedef __packed struct
{
uint8 WriteFlashFlag; /* FLASH寫標誌 */
uint8 DebugFalg; /* 除錯使用標誌 */
uint32 FeedWatDogTime;
}BASE_CFG_T; /* 系統基本配置 */
typedef __packed struct
{
BASE_CFG_T *pBaseCfg; /* 系統基本配置引數 */
uint8 SysWriteCfgSize;
uint8 SysReadCfgSize;
uint8 CfgBuf[SYS_CFG_SIZE];/* 所有人的配置檔案 */
}CFG_INFO_T;/* 配置配置引數的 */
CFG_INFO_T CfgInfo;
#define CFG_FLASH_FLAG(0x57)
以上內容為個人使用過程的方法筆記及個人認為的注意點、知識點.
內容不全面,如之後使用有所補充隨時更新。
如您發現有所問題,希望給我意見。