STM32 的某些系列 MCU 自带 EEPROM。笔者使用的 STM32L151RET6 自带 16 KB 的 EEPROM,可以用来存储自定义的数据。在芯片选型时,自带 EEPROM 也可以作为一个考量点,省去了在外接 EEPROM 的烦恼。
下面简单介绍下 STM32 内部 EEPROM 的读写流程。
Memory Mapping
以笔者使用的这款 STM32L151RET6 MCU 为例,自带 16 KB 的 EEPROM。Map 到了 2 个 Bank 中:
- Data EEPROM Bank1: 0x08080000 ~ 0x08081FFF (8KB)
- Data EEPROM Bank2: 0x08082000 ~ 0x08083FFF (8KB)
Operations
内部 EEPROM 的操作无非就是 读取、写入、擦除 等操作。直接调用库函数或者 HAL 库中对应的 API 即可。这里只是对内部 EEPROM 的操作做一个简要的分析。
本文档主要以库函数中的 EEPROM 接口 API 进行分析。
Unlocking/locking memory
STM32 复位后,Data EEPROM 和 Program/erase 控制寄存器 (FLASH_PECR) 默认是 处于 lock 状态,需要 unlock 之后才能执行写入和擦除操作。
如何 unlock 可以参考芯片对应的 datasheet,简单的说就是往 Program/erase 密钥寄存器 (FLASH_PEKEYR) 写指定的密钥集即可。
- Write PEKEY1= 0x89ABCDEF to the Program/erase key register (FLASH_PEKEYR)
- Write PEKEY2= 0x02030405 to the Program/erase key register (FLASH_PEKEYR)
/**
* @brief Unlocks the data memory and FLASH_PECR register access.
* @param None
* @retval None
*/
void DATA_EEPROM_Unlock(void)
{
if((FLASH->PECR & FLASH_PECR_PELOCK) != RESET)
{
/* Unlocking the Data memory and FLASH_PECR register access*/
FLASH->PEKEYR = FLASH_PEKEY1;
FLASH->PEKEYR = FLASH_PEKEY2;
}
}
#define FLASH_PEKEY1 ((uint32_t)0x89ABCDEF) /*!< Flash program erase key1 */
#define FLASH_PEKEY2 ((uint32_t)0x02030405) /*!< Flash program erase key: used with FLASH_PEKEY2
to unlock the write access to the FLASH_PECR register and
data EEPROM */
如何 lock 可以参考芯片对应的 datasheet,相较于 unlock,lock 仅需要置位 Program/erase 控制寄存器 (FLASH_PECR) 中的 FLASH_PECR 位。
/**
* @brief Locks the Data memory and FLASH_PECR register access.
* @param None
* @retval None
*/
void DATA_EEPROM_Lock(void)
{
/* Set the PELOCK Bit to lock the data memory and FLASH_PECR register access */
FLASH->PECR |= FLASH_PECR_PELOCK;
}
Erasing memory
对于 EEPROM,支持以下 2 种擦除方式:
- Word 和 double word 擦除
- Mass 擦除
对于 Word 和 double word 擦除,这种方式仅针对 EEPROM;但是对于 Mass 擦除,这种方式针对 Program memory、EEPROM 和 Option bytes。所以尽量在使用 EEPROM 的时候采用 Word 和 double word 擦除方式。
EEPROM 擦除方式也很简单,只需要将值 0x00000000 写入到对应的有效的擦除地址中即可。
/**
* @brief Erase a word in data memory.
* @param Address: specifies the address to be erased.
* @note For STM32L1XX_MD, A data memory word is erased in the data memory only
* if the address to load is the start address of a word (multiple of a word).
* @note To correctly run this function, the DATA_EEPROM_Unlock() function
* must be called before.
* Call the DATA_EEPROM_Lock() to disable the data EEPROM access
* and Flash program erase control register access(recommended to protect
* the DATA_EEPROM against possible unwanted operation).
* @retval FLASH Status: The returned value can be:
* FLASH_ERROR_PROGRAM, FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
*/
FLASH_Status DATA_EEPROM_EraseWord(uint32_t Address)
{
FLASH_Status status = FLASH_COMPLETE;
/* Check the parameters */
assert_param(IS_FLASH_DATA_ADDRESS(Address));
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT);
if(status == FLASH_COMPLETE)
{
/* Write "00000000h" to valid address in the data memory" */
*(__IO uint32_t *) Address = 0x00000000;
}
/* Return the erase status */
return status;
}
Programming memory
写入 EEPROM 的步骤也很简单,一般的流程如下:
- unlock
- erase
- write
- lock
/**
* @brief Programs a word at a specified address in data memory without erase.
* @note To correctly run this function, the DATA_EEPROM_Unlock() function
* must be called before.
* Call the DATA_EEPROM_Lock() to disable the data EEPROM access
* and Flash program erase control register access(recommended to protect
* the DATA_EEPROM against possible unwanted operation).
* @note The function DATA_EEPROM_FixedTimeProgramCmd() can be called before
* this function to configure the Fixed Time Programming.
* @param Address: specifies the address to be written.
* @param Data: specifies the data to be written.
* @retval FLASH Status: The returned value can be:
* FLASH_ERROR_PROGRAM, FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
*/
FLASH_Status DATA_EEPROM_ProgramWord(uint32_t Address, uint32_t Data)
{
FLASH_Status status = FLASH_COMPLETE;
/* Check the parameters */
assert_param(IS_FLASH_DATA_ADDRESS(Address));
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT);
if(status == FLASH_COMPLETE)
{
*(__IO uint32_t *)Address = Data;
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT);
}
/* Return the Write Status */
return status;
}
Reading Momory
读取 EEPROM 中的数据就没那么多步骤了,直接读取对应的 Map 的 Bank 即可。
Note
对于 EEPROM 的操作,以下几点需要注意:
- 尽量以 4 字节为一个单位进行操作
- 在执行写入或者擦除操作的时候,尽量将全局中断关闭,以免中断触发引起其它的问题
- 不要对相同的一个 Bank 同时做多种操作,尽量保证一个 Bank 只有一种操作在执行
列出一段 EEPROM 的参考代码:
void EEPROM_Test(void)
{
__set_PRIMASK(1);
DATA_EEPROM_Unlock();
/* EEPROM Operations */
DATA_EEPROM_Lock();
__set_PRIMASK(0);
}