flash 磨损均衡处理

4 篇文章 0 订阅
1 篇文章 0 订阅

背景

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; 
	}
}


不定长数据读写

只需要修改一下数据帧格式就好,数据帧可做类链表串联,留一个字节值向下一个数据帧序号。
|帧头|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;
在这里插入图片描述

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

代码有空再补。

总结

只做抛砖引玉。

  • 4
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Linux中,磨损均衡算法是用来平衡闪存设备上的擦除操作,以延长设备的寿命并提高性能。根据引用\[1\]和引用\[2\]的内容,确定性算法和随机性算法是两种常见的磨损均衡算法。 确定性算法可以进一步分为周期性算法和全局性算法。周期性算法将闪存的寿命看作由一系列磨损均衡周期组成。在每个处理周期中,达到规定擦除次数的块将不会被选中进行擦除,以确保各个块达到相同的擦除次数。全局性算法则不划分处理周期,而是在全局范围内控制块的磨损均衡。当任何两个块的擦除次数之差超过给定的阈值,或者某个块的擦除次数超过所有块的平均擦除次数时,启动磨损均衡处理,将擦除次数少的块上的数据和擦除次数多的块上的数据进行交换,从而实现磨损均衡。 在Linux中,具体的磨损均衡算法实现会依赖于具体的闪存设备和文件系统。不同的设备和文件系统可能采用不同的算法来实现磨损均衡。因此,具体的算法实现可以在设备驱动程序或文件系统中找到。 引用\[3\]是一个Makefile示例,用于编译名为"flash_stress"的应用程序,该程序可能与闪存设备的磨损均衡算法相关。然而,根据提供的信息,无法确定该Makefile与具体的磨损均衡算法有何关联。 总结起来,Linux中的磨损均衡算法可以采用确定性算法或随机性算法,其中确定性算法又分为周期性算法和全局性算法。具体的算法实现取决于闪存设备和文件系统。提供的Makefile示例可能与闪存设备的磨损均衡算法相关,但无法确定具体的关联。 #### 引用[.reference_title] - *1* *2* [Flash磨损均衡技术解析(wear-leveling)](https://blog.csdn.net/dongyanxia1000/article/details/79458082)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [采用dd工具做nandflash磨损均衡](https://blog.csdn.net/chenliang0224/article/details/83142195)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值