STM32CubeMX | 28 - STM32片內Flash的使用

本系列文章彙總:


本篇文章主要介紹如何使用STM32中的片內FLash。

1. 準備工作

硬件準備

  • 開發板
    首先需要準備一個開發板,這裏我準備的是STM32L4的開發板(BearPi):

軟件準備

  • 需要安裝好Keil - MDK及芯片對應的包,以便編譯和下載生成的代碼;

2.生成MDK工程

如果使用的是STM32F1系列,請先看這篇文章!!!(STM32CubeMX生成F1的工程中造成 下載器無法下載 問題的解決方案

選擇芯片型號

打開STM32CubeMX,打開MCU選擇器:

搜索並選中芯片STM32L431RCT6:

配置時鐘源

  • 如果選擇使用外部高速時鐘(HSE),則需要在System Core中配置RCC;
  • 如果使用默認內部時鐘(HSI),這一步可以略過;

這裏我都使用外部時鐘:

配置串口

小熊派開發闆闆載ST-Link並且虛擬了一個串口,原理圖如下:

mark

這裏我將開關撥到AT-MCU模式,使PC的串口與USART1之間連接。

接下來開始配置USART1

mark

配置時鐘樹

STM32L4的最高主頻到80M,所以配置PLL,最後使HCLK = 80Mhz即可:

生成工程設置

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-uC8VFP4r-1591787333797)(http://mculover666.cn/blog/20200610/zENXSh7HqWOq.png?imageslim)]

代碼生成設置

最後設置生成獨立的初始化文件:

生成代碼

點擊GENERATE CODE即可生成MDK-V5工程:

3. 在MDK中編寫、編譯、下載用戶代碼

重定向printf

STM32內部Flash及HAL庫API

查看所使用芯片的信息,Flash起始地址爲0x08000000,大小爲0x00040000(256KB):

STM32L4x1芯片內部的Flash存儲器內存分佈如下:

STM32L431RCT6的Flash容量是256KB,所以只有Bank1,有128頁,每頁2KB。

解鎖/上鎖Flash操作

擦除或者寫入內部Flash的時候,需要先解鎖再操作,操作完畢之後上鎖:

HAL_StatusTypeDef  HAL_FLASH_Unlock(void);
HAL_StatusTypeDef  HAL_FLASH_Lock(void);

擦除操作

HAL庫中定義了一個Flash初始化結構體,如下:

/**
  * @brief  FLASH Erase structure definition
  */
typedef struct
{
  uint32_t TypeErase;   /*!< Mass erase or page erase.
                             This parameter can be a value of @ref FLASH_Type_Erase */
  uint32_t Banks;       /*!< Select bank to erase.
                             This parameter must be a value of @ref FLASH_Banks
                             (FLASH_BANK_BOTH should be used only for mass erase) */
  uint32_t Page;        /*!< Initial Flash page to erase when page erase is disabled
                             This parameter must be a value between 0 and (max number of pages in the bank - 1)
                             (eg : 255 for 1MB dual bank) */
  uint32_t NbPages;     /*!< Number of pages to be erased.
                             This parameter must be a value between 1 and (max number of pages in the bank - value of initial page)*/
} FLASH_EraseInitTypeDef;

第一個參數TypeErase是參數類型,分爲頁擦除和塊擦除:

/** @defgroup FLASH_Type_Erase FLASH Erase Type
  * @{
  */
#define FLASH_TYPEERASE_PAGES     ((uint32_t)0x00)  /*!<Pages erase only*/
#define FLASH_TYPEERASE_MASSERASE ((uint32_t)0x01)  /*!<Flash mass erase activation*/
/**
  * @}
  */

第二個參數Banks是選擇需要擦除哪一塊:

/** @defgroup FLASH_Banks FLASH Banks
  * @{
  */
#define FLASH_BANK_1              ((uint32_t)0x01)                          /*!< Bank 1   */
#if defined (STM32L471xx) || defined (STM32L475xx) || defined (STM32L476xx) || defined (STM32L485xx) || defined (STM32L486xx) || \
    defined (STM32L496xx) || defined (STM32L4A6xx) || defined (STM32L4R5xx) || \
    defined (STM32L4R7xx) || defined (STM32L4R9xx) || defined (STM32L4S5xx) || defined (STM32L4S7xx) || defined (STM32L4S9xx)
#define FLASH_BANK_2              ((uint32_t)0x02)                          /*!< Bank 2   */
#define FLASH_BANK_BOTH           ((uint32_t)(FLASH_BANK_1 | FLASH_BANK_2)) /*!< Bank1 and Bank2  */
#else
#define FLASH_BANK_BOTH           ((uint32_t)(FLASH_BANK_1))                /*!< Bank 1   */
#endif
/**
  * @}
  */

由參數中可以看到,STM32L431RCT6中只有Bank1可選。

第三個參數Page是初始化擦除頁,在STM32L431RCT6中,該值範圍是0-127。

第四個參數NbPages是要擦除的頁數,在STM32L431RCT6中,在1-(127-初始化參數頁的編號)。

擦除的時候,調用的API如下:

HAL_StatusTypeDef HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *PageError);

寫入操作

HAL_StatusTypeDef  HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data);

第一個參數是寫入類型:

/** @defgroup FLASH_Type_Program FLASH Program Type
  * @{
  */
#define FLASH_TYPEPROGRAM_DOUBLEWORD    ((uint32_t)0x00)  /*!<Program a double-word (64-bit) at a specified address.*/
#define FLASH_TYPEPROGRAM_FAST          ((uint32_t)0x01)  /*!<Fast program a 32 row double-word (64-bit) at a specified address.
                                                                 And another 32 row double-word (64-bit) will be programmed */
#define FLASH_TYPEPROGRAM_FAST_AND_LAST ((uint32_t)0x02)  /*!<Fast program a 32 row double-word (64-bit) at a specified address.
                                                                 And this is the last 32 row double-word (64-bit) programmed */
/**
  * @}
  */

讀取操作

這個就不用API啦~CPU可以直接訪問到地址的,讀取就好。

實驗內容

首先包含進來頭文件:

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h> //使用到了memcpy
/* USER CODE END Includes */

然後定義一個測試數據長度:

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define LEN						10
/* USER CODE END PD */

編寫測試函數:

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void Onchip_Flash_Test(void)
{
	int i;
	uint32_t PageError = 0;
	FLASH_EraseInitTypeDef FlashSet;
	HAL_StatusTypeDef status;
	
	uint32_t addr = 0x0803F800;
	uint32_t data_buf[LEN];
	
	/* 讀取Flash內容 */
	memcpy(data_buf, (uint32_t*)addr, sizeof(uint32_t)*LEN);
	printf("read before erase:\r\n\t");
	for(i = 0;i < LEN;i++)
	{
		printf("0x%08x ", data_buf[i]);
	}
	printf("\r\n");
	
	/* 寫入新的數據 */
	//擦除最後一頁
	FlashSet.TypeErase = FLASH_TYPEERASE_PAGES;
	FlashSet.Banks = FLASH_BANK_1;
	FlashSet.Page = 127;
	FlashSet.NbPages = 1;
	//解鎖Flash操作
	HAL_FLASH_Unlock();
	status = HAL_FLASHEx_Erase(&FlashSet, &PageError);
	HAL_FLASH_Lock();
	if(status != HAL_OK)
	{
		printf("erase fail, PageError = %d\r\n", PageError);
	}
	printf("erase success\r\n");
	
	
	/* 讀取Flash內容 */
	memcpy(data_buf, (uint32_t*)addr, sizeof(uint32_t)*LEN);
	printf("read after erase:\r\n\t");
	for(i = 0;i < LEN;i++)
	{
		printf("0x%08x ", data_buf[i]);
	}
	printf("\r\n");

	//寫入Flash內容
	HAL_FLASH_Unlock();
	for (i = 0; i < LEN * sizeof(uint32_t); i+=8)
	{
			//一個字是32位,一次寫入兩個字,64位,8個字節
			status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, addr + i, (uint64_t)i);
			if(status != HAL_OK)
			{
				break;
			}
	}
	HAL_FLASH_Lock();
	if(i < LEN)
	{
		printf("write fail\r\n");
	}
	else
	{
		printf("write success\r\n");
	}

	/* 讀取Flash內容 */
	addr = 0x0803F800;
	memcpy(data_buf, (uint32_t*)addr, sizeof(uint32_t)*LEN);
	printf("read after write:\r\n\t");
	for(i = 0;i < LEN;i++)
	{
		printf("0x%08x ", data_buf[i]);
	}
	printf("\r\n");
	
}
/* USER CODE END 0 */

最後在main.c 調用:

/* USER CODE BEGIN 2 */
printf("stm32l4 onchip flash test...\r\n");
Onchip_Flash_Test();

/* USER CODE END 2 */

實驗現象

編譯、下載、實驗現象如下:

更多精彩文章及資源,請關注我的微信公衆號:『mculover666』。

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