STM32内部FLASH读写操作详解(附代码)

STM32 芯片内部的 FLASH 存储器,主要用于存储我们代码。

如果内部FLASH存储完我们的代码还有剩余的空间,那么这些剩余的空间我们就可以利用起来,存储一些需要掉电保存的数据。

本文以STM32103ZET6为例。STM32103ZET6属于大容量产品,其闪存模块组织如下:

其主存储器大小为512KB,分为256页,每页大小都为2KB。我们的程序一般默认烧写到第0页的起始地址(0x08000000)处。

当BOOT0引脚和BOOT1引脚都接GND时,就是从这个地址开始运行代码的。这个地址在keil中可以看到:

假如我们要下载的程序大小为4.05KB,则第0、1、2页用于保存我们的程序,我们需要掉电保存的数据只能保存在第3~第255页这一部分空间内。

我们最终要下载的程序大小可在工程对应的.map文件中看到。.map文件可以双击工程的Target的名字快速打开,如:

下面对STM32内部FLASH进行简单的读写测试:

内部FLASH读写测试

流程图如下:

本流程图省略异常情况,只考虑成功的情况:

示例代码:

本例的关键代码如下(以读写第255页为例):

左右滑动查看全部代码>>>

/*******************************************************************************************************
*------------------------------------------STM32 Demo---------------------------------------------------
*
* 工程说明:STM32内部FLASH实验
* 作 者:ZhengNian
* 博 客:zhengnianli.github.io
* 公 众 号:嵌入式大杂烩
*
********************************************************************************************************/
#define MAIN_CONFIG
#include "config.h"

/* STM32F103ZET6有256页,每一页的大小都为2KB */
#define ADDR_FLASH_PAGE_255 ((uint32_t)0x0807F800) /* Page255 2KB */

/* FLASH读写测试结果 */
#define TEST_ERROR -1/* 错误(擦除、写入错误) */
#define TEST_SUCCESS 0/* 成功 */
#define TEST_FAILED 1/* 失败 */

/* Flash读写测试buf */
#define BufferSize 6
uint16_t usFlashWriteBuf[BufferSize] = {0x0101,0x0202,0x0303,0x0404,0x0505,0x0606};
uint16_t usFlashReadBuf[BufferSize] = {0};

/* 供本文件调用的函数声明 */
static int FlashReadWriteTest(void);

/*******************************************************************************************************
** 函数: main
**------------------------------------------------------------------------------------------------------
** 参数: void
** 返回: 无
** 说明: 主函数
********************************************************************************************************/
int main(void)
{
/* 上电初始化 */
SysInit();

/* 内部Flash读写测试 */
if (TEST_SUCCESS == FlashReadWriteTest())
{
printf("Flash test success!\n");
}
else
{
printf("Flash test failed!\n");
}

while (1)
{}
}

/*******************************************************************************************************
** 函数: FlashReadWriteTest, 内部Flash读写测试函数
**------------------------------------------------------------------------------------------------------
** 参数: void
** 返回: TEST_ERROR:错误(擦除、写入错误) TEST_SUCCESS:成功 TEST_FAILED:失败
** 说明: 无
********************************************************************************************************/
static int FlashReadWriteTest(void)
{
uint32_t ucStartAddr;

/* 解锁 */
FLASH_Unlock();

/* 擦除操作 */
ucStartAddr = ADDR_FLASH_PAGE_255;
if (FLASH_COMPLETE != FLASH_ErasePage(ucStartAddr))
{
printf("Erase Error!\n");
return TEST_ERROR;
}
else
{
ucStartAddr = ADDR_FLASH_PAGE_255;
printf("擦除成功,此时FLASH中值为:\n");
for (int i = 0; i < BufferSize; i++)
{
usFlashReadBuf[i] = *(uint32_t*)ucStartAddr;
printf("ucFlashReadBuf[%d] = 0x%.4x\n", i, usFlashReadBuf[i]);
ucStartAddr += 2;
}
}

/* 写入操作 */
ucStartAddr = ADDR_FLASH_PAGE_255;
printf("\n往FLASH中写入的数据为:\n");
for (int i = 0; i < BufferSize; i++)
{
if (FLASH_COMPLETE != FLASH_ProgramHalfWord(ucStartAddr, usFlashWriteBuf[i]))
{
printf("Write Error!\n");
return TEST_ERROR;
}
printf("ucFlashWriteBuf[%d] = 0x%.4x\n", i, usFlashWriteBuf[i]);
ucStartAddr += 2;
}

/* 上锁 */
FLASH_Lock();

/* 读取操作 */
ucStartAddr = ADDR_FLASH_PAGE_255;
printf("\n从FLASH中读出的数据为:\n");
for (int i = 0; i < BufferSize; i++)
{
usFlashReadBuf[i] = *(__IO uint16_t*)ucStartAddr;
printf("ucFlashReadBuf[%d] = 0x%.4x\n", i, usFlashReadBuf[i]);
ucStartAddr += 2;
}

/* 读出的数据与写入的数据做比较 */
for (int i = 0; i < BufferSize; i++)
{
if (usFlashReadBuf[i] != usFlashWriteBuf[i])
{
return TEST_FAILED;
}
}

return TEST_SUCCESS;
}

/*********************************************************************************************************
** End Of File
********************************************************************************************************/

(1)进行解锁操作

STM32 的闪存编程是由内嵌的闪存编程/擦除控制器(FPEC)管理 ,这个模块包含的寄存器如下:

STM32 复位后, FPEC 模块是被保护的, 不能写入 FLASH_CR 寄存器;通过写入特定的序列到 FLASH_KEYR 寄存器可以打开 FPEC 模块(即写入 KEY1 和KEY2) , 只有在写保护被解除后, 我们才能操作相关寄存器。固件库中的函数为:

左右滑动查看全部代码>>>

/* 解锁 */
void FLASH_Unlock(void);

(2)擦除将要写的页

STM32 的 FLASH 在编程的时候,也必须要求其写入地址的 FLASH 是被擦除了的(也就是其值必须是 0XFFFF),否则无法写入,在 FLASH_SR 寄存器的 PGERR 位将得到一个警告。

STM32 的闪存擦除分为两种:页擦除和整片擦除。也就是其最小擦除单位为1页,尽管你只需往某页里写10个字节数据或者更少的数据,你也必须先擦除该页(2*1024个字节)。我们这里使用按页擦除,固件库中按页擦除的函数为:

左右滑动查看全部代码>>>

/* 按页擦除 */
FLASH_Status FLASH_ErasePage(uint32_t Page_Address);

其返回值为枚举:

左右滑动查看全部代码>>>

typedef enum
{
FLASH_BUSY = 1, /* 忙 */
FLASH_ERROR_PG, /* 编程错误 */
FLASH_ERROR_WRP, /* 写保护错误 */
FLASH_COMPLETE, /* 操作完成 */
FLASH_TIMEOUT /* 操作超时 */
}FLASH_Status;

(3)往上一步擦写成功的页写入数据

STM32 闪存的编程每次必须写入16 位。虽然固件库中有如下三个写操作的函数:

左右滑动查看全部代码>>>

FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data);
FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);
FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data);

分别为按字(32bit)写入、按半字(16bit)写入、按字节(8bit)写入函数。

32 位字节写入实际上是写入的两次 16 位数据,写完第一次后地址+2,这与我们前面讲解的 STM32 闪存的编程每次必须写入 16 位并不矛盾。写入 8 位实际也是占用的两个地址了,跟写入 16 位基本上没啥区别。

(4)写入操作完成后进行上锁操作

对FLASH进行写操作完成后要进行上锁操作,对应的固件库中函数为:

左右滑动查看全部代码>>>

/* 上锁 */
void FLASH_Lock(void);

(5)读出数据

固件库中并没有与读操作的函数。读操作其实就是读取FLASH某个地址的数据。

(6)对比写入的数据与读出的数据是否相等

最后对比我们写入的数据与读出的数据是否完全一致,若一致则表明读写测试成功,否则失败。

程序执行结果:

可见,读出的数据与写入的数据一致,表明读写测试成功。

最后

STM32的内部FLASH读写步骤大致如上,有时候我们还需要封装一些读写函数,但步骤大都如上。写入数据之前需要先进行擦除操作。以上就是本次的笔记分享,如有错误,欢迎指出!

(0)

相关推荐

  • 如何把Modbus的通信参数存在单片机的flash中-FreeModbus从站设计(11)

    FreeModbus从站设计(11)-把Modbus的通信参数存在单片机的flash中 关键词:FreeModbus CubeMX HAL库 flash 通信参数 作为从站,Modbus-RTU通信的 ...

  • U盘容量大小造假技术手段实现之8M变4G(以STM32 SPI_FLASH为例)

    以前经常听别人说上某多或者某宝买便宜U盘的时候发现被坑,比如一个U盘大小是4GB,买回来到了手上插上PC端电脑显示也是4GB,但是真正用的时候发现并没有那么多,可能就只有那么几百MB的大小,甚至是几M ...

  • STM32如何IAP升级用户程序

    IAP简介 IAP( In Application Programming)即在应用编程,也就是用户可以使用自己的程序对MCU的中的运行程序进行更新,而无需借助于外部烧写器.ST官网也给出了IAP的示 ...

  • 用CubeMX + HAL库开发它不香吗?

    干货福利,第一时间送达! 摘要:如果你学过STC51,你一定知道STC51操作是极其方便的.如果你学过STM32的库函数,你一定知道STM32操作是极其繁琐的.传统的库函数开发方式,将太多时间花费在各 ...

  • STM32_H750_ADC总结

    DMA中断方式 STM32cubemx的一些基础配置 注意H750引脚,很多引脚有后缀例如PC3_C(不是PC3) ADC和DMA都不是连续模式 初始化开启DMA传输 HAL_ADC_Start_DM ...

  • STM32入坑(12)串口发送字节、半字、字、字符串、数组及实现串口控制

    串口发送字节.半字.字.字符串.数组及实现串口控制 简介 串口的配置 1. 配置usart的TX和RX引脚 2. 配置串口模式 3.配置串口的优先级(使用串口中断时) 4.串口初始化 编写发送函数 发 ...

  • STM32 Flash详解

    本文将根据ST官方Flashprogramming manual,文档编号:PM0059,讲解STM32F207内部Flash编程. 01 概述 这里的flash是指STM32F207内部集成的Fla ...

  • GD32F103替换STM32F103需要注意的地方

    查了下GD的手册和一些论坛中使用过的大佬发布的帖子,GD32F103替换STM32F103需要注意的地方总结如下: 一.相同点 1) .外围引脚定义: 相同型号的管脚定义相同 2) .Cortex M ...

  • Keil、IAR实现处理器复位而变量不被初始化的方法

    有时候,我们有这种需求:处理器复位,要求变量不被初始化. 比如:一个实时统计数据的系统,不想因为某种原因(异常原因死机.看门狗等)复位,而丢失正在计数而未来得及保存的数据. 下面就来讲讲在Keil. ...

  • STM32:Flash擦除与读写操作(HAL库)

    应用平台:STM32F030F4P6ST官方库:STM32Cube_FW_F0_V1.9.0背景知识绝大多数的单片机和微控制器(ARM,x86),地址空间都是以字节为单位的,也就是说一个地址是一个字节 ...