用stm32的flash保存数据的优化方法

最开始用stm32的flash保存数据的方法都是用原子的例程,STM32F1的话,原子的方法大概是创建一个1K或者2K的缓存,修改数据的时候,先把该扇区的所有数据写到该缓存,然后查看是否需要擦除整个扇区,一般在一个地方写的话,必须要擦除,要想不擦除,就需要一个变量记录下一次要写的地址,和数据一块保存。STM32F4的话,因为其最小扇区为16K,最大128K,写个稍大点的程序,就得用大扇区,原子的做法干脆不缓存了,直接擦了扇区,重写!(吐个槽,原子的一些程序可以再优化一下,感觉有些源码就是应付事儿,可以向更实用的更有效的方向发展发展嘛!!)回归正题,有一天,有一个项目用的屏幕不是静态显示的,需要不停的扫,每次保存数据的时候屏都会闪一次,原来是保存数据的时候,要擦除扇区,1K的扇区大概要15ms的时间才能擦除完成,而且这段时间单片机什么都不能干。为了解决这个问题,发现了网上有stm32flash模拟EEPROM的程序,学习后发现,比原子的例程更实用,更有效,既提高了存取速度,又能平均磨损flash,延长flash改写寿命。大家可百度stm32flash模拟EEPROM,还有人优化了官方给的demo。优化过后,加入了CurWrAddress,意在提高读写速度,但是正是这个CurWrAddress,引起了一些bug。

1 /* Global variable used to store variable value in read sequence */
2 uint16_t DataVar = 0;
3 uint32_t CurWrAddress;
4 /* Virtual address defined by the user: 0xFFFF value is prohibited */
5 extern uint16_t VirtAddVarTab[NumbOfVar];

第3行 把CurWrAddress初始化为0,就是一个bug。修改后的代码, 把InitCurrWrAddress()函数放在了__EE_Init()之后,也就是说只要__EE_Init()函数用到了CurWrAddress,那么CurWrAddress = 0,有某种情况下,这是个灾难。

uint16_t EE_Init(void)
{
uint16_t FlashStatus;
   
   FlashStatus=__EE_Init();
   
   InitCurrWrAddress();
   
   return(FlashStatus);
}

 

接下来,看__EE_Init()函数

  1   uint16_t __EE_Init(void)
  2    {
      ...
13 14 /* Check for invalid header states and repair if necessary */ 15 switch (PageStatus0) 16 { 17 case ERASED:
...
33 case RECEIVE_DATA: 34 if (PageStatus1 == VALID_PAGE) /* Page0 receive, Page1 valid */ 35 {           ... 43 if (VarIdx != x) 44 { 45 /* Read the last variables' updates */ 46 ReadStatus = EE_ReadVariable(VirtAddVarTab[VarIdx], &DataVar); .... 57 } 59 } ... 85 case VALID_PAGE: 95 {103 if (VarIdx != x) 104 { 105 /* Read the last variables' updates */ 106 ReadStatus = EE_ReadVariable(VirtAddVarTab[VarIdx], &DataVar);           ...118 } 119 } 120 ....140 } 141 142 return FLASH_COMPLETE; 143 }

我保留的出灾难的两种情况,第一PAGE0=VALID_PAGE,PAGE0 = RECEIVE_DATA,另一种反过来,其实就是在换页的时候,没有完成就掉电了,上电后初始化时,就会有bug了。为什么会有bug?因为这两种情况都用到了EE_ReadVariable函数,而优化后的EE_ReadVariable函数在读取保存变量的时候,是从CurWrAddress-2开始往后读,直到这一页的开始,问题来了,一开始CurWrAddress=0啊,uint32_t类型的0,减去2后等于多少?关键是还要用这个数,作为地址去读flash。。。所以一旦出现这种情况,GG。解决办法就是在碰到这两种情况的时候在读取变量之前,先调用InitCurrWrAddress(),并声明CurWrAddress的时候初始化为第二页的结尾(最起码不会GG了),调用InitCurrWrAddress()之后,会把CurrWrAddress更改为有效页或者RECEIVE页(如果状态为RECEIVE_DATA),然后再去读取数据,进行换页。这种办法并不能解决全部已知bug。

到这里还没完,还有一个bug,就是PAGE1要满了,换到PAGE0的时候。

 1 static uint16_t EE_PageTransfer(uint16_t VirtAddress, uint16_t Data)
 2 {
        ...
 8   /* Get active Page for read operation */
 9   ValidPage = EE_FindValidPage(READ_FROM_VALID_PAGE);
10 
11      ...19   if (ValidPage == PAGE1)  /* Page0 valid */
20   {
21     /* New page address where variable will be moved to */
22     NewPageAddress = PAGE0_BASE_ADDRESS;
23 
24     /* Old page address where variable will be taken from */
25     OldPageAddress = PAGE1_BASE_ADDRESS;
26   }
    ...
32   /* Set the new Page status to RECEIVE_DATA status */
33   FlashStatus = FLASH_ProgramHalfWord(NewPageAddress, RECEIVE_DATA);
    ...
40   InitCurrWrAddress();//aft 重新初始化写地址
41   /* Write the variable passed as parameter in the new active page */
42   EepromStatus = EE_VerifyPageFullWriteVariable(VirtAddress, Data);
    ....
49   /* Transfer process: transfer variables from old to the new active page */
50   for (VarIdx = 0; VarIdx < NumbOfVar; VarIdx++)
51   {
52     if (VirtAddVarTab[VarIdx] != VirtAddress)  /* Check each variable except the one passed as parameter */
53     {
54       /* Read the other last variable updates */
55       ReadStatus = EE_ReadVariable(VirtAddVarTab[VarIdx], &DataVar);
      ...67     }
68   }
69 
70   /* Erase the old Page: Set old Page status to ERASED status */
71   FlashStatus = FLASH_ErasePage(OldPageAddress);
72   /* If erase operation was failed, a Flash error code is returned */
73   if (FlashStatus != FLASH_COMPLETE)
74   {
75     return FlashStatus;
76   }
77 
78   /* Set new Page status to VALID_PAGE status */
79   FlashStatus = FLASH_ProgramHalfWord(NewPageAddress, VALID_PAGE);
80   /* If program operation was failed, a Flash error code is returned */
81   if (FlashStatus != FLASH_COMPLETE)
82   {
83     return FlashStatus;
84   }
85 
86   /* Return last operation flash status */
87   return FlashStatus;
88 }

33行执行完后,要更改CurrAddress了,这时如果是PAGE0要接收,那么CurrAddress = PAGE0_StarAdress+4了,那么在后边的读取数据,用于转换的时候,有个判断

Address=CurWrAddress-2;

while (Address > (PageStartAddress + 2)) 这个地方PageStartAddress = Page1_StarAddress ,而PAGE0_StarAdress+2肯定小于Page1_StarAddress啊,直接跳过了,导致不能转存其他数据

 1  EE_ReadVariable函数
    /* Get active Page for read operation */
    ValidPage = EE_FindValidPage(READ_FROM_VALID_PAGE);//而READ的规则是谁有效 就是谁 不关系是否RECIVE 和写有效区分
  PageStartAddress = (uint32_t)(EEPROM_START_ADDRESS + (uint32_t)(ValidPage * PAGE_SIZE)); 2 3 /* Get the valid Page end Address */ 4 //Address = (uint32_t)((EEPROM_START_ADDRESS - 2) + (uint32_t)((1 + ValidPage) * PAGE_SIZE)); 5 Address=CurWrAddress-2; 6 7 /* Check each active page address starting from end */ 8 while (Address > (PageStartAddress + 2)) 9 { 10 /* Get the current location content to be compared with virtual address */ 11 AddressValue = (*(__IO uint16_t*)Address); 12 13 /* Compare the read address with the virtual address */ 14 if (AddressValue == VirtAddress) 15 { 16 /* Get content of Address-2 which is variable value */ 17 *Data = (*(__IO uint16_t*)(Address - 2)); 18 19 /* In case variable value is read, reset ReadStatus flag */ 20 ReadStatus = 0; 21 22 break; 23 } 24 else 25 { 26 /* Next address location */ 27 Address = Address - 4; 28 } 29 }

我的做法是,修改 EE_ReadVariable函数,在读取数据的时候,如果PAGE0或者PAGE1有一个状态为正在接受,那么Address就用官方Demo给的语句赋值。这时解决全部这篇所说bug的方法。

//之前加上读取PageStatus0和1的语句
1     /* Get the valid Page end Address */
2     if((PageStatus0 == RECEIVE_DATA)||(PageStatus1 == RECEIVE_DATA))//当在页传输时,地址放在有效页的末尾来搜索所有的存储信息
3     {
4         Address = (uint32_t)((EEPROM_START_ADDRESS - 2) + (uint32_t)((1 + ValidPage) * PAGE_SIZE));
5     }
6     else
7     {
8         Address=CurWrAddress-2;
9     }

目前遇到的bug就这些。

综上所述,都是CurWrAddress闹得,

第一种:因为初始化CurWrAddress为0,在刚上电时候,恰巧碰到有一页状态为RECEIVE_DATA(概率不大),则在执行EE_ReadVariable函数的时候,Address=CurWrAddress-2;导致得到一个非法地址。

第二种:PAGE1满 要转给PAGE0,还是出在EE_ReadVariable函数里,Address=CurWrAddress-2;因为EE_PageTransfer中在转移数据之前,重新调整了CurWrAddress写地址到PAGE0了,而在读取未转移的地址需要从PAGE1底部开始查询,这就导致转移不全。

解决办法就是修改EE_ReadVariable函数,加入读取PAGE0和PAGE1的状态语句,并判断,如果有一个状态为RECEIVE_DATA,则按官方的调整Address。

 

加上stm32的掉电检测保存数据,上电后,检测扇区剩余空间,不够下一次保存的话,提前换页,因为stm32f4擦除一个扇区要1s多,掉电那些时间根本不够,另外掉电后,阈值2.7v的话,测试发现,保守估计可保存1000个字,和外围器件,电容有关。

最后上张stm32flash保存数据的图,0x8040000是扇区6的首地址,用于保存这一页的状态00 00为有效页标志,0x8040004和05保存的是数据,06和07的01 A1保存的是变量虚拟地址,实际上是0XA101,因为STM32是小端模式,低位字节位排放在低地址端,高位字节排放在高位地址端。

 

转载于:https://www.cnblogs.com/Rainingday/p/6573968.html

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32系列微控制器可以使用Flash存储器来模拟EEPROM的功能,用来保存数据Flash存储器是一种非易失性存储器,可以长期保存数据而无需外部电源供应。在STM32中,Flash存储器通过特定的编程方式可以被分成多个扇区,每个扇区可以被单独擦除和编程。 通常情况下,STM32Flash存储器会被分成两个部分:主存储区和系统存储区。主存储区用来保存程序代码,而系统存储区则可以被用来当作EEPROM来保存数据。在使用STM32Flash存储器来模拟EEPROM时,首先需要确定要保存数据类型和大小,然后选择适当的存储区域来进行操作。 为了在Flash存储器中保存数据,首先需要将数据写入一个指定的存储区。STM32通过提供的Flash编程库函数可以实现对Flash存储器的写入、擦除和读取操作。在写入数据时,需要确保数据写入的地址和长度是合法的,并且需要进行相应的校验以确保数据写入的准确性和完整性。 另外,为了避免频繁的擦写操作导致Flash存储器的寿命缩短,可以采用一些优化策略,比如使用存储器块循环写入数据、使用擦除标记来减少擦写次数等。同时,还可以通过定期备份数据或者使用数据校验码的方式来确保数据的安全性和完整性。 总而言之,STM32系列微控制器可以很方便地使用Flash存储器来模拟EEPROM的功能,通过合理的编程和管理,可以实现数据的长期保存和安全性保障。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值