本帖最后由 sky.sun.zz 于 2018-12-19 22:06 编辑
读《EEPROM emulation in STM32F0xx microcontrolles》一个BUG的修正
项目开发时需要在STM32F051断电前保存少量变量数据,于是在STM32中文官网找到《EEPROM emulation in STM32F0xx microcontrolles》这篇应用笔记。学习这篇应用笔记让我收益匪浅。它把STM32F0xx的内部Flash的2个页拿来仿真EEPROM,解决了低成本产品无需外挂eeprom断电保存数据的难题。 这个仿真EEPROM的构思实在巧妙,它为每一个要保存的数据预先设置一个16位虚拟地址,紧跟一个16位变量数据。当写入一个新数据时,只需重新写一次该数据的虚拟地址,接着写入新的数据即可。旧的数据作废,直到本页写满。读数据时搜索最后一次写入的地址,就能读出最新的数据。无需整页Flash都删除再写入,大大提高了Flash的读写寿命。本项目开发使用这个EEPROM仿真是优选的方案。
到 https://www.stmcu.com.cn/Designresource/design_resource_detail?file_name=STSW_STM32117_STM32F0xx%E5%BE%AE%E6%8E%A7%E5%88%B6%E5%99%A8%E4%B8%AD%E7%9A%84EEPROM%E4%BB%BF%E7%9C%9F&lang=EN&ver=1.0.0 下载了“STSW_STM32117_STM32F0xx微控制器中的EEPROM仿真”固件包。这个固件包演示了在仿真EEPROM的页里对VarData[0]、VarData[1]、VarData[2]写入数据,然后读出。
for (VarValue = 1; VarValue <= 0x64; VarValue++)
{ EE_WriteVariable(VirtAddVarTab[0], VarValue);
}
再读出。
EE_ReadVariable(VirtAddVarTab[0], &VarDataTab[0]);
for (VarValue = 1; VarValue <= 0xC8; VarValue++)
{ EE_WriteVariable(VirtAddVarTab[1], VarValue);
}
EE_ReadVariable(VirtAddVarTab[0], &VarDataTab[0]);
EE_ReadVariable(VirtAddVarTab[1], &VarDataTab[1]);
for (VarValue = 1; VarValue <= 0x1C2; VarValue++)
{ EE_WriteVariable(VirtAddVarTab[2], VarValue);
}
EE_ReadVariable(VirtAddVarTab[0], &VarDataTab[0]);
EE_ReadVariable(VirtAddVarTab[1], &VarDataTab[1]);
EE_ReadVariable(VirtAddVarTab[2], &VarDataTab[2]);
在NUCLEO开发板上运行演示代码进行评估验证,仿真EEPROM的数据写入读出都正确,放心移植到目标板了。
不料,目标板调试过程中发现,只要断电,再上电后读出的数据就不是断电前写入的数据了。排除硬件故障的可能性后,接下来是判断是写出错还是读出错。这片STM32F051C6T6的Flash长度为32KB,我照抄演示文件中的代码,将第8和第9扇区设置用来仿真EEPROM,它的每页长为0x400,地址从0x08002000到0x080027FF。使用STM32 ST-LINK Utility工具读该扇区,Flash已经正确写入。判断故障在读操作。 先查阅STM32F0xx的eeprom.h,其中有: #define PAGE_SIZE ((uint32_t)0x0400)
#define EEPROM_START_ADDRESS ((uint32_t)0x08002000)
这里定义:#defineEEPROM_START_ADDRESS ((uint32_t)0x08002000)
再查阅STM32F0xx的 eeprom.c: uint16_t EE_ReadVariable(uint16_t VirtAddress, uint16_t* Data)
{ uint16_t ValidPage = PAGE0;
uint16_t AddressValue = 0x7777, ReadStatus = 1;
uint32_t Address = 0x08001000, PageStartAddress = 0x08001000;
}
这里地址的分配是这样安排的: uint32_t Address= 0x08001000, PageStartAddress = 0x08001000; 这个EEPROM emulation in STM32F0xx是从EEPROM emulation in STM32F10xx移植过来的。于是查阅F1系列相关的文件,
STM32F10xx 的eeprom.h是这样的: #define EEPROM_START_ADDRESS ((uint32_t)0x08010000)
STM32F10xx的 eeprom.c是这样的: uint16_t EE_ReadVariable(uint16_t VirtAddress, uint16_t* Data)
{ uint16_t ValidPage = PAGE0;
uint16_t AddressValue = 0x5555, ReadStatus = 1;
uint32_t Address =0x08010000, PageStartAddress =0x08010000;
}
ST工程师在移植F1仿真EEPROM到F0仿真EEPROM时,忘记把F0的eeprom.h和eeprom.c文件里关于仿真Flash页的地址修改一致: 在F051写入的首地址是0x08002000,而读出的首地址则在0x08001000。从而导致读数据的地址不是写入数据的地址——读出错。 修改eeprom.c代码: uint32_t Address= 0x08002000, PageStartAddress = 0x08002000; 故障排除。 那为何在用nucleo进行评估验证时却读写正常呢?下载有BUG的原代码进行跟踪调试可知:由于读地址与写地址不符,进入读函数后当判断地址不符,就执行return了。根本就没有读数据。而恰好在演示代码中写完Flash后数组变量没有清空,在读出错return后,数组变量里保留着写操作留下的残余,让你误以为读写正常。 这个F0里的BUG,也希望ST工程师能纠正。
|