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
这次用的是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();
}