我正在使用STM32F401VCT6U"发现"我需要为用户提供一种在运行时在内存中写入地址的方法。
我写了可以简化为以下函数的内容:
uint8_t Write(uint32_t address, uint8_t* values, uint8_t count)
{
uint8_t index;
for (index = 0; index < count; ++index) {
if (IS_FLASH_ADDRESS(address+index)) {
/* flash write */
FLASH_Unlock();
if (FLASH_ProgramByte(address+index, values[index]) != FLASH_COMPLETE) {
return FLASH_ERROR;
}
FLASH_Lock();
} else {
/* ram write */
((uint8_t*)address)[index] = values[index]
}
}
return NO_ERROR;
}
在上面,address
是基地址,values
是一个大小至少为count
的缓冲区,其中包含要写入内存的字节数和count
个数字要写的字节数。
现在,我的问题如下:当在flash和address
中使用基座count=100
调用上述函数时,它会在前几次正常工作,编写传递的values
缓冲到闪光。然而,在那些前几次调用之后,我不能再写任何值了:我只能复位flash中的值中的位,例如,尝试将0xFF写入0x7F将在闪存中留下0x7F,而将0xFE写入0x7F将留下0x7E,任何值的0x00都会成功(但之后其他值都不会写入地址)。
我仍然可以通过更改基座address
来正常写入闪存中的其他地址,但只能再次几次(使用count=100
进行两次或三次调用)。
此行为表明已达到闪存的最大写入次数,但我无法想象它会如此之快。在疲惫之前我至少会想到10,000次写作。 那么我做错了什么?
答案 0 :(得分:5)
你误解了闪存是如何工作的 - 例如,它不像写EEPROM那么直接。您要描述的行为对于闪光灯来说是正常的 要重复写入相同的闪存地址,必须首先使用FLASH_EraseSector擦除整个扇区。通常,在擦除期间需要保留的任何数据都需要缓冲在RAM或另一个闪存扇区中 如果你重复编写一小块数据并且担心闪存烧坏需要多次擦除写入周期,你可能需要写一个闪存接口,每次写入你沿着闪存扇区移动你的数据来取消闪存,跟踪它从行业开始的当前偏差。只有当你在扇区中用完字节时才需要擦除并在扇区开始时重新开始。
答案 1 :(得分:1)
ST的“正确方式”详见AN3969: EEPROM emulation in STM32F40x/STM32F41x microcontrollers
这或多或少是这个过程:
这很疯狂,但我没想出来。
答案 2 :(得分:0)
我有一个经过工作和测试的解决方案,但它与@Ricibob的答案有很大不同,所以我决定回答这个问题。
由于我的用户可以在精选闪存扇区中的任何地方写入,因此我的应用程序无法处理在需要时擦除扇区的责任,同时仅缓存需要保留的数据。
结果,当我对用户的写操作不起作用时,我向用户转移了擦除扇区的责任(这样,用户可以自由地使用扇区中的另一个地址来避免写入擦除周期太多)。
基本上,我公开了write(uint32_t startAddress, uint8_t count, uint8_t* values)
函数,该函数在失败的情况下具有WRITE_SUCCESSFUL
返回码和CANNOT_WRITE_FLASH
。
我还为我的用户提供了一个getSector(uint32_t address)
函数,该函数返回与作为参数传递的地址对应的扇区的id,起始地址和结束地址。这样,用户知道擦除操作影响了什么范围的地址。
最后,我公开了一个eraseSector(uint8_t sectorID)
函数,用于擦除其id已作为参数传递的flash扇区。
写入失败的策略不同于@Ricibob的建议“擦除flash中的值是否与FF不同”,因为只要它只是bitreset,写入就会成为documented in the Flash programming manual({3}}这符合我在问题中观察到的行为):
注意:可以在不需要擦除操作的情况下进行连续写操作 将位从“1”更改为“0”。 写入“1”需要闪存擦除操作。 如果同时请求擦除和编程操作,则擦除操作是 首先执行。
所以我使用宏CAN_WRITE(a,b)
,其中a
是flash中的原始值,b
是所需的值。宏定义为:
!(~a & b)
因为:
!
)将0转换为true
,其他所有内容转换为false
,因此~a & b
必须等于0才能使宏true
}; a
中1位的任何位都在~a
中为0,因此无论b
中的值是什么,它都为0(您可以转换1合1或0 ); a
中的位为0,那么~a
中的位为1,如果b
等于1,则为~a & b != 0
,如果b
我们无法写入等于0没关系(你只能将0转换为0,而不是1 )。 最后以及将来的参考(因为它不容易找到),STM32中的闪存扇区列表可以在Flash programming manual的第7页找到。