原项目使用了一颗AT24C02来存储系统配置参数,为了降低成本,需要把这颗料砍掉,将配置参数保存到MCU内部的Flash上。这个改动其实简单,也就是读写内部flash而已,但是如果需要考虑擦写平衡这些特性,自己写就比较麻烦。而ST官方提供了一个使用内部Flash模拟EEPROM的模块,叫X-CUBE-EEPROM,包括了这些特性,实用性更强。
下面是从‘EEPROM emulation in STM32L4 series microcontrollers' (AN4894)中摘抄的特性介绍,更详细的说明还是看文档吧:
- Lightweight implementation and reduced footprint
- Simple API that consists of a few functions to format, initialize, read and write data, and clean up Flash memory pages
- At least two Flash memory pages to be used for internal data management
- Clean-up simplified for the user (background page erase)
- Wear leveling algorithm to increase emulated EEPROM cycling capability
- Robust against asynchronous resets and power failures.
轻量级其实是相对的,对于最简单擦写,1个page就可以了,比这个还轻量。使用这个模块看重无非是最后两点,毕竟Flash和EEPROM的特性是不一样的。
EEPROM_Emul的结构
EEPROM_Emul还是挺轻量的,没几个文件,就两部分核心实现和flash接口。使用前还需要做些额外的工作:
- 将
eeprom_emul_conf_template.h
拷贝到自己的应用程序路径下,重命名为eeprom_emul_conf.h
。我这里就直接重命名了。 - 修改
flash_interface.c
里的GetBankNumber函数,我使用的是STM32L431,只有一个bank,所以直接return FLASH_BANK_1;
。别的型号可能不需要改这部分。
EEPROM_Emul的配置
在AN4894文档的4.1.3节详细介绍了eeprom_emul_conf.h
中各个宏。
- NB_OF_VARIABLES设置为256;AT24C02也就256B,够用了。
- 重点讲一下怎么确定START_PAGE_ADDRESS。在
eeprom_emul.h
里有这么一个宏PAGES_NUMBER
,
#define PAGES_NUMBER (((((NB_OF_VARIABLES + NB_MAX_ELEMENTS_BY_PAGE) / NB_MAX_ELEMENTS_BY_PAGE) * 2U) * CYCLES_NUMBER) + GUARD_PAGES_NUMBER)
表示共使用了多少个PAGE来模拟EEPROM,它的值取决于配置和移植的多个宏。所以在配置好除START_PAGE_ADDRESS外的宏后,根据配置去计算PAGES_NUMBER的值。START_PAGE_ADDRESS到flash结尾的空间必须 ≥ (PAGES_NUMBERPAGE_SIZE),否则在初始化时会出现Hardfault。*
EEPROM_Emul的使用
- 1、调用
EE_Init(VirtAddVarTab, EE_FORCED_ERASE);
进行初始化;
VirtAddVarTab是一个数组,保存到是虚拟EEPROM的地址,这个地址可以是连续的也可以是不连续的,但不能是0x0000或者0xFFFF。
EE_FORCED_ERASE是默认使用参数,如果能保证在擦写的时候是安全的(不会复位或者掉电),那也可以用EE_CONDITIONAL_ERASE这个参数。 - 2、重写原来的EEPROM写接口,这里在触发clean时使用查询模式,EE_CleanUp();
void EEPROM_WriteData(uint8_t Reg, uint8_t Value) { EE_Status ee_status = EE_OK; /* Unlock the Flash Program Erase controller */ HAL_FLASH_Unlock(); ee_status = EE_WriteVariable8bits(VirtAddVarTab[Reg], Value); /* Start cleanup polling mode, if cleanup is needed */ if ((ee_status & EE_STATUSMASK_CLEANUP) == EE_STATUSMASK_CLEANUP) { ee_status |= EE_CleanUp(); } /* Lock the Flash Program Erase controller */ HAL_FLASH_Lock(); if (ee_status != EE_OK) { printf("Write EEPROM_EMUL failed at addr %x\r\n", Reg); } }
- 3、重写原来的EEPROM读接口;
uint8_t EEPROM_ReadData(uint8_t Reg) { EE_Status ee_status = EE_OK; uint8_t value = 0; ee_status = EE_ReadVariable8bits(VirtAddVarTab[Reg], &value); if (ee_status != EE_OK) { printf("Read EEPROM_EMUL failed at addr %x\r\n", Reg); } return value; }
网友评论