基于STM32的FLASH读写实验含代码(HAL库)_stmflash_write函数

1.2 STM32的Flash补充说明

(1) STM32根据闪存(Flash)容量的大小,将Flash分为每页1K字节每页2K字节超过256K容量的每页为2K字节。对于本次使用的SMT32F103C8T6,其容量为64K(可选配置128k,官方不推荐可能存在隐患),则内部分为每页1K字节。

(2) SMT32的Flash起始地址为0X0800 0000 。本次使用的STM32F103C8T6的FLASH范围是0X08000000-0X0800FFFF。示意图如下:

(3) STM32运行代码从地址0X0800 0000开始,所以我们使用的Flash空间开始地址应该往后偏移,不然就会将程序部分覆盖掉。

(4) Flash的写操作,需要擦除一整页后再重新写入,不能对特定处进行修改,写的时候可以分多次写入

(5) 擦写次数较多数据的不建议使用内部Flash进行存储,手册中给的数据是擦写1W次

二、CubexMX配置

1、RCC配置外部高速晶振(精度更高)——HSE;

2、SYS配置:Debug设置成Serial Wire否则可能导致芯片自锁);

3、时钟树配置:

4、工程配置

三、代码

flash读写流程图:

注意:要明白**Flash的编程原理都是只能将1写为0,而不能将0写为1,**所以在进行Flash编程前,必须将对应的块擦除,即将该块的每一位都变为1,块内所有字节变为0xFF。

flash.h函数:

#ifndef __FLASH_H__
#define __FLASH_H__
#include "main.h"  
//=========================数据类型宏定义
#define u8 uint8_t
#define u16 uint16_t
#define u32 uint32_t
#define __IO    volatile 
typedef __IO uint16_t vu16;
 
//=========================用户根据自己的需要设置
#define STM32_FLASH_SIZE 	64 	 	//所选STM32的FLASH容量大小(单位为K)
    #if     STM32_FLASH_SIZE < 256      //设置扇区大小
    #define STM_SECTOR_SIZE     1024    //1K字节
    #else 
    #define STM_SECTOR_SIZE	    2048    //2K字节
    #endif	
#define STM32_FLASH_BASE    0x08000000 		//STM32 FLASH的起始地址
#define FLASH_SAVE_ADDR     STM32_FLASH_BASE+STM_SECTOR_SIZE*62	//写Flash的地址,这里从倒数第二页开始
#define STM32_FLASH_WREN 	1              	//使能FLASH写入(0,不是能;1,使能)
#define FLASH_WAITETIME  	50000          	//FLASH等待超时时间

 
u8 STMFLASH_GetStatus(void);				  //获得状态
u8 STMFLASH_WaitDone(u16 time);				  //等待操作结束
u8 STMFLASH_ErasePage(u32 paddr);			  //擦除页
u8 STMFLASH_WriteHalfWord(u32 faddr, u16 dat);//写入半字
u16 STMFLASH_ReadHalfWord(u32 faddr);		  //读出半字  
void STMFLASH_WriteLenByte(u32 WriteAddr,u32 DataToWrite,u16 Len);	//指定地址开始写入指定长度的数据
u32 STMFLASH_ReadLenByte(u32 ReadAddr,u16 Len);						//指定地址开始读取指定长度数据
void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite);		//从指定地址开始写入指定长度的数据
void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead);   		//从指定地址开始读出指定长度的数据
void Flash_PageErase(uint32_t PageAddress);     //扇区擦除
						   
#endif

flash.c函数:

#include "flash.h"

FLASH_ProcessTypeDef p_Flash; 
u16 STMFLASH_BUF[STM_SECTOR_SIZE/2];    //缓存数组
 
 /**********************************************************************************
  * 函数功能: 读取指定地址的半字(16位数据) 
  * 输入参数: faddr:读地址
  * 返 回 值: 对应数据
  * 说    明: 
  */
u16 STMFLASH_ReadHalfWord(u32 faddr)
{
	return *(vu16*)faddr; 
}
 
#if STM32_FLASH_WREN	//如果使能了写   
 /**********************************************************************************
  * 函数功能:不检查的写入
  * 输入参数: WriteAddr:起始地址、pBuffer:数据指针、NumToWrite:半字(16位)数 
  * 返 回 值: 无
  * 说    明: 
  */
void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)   
{ 			 		 
	u16 i;
	for(i=0;i<NumToWrite;i++)
	{
		HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD,WriteAddr,pBuffer[i]);
	    WriteAddr+=2;//地址增加2.
	}  
} 
 /**********************************************************************************
  * 函数功能:从指定地址开始写入指定长度的数据
  * 输入参数:WriteAddr:起始地址(此地址必须为2的倍数!!)、pBuffer:数据指针、NumToWrite:半字(16位)数(就是要写入的16位数据的个数.)
  * 返 回 值: 无
  * 说    明: 
  */
void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)	
{
	u32 secpos;	   //扇区地址
	u16 secoff;	   //扇区内偏移地址(16位字计算)
	u16 secremain; //扇区内剩余地址(16位字计算)	   
 	u16 i;    
	u32 offaddr;   //去掉0X08000000后的地址
	
	if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return;//非法地址
	
	HAL_FLASH_Unlock();					    //解锁
	offaddr=WriteAddr-STM32_FLASH_BASE;		//实际偏移地址.
	secpos=offaddr/STM_SECTOR_SIZE;			//扇区地址  0~64 for STM32F103C8T6
	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();		//上锁
}
#endif
 /**********************************************************************************
  * 函数功能:从指定地址开始读出指定长度的数据
  * 输入参数:ReadAddr:起始地址、pBuffer:数据指针、NumToWrite:半字(16位)数
  * 返 回 值: 无
  * 说    明: 
  */
void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)   	
{
	u16 i;
	for(i=0;i<NumToRead;i++)
	{
		pBuffer[i]=STMFLASH_ReadHalfWord(ReadAddr);//读取2个字节.
		ReadAddr+=2;//偏移2个字节.	
	}
}
 
 /**********************************************************************************
  * 函数功能:擦除扇区
  * 输入参数:PageAddress:擦除扇区地址
  * 返 回 值: 无
  * 说    明: 
  */
void Flash_PageErase(uint32_t PageAddress)
{
  /* Clean the error context */
  p_Flash.ErrorCode = HAL_FLASH_ERROR_NONE;
 
#if defined(FLASH_BANK2_END)
  if(PageAddress > FLASH_BANK1_END)
  { 
    /* Proceed to erase the page */
    SET_BIT(FLASH->CR2, FLASH_CR2_PER);
    WRITE_REG(FLASH->AR2, PageAddress);
    SET_BIT(FLASH->CR2, FLASH_CR2_STRT);
  }
  else
  {
#endif /* FLASH_BANK2_END */
    /* Proceed to erase the page */
    SET_BIT(FLASH->CR, FLASH_CR_PER);
    WRITE_REG(FLASH->AR, PageAddress);
    SET_BIT(FLASH->CR, FLASH_CR_STRT);
#if defined(FLASH_BANK2_END)
 
## 最后

**自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**

**深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。**

**因此收集整理了一份《2024年嵌入式&物联网开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**

![img](https://img-blog.csdnimg.cn/img_convert/d0b70dd8f8e87ec159a20d3fa7c30fa8.png)

![img](https://img-blog.csdnimg.cn/img_convert/8d09bf984bdbf234d9a57d9e9eb2faa1.jpeg)

![img](https://img-blog.csdnimg.cn/img_convert/2f4517c72fdc82add50c161a19e1feba.png)

 ![img](https://img-blog.csdnimg.cn/img_convert/e1efeec80026a9c9cce7055f6eb51a48.png)

![img](https://img-blog.csdnimg.cn/img_convert/0420fffaa5d67f954cdb13963fdc8d43.png)

![img](https://img-blog.csdnimg.cn/img_convert/dc444a99c56a13f06a883f21a7a486cc.png)

![](https://img-blog.csdnimg.cn/img_convert/7617fb764ba63e4018baf1302243eae1.png)

 

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!**

[**如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!**](https://bbs.csdn.net/topics/618654289)

**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**!!


g-bo0aIIcR-1715684554796)]

 

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!**

[**如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!**](https://bbs.csdn.net/topics/618654289)

**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**!!


  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值