STM32CubeMX学习笔记16--- STM32内部FLASH

这篇具有很好参考价值的文章主要介绍了STM32CubeMX学习笔记16--- STM32内部FLASH。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 内部FLASH简介

        之前的文章中介绍过STM32F1利用SPI与外部FLASH(W25QXX芯片)通讯的例程,本例程将介绍STM32F1的内部FLASH,通过内部FLASH实现数据读写操作。 不同型号的STM32,其FLASH容量也有所不同,最小的只有16K字节,最大的则达到了1024K字节。此处我们使用的是STM32F103ZET6,其FLASH容量为512K字节,属于大容量产品,大容量产品的闪存模块组织图如下图示

cubemx 配置stm32内部flash存,stm32学习,stm32,学习,笔记

STM32F1的闪存模块由:主存储器、信息块和闪存存储器接口寄存器3部分组成

  • 主存储器:用来存放代码和数据常量,起始地址是0x08000000,BOOT0和BOOT1都接GND时,就是从该起始地址运行代码的
  • 信息块:分为2个小部分,启动程序代码是用来存储ST自带的启动程序,用于串口下载代码,BOOT0接3.3V,BOOT1接GND时,运行的就是这部分代码;选择字节则一般用于配置写保护、读保护等功能
  • 闪存存储器接口寄存器:用于控制闪存读写等,是整个闪存模块的控制机构

对主存储器和信息块的写入由内嵌的闪存编程/擦除控制器(FPEC)管理:编程与擦除的高电压由内部产生。在执行闪存写操作时,任何对闪存的读操作都会锁住总线,在写操作完成后读操作才能正确地进行,即在进行写或擦除操作时,不能进行代码或数据的读取操作。

下面介绍闪存的读取、编程和擦除:

<1>. 闪存的读取 内置闪存模块可以在通用地址空间直接寻址,任何32位数据的读操作都能访问闪存模块的内容并得到相应的数据。

例如,要从地址addr,读取一个半字,可通过如下语句读取:  data = *(__IO uint16_t*)addr 将addr强制转换为vu16指针,然后取该指针所指向的地址的值,即得到了addr地址的值

<2>. 闪存的编程 STM32的闪存编程是由FPEC(闪存编程和擦除控制器)模块处理的,这个模块包含7个32位寄存器,它们分别是:

FPEC键寄存器(FLASH_KEYR)
选择字节键寄存器(FLASH_OPTKEYR)
闪存控制寄存器(FLASH_CR)
闪存状态寄存器(FLASH_SR)
闪存地址寄存器(FLASH_AR)
选择字节寄存器(FLASH_OBR)
写保护寄存器(FLASH_WRPR)

其中FPEC键寄存器共有3中键值: PDPRT=0x000000A5; KEY1=0x45670123; KEY2=0xCDEF89AB STM32复位后,FPEC模块是被保护的,不能写入FLASH_CR寄存器;通过写入特定的序列到FLASH_KEYR寄存器可以打开FPEC模块(即写入KEY1和KEY2),只有在写保护被解除后,才能操作相关寄存器。 闪存编程过程如下图所示:

cubemx 配置stm32内部flash存,stm32学习,stm32,学习,笔记

<3>. 闪存的擦除 闪存编程的时候,要先判断其写入地址的FLASH是被擦除了的(也就是其值必须是0xFFFF),否则无法写入。闪存擦除分为页擦除和整片擦除。 闪存页擦除过程如下图示:

cubemx 配置stm32内部flash存,stm32学习,stm32,学习,笔记

官方固件HAL库FLASH操作的几个常见函数:

//源文件: stm32f1xx_hal_flash.c和stm32f1xx_hal_flash_ex.c
HAL_FLASH_Unlock(void); //解锁函数
HAL_FLASH_Lock(void);   //锁定函数
HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data);   //写操作函数
HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *SectorError);   //擦除函数
HAL_FLASH_WaitForLastOperation(uint32_t Timeout);   //等待操作完成函数

 

2、 硬件设计

led2指示灯用来提示系统运行状态,s1按键用来控制FLASH的数据写入,s2按键用来控制FLASH的数据读取,数据的写入与读取信息通过串口1打印出来

  • LED2指示灯
  • S2和S1按键
  • USART1
  • STM32F1内部FLASH

 3、STM32CubeMX设置

  • RCC设置外接HSE,时钟设置为72M
  • PE5设置为GPIO推挽输出模式、上拉、高速、默认输出电平为高电平
  • USART1选择为异步通讯方式,波特率设置为115200Bits/s,传输数据长度为8Bit,无奇偶校验,1位停止位
  • PE3,PE4设置为GPIO输入模式、上拉模式
  • 输入工程名,选择工程路径(不要有中文),选择MDK-ARM V5;勾选Generated periphera initialization as a pair of ‘.c/.h’ files per IP ;点击GENERATE CODE,生成工程代码

4、程序编程

  • 创建按键驱动文件key.c 和相关头文件key.h

如果要对FLASH进行写入数据,需要执行以下四步:

  1. 解锁FLASH
  2. 擦除FLASH
  3. 写入FLASH
  4. 锁住FLASH
  • 创建FLASH驱动文件stmflash.c 和相关头文件stmflash.h
#ifndef __STMFLASH_H__
#define __STMFLASH_H__

#include "main.h"  

//FLASH起始地址
#define STM32_FLASH_BASE 0x08000000 		//STM32 FLASH的起始地址

#define FLASH_WAITETIME 50000

extern void FLASH_PageErase(uint32_t PageAddress);
uint16_t STMFLASH_ReadHalfWord(uint32_t faddr);		  //读出半字  
void STMFLASH_Write_NoCheck(uint32_t WriteAddr,uint16_t *pBuffer,uint16_t NumToWrite);
void STMFLASH_Write(uint32_t WriteAddr,uint16_t *pBuffer,uint16_t NumToWrite);		//从指定地址开始写入指定长度的数据
void STMFLASH_Read(uint32_t ReadAddr,uint16_t *pBuffer,uint16_t NumToRead);   		//从指定地址开始读出指定长度的数据

							   
#endif


#include "stmflash.h"



uint16_t STMFLASH_ReadHalfWord(uint32_t faddr)
{
	return *(__IO uint16_t*)faddr; 
}


void STMFLASH_Write_NoCheck(uint32_t WriteAddr,uint16_t *pBuffer,uint16_t NumToWrite)   
{ 			 		 
	uint16_t i;
	for(i=0;i<NumToWrite;i++)
	{
		HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD,WriteAddr,pBuffer[i]);
	  WriteAddr+=2;		//地址增加2.
	}  
} 



#define STM_SECTOR_SIZE	2048	//大容量STM32的扇区大小为2K
		 
uint16_t STMFLASH_BUF[STM_SECTOR_SIZE/2];

void STMFLASH_Write(uint32_t WriteAddr,uint16_t *pBuffer,uint16_t NumToWrite)	
{
	uint32_t secpos;	   //扇区地址
	uint16_t secoff;	   //扇区内偏移地址(16位字计算)
	uint16_t secremain; //扇区内剩余地址(16位字计算)	   
 	uint16_t i;    
	uint32_t offaddr;   //去掉0X08000000后的地址
	
	if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*512)))return;//非法地址
	
	HAL_FLASH_Unlock();					//解锁
	offaddr=WriteAddr-STM32_FLASH_BASE;		//实际偏移地址.
	secpos=offaddr/STM_SECTOR_SIZE;			//扇区地址  0~127 for STM32F103RBT6
	secoff=(offaddr%STM_SECTOR_SIZE)/2;		//在扇区内的偏移(2个字节为基本单位.)
	secremain=STM_SECTOR_SIZE/2-secoff;		//扇区剩余空间大小   
	if(NumToWrite<=secremain)secremain=NumToWrite;//不大于该扇区范围
	while(1) 
	{	
		STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//读出整个扇区的内容
		for(i=0;i<secremain;i++)	//校验数据
		{
			if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;//需要擦除  	  
		}
		if(i<secremain)				//需要擦除
		{
			FLASH_PageErase(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE);	//擦除这个扇区
			FLASH_WaitForLastOperation(FLASH_WAITETIME);            	//等待上次操作完成
			CLEAR_BIT(FLASH->CR, FLASH_CR_PER);							//清除CR寄存器的PER位,此操作应该在FLASH_PageErase()中完成!
																		//但是HAL库里面并没有做,应该是HAL库bug!
			for(i=0;i<secremain;i++)//复制
			{
				STMFLASH_BUF[i+secoff]=pBuffer[i];	  
			}
			STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//写入整个扇区  
		}else 
		{
			FLASH_WaitForLastOperation(FLASH_WAITETIME);       	//等待上次操作完成
			STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain);//写已经擦除了的,直接写入扇区剩余区间. 
		}

		if(NumToWrite==secremain)break;//写入结束了
		else//写入未结束
		{
			secpos++;				//扇区地址增1
			secoff=0;				//偏移位置为0 	 
		   	pBuffer+=secremain;  	//指针偏移
			WriteAddr+=secremain*2;	//写地址偏移(16位数据地址,需要*2)	   
		   	NumToWrite-=secremain;	//字节(16位)数递减
			if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;//下一个扇区还是写不完
			else secremain=NumToWrite;//下一个扇区可以写完了
		}	 
	};	
	HAL_FLASH_Lock();		//上锁
}



void STMFLASH_Read(uint32_t ReadAddr,uint16_t *pBuffer,uint16_t NumToRead)   	
{
	uint16_t i;
	for(i=0;i<NumToRead;i++)
	{
		pBuffer[i]=STMFLASH_ReadHalfWord(ReadAddr);//读取2个字节.
		ReadAddr+=2;//偏移2个字节.	
	}
}

  • 在main.c文件下编写STM32 flash测试代码
/* USER CODE BEGIN 0 */
const uint8_t Text_Buf[] = {"STM32F103ZET6 FLASH TEST"};
#define TEXTSIZE sizeof(Text_Buf)
#define FLASH_SAVE_ADDR 0x08070000

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

///***省略***///
 /* USER CODE BEGIN WHILE */
	
	uint8_t key;
  uint8_t Read_Buf[TEXTSIZE];
	printf1("STM32 Flash Test...\r\n");
	
  while (1)
  {
		 key = KEY_Scan(0);
    if(key == 1){
        STMFLASH_Write(FLASH_SAVE_ADDR,(uint16_t *)Text_Buf,TEXTSIZE);
        printf1("FLASH Write : %s\r\n",Text_Buf);
    }

    if(key == 2){
        STMFLASH_Read(FLASH_SAVE_ADDR,(uint16_t *)Read_Buf,TEXTSIZE);
        printf1("FLASH Read : %s\r\n",Read_Buf);
    }

    HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_5);
    HAL_Delay(200);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

5、下载验证

编译无误下载到开发板后,可以看到LED2指示灯不断闪烁,当按下S1按键后数据写入到FLASH内,当按下S2按键后将写入的数据读取出来,同时串口打印出相应信息

cubemx 配置stm32内部flash存,stm32学习,stm32,学习,笔记

6、参考文献 

STM32CubeMX系列 | STM32内部FLASH - 知乎 (zhihu.com)

 STM32CUBEMX-读写内部Flash_stm32cubemx flash-CSDN博客

STM32CubeMX学习笔记(51)——读写内部Flash_cubemx flash-CSDN博客文章来源地址https://www.toymoban.com/news/detail-845825.html

到了这里,关于STM32CubeMX学习笔记16--- STM32内部FLASH的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【STM32CubeMX学习】SPI读写W25Q16

            SPI分为主从工作模式,通常有一个主设备和一个或多个从设备,本文中MCU为主机,W25Q16为从机。 SPI通信有以下四根线: MISO:主设备数据输入,从设备数据输出。 MOSI:主设备数据输出,从设备数据输入。 SCLK:时钟信号,由主设备产生。 CS:从设备片选信号,由

    2024年02月03日
    浏览(40)
  • STM32初学入门笔记(2):STM32CubeMX配置STM32输出可调PWM方波

    PWM是一种应用广泛的利用微处理器的数字输出来对模拟电路进行控制的一种技术(即对脉冲宽度的控制)PWM同时也是驱动蜂鸣器,驱动舵机,通信等重要的一环,而对于初学者而言,点完灯的下一个程序就是驱动蜂鸣器,本篇将讲述如何使用及调整PWM输出频率,占空比 工程文

    2024年02月14日
    浏览(37)
  • STM32CubeMX学习六 之ADC配置

    记录一下STM32CubeMX的学习笔记,同时分享给初学的小白,希望一起进步。 如何使用STM32CubeMX以及工程创建在之前的博客有提到,这里就直接从ADC配置讲起。 编译环境:KEIL 代码生成:STM32CubeMX 库:HAL MCU:STM32F072 假设你的cubeMX工程已经建好,这里我们开始配置ADC引脚(PA1设置为

    2023年04月10日
    浏览(29)
  • STM32初学入门笔记(3):STM32CubeMX配置STM32实现多通道ADC+DMA读取模拟量

    模拟信号的读取是我们在做很多项目是都要用到的,而模拟量的读取就要依赖于ADC数模转换器。对于初学者,学习使用ADC可以很大的帮助以后的STM32学习。 目录 ADC简介 : DMA简介:  工程开始: STM32CubeMX配置区: 配置外部时钟: 配置调试: 配置ADC: 配置DMA: 配置串口: 配

    2024年02月09日
    浏览(41)
  • STM32CubeMX学习四 之定时器配置

    记录一下STM32CubeMX的学习笔记,同时分享给初学的小白,希望一起进步。 如何使用STM32CubeMX以及工程创建在之前的博客有提到,这里就直接从定时器讲起。 编译环境:KEIL 代码生成:STM32CubeMX 库:HAL MCU:STM32F072 假设你的cubeMX工程已经建好,这里我们开始配置定时器2(TIM2),

    2024年02月01日
    浏览(34)
  • STM32基础入门学习笔记:内部高级功能应用

    文章目录: 一:低功耗模式 1.睡眠模式测试程序 NVIC.h NVIC.c key.h key.c main.c 2.停机模式测试程序 main.c 3.待机模式测试程序 main.c 二:看门狗 1.独立看门狗测试程序 iwdg.h iwdg.c main.c 2.窗口看门狗测试程序 wwdg.h wwdg.c main.c 三:TIM定时器 tim.h tim.c main.c 四:CRC循环冗余校验计算单元与

    2024年02月13日
    浏览(34)
  • STM32CubeMX学习笔记(43)——USB接口使用(CDC虚拟串口)

    USB(Universal Serial BUS)通用串行总线 ,是一个外部总线标准,用于规范电脑与外部设备的连接和通讯。是应用在 PC 领域的接口技术。USB 接口支持设备的即插即用和热插拔功能。USB 是在 1994 年底由英特尔、康柏、IBM、Microsoft 等多家公司联合提出的。 USB 发展到现在已经有 US

    2024年02月02日
    浏览(39)
  • STM32之CubeMX学习笔记(7)SPI驱动OLED及其优化

    购买了逻辑分析仪后,总想着把所有的通讯信号全都看一遍。之前一篇笔记讲的是串口通信,做了一些小实验,搞清楚了如何基于底层利用串口传一些“非标”的数据。关于通信协议的第二篇,我想来看看SPI通信。 SPI通信是Serial peripheral interface的缩写,中文是串行外设接口,

    2024年02月04日
    浏览(64)
  • STM32CubeMX学习笔记(46)——USB接口使用(HID自定义设备)

    USB(Universal Serial BUS)通用串行总线 ,是一个外部总线标准,用于规范电脑与外部设备的连接和通讯。是应用在 PC 领域的接口技术。USB 接口支持设备的即插即用和热插拔功能。USB 是在 1994 年底由英特尔、康柏、IBM、Microsoft 等多家公司联合提出的。 USB 发展到现在已经有 US

    2024年02月14日
    浏览(39)
  • STM32 学习笔记(六)定时器中断:内部时钟模式,外部时钟模式

    定时器是功能最强大,内容最复杂的32结构。 之前51用过的功能,定时产生中断。 输出比较,常用于产生 PWM 波形,驱动电机等。 输入捕获,测量方波频率。 编码器,读取正交编码器的波形。 最大定时时间:72M/65536/65536=中断频率,中断频率取倒数是最大定时时间。 定时器可

    2024年02月08日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包