stm32L4的flash读写问题 CUBEMX

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();
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章