最近有个项目,需要频繁的操作移远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   微信公众号:物联网技术交流与学习

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

  1. 物联网-移远M26模块OpenCPU开发第3讲(看门狗)

    之前在物联网-移远M26模块OpenCPU开发第1讲中提到过需要给外部看门狗芯片喂狗才能正常工作,阅读过的读者应该知道,我是通过周期性的控制M26的一个GPIO引脚电平变化来实现喂狗的.关于其硬件参考 ...

  2. 物联网-移远M26模块OpenCPU开发第1讲

    心心恋恋的基于移远M26模块设计的用于开发OpenCPU功能的板子终于完成,废话少说,直接上图: 对于板子外观这里不作讨论,毕竟不是我设计的,期望能用吧. 可能是由于设计者经验不足的原因吧.我刚拿到板 ...

  3. 物联网-移远M26模块MQTT开发(AT命令)

    MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)是IBM开发的一个即时通讯协议,有可能成为物联网的重要组成部分.该协议支持所有平台,几乎可以把所有联 ...

  4. 物联网-移柯L206模块TCP开发(基于STM32+AT命令)

    最近一直在做2g模块联网的项目,用到了移柯L206 2G模块,今天就简单讲一下移柯L206 2G模块基于STM32+AT命令开发TCP联网的过程,参考文档:移柯L206 TCPIP开发流程说明V1.1 ...

  5. NBIOT 移远BC28模块+stm32开发板例程、教程(打通TCP、COAP协议)

    为了开发者能够快速入门BC28的开发,加快开发者的项目进度,开发出此款针对BC28的stm32开发板和完善的发送网络数据的示例程序(TCP协议.COAP协议),程序流程逻辑清楚,注释完善,上手就可以玩 ...

  6. 大白NBIOT 移远BC28模块+stm32开发板例程、教程(打通TCP、COAP协议)

    大白为了开发者能够快速入门BC28的开发,加快开发者的项目进度,大白开发出此款针对BC28的stm32开发板和完善的发送网络数据的示例程序(TCP协议.COAP协议),程序流程逻辑清楚,注释完善,上手 ...

  7. 嵌入式linux驱动开发之移远4G模块EC800驱动移植指南

    回顾下移远4G模块移植过程, 还是蛮简单的.一通百通,无论是其他4G模块都是一样的.这里记录下过程,分享给有需要的人.环境使用正点原子的imax6ul开发板,板子默认支持中兴和移远EC20的驱动,这里 ...

  8. NBIOT(3)---NBIOT 移远BC28模块+stm32

    NBIOT 移远BC28模块+stm32 大白为了开发者能够快速入门BC28的开发,加快开发者的项目进度,大白开发出此款针对BC28的stm32开发板和完善的发送网络数据的示例程序(TCP协议.COA ...

  9. 大白NBIOT 移远BC26模块模组OpenCpu开发视频教程(有连接移动、电信平台的opencpu源码)

    NBIOT(BC26.BC28,BC35)模块 移远BC26全网通模组OpenCpu实现TCPCLient网络数据传输 [复制链接]   http://www.openedv.com/forum.ph ...

最新文章

  1. 栈溢出原理 小记 基础中的基础~~
  2. IOS开发笔记16-Object-C中的属性
  3. c#中string字符串转为json与json转对象
  4. 机器学习方法三要素-阿里云大学
  5. tomcat7 java_Tomcat7安装(jdk 1.7环境)
  6. [JavaWeb-JavaScript]JavaScript特殊语法
  7. String,StringBuffer,StringBuilder简单对比
  8. Oracle 时区(TimeZone )-- DST
  9. 套装门安装_室内套装门-油漆工艺
  10. USACO-Section1.2 Friday the Thirteenth (简单日期处理)
  11. div iframe 显示html,IE中iframe标签显示在DIV之上的问题解决方案
  12. 最常用的数据库脚本前十名
  13. SAP License:新总账的凭证分割
  14. buntu12.10 64位 + android-ndk-r9 编译ffmpeg遇到的问题
  15. 红米k30pro工程测试代码_红米K30 PRO代号曝光,确定推出双版本,更强拍照对标荣耀30...
  16. echarts地图自动轮播
  17. MySQL学习笔记(七)视图,触发器,事务,存储过程,函数,流程控制,索引
  18. LeetCode——5805. 最小未被占据椅子的编号(The Number of the Smallest Unoccupied Chair)[中等]——分析及代码(Java)
  19. 三种设置session有效时间的方法
  20. Kudu 原理、API使用、代码

热门文章

  1. 关于微信和支付宝小程序审核记录分享
  2. cdn dashjs_CSS以及JS各种库的在线CDN引用地址
  3. Flink java作为消费者连接虚拟机中的kafka/或本地的kafka,并解决java.net.UnknownHostException报错
  4. 消融实验——Ablation experiment
  5. Easyconnect mac版本下载地址
  6. mysql数据类型的学习心得_MySQL自学总结 (一到四章)
  7. android没有界面app,Android离线打包 app处于原生界面 切换到桌面再点击app 原生界面被关闭...
  8. 怎样让计算机快速开机,怎样设置可以让电脑快速启动开机.doc
  9. Beta版本(有更改)
  10. [BZOJ5286][洛谷P4425][HNOI2018]转盘(线段树)