当前位置:   article > 正文

flash 磨损均衡处理_flash磨损均衡

flash磨损均衡

背景

flash为嵌入式设备中常见的存储器,优点:便宜,容量大,但缺点也比较明显,最大的缺点是寿命问题,flash编程只能将bit由1位置0,不能将0位置1,将0置1只能擦除扇区,而扇区往往比编程单位要大很多,哪怕我们只对对一个地址写两个字节的数据,也需要擦除整个扇区来完成数据更新,频繁擦写导致flash坏块。

本人这边做的一个小玩意里面需要存储一些掉电保存的数据,但修改频次又有点多,硬件上没有掉电维持电路,只能尽可能减少flash扇区擦写次数了。

磨损均衡原理

说人话就是,将一个大数据扇区差分城多个数据帧,轮流写操作数据帧,当所有的数据帧都写过了再擦除重新来过。这样本来我们每次修改都需要擦出扇区,优化为现在写满一个扇区再擦除,寿命得到了成倍的提升。这里是对flash单一扇区里的数据帧磨损均衡,同样可以对多个扇区进行扇区磨损均衡,同理。

磨损均衡实现

定长数据读写

本人需要存储的数据一个定长浮点型数组,只针对本人项目的需要,实现起来也相对来说简单些。

例如一个flash 扇区 2k,我们存储一个short[14]的数组。
我们将数据帧格式定义为 |帧头|data[28]||unused[2]|帧尾|
帧头 0xA5 为 数据帧有效,0XFF代表未使用,其他则代表数据帧废弃。扇区中只存在一个有效的数据帧。

读取:从扇区中遍历数据块,找到帧头0XA5的数据帧并检验数据返回
写入:从扇区中遍历数据块,找到帧头0XA5的数据帧置0(本人亲测STM32F103内部flash是可写入,可能有的平台不可以,未使用数据可拿一位做擦除标记位,),找到0XFF的帧头,若未找到则擦除扇区写入。

源码如下

#define SECTOR_SIZE      2048
#define FRAME_SIZE  32   //枕头帧尾  14*U16 + 2*U8

typedef struct {
	const u32 addr;   //起始地址
	u32 currAddr;   //起始地址
	u16 count;  //当前读写块
	u8 buff[FRAME_SIZE];  //数据
} FLASH_LEVELINGType;


FLASH_LEVELINGType flashLeveling = {
	.addr = 0x0803F800,
	.currAddr= 0x0803F800,
	.count= 0,
	.buff = {0},
};


void FlashLeveingWrite(u8 * data)
{
	FLASH_Unlock();					//解锁
	FlashNewFrame( );
	flashLeveling.buff[0] = 0xa5;
	flashLeveling.buff[FRAME_SIZE-1] = 0x5a;
	memcpy(flashLeveling.buff+1,data,FRAME_SIZE-2);
	FLASH_WaitForLastOperation(FLASH_WAITETIME);       	//等待上次操作完成
	STMFLASH_Write_NoCheck(flashLeveling.currAddr,(u16 *)flashLeveling.buff,FRAME_SIZE/2);
	
	DBG_DEBUG("0x%x %d写完%x\r\n",flashLeveling.currAddr,flashLeveling.count,flashLeveling.buff[0]);
	FLASH_Lock();		//上锁
}
u8* FlashLeveingRead()
{
	FlashActiveFrame();
	return flashLeveling.buff;
}

void FlashNewFrame()
{
	u16 i = 0;
	while(i <= SECTOR_SIZE/FRAME_SIZE)
	{
		flashLeveling.currAddr = flashLeveling.addr +i* FRAME_SIZE;
		FLASH_Read(flashLeveling.buff,flashLeveling.currAddr, FRAME_SIZE);
		if( flashLeveling.buff[0] == 0xFF && flashLeveling.buff[FRAME_SIZE-1] == 0xFF)
		{
			DBG_DEBUG("%d写\r\n",i);
			flashLeveling.count = i;
			return;
		}else if( flashLeveling.buff[0] == 0xa5 && flashLeveling.buff[FRAME_SIZE-1] == 0x5a)
		{
			memset(flashLeveling.buff,0,FRAME_SIZE);
			FLASH_WaitForLastOperation(FLASH_WAITETIME);       	//等待上次操作完成
			STMFLASH_Write_NoCheck(flashLeveling.currAddr,(u16 *)flashLeveling.buff,FRAME_SIZE/2);//清0
		}
		i++;
	}
	if(i >SECTOR_SIZE/FRAME_SIZE )
	{
		FLASH_WaitForLastOperation(FLASH_WAITETIME);            	//等待上次操作完成
		DBG_DEBUG("擦%d\r\n",FLASH_ErasePage(flashLeveling.addr));
		CLEAR_BIT(FLASH->CR, FLASH_CR_PER);							/																
		flashLeveling.count = 0;
		flashLeveling.currAddr = flashLeveling.addr;
	}
}
void FlashActiveFrame()
{
	u16 i = 0;
	while(i <= SECTOR_SIZE/FRAME_SIZE)
	{
		flashLeveling.currAddr = flashLeveling.addr +i* FRAME_SIZE;
		FLASH_Read(flashLeveling.buff,flashLeveling.currAddr, FRAME_SIZE);
		if( flashLeveling.buff[0] == 0xa5 && flashLeveling.buff[FRAME_SIZE-1] == 0x5a)
		{
			flashLeveling.count = i;
			return;
		}
		i++;
	}
	if(i >SECTOR_SIZE/FRAME_SIZE )  //没存数据
	{
		flashLeveling.count = 0xffFF;//标示未使用一个数据块
		flashLeveling.buff[0] = 0xff; 
	}
}


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89

不定长数据读写

只需要修改一下数据帧格式就好,数据帧可做类链表串联,留一个字节值向下一个数据帧序号。
|帧头|data[28]||nextFrame|帧尾|

代码有空再补。

多个不定长数据存储读写

上面的其实都是对单一数据存储,一个时间点中只有一个有效数据在读写,而多个不定长数据存储,则某种程度上已经类似文件管理。

这样扇区应当分成两个区,一个作为 Frame Allocation Table(数据帧分配表),另一个作为数据区
Frame Allocation Table(数据帧分配表) 子项结构

以2048字节一个扇区为例 ,前64个字节作为FAT表,后面的为数据区,32字节为一数据帧。

FAT表子项结构定义:u16 :0x00 擦除帧,0xf7 数据帧被使用且是首帧, 0xf3 数据帧被使用非头帧

FRAME 结构:
|帧头|data[28]||nextFrame|帧尾|

这样可以根据FAT表获取扇区中总共有多少个数据被存储,至于区分这些数据哪一个数自己想要的,可以在frame结构着手。

例如 数据首帧,最开始存储的字符串为数据名,根据数据名获取对应的数据。

例如 saveDate = data1+ data2+ data3;
在这里插入图片描述

根据以上思路,应该是可以实现的。

代码有空再补。

总结

只做抛砖引玉。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/676302
推荐阅读
  

闽ICP备14008679号