如何在 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 这是不正确的。 OPTKEY1OPTKEY2 是值,而不是地址。将它们添加到 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的主要内容,如果未能解决你的问题,请参考以下文章

STM32F4 HAL库开发 -- 工程模板解读

STM32F4 HAL库开发 -- 工程模板解读

STM32F4 HAL库开发 -- 工程模板解读

STM32F030系列实现仿位带操作

STM32F030系列实现仿位带操作

STM32F3, STM32F4编程手册