目錄
1:Flash簡介
FLASH存儲器是閃速存儲器,它的主要特點是在不加電的情況下能長期保持存儲的信息。就其本質而言,Flash Memory屬於EEPROM(電擦除可編程只讀存儲器)類型。它既有ROM的特點,又有很高的存取速度,而且易於擦除和重寫, 功耗很小。
2:嵌入式Flash特性
- 對於 STM32F40x 和 STM32F41x,容量高達 1 MB;對於 STM32F42x 和 STM32F43x,容量高達 2 MB
- 128 位寬數據讀取
- 字節、半字、字和雙字數據寫入
- 扇區擦除與全部擦除
- 低功耗模式
STM32F40x和STM32F41x Flash 模塊構成
可以看到
- 主存儲器
該部分用來存放代碼和數據常數(如const類型的數據)。分爲12個扇區,前4個扇區爲16KB大小,然後扇區4是64KB大小,扇區5~11是128K大小,從上圖可以看出主存儲器的起始地址就是0X0800 0000,B0、B1都接GND的時候,就是從0X0800 0000開始運行代碼
2.系統存儲器
這個主要用來存放STM32F4的bootloader代碼,此代碼是出廠的時候就固化在STM32F4裏面了,專門來給主存儲器下載代碼的。當B0接3.3V,B1接GND的時候,從該存儲器啓動(即進入串口下載模式)。
3.OTP區域
即一次性可編程區域,共528字節,被分成兩個部分,前面512字節(32字節爲1塊,分成16塊),可以用來存儲一些用戶數據(一次性的,寫完一次,永遠不可以擦除!!),後面16字節,用於鎖定對應塊。
4.選項字節
用於配置讀保護、BOR級別、軟件/硬件看門狗以及器件處於待機或停止模式下的復位。
3:Flash 閃存的讀取
STM23F4的FLASH讀取是很簡單的。例如,我們要從地址addr,讀取一個字(字節爲8位,半字爲16位,字爲32位),可以通過如下的語句讀取:
data=*(vu32*)addr;
將addr強制轉換爲vu32指針,然後取該指針所指向的地址的值,即得到了addr地址的值。類似的,將上面的vu32改爲vu16,即可讀取指定地址的一個半字。
在執行閃存寫操作時,任何對閃存的讀操作都會鎖住總線,在寫操作完成後讀操作才能正確地進行;既在進行寫或擦除操作時,不能進行代碼或數據的讀取操作。
STM32F4的閃存編程由6個32位寄存器控制,他們分別是
- FLASH訪問控制寄存器(FLASH_ACR)
- FLASH祕鑰寄存器(FLASH_KEYR)
- FLASH選項祕鑰寄存器(FLASH_OPTKEYR)
- FLASH狀態寄存器(FLASH_SR)
- FLASH控制寄存器(FLASH_CR)
- FLASH選項控制寄存器(FLASH_OPTCR)
其中祕鑰寄存器中有兩個鍵值KEY1=0X45670123 KEY2=0XCDEF89AB
STM32F4 Flash標準編程的步驟如下
- 檢查FLASH_SR中的BSY位,確保當前未執行任何FLASH操作。
- 將FLASH_CR寄存器中的PG位置1,激活FLASH編程。
- 針對所需存儲器地址(主存儲器塊或OTP區域內)執行數據寫入操作:
—並行位數爲x8時按字節寫入(PSIZE=00)
—並行位數爲x16時按半字寫入(PSIZE=01)
—並行位數爲x32時按字寫入(PSIZE=02)
—並行位數爲x64時按雙字寫入(PSIZE=03)
- 等待BSY位清零,完成一次編程。
按以上四步操作,就可以完成一次FLASH編程。不過有幾點要注意:1,編程前,要確保要寫入地址的FLASH已經擦除。2,要先解鎖(否則不能操作FLASH_CR)。3,編程操作對OPT區域也有效,方法一模一樣。
4:閃存擦除
FLASH編程的時候,要先判斷所寫地址是否被擦除了,STM32F4的閃存擦除分爲兩種
- 扇區擦除
- 整片擦除
扇區擦除步驟
- 檢查FLASH_CR的LOCK是否解鎖,如果沒有則先解鎖
- 檢查FLASH_SR寄存器中的BSY 位,確保當前未執行任何FLASH操作
- 在FLASH_CR寄存器中,將SER位置1,並從主存儲塊的12個扇區中選擇要擦除的扇區 (SNB)
- 將FLASH_CR寄存器中的STRT位置1,觸發擦除操作 等待BSY位清零
整片擦除步驟
- 檢查 FLASH_SR 寄存器中的 BSY 位,以確認當前未執行任何 Flash 操作
- 將 FLASH_CR 寄存器中的 MER 位置 1(STM32F405xx/07xx 和 STM32F415xx/17xx 器件)
- 將 FLASH_CR 寄存器中的 MER 和 MER1 位置 1(STM32F42xxx 和 STM32F43xxx 器件)
- 將 FLASH_CR 寄存器中的 STRT 位置 1
- 等待 BSY 位清零
5:代碼實現
講幾個重要的函數
void FLASH_Unlock(void) { if((FLASH->CR & FLASH_CR_LOCK) != RESET) { /* Authorize the FLASH Registers access */ FLASH->KEYR = FLASH_KEY1; FLASH->KEYR = FLASH_KEY2; } } //解鎖用的,先查詢CR寄存器的位31,看是否上鎖,若上鎖,則再給KEYR密鑰寄存器寫入兩個密鑰值進行解鎖
void FLASH_DataCacheCmd(FunctionalState NewState) { /* Check the parameters */ assert_param(IS_FUNCTIONAL_STATE(NewState)); if(NewState != DISABLE) { FLASH->ACR |= FLASH_ACR_DCEN; } else { FLASH->ACR &= (~FLASH_ACR_DCEN); } } //FLASH擦除期間,必須禁止數據緩存,操作ACR訪問控制寄存器的位10
uint16_t STMFLASH_GetFlashSector(u32 addr); //根據要操作的地址,來查詢處在哪個扇區
再來看看 FLASH_EraseSector扇區擦除函數中的核心部分
/* if the previous operation is completed, proceed to erase the sector */ FLASH->CR &= CR_PSIZE_MASK; FLASH->CR |= tmp_psize; FLASH->CR &= SECTOR_MASK; FLASH->CR |= FLASH_CR_SER | FLASH_Sector; FLASH->CR |= FLASH_CR_STRT; /* Wait for last operation to be completed */ status = FLASH_WaitForLastOperation(); /* if the erase operation is completed, disable the SER Bit */ FLASH->CR &= (~FLASH_CR_SER); FLASH->CR &= SECTOR_MASK;
- CR控制寄存器位8、9置0
- 根據VCC來操作CR寄存器的位8、9
- CR寄存器位3~7置0
- 操作CR寄存器位3~6來選擇要擦除的扇區,並置位1,激活扇區擦除
- 操作CR寄存器的位16來觸發擦除操作
- 等待操作並查詢SR寄存器,查詢操作的狀態
- 操作完成後,清除掉一些相應位
寫入函數FLASH_ProgramWord
FLASH->CR &= CR_PSIZE_MASK; FLASH->CR |= FLASH_PSIZE_WORD; FLASH->CR |= FLASH_CR_PG; *(__IO uint32_t*)Address = Data; /* Wait for last operation to be completed */ status = FLASH_WaitForLastOperation(); /* if the program operation is completed, disable the PG Bit */ FLASH->CR &= (~FLASH_CR_PG);
- CR控制寄存器位8、9置0
- 操作CR寄存器位8、9,來確定編程並行位數
- 操作CR寄存器的位0,來激活編程
- 把寫入的數據放進扇區地址中
- 等待操作並查詢SR寄存器,查詢操作的狀態
- 操作完成後,清除掉CR寄存器位0
至於讀函數STMFLASH_ReadWord,那就很簡單了
//讀取指定地址的半字(16位數據) //faddr:讀地址 //返回值:對應數據. u32 STMFLASH_ReadWord(u32 faddr) { return *(vu16*)faddr; }