第四十五章 FLASH模擬EEPROM實驗
STM32本身沒有自帶EEPROM,但是STM32具有IAP(在應(yīng)用編程)功能,所以我們可以把它的FLASH當成EEPROM來使用。本章,我們將利用STM32內(nèi)部的FLASH來實現(xiàn)第三十六章實驗類似的效果,不過這次我們是將數(shù)據(jù)直接存放在STM32內(nèi)部,而不是存放在NOR FLASH。
本章分為如下幾個小節(jié):
45.1 STM32 FLASH簡介
45.2 硬件設(shè)計
45.3 軟件設(shè)計
45.4 下載驗證
45.1 STM32 FLASH簡介
不同型號的STM32,其FLASH容量也有所不同,最小的只有16K字節(jié),最大的則達到了1024K字節(jié)。戰(zhàn)艦開發(fā)板選擇的是STM32F103ZET6,其FLASH容量為512K字節(jié),屬于大容量產(chǎn)品(另外還有中容量和小容量產(chǎn)品),大容量產(chǎn)品的閃存模塊組織如表45.1.1所示:
表45.1.1 大容量產(chǎn)品閃存模塊組織表
STM32的閃存模塊由主存儲器、信息塊和閃存存儲器接口寄存器等3部分組成。
主存儲器,該部分用來存放代碼和數(shù)據(jù)常數(shù)(如const類型的數(shù)據(jù))。對于大容量產(chǎn)品,其被劃分為256頁,每一頁2K字節(jié)(注意:小容量和中容量產(chǎn)品每頁只有1K字節(jié))。從上表可以看出主存儲器的起始地址就是0x08000000,B0、B1都接GND的時候,就是從0x08000000開始運行代碼的。
信息塊,該部分分為2個小部分,其中啟動程序代碼,用來存儲ST自帶的啟動程序,用來串口下載代碼,當B0接3V3,B1接GND的時候,運行的就是這部分代碼。用戶選中字節(jié),則一般用于配置寫保護、讀保護等功能,本章不作介紹了。
閃存存儲器接口寄存器,該部分用于控制閃存讀寫等,是整個閃存模塊的控制結(jié)構(gòu)。
對主存儲器和信息塊的寫入由內(nèi)嵌的閃存編程/擦除控制器(FPEC)管理;編程與擦除的高電壓由內(nèi)部產(chǎn)生。
在執(zhí)行閃存寫操作時,任何對閃存的讀操作都會鎖住總線,在寫操作完成后讀操作才能正確地進行。既在進行寫或擦除操作時,不能進行代碼或數(shù)據(jù)的讀取操作。
45.1.1 閃存的讀取
內(nèi)置閃存模塊可以在通用地址空間直接尋址,任何32位數(shù)據(jù)的讀操作都能訪問閃存模塊的內(nèi)容并得到相應(yīng)的數(shù)據(jù)。讀接口在閃存端包含一個讀控制器,還包含一個AHB接口與CPU銜接。這個接口的主要工作是產(chǎn)生讀內(nèi)存的控制信號并預(yù)取CPU要求的指令塊,預(yù)取指令塊僅用于在I-Code總線上的取指操作,數(shù)據(jù)常量是通過D-Code總線訪問的。這兩條總線的訪問目標是相同的閃存模塊,訪問D-Code將比預(yù)取指令優(yōu)先級高。
這里要特別留意一個閃存等待時間,因為CPU運行速度比FLASH快得多,STM32F103的FLASH最快訪問速度≤24Mhz,如果CPU頻率超過這個速度,那么必須加入等待時間,比如我們一般使用72Mhz的主頻,那么FLASH等待周期就必須設(shè)置為2,該設(shè)置通過FLASH_ACR寄存器設(shè)置。
例如,我們要從地址addr,讀取一個半字(半字為16位,字為32位),可以通過如下的語句讀?。?br> data = (vu16)addr;
將addr強制轉(zhuǎn)換為vu16指針,然后取該指針所指向的地址的值,即得到了addr地址的值。類似的,將上面的vu16改為vu8,即可讀取指定地址的一個字節(jié)。相對FLASH讀取來說,STM32 FLASH的寫就復雜一點了。下面我們介紹STM32閃存的編程和擦除。
45.1.2 閃存的編程和擦除
STM32的閃存編程是由FPEC(閃存編程和擦除控制器)模塊處理的,這個模塊包含7個32位寄存器,它們分別是:
?FPEC鍵寄存器(FLASH_KEYR)
?選擇字節(jié)鍵寄存器(FLASH_OPTKEYR)
?閃存控制寄存器(FLASH_CR)
?閃存狀態(tài)寄存器(FLASH_SR)
?閃存地址寄存器(FLASH_AR)
?選擇字節(jié)寄存器(FLASH_WRPR)
其中FPEC鍵寄存器總共有3個鍵值:
RDPRT鍵 = 0X0000 00A5
KEY1 = 0X4567 0123
KEY2 = 0XCDEF 89AB
STM32復位后,F(xiàn)PEC模塊是被保護的,不能寫入FLASH_CR寄存器;通過寫入特定的序列到FLASH_KEYR寄存器可以打開FPEC模塊(即寫入KEY1和KEY2),只有在寫保護被解除后,我們才能操作相關(guān)寄存器。
STM32閃存的編程每次必須寫入16位(不能單純的寫入8位數(shù)據(jù)),當FLASH_CR寄存器的PG位為‘1’時,在一個閃存地址寫入一個半字將啟動一次編程;寫入任何非半字的數(shù)據(jù),F(xiàn)PEC都會產(chǎn)生總線錯誤。在編程過程中(BSY位為’1’),任何讀寫內(nèi)存的操作都會使CPU暫停,直到此次閃存編程結(jié)束。
同樣,STM32的FLASH在編程的時候,也必須要求其寫入地址的FLASH是被擦除了的(其值必須是0xFFFF),否則無法寫入,在FLASH_SR寄存器的PGERR位將得到一個警告。
STM32的FLASH編程過程如圖45.1.2.1所示:
圖45.1.2.1 STM32閃存編程過程
從上圖可以得到閃存的編程順序如下:
1)檢查FLASH_CR的LOCK是否解鎖,如果沒有則先解鎖
2)檢查FLASH_SR寄存器的BSY位,以確認沒有其他正在進行的編程操作
3)設(shè)置FLASH_CR寄存器的PG位為‘1’
4)在指定的地址寫入要編程的半字
5)等待BSY位變?yōu)椤?’
6)讀出寫入地址并驗證數(shù)據(jù)
前面提到,我們在STM32的FLASH編程的時候,要先判斷縮寫地址是否被擦出了,所以,我們有必要再介紹一下STM32的閃存擦除,STM32的閃存擦除分為兩種:頁擦除和整片擦除。頁擦除過程如圖45.1.2.2所示:
圖45.1.2.2 STM32閃存頁擦除過程
從上圖可以看出,STM32的頁擦除順序為:
1)檢查FLASH_CR和LOCK是否解鎖,如果沒有則先解鎖
2)檢查FLASH_SR寄存器的BSY位,以確認沒有其他正在進行的閃存操作
3)設(shè)置FLASH_CR寄存器的PER位為‘1’
4)用FLASH_AR寄存器選擇要擦除的頁
5)設(shè)置FLASH_CR寄存器的STRT位為‘1’
6)等待BSY位變?yōu)椤?’
7)讀出被擦除的頁并做驗證
本章我們只用到了STM32頁擦除功能,整片擦除功能我們在這里就不介紹了。
45.1.3 FLASH寄存器
通過上面的講解,我們基本對STM32閃存的讀寫執(zhí)行步驟有所了解。接下來,我們介紹本實驗需要用到的一些FLASH寄存器。
? FPEC鍵寄存器(FLASH_KEYR)
FPEC鍵寄存器描述如圖45.1.3.2所示:
圖45.1.3.2 FLASH_KEYR寄存器
該寄存器主要用來解鎖FPEC,必須在該寄存器寫入特定的序列(KEY1和KEY2)解鎖后,才能對FLASH_CR寄存器進行寫操作。
? FLASH控制寄存器(FLASH_CR)
FLASH控制寄存器描述如圖45.1.3.3所示:
圖45.1.3.3 FLASH_CR寄存器
該寄存器我們本章只用到了它的LOCK、STRT、PER和PG等4個位。
LOCK位,該位用于指示FLASH_CR寄存器是否被鎖住,該位在檢測到正確的解鎖序列后,硬件將其清零。在一次不成功的解鎖操作后,在下次系統(tǒng)復位之前,該位將不再改變。
STRT位,該位用于開始一次擦除操作。在該位寫入1,將執(zhí)行一次擦除操作。
PER位,該位用于選擇頁擦除操作,在頁擦除的時候,需要將該位置1。
PG位,該位用于選擇編程操作,在往FLASH寫數(shù)據(jù)的時候,該位需要置1。
其他位,我們就不在這里介紹了,請大家參考《STM32F10xxx閃存編程參考手冊》。
? 閃存狀態(tài)寄存器(FLASH_SR)
閃存狀態(tài)寄存器描述如圖45.1.3.4所示:
圖45.1.3.4 FLASH_SR寄存器
該寄存器主要用來指示當前FPEC的操作編程狀態(tài)。由于寄存器中描述比較詳細,這里就不重復了。
? 閃存地址寄存器(FLASH_AR)
閃存地址寄存器描述如圖45.1.3.5所示:
圖45.1.3.5 FLASH_AR寄存器
該寄存器在本章,我們主要用來設(shè)置要擦除的頁。
關(guān)于STM32 FLASH的介紹,我們就介紹到這里。更詳細的介紹,可以參考《STM32F10xxx閃存編程參考手冊》。
45.2 硬件設(shè)計
- 例程功能
按鍵KEY1控制寫入FLASH的操作,按鍵KEY0控制讀出操作,并在TFTLCD模塊上顯示相關(guān)信息,還可以借助USMART進行讀取或者寫入操作。LED0閃爍用于提示程序正在運行。 - 硬件資源
1)LED燈
LED0 – PB5
2)串口1(PA9/PA10連接在板載USB轉(zhuǎn)串口芯片CH340上面)
3)正點原子 2.8/3.5/4.3/7/10寸TFTLCD模塊(僅限MCU屏,16位8080并口驅(qū)動)
4)獨立按鍵
KEY0 – PE4 KEY1 – PE3
45.3 程序設(shè)計
45.3.1 FLASH的HAL庫驅(qū)動
FLASH在HAL庫中的驅(qū)動代碼在stm32f1xx_hal_flash.c和stm32f1xx_hal_flash_ex.c文件(及其頭文件)中。 - HAL_FLASH_Unlock函數(shù)
解鎖閃存控制寄存器訪問的函數(shù),其聲明如下:
HAL_StatusTypeDef HAL_FLASH_Unlock(void);
?函數(shù)描述:
用于解鎖閃存控制寄存器的訪問,在對FLASH進行寫操作前必須先解鎖,解鎖操作也就是必須在FLASH_KEYR寄存器寫入特定的序列(KEY1和KEY2)。
?函數(shù)形參:
無
?函數(shù)返回值:
HAL_StatusTypeDef枚舉類型的值。 - HAL_FLASH_Lock函數(shù)
鎖定閃存控制寄存器訪問的函數(shù),其聲明如下:
HAL_StatusTypeDef HAL_FLASH_Lock (void);
?函數(shù)描述:
用于鎖定閃存控制寄存器的訪問。
?函數(shù)形參:
無
?函數(shù)返回值:
HAL_StatusTypeDef枚舉類型的值。 - HAL_FLASH_Program函數(shù)
閃存寫操作函數(shù),其聲明如下:
HAL_StatusTypeDef HAL_FLASHEx_Program(uint32_t TypeProgram, uint32_t Address,
uint64_t Data);
?函數(shù)描述:
該函數(shù)用于FLASH的寫入。
?函數(shù)形參:
形參1是TypeProgram用來區(qū)分要寫入的數(shù)據(jù)類型,取值可為字節(jié)、半字、字和雙字,用戶根據(jù)寫入數(shù)據(jù)類型選擇即可。
形參2是Address用來設(shè)置要寫入數(shù)據(jù)的FLASH地址。
形參3是Data是要寫入的數(shù)據(jù)類型。該參數(shù)默認64位,如果你要寫入小于64位的數(shù)據(jù),比如16位,程序會進行類型轉(zhuǎn)換。
?函數(shù)返回值:
HAL_StatusTypeDef枚舉類型的值。 - HAL_FLASHEx_Erase函數(shù)
閃存擦除函數(shù),其聲明如下:
HAL_StatusTypeDef HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit,
uint32_t *SectorError);
?函數(shù)描述:
該函數(shù)用于大量擦除或擦除指定的閃存扇區(qū)。
?函數(shù)形參:
形參1是FLASH_EraseInitTypeDef結(jié)構(gòu)體類型指針變量。
typedef struct
{
uint32_t TypeErase; /* 擦除類型(Page擦除 / BANK級別批量擦除) */
uint32_t Banks; /* 擦除的Bank編號(批量擦除時才有效) */
uint32_t PageAddress; /* 擦除頁面地址 */
uint32_t NbPages; /* 擦除的頁面數(shù) */
} FLASH_EraseInitTypeDef;
成員變量TypeErase用來設(shè)置擦除類型,是page擦除還是BANK級別的批量擦除,取值為FLASH_TYPEERASE_PAGES或者FLASH_TYPEERASE_MASSERASE,這個比較好理解,如果一次擦除一個Bank下面的所有Page,那么需要選擇FLASH_TYPEERASE_MASSERASE。成員變量Banks用來設(shè)置要擦除的Bank編號,這個只有設(shè)置為批量擦除的時候才有效。成員變量PageAddress用來設(shè)置要擦除頁面的地址。成員變量NbPages用來設(shè)置要擦除的頁面數(shù)。
形參2是uint32_t類型指針變量,存放錯誤碼,0xFFFFFFFF值表示扇區(qū)已被正確擦除,其它值表示擦除過程中的錯誤扇區(qū)。
?函數(shù)返回值:
HAL_StatusTypeDef枚舉類型的值。
5. FLASH_WaitForLastOperation函數(shù)
等待FLASH操作完成函數(shù),其聲明如下:
HAL_StatusTypeDef FLASH_WaitForLastOperation(uint32_t Timeout);
?函數(shù)描述:
該函數(shù)用于等待FLASH操作完成。
?函數(shù)形參:
形參1是FLASH操作超時時間。
?函數(shù)返回值:
HAL_StatusTypeDef枚舉類型的值。
45.3.2 程序流程圖
圖45.3.2.1 FLASH模擬EEPROM實驗程序流程圖
45.3.3 程序解析
- STM FLASH驅(qū)動代碼
這里我們只講解核心代碼,詳細的源碼請大家參考光盤本實驗對應(yīng)源碼。STM FLASH驅(qū)動源碼包括兩個文件:stmflash.c和stmflash.h。
stmflash.h頭文件做了一些比較重要的宏定義,定義如下:
/* FLASH起始地址 */
#define STM32_FLASH_BASE 0x08000000 /* STM32 FLASH 起始地址 */
#define STM32_FLASH_SIZE 0x80000 /* STM32 FLASH 總大小 */
/* STM32F103扇區(qū)大小 */
#if STM32_FLASH_SIZE < 256 * 1024
#define STM32_SECTOR_SIZE 1024 /* 容量小于256K的F103, 扇區(qū)大小為1K字節(jié) */
#else
#define STM32_SECTOR_SIZE 2048 /* 容量大于等于256K的F103, 扇區(qū)大小為2K字節(jié) */
#endif
STM32_FLASH_BASE和STM32_FLASH_SIZE分別是FLASH的起始地址和FLASH總大小,這兩個宏定義隨著芯片是固定的,我們戰(zhàn)艦開發(fā)板的F103芯片F(xiàn)LASH是512K字節(jié),所以STM32_FLASH_SIZE宏定義值為0x80000。
下面我們開始介紹stmflash.c的程序,下面先介紹一下stmflash寫操作函數(shù),源碼如下:
/**
* @brief 在FLASH 指定位置, 寫入指定長度的數(shù)據(jù)(自動擦除)
* @note 該函數(shù)往 STM32 內(nèi)部 FLASH 指定位置寫入指定長度的數(shù)據(jù)
* 該函數(shù)會先檢測要寫入的扇區(qū)是否是空(全0XFFFF)的?, 如果
* 不是, 則先擦除, 如果是, 則直接往扇區(qū)里面寫入數(shù)據(jù).
* 數(shù)據(jù)長度不足扇區(qū)時,自動被回擦除前的數(shù)據(jù)
* @param waddr : 起始地址 (此地址必須為2的倍數(shù)!!,否則寫入出錯!)
* @param pbuf : 數(shù)據(jù)指針
* @param length : 要寫入的 半字(16位)數(shù)
* @retval 無
*/
uint16_t g_flashbuf[STM32_SECTOR_SIZE / 2]; /* 最多是2K字節(jié) */
void stmflash_write(uint32_t waddr, uint16_t *pbuf, uint16_t length)
{
uint32_t secpos; /* 扇區(qū)地址 */
uint16_t secoff; /* 扇區(qū)內(nèi)偏移地址(16位字計算) */
uint16_t secremain; /* 扇區(qū)內(nèi)剩余地址(16位字計算) */
uint16_t i;
uint32_t offaddr; /* 去掉0X08000000后的地址 */
FLASH_EraseInitTypeDef flash_eraseop;
uint32_t erase_addr; /* 擦除錯誤,這個值為發(fā)生錯誤的扇區(qū)地址 */
if(waddr<STM32_FLASH_BASE||(waddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))
{
return; /* 非法地址 */
}
HAL_FLASH_Unlock(); /* FLASH解鎖 */
offaddr = waddr - STM32_FLASH_BASE; /* 實際偏移地址 */
secpos = offaddr / STM32_SECTOR_SIZE; /* 得到扇區(qū)編號 */
secoff = (offaddr % STM32_SECTOR_SIZE) / 2; /* 在扇區(qū)內(nèi)的偏移(2B為基本單位) */
secremain = STM32_SECTOR_SIZE / 2 - secoff; /* 扇區(qū)剩余空間大小 */
if (length <= secremain)
{
secremain = length; /* 不大于該扇區(qū)范圍 */
}
while (1)
{
stmflash_read(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE,
g_flashbuf, STM32_SECTOR_SIZE / 2); /* 讀出整個扇區(qū)的內(nèi)容 */
for (i = 0; i < secremain; i++) /* 校驗數(shù)據(jù) */
{
if (g_flashbuf[secoff + i] != 0XFFFF)
{
break; /* 需要擦除 */
}
}
if (i < secremain) /* 需要擦除 */
{
flash_eraseop.TypeErase = FLASH_TYPEERASE_PAGES; /* 選擇頁擦除 */
flash_eraseop.NbPages = 1; /* 要擦除的頁數(shù) */
flash_eraseop.PageAddress = secpos * STM32_SECTOR_SIZE +
STM32_FLASH_BASE; /* 要擦除的起始地址 */
HAL_FLASHEx_Erase(&flash_eraseop, &erase_addr);
for (i = 0; i < secremain; i++) /* 復制 */
{
g_flashbuf[i + secoff] = pbuf[i];
}
stmflash_write_nocheck(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE,
g_flashbuf, STM32_SECTOR_SIZE / 2); /* 寫入整個扇區(qū) */
}
else
{ /* 寫已經(jīng)擦除了的,直接寫入扇區(qū)剩余區(qū)間 */
stmflash_write_nocheck(waddr, pbuf, secremain);
}
if (length == secremain)
{
break; /* 寫入結(jié)束了 */
}
else /* 寫入未結(jié)束 */
{
secpos++; /* 扇區(qū)地址增1 */
secoff = 0; /* 偏移位置為0 */
pbuf += secremain; /* 指針偏移 */
waddr += secremain * 2; /* 寫地址偏移(16位數(shù)據(jù)地址,需要*2) */
length -= secremain; /* 字節(jié)(16位)數(shù)遞減 */
if (length > (STM32_SECTOR_SIZE / 2))
{
secremain = STM32_SECTOR_SIZE / 2; /* 下一個扇區(qū)還是寫不完 */
}
else
{
secremain = length; /* 下一個扇區(qū)可以寫完了 */
}
}
}
HAL_FLASH_Lock(); /* 上鎖 */
}
該函數(shù)用于在STM32的指定地址寫入指定長度的數(shù)據(jù)。函數(shù)的實現(xiàn)基本類似SPI章節(jié)的norflash_write函數(shù),不過該函數(shù)對于寫入地址是有要求,必須保證以下兩點:
1、寫入地址必須是用戶代碼區(qū)以外的地址。
2、寫入地址必須是2的倍數(shù)。
第1點比較好理解,如果把用戶代碼給擦了,可想而知你運行的程序可能就被廢了,從而很可能出現(xiàn)死機的情況。第2點則是STM32 FLASH的要求,每次必須寫入16位,如果你寫的地址不是2的倍數(shù),那么寫入的數(shù)據(jù),可能就不是寫在你要寫的地址了。
另外,該函數(shù)的g_flashbuf數(shù)組,也是根據(jù)所用STM32的FLASH容量來確定的,戰(zhàn)艦STM32開發(fā)板的FLASH是512K字節(jié),所以STM_SECTOR_SIZE的值為2048,故該數(shù)組大小為2K字節(jié)。
stmflash_write函數(shù)實質(zhì)是調(diào)用stmflash_write_nocheck函數(shù)進行實現(xiàn),下面再來看一下stmflash_write函數(shù)代碼,其代碼如下:
/**
* @brief 不檢查的寫入
這個函數(shù)的假設(shè)已經(jīng)把原來的扇區(qū)擦除過再寫入
* @param waddr : 起始地址 (此地址必須為2的倍數(shù)!!,否則寫入出錯!)
* @param pbuf : 數(shù)據(jù)指針
* @param length : 要寫入的 半字(16位)數(shù)
* @retval 無
*/
void stmflash_write_nocheck(uint32_t waddr, uint16_t *pbuf, uint16_t length)
{
uint16_t i;
for (i = 0; i < length; i++)
{
HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, waddr, pbuf[i]);
waddr += 2; /* 指向下一個半字 */
}
}
該函數(shù)的實現(xiàn)依靠flash的HAL庫驅(qū)動HAL_FLASH_Program進行實現(xiàn)。由于前面已經(jīng)對HAL_FLASH_Program進行說明,這里就不作展開說明了。
接下來,講解一下STM FLASH讀相關(guān)的函數(shù),寫函數(shù)也有調(diào)用到讀函數(shù),其代碼如下:
/**
* @brief 從指定地址讀取一個半字 (16位數(shù)據(jù))
* @param faddr : 讀取地址 (此地址必須為2的倍數(shù)!!)
* @retval 讀取到的數(shù)據(jù) (16位)
*/
uint16_t stmflash_read_halfword(uint32_t faddr)
{
return *(volatile uint16_t *)faddr;
}
/**
* @brief 從指定地址開始讀出指定長度的數(shù)據(jù)
* @param raddr : 起始地址
* @param pbuf : 數(shù)據(jù)指針
* @param length: 要讀取的半字(16位)數(shù),即2個字節(jié)的整數(shù)倍
* @retval 無
*/
void stmflash_read(uint32_t raddr, uint16_t *pbuf, uint16_t length)
{
uint16_t i;
for (i = 0; i < length; i++)
{
pbuf[i] = stmflash_read_halfword(raddr); /* 讀取2個字節(jié) */
raddr += 2; /* 偏移2個字節(jié) */
}
}
前面也提及到STM32對FLASH寫入,其寫入地址的值必須是0xFFFFFFFF,所以讀函數(shù)主要是讀取地址的值,以給寫函數(shù)調(diào)用檢驗,確保能寫入成功。讀函數(shù)實現(xiàn)比較簡單,這里就不做展開了。
2. main.c代碼
在main.c里面編寫如下代碼:
const uint8_t g_text_buf[] = {“STM32 FLASH TEST”}; /* 要寫入的FLASH字符串數(shù)組 */
#define TEXT_LENTH sizeof(g_text_buf) /* 數(shù)組長度 */
/* SIZE表示半字長(2字節(jié)), 大小必須是2的整數(shù)倍, 如果不是的話, 強制對齊到2的整數(shù)倍 */
#define SIZE TEXT_LENTH / 2 + ((TEXT_LENTH % 2) ? 1 : 0)
/* 設(shè)置FLASH 保存地址(必須為偶數(shù),且其值要大于本代碼所占用FLASH的大小 + 0X08000000) */
#define FLASH_SAVE_ADDR 0X08070000
int main(void)
{
uint8_t key = 0;
uint16_t i = 0;
uint8_t datatemp[SIZE];
HAL_Init(); /* 初始化HAL庫 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 設(shè)置時鐘, 72Mhz */
delay_init(72); /* 延時初始化 */
usart_init(115200); /* 串口初始化為115200 */
usmart_dev.init(72); /* 初始化USMART */
led_init(); /* 初始化LED */
lcd_init(); /* 初始化LCD */
key_init(); /* 初始化按鍵 */
lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
lcd_show_string(30, 70, 200, 16, 16, "FLASH EEPROM TEST", RED);
lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
lcd_show_string(30, 110, 200, 16, 16, "KEY1:Write KEY0:Read", RED);
while (1)
{
key = key_scan(0);
if (key == KEY1_PRES) /* KEY1按下,寫入STM32 FLASH */
{
lcd_fill(0, 150, 239, 319, WHITE);
lcd_show_string(30, 160, 200, 16, 16, "Start Write FLASH....", RED);
stmflash_write(FLASH_SAVE_ADDR, (uint16_t *)g_text_buf, SIZE);
lcd_show_string(30, 150, 200, 16, 16, "FLASH Write Finished!", RED);
}
if (key == KEY0_PRES) /* KEY0按下,讀取字符串并顯示 */
{
lcd_show_string(30, 150, 200, 16, 16, "Start Read FLASH.... ", RED);
stmflash_read(FLASH_SAVE_ADDR, (uint16_t *)datatemp, SIZE);
lcd_show_string(30, 150, 200, 16, 16, "The Data Readed Is: ", RED);
lcd_show_string(30, 170, 200, 16, 16, (char *)datatemp, BLUE);
}
i++;
delay_ms(10);
if (i == 20)
{
LED0_TOGGLE(); /* 提示系統(tǒng)正在運行 */
i = 0;
}
}
}
主函數(shù)代碼邏輯比較簡單,當檢測到按鍵KEY1按下后往FLASH指定地址開始的連續(xù)地址空間寫入一段數(shù)據(jù),當檢測到按鍵KEY0按下后讀取FLASH指定地址開始的連續(xù)空間數(shù)據(jù)。
最后,我們將stmflash_read_word和test_write函數(shù)加入USMART控制,這樣,我們就可以通過串口調(diào)試助手,調(diào)用STM32F103的FLASH讀寫函數(shù),方便測試。
45.4 下載驗證
將程序下載到開發(fā)板后,可以看到LED0不停的閃爍,提示程序已經(jīng)在運行了。LCD顯示的內(nèi)容如圖45.4.1所示:
圖45.4.1程序運行效果圖
通過先按KEY1按鍵寫入數(shù)據(jù),然后按KEY0讀取數(shù)據(jù),得到如圖45.4.2所示:文章來源:http://www.zghlxwxcb.cn/news/detail-718636.html
圖45.4.2 操作后的顯示效果圖
本實驗的測試,我們還可以借助USMART,調(diào)用:stmflash_read_word和test_write函數(shù)進行測試!文章來源地址http://www.zghlxwxcb.cn/news/detail-718636.html
到了這里,關(guān)于【正點原子STM32連載】 第四十五章 FLASH模擬EEPROM實驗 摘自【正點原子】STM32F103 戰(zhàn)艦開發(fā)指南V1.2的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!