STM32如何将double/float型参数写入Flash - STM32F4保存参数

      分享一篇如何在工程中将 double/float 型参数写入Flash并在需要的时候读取出来的代码。大家都知道 Flash 的读写是有寿命限制的,虽然我们读写的次数远不会达到这个次数,但是作为有强迫症的程序猿们一般都不能接受。而且读写 Flash 还要考虑安全性,不能破坏程序。STM32的 Flash 是分扇区的,Flash 的读写必须是以扇区为单位。下面介绍如何通过细分算法高效读写Flash 资源。

       下面的代码是基于STM32F407VGT的 Flash 的读写,为了尽可能不对程序造成影响,我选择扇区十一作为参数保存的区域,然后对这个扇区再以 256Bit 进行细分,当这个扇区空余内存不足 256Bit 时自动对扇区十一进行擦除。通过这个方法,不用再担心反复擦除带来的寿命问题。

头文件

       代码中的 sys.h 是采用的正点原子的文件。Flash_MicroSize要大于需要写入的数据的长度。

#ifndef __STMFLASH_H__
#define __STMFLASH_H__
#include "sys.h" 

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

//FLASH 扇区的起始地址
#define ADDR_FLASH_SECTOR_0     ((u32)0x08000000) 	//扇区0起始地址, 16 Kbytes  
#define ADDR_FLASH_SECTOR_1     ((u32)0x08004000) 	//扇区1起始地址, 16 Kbytes  
#define ADDR_FLASH_SECTOR_2     ((u32)0x08008000) 	//扇区2起始地址, 16 Kbytes  
#define ADDR_FLASH_SECTOR_3     ((u32)0x0800C000) 	//扇区3起始地址, 16 Kbytes  
#define ADDR_FLASH_SECTOR_4     ((u32)0x08010000) 	//扇区4起始地址, 64 Kbytes  
#define ADDR_FLASH_SECTOR_5     ((u32)0x08020000) 	//扇区5起始地址, 128 Kbytes  
#define ADDR_FLASH_SECTOR_6     ((u32)0x08040000) 	//扇区6起始地址, 128 Kbytes  
#define ADDR_FLASH_SECTOR_7     ((u32)0x08060000) 	//扇区7起始地址, 128 Kbytes  
#define ADDR_FLASH_SECTOR_8     ((u32)0x08080000) 	//扇区8起始地址, 128 Kbytes  
#define ADDR_FLASH_SECTOR_9     ((u32)0x080A0000) 	//扇区9起始地址, 128 Kbytes  
#define ADDR_FLASH_SECTOR_10    ((u32)0x080C0000) 	//扇区10起始地址,128 Kbytes  
#define ADDR_FLASH_SECTOR_11    ((u32)0x080E0000) 	//扇区11起始地址,128 Kbytes  

#define Flash_MicroSize		256

FLASH_Status FLASH_ProgramDouble(uint32_t Address, int64_t Data);
void STMFLASH_WriteDouble(u32 WriteAddr, double* pBuffer, u32 NumToWrite);		//从指定地址开始写入指定长度的数据

int64_t STMFLASH_ReadDoubleWord(u32 faddr);		  	//读出字  
void STMFLASH_ReadDouble(u32 ReadAddr, double* pBuffer, u32 NumToRead);   		//从指定地址开始读出指定长度的数据
u8 STMFlash_ReadDoubleLast(u32 ReadAddr, double* pBuffer, u32 NumToRead);

#endif


源代码

       在工程文件中需要添加 stm32f4xx_flash.c 源文件,这里将需要使用到的函数都封装好了,在需要的地方直接调用函数即可。Address参数一般为需要进行读写的扇区的首地址。

#include "Delay.h"
#include "USART.h"
#include "STMFlash.h"

uint16_t STMFLASH_GetFlashSector(u32 addr)
{
	if (addr<ADDR_FLASH_SECTOR_1)return FLASH_Sector_0;
	else if (addr<ADDR_FLASH_SECTOR_2)return FLASH_Sector_1;
	else if (addr<ADDR_FLASH_SECTOR_3)return FLASH_Sector_2;
	else if (addr<ADDR_FLASH_SECTOR_4)return FLASH_Sector_3;
	else if (addr<ADDR_FLASH_SECTOR_5)return FLASH_Sector_4;
	else if (addr<ADDR_FLASH_SECTOR_6)return FLASH_Sector_5;
	else if (addr<ADDR_FLASH_SECTOR_7)return FLASH_Sector_6;
	else if (addr<ADDR_FLASH_SECTOR_8)return FLASH_Sector_7;
	else if (addr<ADDR_FLASH_SECTOR_9)return FLASH_Sector_8;
	else if (addr<ADDR_FLASH_SECTOR_10)return FLASH_Sector_9;
	else if (addr<ADDR_FLASH_SECTOR_11)return FLASH_Sector_10;
	return FLASH_Sector_11;
}

/**************************************************************
函数名:FLASH_ProgramDouble(uint32_t Address, int64_t Data)
参数:待写入数据的起始地址Address、数据Data
功能:向Flash中写入一个Double类型数据
返回值:FLASH_Status状态值
***************************************************************/
FLASH_Status FLASH_ProgramDouble(uint32_t Address, int64_t Data)
{
	FLASH_Status status = FLASH_COMPLETE;

	assert_param(IS_FLASH_ADDRESS(Address));
	status = FLASH_WaitForLastOperation();

	if (status == FLASH_COMPLETE)
	{
		FLASH->CR &= CR_PSIZE_MASK;
		FLASH->CR |= FLASH_PSIZE_WORD;
		FLASH->CR |= FLASH_CR_PG;

		*(__IO int64_t*)Address = Data;

		status = FLASH_WaitForLastOperation();

		FLASH->CR &= (~FLASH_CR_PG);
	}
	return status;
}

/*********************************************************************
函数名:STMFLASH_WriteDouble(u32 WriteAddr,double* pBuffer,u32 NumToWrite)
参数:待写入数据的起始地址Address、Double类型数据指针pBuffer
写入数据的长度NumToWrite
功能:将Double类型数组全部写入到Flash中
返回值:无
**********************************************************************/
void STMFLASH_WriteDouble(u32 WriteAddr, double* pBuffer, u32 NumToWrite)
{
	int64_t temp;
	u32 addrx = 0;
	u32 endaddr = 0;
	FLASH_Status status = FLASH_COMPLETE;

	if (WriteAddr<STM32_FLASH_BASE || WriteAddr % 8)
	{
		//printf("待写入的地址是非法地址");
		return;//非法地址
	}

	FLASH_Unlock();
	addrx = WriteAddr;
	if (addrx < ADDR_FLASH_SECTOR_8)
	{
		FLASH_DataCacheCmd(DISABLE);//FLASH擦除期间,必须禁止数据缓存

		while (STMFLASH_ReadDoubleWord(addrx) != 0XFFFFFFFFFFFFFFFF)	//Flash_MicroSize Byte分割一个小区间,查询可用区间
		{
			addrx = addrx + Flash_MicroSize;
			if (ADDR_FLASH_SECTOR_8 - addrx < Flash_MicroSize)				//第七扇区已满,进行擦除
			{
				//printf("\r\nFlash第七扇区已满,准备擦除\r\n");
				addrx = WriteAddr;
				status = FLASH_EraseSector(STMFLASH_GetFlashSector(addrx), VoltageRange_3);//VCC=2.7~3.6V之间!!
				if (status != FLASH_COMPLETE)
				{
					//printf("Flash擦除失败\r\n");	
					break;									//发生错误了
				}
				//printf("Flash擦除成功\r\n");
			}
		}

		WriteAddr = addrx;
		endaddr = addrx + NumToWrite * 8;

		if (status == FLASH_COMPLETE)		//写入数据
		{
			while (WriteAddr < endaddr)	//写数据
			{
				temp = (int64_t)(*pBuffer * 100000000.0);
				if (FLASH_ProgramDouble(WriteAddr, temp) != FLASH_COMPLETE)
				{
					//printf("Flash写入失败\r\n");
					break;	//写入异常
				}
				WriteAddr += 8;
				pBuffer++;
			}
		}

		FLASH_DataCacheCmd(ENABLE);	//FLASH擦除结束,开启数据缓存
		FLASH_Lock();//上锁
	}
}

/*******************************************************************
函数名:STMFLASH_ReadDoubleWord(u32 faddr)
参数:准备读取数据的起始地址Address
功能:从Flash中读取一个Double类型数据
返回值:int64_t类型数据
********************************************************************/
int64_t STMFLASH_ReadDoubleWord(u32 faddr)
{
	return *(__IO int64_t*)faddr;
}

/*******************************************************************
函数名:STMFLASH_ReadDouble(u32 ReadAddr,double* pBuffer,u32 NumToRead)
参数:准备读取数据的起始地址Address、存放数据的Double类型数组pBuffer
读取的长度NumToRead
功能:从Flash中读取一组Double类型数据
返回值:无
********************************************************************/
void STMFLASH_ReadDouble(u32 ReadAddr, double* pBuffer, u32 NumToRead)
{
	for (u32 i = 0; i<NumToRead; i++)
	{
		pBuffer[i] = (double)(*(__IO int64_t*)ReadAddr) / 100000000.0;//读取8个字节.
		ReadAddr += 8;//偏移8个字节.	
	}
}

/********************************************************************
函数名:STMFlash_ReadDoubleLast(u32 ReadAddr,double* pBuffer,u32 NumToRead)
参数:准备读取数据的起始地址Address、存放数据的Double类型数组pBuffer
读取的长度NumToRead
功能:从Flash中读取最新存入的一组Double类型数据
返回值:读取是否成功
*********************************************************************/
u8 STMFlash_ReadDoubleLast(u32 ReadAddr, double* pBuffer, u32 NumToRead)
{
	u32 addrx;
	addrx = ReadAddr;
	while ((addrx < ADDR_FLASH_SECTOR_8) && (STMFLASH_ReadDoubleWord(addrx) != 0XFFFFFFFFFFFFFFFF))
	{
		addrx = addrx + Flash_MicroSize;
	}
	addrx = addrx - Flash_MicroSize;

	if (addrx < ADDR_FLASH_SECTOR_7)
	{
		//printf("\r\n没有预置的标准样片值!\r\n");
		return 0;
	}
	else
	{
		STMFLASH_ReadDouble(addrx, pBuffer, NumToRead);
	}

	return 1;
}


猜你喜欢

转载自blog.csdn.net/yishuicanhong/article/details/80717516