本系列文章汇总:
本篇文章主要介绍如何使用STM32中的片内FLash。
如果使用的是STM32F1系列,请先看这篇文章!!!(STM32CubeMX生成F1的工程中造成 下载器无法下载 问题的解决方案)
打开STM32CubeMX,打开MCU选择器:
搜索并选中芯片STM32L431RCT6
:
这里我都使用外部时钟:
小熊派开发板板载ST-Link并且虚拟了一个串口,原理图如下:
这里我将开关拨到AT-MCU
模式,使PC的串口与USART1之间连接。
接下来开始配置USART1
:
STM32L4的最高主频到80M,所以配置PLL,最后使HCLK = 80Mhz
即可:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uC8VFP4r-1591787333797)(http://mculover666.cn/blog/20200610/zENXSh7HqWOq.png?imageslim)]
最后设置生成独立的初始化文件:
点击GENERATE CODE
即可生成MDK-V5工程:
查看所使用芯片的信息,Flash起始地址为0x08000000,大小为0x00040000(256KB):
STM32L4x1芯片内部的Flash存储器内存分布如下:
STM32L431RCT6的Flash容量是256KB,所以只有Bank1,有128页,每页2KB。
擦除或者写入内部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) /*!
#define FLASH_TYPEERASE_MASSERASE ((uint32_t)0x01) /*!
/**
* @}
*/
第二个参数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) /*!
#define FLASH_TYPEPROGRAM_FAST ((uint32_t)0x01) /*!
#define FLASH_TYPEPROGRAM_FAST_AND_LAST ((uint32_t)0x02) /*!
/**
* @}
*/
这个就不用API啦~CPU可以直接访问到地址的,读取就好。
首先包含进来头文件:
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include
#include //使用到了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』。