这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » STM32 » NUCLEO-U083RC学习历程22-使用内部flash存储数据

共2条 1/1 1 跳转至

NUCLEO-U083RC学习历程22-使用内部flash存储数据

助工
2025-01-16 19:08:14     打赏

    ·前言:STM32 本身没有自带 EEPROM,但是 STM32 具有 IAP(在应用编程)功能,所以我们可以把它的 FLASH 当成 EEPROM 来使用。本章,我们将利用 STM32 内部的 FLASH 来实现数据的存储功能,这次我们是将数据直接存放在 STM32 内部的flash内部。

    STM32 的闪存模块由:主存储器、信息块和闪存存储器接口寄存器等3部分组成。主存储器,该部分用来存放代码和数据常数(如 const 类型的数据)。对于大容量产品,其被划分为 256 页,每页 2K 字节。注意,小容量和中容量产品则每页只有 1K 字节。从上图可以看出主存储器的起始地址就是 0X08000000, B0、B1 都接 GND 的时候,就是从 0X08000000开始运行代码的。

    信息块,该部分分为2个小部分,其中启动程序代码,是用来存储ST自带的启动程序,用于串口下载代码,当 B0 接 V3.3,B1 接 GND 的时候,运行的就是这部分代码。用户选择字节,则一般用于配置写保护、读保护等功能,本章不作介绍。闪存存储器接口寄存器,该部分用于控制闪存读写等,是整个闪存模块的控制机构。对主存储器和信息块的写入由内嵌的闪存编程/擦除控制器(FPEC)管理: 编程与擦除的高电压由内部产生。

   在执行闪存写操作时,任何对闪存的读操作都会锁住总线,在写操作完成后读操作才能正确地进行;既在进行写或擦除操作时,不能4进行代码或数据的读取操作。

二:闪存的软件编写流程:

1:检查 FLASH CR 的 LOCK 是否解锁,如果没有则先解锁

2:检查 FLASH SR 寄存器的 BSY 位,以确认没有其他正在进行的编程操作设置 FLASH CR 寄存器的 PG 位为’1’

3:在指定的地址写入要编程的半字

4:等待 BSY 位变为’0’

5:读出写入的地址并验证数据

三:STM32 的页擦除顺序为:

检查 FLASH CR 的 LOCK 是否解锁,如果没有则先解锁

检査 FLASH SR 寄存器的 BSY 位,以确认没有其他正在进行的闪存操作

设置 FLASH CR 寄存器的 PER 位为’1用 FLASH AR 寄存器选择要擦除的页

设置 FLASH CR 寄存器的 STRT 位为’1’等待 BSY 位变为’0’

读出被擦除的页并做验证

四:软件擦写几个常用的函数

4.1.锁定解锁函数

上面讲解到在对 FLASH 进行写操作前必须先解锁,解锁操作也就是必须在 FLASH寄存器内部;

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

我们在操作flash时候,操作之前需要将flash解锁,当操作完成后,需要将flash上锁。

4.2 写函数:

FLASH Status FLASH ProgramWord(uint32 tAddress, uint32 t Data);
FLASH Status FLASH ProgramHalfWord(uint32 tAddress, uintl6 t Data);
FLASH Status FLASH ProgramOptionByteData(uint32 t Address, uint8 t Data);

顾名思义分别为:FLASH ProgramWord 为 32 位字写入函数,其他分别为 16 位半字写入和用户选择字节写入函数。这里需要说明,32 位字节写入实际上是写入的两次 16 位数据,写完第一次后地址+2,这与我们前面讲解的 STM32 闪存的编程每次必须写入 16 位并不矛盾。写入 8位实际也是占用的两个地址了,跟写入 16 位基本上没啥区别。

4.3 擦写函数:

FLASH Status FLASH ErasePage(uint32 tPage Address);
FLASH Status FLASH EraseAllPages(void);
FLASH Status FLASH EraseOptionBytes(void);

这三个函数顾名思义,第一个函数是页擦除函数,根据页地址擦除特定的页数据,第二个函数是擦除所有的页数据,第三个函数是擦除用户选择字节数据。这三个函数使用非常简单。

4.4 获取flash状态功能

FLASH Status FLASH GetStatus(void);
typedef enum
{
FLASH BUSY = 1,//忙
FLASH ERROR PG,//编程错误
FLASH ERROR WRP,//写保护错误
FLASH COMPLETE,//操作完成
FLASH TIMEOUT//操作超时
}
FLASH Status;

操作之前需要判断一下,当时flash的状态,类似于DMA发送时,判断忙是一样的道理;

4.5.等待操作完成函数

在执行闪存写操作时,任何对闪存的读操作都会锁住总线,在写操作完成后读操作才能正确地进行;既在进行写或擦除操作时,不能进行代码或数据的读取操作。所以在每次操作之前,我们都要等待上一次操作完成这次操作才能开始。使用的函数是FLASH Status FLASH WaitForLastOperation(uint32 t Timeout)入口参数为等待时间,返回值是 FLASH 的状态,这个很容易理解,这个函数本身我们在固件库中使用得不多,但是在固件库函数体中间可以多次看到。

五:代码分享:

u16 STMFLASH_ReadHalfWord(u32 faddr)
{
	return *(vu16*)faddr; 
}
#if STM32_FLASH_WREN    
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;
	}  
} 

#if STM32_FLASH_SIZE<256
#define STM_SECTOR_SIZE 1024 //字节
#else 
#define STM_SECTOR_SIZE	2048
#endif		 
u16 STMFLASH_BUF[STM_SECTOR_SIZE/2];//最多是2K字节
void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)	
{
	u32 secpos;	  
	u16 secoff;	   
	u16 secremain; 	   
 	u16 i;    
	u32 offaddr;  
	
	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;			
	secoff=(offaddr%STM_SECTOR_SIZE)/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);		
																	
			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++;				
			secoff=0;				 	 
		   	pBuffer+=secremain;  	
			WriteAddr+=secremain*2;	   
		   	NumToWrite-=secremain;	
			if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;
			else secremain=NumToWrite;/
		}	 
	};	
	HAL_FLASH_Lock();		//上锁
}
#endif

void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)   	
{
	u16 i;
	for(i=0;i<NumToRead;i++)
	{
		pBuffer[i]=STMFLASH_ReadHalfWord(ReadAddr);
		ReadAddr+=2;	
	}
}

void Test_Write(u32 WriteAddr,u16 WriteData)   	
{
	STMFLASH_Write(WriteAddr,&WriteData,1);
}



关键词: NUCLEO-U083RC     flash     存储数据    

专家
2025-01-16 21:17:51     打赏
2楼

感谢分享


共2条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]