如何在 STM32F4、Cortex M4 上写入/读取 FLASH
Posted
技术标签:
【中文标题】如何在 STM32F4、Cortex M4 上写入/读取 FLASH【英文标题】:How to write/read to FLASH on STM32F4, Cortex M4 【发布时间】:2017-11-10 15:14:25 【问题描述】:我想写一个变量,例如一个数字为 5 的整数到 FLASH 中,然后在电源消失并且设备重新打开后再次读取它。
我已经知道,要写东西,我首先需要擦除页面,然后再写。
手册上写着:
在闪存选项密钥寄存器 (FLASH_OPTKEYR) 中写入 OPTKEY1 = 0x0819 2A3B 在闪存选项密钥寄存器 (FLASH_OPTKEYR) 中写入 OPTKEY2 = 0x4C5D 6E7F
如何执行此任务?
Sector 0 有一个 Block 地址从 0x0800 0000 到 0x0800 3FFF,这是我要写的地方。
这里是手册的链接,第 71 页:STM32 Manual
【问题讨论】:
它高度依赖于硬件。某些类型的闪存根本不允许非块操作。 @4386427 这是不正确的。OPTKEY1
和 OPTKEY2
是值,而不是地址。将它们添加到 FLASH_OPTKEYR 的地址会导致崩溃或意外行为。
@duskwuff - 我明白了。我读它好像 OPTKEY1 和 OPTKEY2 是一组称为 FLASH_OPTKEYR 的寄存器中的两个寄存器。评论已删除。谢谢。
深入阅读手册当然是一种选择,但通常也会有一个应用说明来说明如何做到这一点。检查“eeprom emulation”或“bootloader”应用说明。
我只想指出,闪存在降级之前只有有限数量的擦写周期。我强烈建议使用电池备份的 NVRAM 或一些高耐用性外部非易失性存储器(例如 MRAM)来存储经常更改的非易失性数据。就我个人而言,我总是选择 MRAM 来处理这些事情。
【参考方案1】:
您可以使用以下代码通过 HAL 库将数据写入闪存。
void Write_Flash(uint8_t data)
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGSERR );
FLASH_Erase_Sector(FLASH_SECTOR_6, VOLTAGE_RANGE_3);
HAL_FLASH_Program(TYPEPROGRAM_WORD, FlashAddress, data);
HAL_FLASH_Lock();
您应该如下更新链接描述文件。在MEMORY
中添加DATA
,在SECTIONS
中添加.user_data
。
MEMORY
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 320K
CCMRAM (rw) : ORIGIN = 0x10000000, LENGTH = 64K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 2048K
DATA (rwx) : ORIGIN = 0x08040000, LENGTH = 128k
/* Define output sections */
SECTIONS
.user_data :
. = ALIGN(4);
KEEP(*(.user_data))
. = ALIGN(4);
> DATA
您应该在主代码上添加以下属性,以便在上电后读取数据
__attribute__((__section__(".user_data"))) const char userConfig[64];
完成所有这些之后,您可以通过调用userConfig[0]
来读取您的闪存数据。
【讨论】:
谢谢,我将如何读取闪存中的值?因为首先我需要读取它们,擦除闪存,最后写入旧的和新的。对吗? 您可以通过调用userConfig[0]
读取第一个8bit 变量,然后在userConfig[1]
跟随8bit。
这里是另一个例子。我认为这是一个不同的 MCU,但同一个 Cortex 系列。我很确定它应该可以工作。 os.mbed.com/users/olympux/code/eeprom_flash
我注意到,虽然从板载闪存读取速度非常快,但写入它(即使在扇区被擦除后)却非常慢。这是预期的行为(即它是硬件限制)吗?我测量的写入速度比通过 SPI 以 1MHz 写入闪存设备(微型 SD 卡)慢约 7 倍。
@karl71 SDCard 与片上 MCU 闪存的速度额定值可能有很大不同,因为前者通常对速度至关重要(这主要是由于流程优化、执行单元时序与闪存时序特性)。例如,在 STM32L412xx 的数据表(第 6.3.10 节)中,您将看到“闪存特性”的时序表。 64b 编程时间为 90ms,32-dword 时间约为 2-3ms。但是,如果您查看 SDCard 中使用的超快 SanDisk MD8832(数据表表 11,第 10.3.2 节),页面写入时间为 30ns。速度提高了 100-1000 倍。【参考方案2】:
我正在为 STM32 版本 9.3.0 使用 STM32F407 和 Atollic TrueSTUDIO®。
使用上述建议代码时
属性((section(".user_data"))) const char userConfig[64];
我的编译器假定userConfig
为常数零。我必须从声明中删除 const
才能使其正常工作。
我的完整解决方案由两部分组成(如上所述,但有一些进一步的修改):
第一步编辑链接器文件:
在“记忆”中
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 896K /* origin size was 1024k, subtracted size of DATA */
DATA (rx) : ORIGIN = 0x080E0000, LENGTH = 128K
在“部分”中
/* User data section at the end of the flash to store calibration data etc. */
.user_data (NOLOAD):
. = ALIGN(4);
_user_data_start = .; /* create a global symbol at user_data start */
KEEP(*(.user_data))
. = ALIGN(4);
_user_data_end = .; /* create a global symbol at user_data end */
>DATA
第二步写代码:
uint8_t userConfig[64] __attribute__ ((section(".user_data")));
extern uint32_t _user_data_start;
extern uint32_t _user_data_end;
uint8_t ConfArray[16];
uint32_t TestArray[2];
// Copy a part from the userConfig to variable in RAM
for (i = 0; i < 16; i++)
ConfArray[i] = userConfig[i];
// get the address of start and end of user_data in flash
// the & is importand, else you would get the value at the address _user_data_start and _user_data_end points to
TestArray[0] = (uint32_t)&_user_data_start;
TestArray[1] = (uint32_t)&_user_data_end;
【讨论】:
以上是关于如何在 STM32F4、Cortex M4 上写入/读取 FLASH的主要内容,如果未能解决你的问题,请参考以下文章