這個(gè)玩意吧,說(shuō)起來(lái)很簡(jiǎn)單,就是幾行代碼的事,但楞是折騰了我大半天時(shí)間才搞定。原因后面說(shuō),先看代碼吧:
讀操作
讀操作很簡(jiǎn)單,以32位方式讀取的時(shí)候是這樣的:
data = *(__IO uint32_t *)(0x0800F000);
需要注意的是,當(dāng)以32位方式讀取時(shí),地址需要是4的整數(shù)倍,即32位。
8位或16位方式類似操作即可
寫(xiě)操作
需要注意的是,寫(xiě)操作時(shí),是以64位方式寫(xiě)入數(shù)據(jù),即以雙字的方式寫(xiě)入,以下代碼是將一個(gè)u64的值0x12345678aabbccdd,寫(xiě)入0x0800F000這個(gè)地址
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);
HAL_FLASH_Program(0, 0x0800F000, 0x12345678aabbccdd);
HAL_FLASH_Lock();
扇區(qū)擦除
EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; //刪除方式
EraseInitStruct.Page = start; //超始頁(yè)號(hào)
EraseInitStruct.NbPages = len; //頁(yè)的數(shù)量
EraseInitStruct.Banks = bank; //bank號(hào)
HAL_FLASH_Unlock(); //解鎖,以準(zhǔn)備進(jìn)行FLASH操作
HAL_FLASHEx_Erase(&EraseInitStruct, &err); //擦除
HAL_FLASH_Lock(); //上鎖,以結(jié)束FLASH操作
調(diào)試
寫(xiě)完燒錄開(kāi)始調(diào)試,發(fā)現(xiàn)問(wèn)題了,有時(shí)能寫(xiě)入,有時(shí)不能寫(xiě)入。
先找了正點(diǎn)原子的例程來(lái)做參考,他的可以寫(xiě)入,但原子的例程是操作寄存器進(jìn)行讀寫(xiě)的,不直觀,移植性也不好,個(gè)人還是喜歡用HAL庫(kù)的方式來(lái)做東西,于是作罷。
然后又找了ST的例程來(lái)看,剛好手上有一塊G4的開(kāi)發(fā)板,于是編譯,報(bào)錯(cuò),可能是我的開(kāi)發(fā)環(huán)境比較新,與ST官方的編譯環(huán)境不同,又是一通折騰,編譯通過(guò),但一加載調(diào)試,就卡死不動(dòng)。
于是新建工程,再把ST的例程移植到我的工程中,編譯通過(guò),可以調(diào)試,還是有時(shí)能寫(xiě)有時(shí)不能寫(xiě)。又回到了起點(diǎn)。
不過(guò)在前面的折騰中總結(jié)了一個(gè)規(guī)律,第1次寫(xiě)入幾乎都會(huì)失敗,第2次有一半的機(jī)率成功,但后續(xù)成功率很高,幾乎不會(huì)寫(xiě)入失敗。于是改進(jìn)一下,增加對(duì)寫(xiě)入的判斷,如果發(fā)生錯(cuò)誤,會(huì)重復(fù)寫(xiě)入不超過(guò)10次,如果超過(guò)10次仍然錯(cuò)誤,則寫(xiě)入失敗。這樣就可以保證寫(xiě)入成功了。
測(cè)試代碼如下
未包含寫(xiě)入失敗的相關(guān)代碼,需要的自行添加。
if(GET_KEY() == 0)
{
data32[0] = *(__IO uint32_t *)(0x08010000+i*8); //FLASH讀數(shù)據(jù),以字的方式讀取
data32[1] = *(__IO uint32_t *)(0x08010000+i*8+4);
if((data32[0] == 0xffffffff) && (data32[1] == 0xffffffff)) //FLASH內(nèi)容為空
{
LED1(1);
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);
flash_count=0;
while(flash_count < 10)
{
if(HAL_FLASH_Program(0, 0x08010000+i*8, 0xaabbccdd12340000+i) == HAL_OK)
break;
else
flash_count++;
}
HAL_FLASH_Lock();
}
data32[0] = *(__IO uint32_t *)(0x08010000+i*8);
data32[1] = *(__IO uint32_t *)(0x08010000+i*8+4);
while(GET_KEY() == 0)
;
LED1(0);
i++;
}
本段代碼能正確運(yùn)行的前提,是待寫(xiě)入?yún)^(qū)域是空的,即全都是0xFF才行。
封裝為函數(shù)
將代碼封裝一下,以便后續(xù)調(diào)用,并增加了一些條件判斷
頭文件如下:
//********************************************************************************************************
//*
//* 文件名:LL_flash.h
//* 文件說(shuō)明:STM32G系列片上FLASH的相關(guān)操作
//* 作者:李佳
//* 微信:LAOLIDESENLIN
//* 說(shuō)明:本文檔遵循GNU3.0開(kāi)源許可證規(guī)范,即代碼可開(kāi)源并免費(fèi)使用,引用、修改、衍生代碼也需要開(kāi)源、免費(fèi)使用,
//* 但不允許修改后和衍生的代碼做為閉源的商業(yè)軟件發(fā)布和銷售
//*
//********************************************************************************************************
#ifndef __LL_FLASH_H__
#define __LL_FLASH_H__
#include "LL_define.h"
/**************************************************************************************/
/* G431芯片的128KFLASH容量的頁(yè)地址分布如下,共有1個(gè)BANK,64頁(yè),每一頁(yè)2kb大小 */
#define STM32_FLASH_BASE 0x08000000 /* STM32 FLASH 起始地址 */
#define STM32_FLASH_SIZE 0x20000 /* STM32 FLASH 總大小*/
#define STM32_FLASH_PAGE_SIZE 0x800 /* STM32 FLASH 頁(yè)大小*/
u8 LL_flash_erase_page(u16 start, u8 len, u8 bank); //刪除FLASH扇區(qū)
u8 LL_flash_read(u32 addr, u64* pdata64, u32 len_64); //讀片上FLASH
u8 LL_flash_write(u32 addr, u64* pdata64, u32 len_64); //向片上FLASH寫(xiě)入數(shù)據(jù)
#endif
C文件如下
//********************************************************************************************************
//*
//* 文件名:LL_flash.c
//* 文件說(shuō)明:STM32G系列片上FLASH的相關(guān)操作
//* 作者:李佳
//* 微信:LAOLIDESENLIN
//* 說(shuō)明:本文檔遵循GNU3.0開(kāi)源許可證規(guī)范,即代碼可開(kāi)源并免費(fèi)使用,引用、修改、衍生代碼也需要開(kāi)源、免費(fèi)使用,
//* 但不允許修改后和衍生的代碼做為閉源的商業(yè)軟件發(fā)布和銷售
//*
//*
//*
//********************************************************************************************************
#include "LL_flash.h"
#include "LL_IO.h"
//按頁(yè)刪除片上FLASH數(shù)據(jù)
//start: 起始頁(yè)號(hào)
//len: 待刪除的頁(yè)的數(shù)量
//bank: bank編號(hào)
//返回值:錯(cuò)誤類型,0表示無(wú)錯(cuò)誤
u8 LL_flash_erase_page(u16 start, u8 len, u8 bank)
{
u32 err;
u8 ret;
FLASH_EraseInitTypeDef EraseInitStruct;
u8 flash_count=0;
EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; //刪除方式
EraseInitStruct.Page = start; //超始頁(yè)號(hào)
EraseInitStruct.NbPages = len; //頁(yè)的數(shù)量
EraseInitStruct.Banks = bank; //bank號(hào)
HAL_FLASH_Unlock(); //解鎖,以準(zhǔn)備進(jìn)行FLASH操作
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);
for(u8 i=0; i<10; i++) //最多重復(fù)10次,如果仍然失敗,則返回
{
ret = HAL_FLASHEx_Erase(&EraseInitStruct, &err); //擦除
if (ret == HAL_OK)
break;
}
if(ret != 0)
led_err_flash(10, 100); //擦除失敗后閃燈提示, 供調(diào)試階段使用, 正式使用時(shí)可刪除
HAL_FLASH_Lock(); //上鎖,以結(jié)束FLASH操作
return ret;
}
//讀片上FLASH
//addr: 32位的地址值,該值應(yīng)當(dāng)是8的整數(shù)倍,因?yàn)槭前凑?4位的方式讀取數(shù)據(jù)的
//pdata: 返回的數(shù)據(jù)首地址
//len: 待讀取數(shù)據(jù)的長(zhǎng)度, 長(zhǎng)度是按u64的數(shù)量
//返回值: 錯(cuò)誤類型,0表示無(wú)錯(cuò)誤
u8 LL_flash_read(u32 addr, u64* pdata64, u32 len_64)
{
u64 data;
for(u32 i=0; i<len_64; i++)
{
data = *(__IO uint64_t *)(addr+i*8);
pdata64[i] = data;
}
return 0;
}
//向片上FLASH寫(xiě)入數(shù)據(jù),每次寫(xiě)入8字節(jié),不夠8字節(jié)的,以0xFF補(bǔ)足
//addr: 32位的地址值,該值應(yīng)當(dāng)是8的整數(shù)倍,因?yàn)槭前凑?4位的方式讀取數(shù)據(jù)的
//pdata: 待寫(xiě)入的數(shù)據(jù)首地址
//len: 待寫(xiě)入數(shù)據(jù)的長(zhǎng)度,長(zhǎng)度是按u64的數(shù)量
//返回值: 錯(cuò)誤類型,0表示無(wú)錯(cuò)誤
u8 LL_flash_write(u32 addr, u64* pdata64, u32 len_64)
{
u8 ret;
u64 read;
HAL_FLASH_Unlock(); //上鎖,以結(jié)束FLASH操作
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);
for(u16 j=0; j<len_64; j++) //按u64計(jì)算的
{
for(u8 i=0; i<10; i++) //不超過(guò)10次的重復(fù)操作,以保證寫(xiě)入成功
{
ret = HAL_FLASH_Program(0, addr+j*8, *(pdata64+j));
if(ret == HAL_OK)
{
read = *(__IO uint64_t *)(addr+j*8); //在MCU認(rèn)為寫(xiě)入正確以后,再次讀取一下數(shù)據(jù),并進(jìn)行比對(duì),如果比對(duì)不成功,也說(shuō)明寫(xiě)入出錯(cuò)
if(read != *(pdata64+j))
ret = 255;
return ret;
}
}
}
if(ret != 0)
led_err_flash(10, 100); //擦除失敗后閃燈提示, 供調(diào)試階段使用, 正式使用時(shí)可刪除
HAL_FLASH_Lock(); //上鎖,以結(jié)束FLASH操作
return ret;
}
封裝后的測(cè)試函數(shù)
u64 data64[32]={0};
u8 g_text_buf[64] = {"LIJIA000LIJIA001LIJIA002LIJIA003LIJIA004LIJIA005LIJIA006LIJIA007"};
if(GET_KEY() == 0)
{
HAL_Delay(200);
LL_flash_read(0x08010000, data64, 4);
if(data64[0] == 0xffffffffffffffff) //如果是空的,則寫(xiě)入
{
LED1(1);
LED2(1);
if(LL_flash_write(0x08010000, (u64*)g_text_buf, 4) != 0)
continue;
}
else //如果為非空,則擦除
{
LL_flash_erase_page(32, 1, 0); //第32頁(yè),長(zhǎng)度1頁(yè),BANK 0
}
LL_flash_read(0x08010000, data64, 4);
while(GET_KEY() == 0)
;
LED1(0);
LED2(0);
i++;
}
測(cè)試結(jié)果如下圖,如果寫(xiě)入成功,LED燈會(huì)在操作之后滅掉,如果寫(xiě)入失敗,則LED燈會(huì)保持點(diǎn)亮狀態(tài),
總結(jié)
絕對(duì)不能只寫(xiě)入一次,就認(rèn)為寫(xiě)入正確,恰恰相反,最開(kāi)始的2次寫(xiě)入,極有可能寫(xiě)入失敗。
所以必須重復(fù)寫(xiě)入幾次,并且增加校驗(yàn),即寫(xiě)入完成后,再讀取數(shù)據(jù),并進(jìn)行比較,以確保正確。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-801564.html
下載
h和c文件已更新,可以讀寫(xiě)任意連續(xù)頁(yè)(扇區(qū)),對(duì)于只寫(xiě)一部分的頁(yè),未寫(xiě)的那部分的已有數(shù)據(jù)會(huì)予以保留,比如:一個(gè)扇區(qū)總共256個(gè)u64(雙字)寫(xiě)入了若干數(shù)據(jù),如果只需要改寫(xiě)其中的10個(gè)雙字,該寫(xiě)函數(shù)會(huì)自動(dòng)保留其他原始數(shù)據(jù),只更新其中的10個(gè)雙字,該功能已封裝到c文件中,可直接下載使用
下載地址:https://download.csdn.net/download/13011803189/88763405文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-801564.html
到了這里,關(guān)于嵌入式開(kāi)發(fā)--STM32G4系列片上FLASH的讀寫(xiě)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!