一坯临、簡(jiǎn)介
在STM32芯片內(nèi)部有一個(gè) FLASH 存儲(chǔ)器,它主要用于存儲(chǔ)代碼,我們?cè)陔娔X上編寫(xiě)好應(yīng)用程序后单寂,使用下載器把編譯后的代碼文件燒錄到該內(nèi)部 FLASH 中,由于 FLASH 存儲(chǔ)器的內(nèi)容在掉電后不會(huì)丟失吐辙,芯片重新上電復(fù)位后宣决,內(nèi)核可從內(nèi)部 FLASH 中加載代碼并運(yùn)行。
STM32 的內(nèi)部 FLASH 包含主存儲(chǔ)器昏苏、系統(tǒng)存儲(chǔ)器以及選項(xiàng)字節(jié)區(qū)域尊沸,它們的地址分布及大小見(jiàn)下表
- 主存儲(chǔ)器
一般我們說(shuō) STM32 內(nèi)部 FLASH 的時(shí)候,都是指這個(gè)主存儲(chǔ)器區(qū)域贤惯,它是存儲(chǔ)用戶應(yīng)用程序的空間洼专,芯片型號(hào)說(shuō)明中的 256K FLASH、512K FLASH 都是指這個(gè)區(qū)域的大小孵构。
主存儲(chǔ)器分為 256 頁(yè)屁商,每頁(yè)大小為 2KB,共 512KB浦译。這個(gè)分頁(yè)的概念棒假,實(shí)質(zhì)就是 FLASH 存儲(chǔ)器的扇區(qū),與其它 FLASH 一樣精盅,在寫(xiě)入數(shù)據(jù)前帽哑,要先按頁(yè)(扇區(qū))擦除。
注意上表中的主存儲(chǔ)器是本實(shí)驗(yàn)板使用的 STM32VET6 型號(hào)芯片的參數(shù)叹俏,即 STM32F1 大容量產(chǎn)品妻枕。若使用超大容量、中容量或小容量產(chǎn)品粘驰,它們主存儲(chǔ)器的頁(yè)數(shù)量屡谐、頁(yè)大小均有不同,使用的時(shí)候要注意區(qū)分蝌数。
主存儲(chǔ)器是以頁(yè)為單位劃分的愕掏。stm32根據(jù)FLASH主存儲(chǔ)塊容量、頁(yè)面的不同顶伞,系統(tǒng)存儲(chǔ)器的不同饵撑,分為小容量剑梳、中容量、大容量滑潘、互聯(lián)型垢乙,共四類產(chǎn)品。
- 小容量產(chǎn)品:主存儲(chǔ)塊1-32KB语卤, 每頁(yè)1KB追逮。系統(tǒng)存儲(chǔ)器2KB
- 中容量產(chǎn)品:主存儲(chǔ)塊64-128KB, 每頁(yè)1KB粹舵。系統(tǒng)存儲(chǔ)器2KB
- 大容量產(chǎn)品:主存儲(chǔ)塊256KB以上钮孵, 每頁(yè)2KB。系統(tǒng)存儲(chǔ)器2KB
- 互聯(lián)型產(chǎn)品:主存儲(chǔ)塊256KB以上齐婴, 每頁(yè)2KB油猫。系統(tǒng)存儲(chǔ)器18KB
- 系統(tǒng)存儲(chǔ)區(qū)
系統(tǒng)存儲(chǔ)區(qū)是用戶不能訪問(wèn)的區(qū)域,它在芯片出廠時(shí)已經(jīng)固化了啟動(dòng)代碼柠偶,它負(fù)責(zé)實(shí)現(xiàn)串口、USB 以及 CAN 等 ISP 燒錄功能睬关。
- 選項(xiàng)字節(jié)
選項(xiàng)字節(jié)用于配置 FLASH 的讀寫(xiě)保護(hù)诱担、待機(jī)/停機(jī)復(fù)位、軟件/硬件看門狗等功能电爹,這部分共 16 字節(jié)蔫仙。可以通過(guò)修改 FLASH 的選項(xiàng)控制寄存器修改丐箩。
二摇邦、查看工程的空間分布
由于內(nèi)部 FLASH 本身存儲(chǔ)有程序數(shù)據(jù),若不是有意刪除某段程序代碼屎勘,一般不應(yīng)修改程序空間的內(nèi)容施籍,所以在使用內(nèi)部 FLASH 存儲(chǔ)其它數(shù)據(jù)前需要了解哪一些空間已經(jīng)寫(xiě)入了程序代碼,存儲(chǔ)了程序代碼的扇區(qū)都不應(yīng)作任何修改概漱。通過(guò)查詢應(yīng)用程序編譯時(shí)產(chǎn)生
的“*.map”后綴文件丑慎,可以了解程序存儲(chǔ)到了哪些區(qū)域。
打開(kāi) map 文件后瓤摧,查看文件最后部分的區(qū)域竿裂,可以看到一段以 “Memory Map of the image” 開(kāi)頭的記錄(若找不到可用查找功能定位)
觀察表中的最后一項(xiàng),它的基地址是 0x0800175c照弥,大小為 0x00000020腻异,可知它占用的
最高的地址空間為 0x0800177c,跟執(zhí)行區(qū)域的最高地址 0x0000177c 一樣这揣,但它們比加載
區(qū)域說(shuō)明中的最高地址 0x80017a8 要小悔常,所以我們以加載區(qū)域的大小為準(zhǔn)影斑。對(duì)比表 45-1 的
內(nèi)部 FLASH 頁(yè)地址分布表,可知僅使用頁(yè) 0 至頁(yè) 2 就可以完全存儲(chǔ)本應(yīng)用程序这嚣,所以從頁(yè)
3(地址 0x08001800)后的存儲(chǔ)空間都可以作其它用途鸥昏,使用這些存儲(chǔ)空間時(shí)不會(huì)篡改應(yīng)用程
序空間的數(shù)據(jù)。
三姐帚、寫(xiě)入Flash
3.1 寫(xiě)入過(guò)程
3.1.1 解鎖
由于內(nèi)部 FLASH 空間主要存儲(chǔ)的是應(yīng)用程序吏垮,是非常關(guān)鍵的數(shù)據(jù),為了防止誤操作修改了這些內(nèi)容罐旗,芯片復(fù)位后默認(rèn)會(huì)給控制寄存器 FLASH_CR 上鎖膳汪,這個(gè)時(shí)候不允許設(shè)置 FLASH 的控制寄存器,從而不能修改 FLASH 中的內(nèi)容九秀。
所以對(duì) FLASH 寫(xiě)入數(shù)據(jù)前遗嗽,需要先給它解鎖。解鎖的操作步驟如下:
- 往 FPEC 鍵寄存器 FLASH_KEYR 中寫(xiě)入 KEY1 = 0x45670123
- 再往 FPEC 鍵寄存器 FLASH_KEYR 中寫(xiě)入 KEY2 = 0xCDEF89AB
3.1.2 頁(yè)擦除
在寫(xiě)入新的數(shù)據(jù)前鼓蜒,需要先擦除存儲(chǔ)區(qū)域痹换,STM32 提供了頁(yè)(扇區(qū))擦除指令和整個(gè) FLASH 擦除(批量擦除)的指令,批量擦除指令僅針對(duì)主存儲(chǔ)區(qū)都弹。
頁(yè)擦除的過(guò)程如下:
- 檢查 FLASH_SR 寄存器中的“忙碌寄存器位 BSY”娇豫,以確認(rèn)當(dāng)前未執(zhí)行任何 Flash 操作;
- 在 FLASH_CR 寄存器中畅厢,將“激活頁(yè)擦除寄存器位 PER ”置 1冯痢;
- 用 FLASH_AR 寄存器選擇要擦除的頁(yè);
- 將 FLASH_CR 寄存器中的“開(kāi)始擦除寄存器位 STRT ”置 1框杜,開(kāi)始擦除浦楣;
- 等待 BSY 位被清零時(shí),表示擦除完成咪辱。
3.1.3 寫(xiě)入數(shù)據(jù)
擦除完畢后即可寫(xiě)入數(shù)據(jù)振劳,寫(xiě)入數(shù)據(jù)的過(guò)程并不是僅僅使用指針向地址賦值,賦值前還需要配置一系列的寄存器梧乘,步驟如下:
- 檢查 FLASH_SR 中的 BSY 位澎迎,以確認(rèn)當(dāng)前未執(zhí)行任何其它的內(nèi)部 Flash 操作;
- 將 FLASH_CR 寄存器中的 “激活編程寄存器位 PG” 置 1选调;
- 向指定的 FLASH 存儲(chǔ)器地址執(zhí)行數(shù)據(jù)寫(xiě)入操作夹供,每次只能以 16 位的方式寫(xiě)入;
- 等待 BSY 位被清零時(shí)仁堪,表示寫(xiě)入完成哮洽。
3.2 寫(xiě)入函數(shù)
/* STM32大容量產(chǎn)品每頁(yè)大小2KByte,中弦聂、小容量產(chǎn)品每頁(yè)大小1KByte */
#if defined (STM32F10X_HD) || defined (STM32F10X_HD_VL) || defined (STM32F10X_CL) || defined (STM32F10X_XL)
#define FLASH_PAGE_SIZE ((uint16_t)0x800) // 2048
#else
#define FLASH_PAGE_SIZE ((uint16_t)0x400) // 1024
#endif
#define WRITE_START_ADDR ((uint32_t)0x08008000)
#define WRITE_END_ADDR ((uint32_t)0x0800C000)
/**
@brief 內(nèi)部Flash寫(xiě)入
@param address -[in] 寫(xiě)入的地址
@param pData -[in&out] 指向需要操作的數(shù)據(jù)
@param dataLen -[in] 數(shù)據(jù)長(zhǎng)度
@return true - 成功鸟辅;false - 失敗
*/
bool Internal_WriteFlash(uint32_t addrStart, uint32_t *pData, uint32_t dataLen)
{
uint32_t i = 0;
uint32_t eraseCounter = 0x00; // 記錄要擦除多少頁(yè)
uint32_t address = 0x00; // 記錄寫(xiě)入的地址
uint32_t numberOfPage = 0x00; // 記錄寫(xiě)入多少頁(yè)
FLASH_Status flashStatus = FLASH_COMPLETE; // 記錄每次擦除的結(jié)果
address = addrStart;
FLASH_Unlock(); // 解鎖
numberOfPage = (WRITE_END_ADDR - address) / FLASH_PAGE_SIZE; // 計(jì)算要擦除多少頁(yè)
FLASH_ClearFlag(FLASH_FLAG_BSY | FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR); // 清除所有標(biāo)志
// 按頁(yè)擦除
for(eraseCounter = 0; (eraseCounter < numberOfPage) && (flashStatus == FLASH_COMPLETE); eraseCounter++)
{
flashStatus = FLASH_ErasePage(address + (FLASH_PAGE_SIZE * eraseCounter));
}
for(i = 0; (i < dataLen)&&(flashStatus == FLASH_COMPLETE); i++)
{
flashStatus = FLASH_ProgramWord(address, pData[i]); // 寫(xiě)入一個(gè)字(32位)的數(shù)據(jù)入指定地址
address = address + 4; // 地址偏移4個(gè)字節(jié)
}
FLASH_Lock(); // 重新上鎖
if(flashStatus == FLASH_COMPLETE)
{
return true;
}
return false;
}
四氛什、讀取Flash
4.1 讀取函數(shù)
/**
@brief 內(nèi)部Flash讀取
@param address -[in] 讀取的地址
@param pData -[in&out] 指向需要操作的數(shù)據(jù)
@param dataLen -[in] 數(shù)據(jù)長(zhǎng)度
@return true - 成功;false - 失敗
*/
bool Internal_ReadFlash(uint32_t addrStart, uint32_t *pData, uint32_t dataLen)
{
uint32_t i = 0;
uint32_t address = 0x00;
address = addrStart;
for(i = 0; i < dataLen; i++)
{
pData[i] = (*(__IO uint32_t*) address); // 讀指定地址的一個(gè)字的數(shù)據(jù)
address += 4; // 地址偏移4個(gè)字節(jié)
}
return true;
}
五匪凉、舉例
int main(void)
{
u32 in_data[5]={11,22,33,44,55};//要寫(xiě)入的數(shù)據(jù)
u32 out_data[5];//讀存放
int i;
u8 STATUS=0;
USART1_Config();//串口1配置
GPIO_Configuration();//GPIO配置枪眉,用于點(diǎn)亮led
STATUS=Internal_WriteFlash(0x08001800,in_data,5);
Delay(0x02FFFF);
if(STATUS)
{
GPIO_SetBits(GPIOD, GPIO_Pin_13);//點(diǎn)亮led1
Internal_ReadFlash(0x08001800,out_data,5);
printf("\r\n The Five Data Is : \r\n");
for(i=0;i<5;i++)
{
printf("\r %d \r",out_data[i]);
}
}
while(1);
}
? 由 Leung 寫(xiě)于 2020 年 7 月 13 日
? 參考:[零死角玩轉(zhuǎn)STM32——基于野火F103【指南者】開(kāi)發(fā)板]
stm32——Flash讀寫(xiě)