stm32f4 中的写周期很少
Very few write cycles in stm32f4
我使用的是STM32F401VCT6U "discovery"开发板,我需要提供一种方法让用户在运行时在内存中写入地址。
我写的可以简化为如下函数:
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 和 count=100
中使用基数 address
调用上述函数时,前几次它正常工作,写入传递的 values
缓冲区闪烁。然而,在前几次调用之后,我不能再写入任何值了:我只能重置闪存中值中的位,例如,尝试将 0xFF 写入 0x7F 将在闪存中留下 0x7F,而将 0xFE 写入 0x7F 将留下 0x7E,并且 0x00 到任何值都将成功(但之后没有其他值可写入该地址)。
我仍然可以通过更改基数 address
正常写入闪存中的其他地址,但同样只有几次(使用 count=100
进行两到三次调用)。
此行为表明已达到闪存的最大写入次数,但我无法想象它可以这么快。我预计在耗尽之前至少有 10,000 次写入。
那我做错了什么?
您误解了闪存的工作原理 - 例如,它不像写入 EEPROM 那样简单。您描述的行为对于 Flash 来说是正常的。
要重复写入闪存的相同地址,必须首先使用 FLASH_EraseSector 擦除整个扇区。通常,在此擦除期间需要保留的任何数据都需要缓存在 RAM 或另一个闪存扇区中。
如果您正在重复写入一小块数据并且担心闪存会烧毁,那么您可能希望对闪存编写一个接口,每次写入时,您都将数据沿着闪存扇区移动到未写入的闪存,跟踪它当前从扇区开始的偏移量。只有当您 运行 扇区中的字节不足时,您才需要擦除并从扇区开始重新开始。
我有一个有效且经过测试的解决方案,但它与@Ricibob 的答案有很大不同,所以我决定将其作为答案。
由于我的用户可以在 select 闪存扇区中的任何位置进行写入,因此我的应用程序无法处理在需要时擦除该扇区同时仅将需要保留的数据缓冲到 RAM 的责任。
因此,我将在写入无效时擦除该扇区的责任转移给了我的用户(这样,用户仍然可以自由使用该扇区中的另一个地址以避免写入过多-擦除周期)。
解决方案
基本上,我公开了一个 write(uint32_t startAddress, uint8_t count, uint8_t* values)
函数,它有一个 WRITE_SUCCESSFUL
return 代码和一个 CANNOT_WRITE_FLASH
以防失败。
我还为我的用户提供了一个 getSector(uint32_t address)
函数,该函数 return 是与作为参数传递的地址对应的扇区的 ID、起始地址和结束地址。这样,用户就知道擦除操作影响的地址范围。
最后,我公开了一个 eraseSector(uint8_t sectorID)
函数,该函数擦除其 id 已作为参数传递的闪存扇区。
擦除策略
写入失败的策略不同于@Ricibob "erase if the value in flash is different of FF" 的建议,因为 documented in the Flash programming manual 只要它只是位重置(这与我的行为相匹配),写入就会成功在问题中观察到):
Note: Successive write operations are possible without the need of an erase operation when
changing bits from ‘1’ to ‘0’.
Writing ‘1’ requires a Flash memory erase operation.
If an erase and a program operation are requested simultaneously, the erase operation is
performed first.
所以我使用了宏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)。
STM32F4中flash扇区列表
最后,为了将来参考(因为它不是那么容易找到),STM32 中的闪存扇区列表可以在 Flash programming manual.
的第 7 页找到
ST的"right way"详见AN3969: EEPROM emulation in STM32F40x/STM32F41x microcontrollers
大致是这样的过程:
- 保留两个 Flash 页面
- 将最新数据与其 'EEPROM address'
一起写入下一个可用位置
- 当你运行第一页空间不足时,将所有最新值写入第二页并擦除第一页
- 从第 2 页中断处开始写入值
- 当您 运行 在第 2 页的空间不足时,请在第 1 页重复
这太疯狂了,但我没有想到。
我使用的是STM32F401VCT6U "discovery"开发板,我需要提供一种方法让用户在运行时在内存中写入地址。
我写的可以简化为如下函数:
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 和 count=100
中使用基数 address
调用上述函数时,前几次它正常工作,写入传递的 values
缓冲区闪烁。然而,在前几次调用之后,我不能再写入任何值了:我只能重置闪存中值中的位,例如,尝试将 0xFF 写入 0x7F 将在闪存中留下 0x7F,而将 0xFE 写入 0x7F 将留下 0x7E,并且 0x00 到任何值都将成功(但之后没有其他值可写入该地址)。
我仍然可以通过更改基数 address
正常写入闪存中的其他地址,但同样只有几次(使用 count=100
进行两到三次调用)。
此行为表明已达到闪存的最大写入次数,但我无法想象它可以这么快。我预计在耗尽之前至少有 10,000 次写入。 那我做错了什么?
您误解了闪存的工作原理 - 例如,它不像写入 EEPROM 那样简单。您描述的行为对于 Flash 来说是正常的。
要重复写入闪存的相同地址,必须首先使用 FLASH_EraseSector 擦除整个扇区。通常,在此擦除期间需要保留的任何数据都需要缓存在 RAM 或另一个闪存扇区中。
如果您正在重复写入一小块数据并且担心闪存会烧毁,那么您可能希望对闪存编写一个接口,每次写入时,您都将数据沿着闪存扇区移动到未写入的闪存,跟踪它当前从扇区开始的偏移量。只有当您 运行 扇区中的字节不足时,您才需要擦除并从扇区开始重新开始。
我有一个有效且经过测试的解决方案,但它与@Ricibob 的答案有很大不同,所以我决定将其作为答案。
由于我的用户可以在 select 闪存扇区中的任何位置进行写入,因此我的应用程序无法处理在需要时擦除该扇区同时仅将需要保留的数据缓冲到 RAM 的责任。
因此,我将在写入无效时擦除该扇区的责任转移给了我的用户(这样,用户仍然可以自由使用该扇区中的另一个地址以避免写入过多-擦除周期)。
解决方案
基本上,我公开了一个 write(uint32_t startAddress, uint8_t count, uint8_t* values)
函数,它有一个 WRITE_SUCCESSFUL
return 代码和一个 CANNOT_WRITE_FLASH
以防失败。
我还为我的用户提供了一个 getSector(uint32_t address)
函数,该函数 return 是与作为参数传递的地址对应的扇区的 ID、起始地址和结束地址。这样,用户就知道擦除操作影响的地址范围。
最后,我公开了一个 eraseSector(uint8_t sectorID)
函数,该函数擦除其 id 已作为参数传递的闪存扇区。
擦除策略
写入失败的策略不同于@Ricibob "erase if the value in flash is different of FF" 的建议,因为 documented in the Flash programming manual 只要它只是位重置(这与我的行为相匹配),写入就会成功在问题中观察到):
Note: Successive write operations are possible without the need of an erase operation when changing bits from ‘1’ to ‘0’. Writing ‘1’ requires a Flash memory erase operation. If an erase and a program operation are requested simultaneously, the erase operation is performed first.
所以我使用了宏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)。
STM32F4中flash扇区列表
最后,为了将来参考(因为它不是那么容易找到),STM32 中的闪存扇区列表可以在 Flash programming manual.
的第 7 页找到ST的"right way"详见AN3969: EEPROM emulation in STM32F40x/STM32F41x microcontrollers
大致是这样的过程:
- 保留两个 Flash 页面
- 将最新数据与其 'EEPROM address' 一起写入下一个可用位置
- 当你运行第一页空间不足时,将所有最新值写入第二页并擦除第一页
- 从第 2 页中断处开始写入值
- 当您 运行 在第 2 页的空间不足时,请在第 1 页重复
这太疯狂了,但我没有想到。