目录
一、EEPROM
(1)板载 EEPROM
EEPROM(Electrically Erasable Programmable read only memory)是指带电可擦可编程只读存储器,掉电后数据不丢失,使用方便,可重复擦写,电擦除。
STM32L071 系列提供了高达6千字节的 EEPROM,对 EEPROM 进行编程时采用在按字、半字或字节,可以页执行擦除操作。
(2)接口函数
🔅EEPROM 解锁函数
HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Unlock(void)
解锁数据存储器和 FLASH_PECR 寄存器访问;
- 启用数据EEPROM访问和闪存程序擦除控制寄存器访问;
- 在读写 EEPROM 之前都需要调用该函数;
🔅EEPROM 锁定函数
HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Lock(void)
锁定数据存储器和 FLASH_PECR 寄存器访问;
- 禁用数据 EEPROM 访问和闪存程序擦除控制寄存器访问;
- 推荐在读写操作后使用以保护 DATA_EEPROM 免受可能的意外操作;
🔅EEPROM 写函数
HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data)
参数说明:
- TypeProgram:用于区分要写入的数据类型,取值有:
- FLASH_TYPEPROGRAMDATA_BYTE (字节:8位)
- FLASH_TYPEPROGRAMDATA_HALFWORD(字节:16位)
- FLASH_TYPEPROGRAMDATA_WORD(字节:32位)
- Address:用于设置要写入数据 FLASHEx_DATAEEPROM地址;
- Data:写入的数据;
(3)STM32CubeMX 软件配置
🔅“工程建立、时钟树配置、Debug 串行线配置、代码生成配置” 在【蓝桥杯——物联网设计与开发】基础模块1- GPIO输出 一文中有讲解,这里不再赘述❗️
1️⃣点击引脚 PC15 → 选择 GPIO_Output 模式(此处默认为推挽输出);
2️⃣点击 "GPIO" → 点击引脚 PC15 → 将 "GPIO output level" 栏修改为 "High",即将 PC15 引脚初始化为高电平;
⚠️此处修改不是必须的,应当根据题意要求进行配置,这里默认为上电熄灭;
3️⃣点击左侧 "Connectivity" → 选择 "USART2" → 模式选择 "Asynchronous" (异步通信模式),不使能硬件流控制;
4️⃣在 "Parameter Settings" 中对串口参数进行修改(具体请根据题目要求修改);
- 波特率9600Bit/s
- 8位数据位
- 无奇偶校验
- 1位停止位
- 使能接收和发送
- 16倍过采样
5️⃣生成代码即可;
(4)代码编写
🟢️main 函数
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
uint32_t count[3] = {0}; // 定义一个数组用于存储读取的地址
uint32_t *whole_addr; // 定义地址指针
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
/* EEPROM 读部分 */
HAL_FLASHEx_DATAEEPROM_Unlock(); // 解锁
whole_addr = (uint32_t *)(DATA_EEPROM_BASE + 8); //DATA_EEPROM_BASE(0x08080000UL)是FLASHEx_DATAEEPROM的起始地址
*count = *whole_addr;
HAL_FLASHEx_DATAEEPROM_Lock(); //锁定
count[0]++;
/* EEPROM 写部分 */
HAL_FLASHEx_DATAEEPROM_Unlock(); // 解锁
HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, DATA_EEPROM_BASE + 8, count[0]);
printf("count[0] = %d\r\n", count[0]);
while(count[0]--)
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_15, GPIO_PIN_RESET);
HAL_Delay(500);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_15, GPIO_PIN_SET);
HAL_Delay(500);
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
🔅“串口重定向” 在【蓝桥杯——物联网设计与开发】基础模块4 - 串行通信 一文中有讲解,这里不再赘述❗️
- 在初始化时,在 EEPROM 地址8中读出一个字的数据存入 count,并将该数据 +1 后写会该地址;
- 通过串口将该数据打印出来;
- 并且根据数值闪烁 LD5;
⚠️注意:DATA_EEPROM_BASE(0x08080000UL) 是FLASHEx_DATAEEPROM的起始地址,所以我们写入 EEPROM 的地址需要添加该基地址❗️
由于库函数中没有提供专用的读函数,这次采用指针的方式,读取目标地址上的值即可;
(5)实验现象
- 每一次板子上电后,串口打印出 EEPROM 中的值,并将该值进行 +1 处理;
- LD5 根据数值进行间隔 1s 的亮灭闪烁;
二、EEPROM 接口函数封装
🟡️EEPROM 写函数
/**
* @brief EEPROM Write Function EEPROM写函数
* @param addr EEPROM 地址
@arg 地址范围为0-512,地址必须是4的整数倍,因为主函数中是按照'字'写入的
buf 写入数据的地址
len 写入数据长度
* @retval None
*/
void EEPROM_Write(uint32_t addr, uint32_t *buf, uint32_t len)
{
uint32_t whole_addr; // 定义地址指针
HAL_FLASHEx_DATAEEPROM_Unlock(); // 解锁
whole_addr = (addr + DATA_EEPROM_BASE);
//DATA_EEPROM_BASE(0x08080000UL)是FLASHEx_DATAEEPROM的起始地址
while(len--)
{
HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, whole_addr, *buf++);
whole_addr += 4;
}
HAL_FLASHEx_DATAEEPROM_Lock(); //锁定
}
🟡️EEPROM 读函数
/**
* @brief EEPROM Read Function EEPROM读函数
* @param addr EEPROM 地址
@arg 地址范围为0-512,地址必须是4的整数倍,因为主函数中是按照'字'写入的
buf 写入地址
len 读取数据长度
* @retval None
*/
void EEPROM_Read(uint32_t addr, uint32_t *buf, uint32_t len)
{
uint32_t *whole_addr; // 定义地址指针
HAL_FLASHEx_DATAEEPROM_Unlock(); // 解锁
whole_addr = (uint32_t *)(addr + DATA_EEPROM_BASE);
//DATA_EEPROM_BASE(0x08080000UL)是FLASHEx_DATAEEPROM的起始地址
while(len--)
{
*buf++ = *whole_addr++;
}
HAL_FLASHEx_DATAEEPROM_Lock(); //锁定
}
🔴EEPROM 接口函数调用实例
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
uint32_t count[3] = {0}; // 定义一个数组用于存储读取的地址
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
EEPROM_Read(8, count, 2); // 在EEPROM地址4上读取数据
count[0]++;
count[1]++;
EEPROM_Write(8, count, 2);
printf("count[0] = %d\r\n", count[0]);
printf("count[1] = %d\r\n", count[1]);
while(count[0]--)
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_15, GPIO_PIN_RESET);
HAL_Delay(500);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_15, GPIO_PIN_SET);
HAL_Delay(500);
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
此处测试写两个数据,实验现象正常;
三、踩坑日记
🔅写函数中地址参数需为4的整数倍,因为主函数中是按照'字'写入的,否则系统会卡死在 HAL_FLASHEx_DATAEEPROM_Program 函数中;
⭐在多数据写入时,传入函数 HAL_FLASHEx_DATAEEPROM_Program 的地址参数应该➕4;