物联网-移远M26模块OpenCPU开发第2讲(FLASH处理)

最近有个项目,需要频繁的操作移远M26的内部flash。众所周知,flash的擦写次数为10W次,超过这个次数后flash就没有用了。这个项目需要频繁的对其进行写操作,要想设备工作3~5年,10W明显不够用呀。

通过百度和谷歌查找解决办法,找到一个“平衡磨损算法”,大体思想就是将读写操作平均在每一块FLASH上。参见Leeee的博客:Flash存储器磨损均衡原理及实现。当然了只是理论知识,没有具体的实现。而且这个有一个局限性,它是建立在flash可以按字节擦写的。一般芯片得内部flash都不是按字节擦写的,为了读写速度,大多是按页或者块擦写。查看了一下M26 OpenCPU的指导手册flash相关章节“File System API”,它是以文件系统形式进行操作的,指导手册参见:Quectel_M26-OpenCPU_User_Guide_V1.0

这个应该是不好实现平衡磨损算法了,而且手册中2.2.2章节有一句话,很刺眼,如下:

不支持用户访问,我只能呵呵了,那么大的区域没法访问,浪费呀。那是不是这个项目就要黄了呢?还是要增加外部flash芯片呢?通过翻看移远提供的opencpu开发包里的文档,里面有一个篇文章是介绍安全数据区的,文档如下:Quectel_OpenCPU_Security_Data_Application_Note_V1.0

通过阅读该文档,发现这个其实就是操作内部flash,可以实现断电保持功能。介绍如下:

可惜的是,他也不是按字节访问的,而是按块,他被分成13块,每个块字节数不定,具体介绍如下:

所有介绍详情参见手册,手册不长,也就11页,而且有用部分也就5、6页.

好在每个块最少也有50字节的存储量,对于这个应用完全够了。既然不能按照常规的平衡磨损算法来实现增加FLASH擦写次数,那就退而求其次,使用我的粗暴处理方式,一个块可以擦写10W次,13块不就是130W次了,为了保险起见,每个块擦写99900次。

呵呵,这样处理方法的思路就出来了,先对第一个块进行访问,当其擦写次数达到99900次时,开始对第二个块访问,不再对第一个块进行写操作,以次类推,不就有99900*13=1287000次。每次要将当前次数写入数据区。每十秒操作一次,也就是3757h,每天工作2~3小时,差不多3~5年,完全可以满足要求了。

废话不多说,直接上代码:

// 保存参数,工作时间及已经工作时间
if(++timerCnt[2] >= 10)				
{	
		for(i = 0; i < 13; i++)								
		{														
				Ql_memset(pchData, 0x0, sizeof(pchData));  					         
				dataLen = Ql_sprintf(pchData, "<-- i:%d -->\r\n",i);
				Ql_UART_Write(UART_PORT1,(u8*)pchData,dataLen);	
				
				ret = Ql_SecureData_Read(i+1, &g_WorkTimeRd, 32);
		    if(32 == ret)
		    {					
		    	   // 读出擦写次数        				 
    				 ReadTimes[i] = (u32)g_WorkTimeRd[28]|(u32)(g_WorkTimeRd[29] << 8)|(u32)(g_WorkTimeRd[30] << 16)|(u32)(g_WorkTimeRd[31] << 24);	
    				 
    				 // 小于最大次数,继续使用当前块
			       if(ReadTimes[i] < WRITE_FLASH_TIMES_MAX)
			    	 {					        				 
         				  Ql_memset(pchData, 0x0, sizeof(pchData));					    		 
						      dataLen = Ql_sprintf(pchData, "<-- Read back data from security data region. Security data region index:%d WriteFlashCnt:%d-->\r\n",i+1,ReadTimes[i]);	
								  Ql_UART_Write(UART_PORT1,(u8*)pchData,dataLen);																	 
						 
				    		 	WriteFlashIndex = i+1;
	        				Ql_memset(pchData, 0x0, sizeof(pchData));  					         
									dataLen = Ql_sprintf(pchData, "<-- Security data region index:%d -->\r\n",WriteFlashIndex);
									Ql_UART_Write(UART_PORT1,(u8*)pchData,dataLen);
				    			break;
			    	 }
			  }
			  // 读事变,表示当前块还没有操作过,后面使用当前块
		    else
		    {  
				    WriteFlashIndex = i+1;
    				Ql_memset(pchData, 0x0, sizeof(pchData));  					         
						dataLen = Ql_sprintf(pchData, "<-- Fail to read back data from security data region. %d -->\r\n",ret);
						Ql_UART_Write(UART_PORT1,(u8*)pchData,dataLen);
				    break;
		    }
  	}	
  	  // 全部操作完,重启系统
//										  	if(i >= 13)
//									  		{									 
//						        				Ql_memset(pchData, 0x0, sizeof(pchData));  					         
//														dataLen = Ql_sprintf(pchData, "<-- FLASH 使用期限已到,将关闭系统 -->\r\n");
//														Ql_UART_Write(UART_PORT1,(u8*)pchData,dataLen); 				
//														
//        										Ql_Reset(0);                  // 关闭连接,重新启动系统
//									  		}								    	
    // 擦写次数累加			
		WriteFlashCnt++;
		
		// 超过最大次数重新计数
		if(WriteFlashCnt > WRITE_FLASH_TIMES_MAX)
		{														
				Ql_memset(pchData, 0x0, sizeof(pchData));
				dataLen = Ql_sprintf(pchData, "<-- Security data region index:%d 缓存已满 -->\r\n",WriteFlashIndex-1);
				Ql_UART_Write(UART_PORT1,(u8*)pchData,dataLen);														
				
				WriteFlashCnt = 1;
		}
		
		// 处理要保存的数据		
	 	g_WorkTimeWr[0] = (u8)g_WorkTimeCnt;
	 	g_WorkTimeWr[1] = (u8)(g_WorkTimeCnt>>8);	
	 	g_WorkTimeWr[2] = (u8)(g_WorkTimeCnt>>16);	
	 	g_WorkTimeWr[3] = (u8)(g_WorkTimeCnt>>24);	
	 	
	 	g_WorkTimeWr[4] = (u8)g_WorkTimeSet;
	 	g_WorkTimeWr[5] = (u8)(g_WorkTimeSet>>8);	
	 	g_WorkTimeWr[6] = (u8)(g_WorkTimeSet>>16);	
	 	g_WorkTimeWr[7] = (u8)(g_WorkTimeSet>>24);												 	
	 	
	 	g_WorkTimeWr[8] = (u8)Receive_sjon.nPowerSwitch;											 	
	 	g_WorkTimeWr[9] = (u8)Receive_sjon.nMassageType;	
	 	g_WorkTimeWr[10] = (u8)Receive_sjon.nIntensityType;
	 	g_WorkTimeWr[11] = (u8)Receive_sjon.nHeatType;
	 	g_WorkTimeWr[12] = (u8)Receive_sjon.nPressureSwitch;
	 	
	 	
	 	g_WorkTimeWr[28] = (u8)WriteFlashCnt;
	 	g_WorkTimeWr[29] = (u8)(WriteFlashCnt>>8);	
	 	g_WorkTimeWr[30] = (u8)(WriteFlashCnt>>16);	
	 	g_WorkTimeWr[31] = (u8)(WriteFlashCnt>>24);			
	 												 	
	 	// 保持数据						 	
		ret = Ql_SecureData_Store(WriteFlashIndex, &g_WorkTimeWr, 32);
		if (ret !=QL_RET_OK)
		{
				Ql_memset(pchData, 0x0, sizeof(pchData));
				dataLen = Ql_sprintf(pchData, "<-- Fail to store critical data! Cause:%d -->\r\n", ret);
				Ql_UART_Write(UART_PORT1,(u8*)pchData,dataLen);
		}
		timerCnt[2] = 0;	
}									

这段代码只需要放在定时器1S中断函数里即可。当然了,这个并没有经过长时间的验证,我只是验证过50,100,120次的,每一秒读写一次。验证结果如下:m26 flash平衡磨损算法测试日志

对于以上代码不保证稳定性和安全性,后续会用在产品上进行测试,随时更新测试状态。小弟c语言功底浅薄、读者如有发现不足之处,烦请指出,谢谢。

欢迎读者提出疑问,可以加群讨论:838839442   微信公众号:物联网技术交流与学习

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值