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

 

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