CB系列flash是128K,起始地址一般是0x08000000 考慮到前面要放代碼 可以選在0x0801FC00,還差1K到邊緣,即最後一頁
一般來說:
HAL_FLASH_Unlock(); //擦除
HAL_StatusTypeDef HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *PageError); //解鎖
HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data);//解鎖
HAL_FLASH_Lock(); //上鎖
但既然寫這篇文章,說明一般搜到的方法又不行,一搜就會,一做就卡~_~
之前寫過一個能用的,也是hal庫,芯片是F103
uint32_t addr = 0x0801FC00;
int32_t Flash[12];
int main(void)
{
·······
// memset(&Flash, 0xffffffff, sizeof(Flash));//flash
// WriteFlash(Flash,10);//相當於擦除
memset(&Flash, 0x12345678, sizeof(Flash));//flash
WriteFlash(Flash,10);
if(*(__IO uint32_t*)(addr) != 0xffffffff)
{
// printf("addr:0x%x, data:0x%x\r\n", addr, *(__IO uint32_t*)(addr));
Flash_Int(*(__IO uint32_t*)(addr));
}
}
void Flash_Int(uint8_t num)//根據flash內容更新參數
{
Gas_TypeDef *Gasx;
for(uint8_t i=0; i<10; i++)//讀取falsh內容
Flash[i] = *(__IO int32_t*)(addr + 4*i);
VOC.num = Flash[4];//更新編號數據,忽略變量名字
NO2.num = Flash[5];
SO2.num = Flash[6];
O3.num = Flash[7];
CO.num = Flash[8];
H2S.num = Flash[9];
if(num == VOC.num)//根據flash第一個信息,判斷氣體類型
Gasx = &VOC;
else if(num == NO2.num)
Gasx = &NO2;
else if(num == SO2.num)
Gasx = &SO2;
else if(num == O3.num)
Gasx = &O3;
else if(num == CO.num)
Gasx = &CO;
else if(num == H2S.num)
Gasx = &H2S;
else return;
Gasx->num = Flash[0];//存儲類型
num_gas = Flash[0];
Gasx->xishu_a = Flash[1];//更新氣體系數
Gasx->xishu_b = Flash[2];
Gasx->range = Flash[3]; //更新氣體量程
}
void WriteFlash(int32_t *buff, uint8_t len)//寫入flash信息
{
uint8_t k = 0;
uint32_t Address;
Address = addr;
HAL_FLASH_Unlock();//解鎖
FLASH_EraseInitTypeDef f;
f.TypeErase = FLASH_TYPEERASE_PAGES;
f.PageAddress = Address;//就這個成員和stm32L431不一樣
f.NbPages = 1;
uint32_t PageError = 0;
HAL_FLASHEx_Erase(&f, &PageError);
for(k=0;k<len;k++)
{
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, buff[k]);
Address = Address + 4;
}
HAL_FLASH_Lock();//上鎖
}
這次用的是STM32L431CBT6,移植過來不能用
首先,函數裏面調用的形參結構體FLASH_EraseInitTypeDef成員都不一樣:多了一個Banks,可選FLASH_BANK_1和FLASH_BANK_2,而CBT6不夠大,只有FLASH_BANK_1,就不用選了,關於bank
PageAddress 換成了Page,原來是直接輸入要擦除的地址就行了,現在是要把擦除的地址換成哪一頁(看手冊)再填進去
這款芯片不能一次擦除半字節或單字節,只能擦雙字節(64位/8個char),或者一行(32個雙字節),如下圖
可以參考stm32l476 內部flash HAL庫操作方法
大概
/*****************flash讀寫相關定義******************/
#define FLASH_WiFi_ADDR 0x0801FC00 //flash存放wifi信息的地址
#define flash_status uint8_t
#define FLASH_ERR 0
#define FLASH_OK 1
/* USER CODE BEGIN */
/**
* @brief Erases sector.
* @param Add: Address of sector to be erased.
* @retval FLASH_OK if operation is successeful, FLASH_ERR else.
*/
flash_status Flash_If_Erase(uint32_t Add)
{
uint32_t PageError = 0;
/* Variable contains Flash operation status */
HAL_StatusTypeDef status;
FLASH_EraseInitTypeDef eraseinitstruct;
/* Clear OPTVERR bit set on virgin samples */
//__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_SIZERR | FLASH_FLAG_OPTVERR | FLASH_FLAG_PGSERR | FLASH_FLAG_PROGERR | FLASH_FLAG_BSY);
/* Get the number of sector to erase from 1st sector*/
eraseinitstruct.Banks = Get_Bank(Add);//獲取擦除地址所在的堆
eraseinitstruct.TypeErase = FLASH_TYPEERASE_PAGES;//按頁擦除
eraseinitstruct.Page = Get_Page(Add);//獲取頁位置
eraseinitstruct.NbPages = 1;//擦1頁
status = HAL_FLASHEx_Erase(&eraseinitstruct, &PageError);
if (status != HAL_OK)
{
return FLASH_ERR;
}
return FLASH_OK;
}
/**
* @brief Writes Data into Memory.
* @param src: Pointer to the source buffer. Address to be written to.
* @param dest: Pointer to the destination buffer.
* @param Len: Number of data to be written (in bytes).
* @retval FLASH_OK if operation is successeful, FLASH_ERR else.
*/
flash_status Flash_If_Write(uint8_t *src, uint32_t dest_addr, uint32_t Len)
{
uint32_t i = 0, loca1 = 0;
uint32_t src_tmp[10] = {0};
/* Clear OPTVERR bit set on virgin samples */
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_SIZERR | FLASH_FLAG_OPTVERR | FLASH_FLAG_PGSERR | FLASH_FLAG_PROGERR);
for(i = 0; i < Len; i += 8)
{
if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, (uint32_t)(dest_addr+i), *(uint64_t*)(src+i)) == HAL_OK)//*(uint64_t*)(src+i)表明一字節src的物理地址必須是連續的
{
/* Check the written value */
if(*(uint64_t *)(src + i) != *(uint64_t*)(dest_addr+i))
{
return FLASH_ERR;
}
}
else
{
/* Error occurred while writing data in Flash memory */
return FLASH_ERR;
}
}
return FLASH_OK;
}
/**
* @brief Reads Data into Memory.
* @param src: Pointer to the source buffer. Address to be written to.
* @param dest: Pointer to the destination buffer.
* @param Len: Number of data to be read (in bytes).
* @retval return FLASH_OK.
*/
flash_status Flash_If_Read(uint8_t* buff, uint32_t dest_addr, uint32_t Len)
{
uint32_t i;
for(i = 0; i < Len; i++)
{
buff[i] = *(__IO uint8_t*)(dest_addr + i);
}
/* Return a valid address to avoid HardFault */
return FLASH_OK;
}
/**
* @brief Gets the page of a given address
* @param Addr: Address of the FLASH Memory
* @retval The page of a given address
*/
static uint32_t Get_Page(uint32_t Addr)
{
uint32_t page = 0;
if (Addr < (FLASH_BASE + FLASH_BANK_SIZE))
{
/* Bank 1 */
page = (Addr - FLASH_BASE) / FLASH_PAGE_SIZE;
}
else
{
/* Bank 2 */
page = (Addr - (FLASH_BASE + FLASH_BANK_SIZE)) / FLASH_PAGE_SIZE;
}
return page;
}
/**
* @brief Gets the bank of a given address
* @param Addr: Address of the FLASH Memory
* @retval The bank of a given address
*/
static uint32_t Get_Bank(uint32_t Addr)
{
uint32_t bank = 0;
// if (READ_BIT(SYSCFG->MEMRMP, SYSCFG_MEMRMP_FB_MODE) == 0)
// {
// /* No Bank swap */
// if (Addr < (FLASH_BASE + FLASH_BANK_SIZE))
// {
// bank = FLASH_BANK_1;
// }
// else
// {
// bank = FLASH_BANK_2;
// }
// }
// else
// {
// /* Bank swap */
// if (Addr < (FLASH_BASE + FLASH_BANK_SIZE))
// {
// bank = FLASH_BANK_2;
// }
// else
// {
// bank = FLASH_BANK_1;
// }
// }
// return bank;
return FLASH_BANK_1; //L431只有FLASH_BANK_1
}
/* USER CODE END*/
當時使用這幾個函數尤其是Flash_If_Write(uint8_t *src, uint32_t dest_addr, uint32_t Len)函數時費了一點周折,以爲它的形參*src就是一個變量那,可是一直報錯,後來發現,這個變量不能放在棧區,要放在堆區,而且必須是連續的物理地址,否則執行*(uint64_t*)(src+i)這個語句後會進硬件錯誤中斷,因爲本來src是8位的,想按64位讀,如果src存的東一個,西一個,根本不能連續讀取。
“
在main函數裏面定義的變量是存放在棧中的(其實這種說法也不是很準確,還跟類型有關,比如常量的話就不是這種情況了)。所以未初始化定義的變量是按照先後順序依次存入堆裏,這裏可以用指針查看具體變量的地址,這樣可以更加清楚的看到內存的連續分配情況。(注意一個整形佔4個字節,而一個字符佔1個字節)。
值得說明的是,如果定義的是全局變量,這個時候內存使用的是堆的存儲方式,這個時候內存分配的空間不再連續,而是類似於鏈表的形式。“
”只能開闢一片內存,再調用,如下:
void Parameter_Init(void)//參數初始化
{
uint8_t flash_watch[50];
uint8_t* flash_buff;
HAL_FLASH_Unlock();
//HAL_FLASH_OB_Unlock();
flash_buff = (uint8_t *)malloc(100); // 分配100個字節的連續內存空間,用來存放wifi信息
if(Flash_If_Read(flash_watch, FLASH_WiFi_ADDR, 50) == FLASH_OK)
{
if((flash_watch[0] != 'A') && (flash_watch[1] != 'T'))//如果之前沒有寫過
{
memcpy(set_wifi, "AT+XXXX=\"XXXXX\",\"XXXXX\"\r\n", strlen(set_wifi)+2);//設置wifi模塊的開機wifi賬號和密碼 爲測試手機熱點
memcpy(flash_buff, set_wifi, strlen(set_wifi)+1);//存入寫入緩存區
if(Flash_If_Erase(FLASH_WiFi_ADDR) == FLASH_OK)//擦除地址所在的頁
{
if(Flash_If_Write(flash_buff, FLASH_WiFi_ADDR, strlen(set_wifi)+2) == FLASH_OK)//寫入
{
BUZ_PRO_FUN(4,0);//蜂鳴器提示4次
}
}
}
else
{
Flash_If_Read(flash_buff, FLASH_WiFi_ADDR, strlen(set_wifi)+2);
memcpy(set_wifi, flash_buff, strlen(set_wifi)+2);
}
}
free(flash_buff); // 釋放內存空間
//HAL_FLASH_OB_Lock();
HAL_FLASH_Lock();
}