目录
概述
一、使用方法
二、STM32CubeMx配置
三、Examples
四、运行结果
五、总结
概述
本篇文章介绍如何使用STM32HAL库,操作芯片内部EEprom读写数据,类似操作Flash,可实现掉电保存数据功能。
(注:有些型号才有内部EEprom,没有的话,只能使用内部FLASH模拟EEprom,或者外挂EEprom芯片)
硬件:STM32L051C8T6最小系统板
软件:Keil 5.29 + STM32CubeMX6.2.1
一、使用方法
通过参阅《STM32数据手册》得知,通过目录找到芯片中的内部eeprom章节,如下所示:
在《STM32中文参考手册》pdf文档中找到,第3.3小节:嵌入式闪存,对应的页数57。
这里我使用的是STM32L051C8T6的eeprom是512byte,通过手册得知是属于小容量,所以只需看地址分配图与每一页对应的大小(字节)即可。
想更详细的了解,请阅读《STM32数据手册》。
二、STM32CubeMx配置
三、Examples
打开STM32CubeMx生成的keil工程,新建Bsp文件夹,同时分别新建bsp_eeprom.c与bsp_eeprom.h文件,并把这两个文件,添加keil工程中来即可。
添加头文件路径
1、bsp_eeprom.h文件
/*-------------------------------------------------*/
/* */
/* 实现内部eeprom功能的头文件 */
/* */
/*-------------------------------------------------*/
#ifndef __EEPROM_H
#define __EEPROM_H
#include "stm32l0xx.h" //包含需要的头文件
#define DATA_EEPROM_START_ADDR 0x08080000 //起始地址
#define USER_DATA_EEPROM_ADDR DATA_EEPROM_START_ADDR + 0x00000000 //用户地址
#define DATA_EEPROM_BYTE_SIZE 0x01FF //空间
#define DATA_EEPROM_END_ADDR DATA_EEPROM_START_ADDR + DATA_EEPROM_BYTE_SIZE //结束地址
#define iEEPROM_CHECK_NUM 2
HAL_StatusTypeDef EEPROM_WriteData(uint32_t Address, uint32_t *wData, uint32_t len);
HAL_StatusTypeDef EEPROM_ReadData(uint32_t Address, uint32_t *rData, uint32_t len);
HAL_StatusTypeDef EEPROM_WRITE_Verify_CHECK(uint32_t Address, uint32_t *wData, uint32_t len);
HAL_StatusTypeDef EEPROM_Read_Verify_CHECK(uint32_t Address, uint32_t *rData, uint32_t len);
#endif
2、bsp_eeprom.c文件
/*-------------------------------------------------*/
/* */
/* 实现内部eeprom功能的源文件 */
/* */
/*-------------------------------------------------*/
#include "bsp_eeprom.h" //包含需要的头文件
#include <string.h>
#include "usart.h" //包含需要的头文件
#include "stdio.h"
/*-------------------------------------------------*/
/*函数名:内部eeprom擦除功能 */
/*参 数:Address:擦除地址 */
/*参 数:wData:擦除数据缓冲区 */
/*参 数:len:擦除数据总长 */
/*返回值:无 */
/*-------------------------------------------------*/
HAL_StatusTypeDef EEPROM_EraseData(uint32_t start, uint32_t NumberSectors)
{
uint32_t i;
uint32_t NbrOfPages = 0;
uint32_t Address = start;
HAL_StatusTypeDef status = HAL_OK;
printf("EEPROM_EraseData len:%d\r\n", NumberSectors);
NbrOfPages = (DATA_EEPROM_END_ADDR - Address)/FLASH_PAGE_SIZE;
if(NumberSectors > NbrOfPages) return (HAL_ERROR);
for(i=0; i<NumberSectors; i++){ //for循环,需要写入多少数据,就循环几次
status = HAL_FLASHEx_DATAEEPROM_Erase(Address + i * 4);
}
if (status != HAL_OK)
{
printf("Erase Fail!!!\r\n"); //串口提示写入错误
return (HAL_ERROR);
}
printf("Erase Success.\r\n\r\n");
return HAL_OK;
}
/*-------------------------------------------------*/
/*函数名:内部eeprom写功能 */
/*参 数:Address:写入地址 */
/*参 数:wData:写入数据缓冲区 */
/*参 数:len:写入数据总长 */
/*返回值:无 */
/*-------------------------------------------------*/
HAL_StatusTypeDef EEPROM_WriteData(uint32_t Address, uint32_t *wData, uint32_t len)
{
uint32_t i;
//uint32_t *Data = 0;
HAL_FLASHEx_DATAEEPROM_Unlock(); //解锁
EEPROM_EraseData(Address, len);
printf("Write Address:%d\r\n", Address);
printf("Write Data:\r\n");
for(i=0; i<len; i++) //for循环,需要写入多少数据,就循环几次
{
printf("wData[%d]=%08x\r\n",i,wData[i]);
#if 0
HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, Address + i * 4, *(wData+i)); //字节:FLASH_TYPEPROGRAM_WORD
#else
if(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, Address, *(uint32_t*)(wData+i)) == HAL_OK) //调用写数据函数,如果返回的不是FLASH_COMPLETE,表示写入出错,进入if
{
if (*(uint32_t*)Address != *(uint32_t*)(wData+i))
{
printf("Write Error!!!\r\n"); //串口提示写入错误
HAL_FLASHEx_DATAEEPROM_Lock();
/* FLASHEx_DATAEEPROM content doesn't match SRAM content */
return(HAL_ERROR);
}
Address += 4; //地址递增4,因为一次写一个字,是4个字节
}
#endif
}
printf("Write Success.\r\n\r\n");
HAL_FLASHEx_DATAEEPROM_Lock(); //上锁
return HAL_OK;
}
/*-------------------------------------------------*/
/*函数名:内部eeprom读功能 */
/*参 数:Address:读取地址 */
/*参 数:rData:保存数据缓冲区 */
/*参 数:len:读取数据总长 */
/*返回值:无 */
/*-------------------------------------------------*/
HAL_StatusTypeDef EEPROM_ReadData(uint32_t Address, uint32_t *rData, uint32_t len)
{
uint32_t i;
uint32_t *wAddr = 0;
wAddr = (uint32_t *)(Address); //转换地址,地址从0x08080000开始
printf("Read Address:%d\r\n", Address);
//printf("Read Data:\r\n");
for(i=0;i<len;i++){ //for循环,需要读取多少数据,就循环几次
*rData++ = *wAddr++; //每次读取的数据保存到rData缓冲区
//printf("rData[%d]=%08x\r\n",i,rData[i]);
}
printf("Read Complete.\r\n");
return HAL_OK;
}
/*-------------------------------------------------*/
/*函数名:带有校验操作的内部eeprom写功能 */
/*参 数:Address:写入地址 */
/*参 数:wData:写入数据缓冲区 */
/*参 数:len:写入数据总长 */
/*返回值:无 */
/*-------------------------------------------------*/
HAL_StatusTypeDef EEPROM_WRITE_Verify_CHECK(uint32_t Address, uint32_t *wData, uint32_t len)
{
uint32_t buff[len];
uint32_t i;
for (i=0; i < iEEPROM_CHECK_NUM; i++)
{
EEPROM_WriteData(Address, wData, len);
EEPROM_ReadData(Address, buff, len);
if (memcmp(wData, buff, len)==0)
{
printf("\r\nWRITE_Verify Completing Comparative\r\n\r\n");
return HAL_OK;
}
}
return HAL_ERROR;
}
/*-------------------------------------------------*/
/*函数名:带有校验操作的内部eeprom读功能 */
/*参 数:Address:读取地址 */
/*参 数:rData:保存数据缓冲区 */
/*参 数:len:读取数据总长 */
/*返回值:无 */
/*-------------------------------------------------*/
HAL_StatusTypeDef EEPROM_Read_Verify_CHECK(uint32_t Address, uint32_t *rData, uint32_t len)
{
uint32_t buff0[len];
uint32_t buff1[len];
uint8_t i,j;
// printf("len:%d\r\n", len);
// printf("Address:%d\r\n", Address);
for (i=0; i<iEEPROM_CHECK_NUM; i++)
{
printf("First read Verify\r\n");
EEPROM_ReadData(Address, buff0, len);
printf("Second read Verify\r\n");
EEPROM_ReadData(Address, buff1, len);
if (memcmp(buff0, buff1, len)==0)
{
printf("Read_Verify Completing Comparative\r\n");
for (j=0; j<len; j++)
{
*rData++ = buff0[j];
}
return HAL_OK;
}
}
return HAL_ERROR;
}
3、mian.c文件
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* <h2><center>© Copyright (c) 2021 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "bsp_eeprom.h"
#include "stdio.h"
#include "string.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
//uint8_t wData[10] = "0x5a5a5a5a"; //需要写入的数据
//uint8_t rData[1]; //用于保存读取数据的缓冲区
typedef __PACKED_STRUCT{
uint32_t Device_id; // 设备号
uint32_t Hardware_Version; // 硬件版本信息
uint32_t Application_Version; // APP软件版本
}DEVInfoTypeDef;
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void test_eeprom(void)
{
DEVInfoTypeDef DevInfo_read = {0};
DEVInfoTypeDef DevInfo_default = {
.Device_id = 0x10000111,
.Hardware_Version = 0x10000111,
.Application_Version = 0x10000111
};
DEVInfoTypeDef DevInfo_update= {
.Device_id = 0x20000222,
.Hardware_Version = 0x20000222,
.Application_Version = 0x20000222
};
uint32_t page = sizeof(DevInfo_default) / 4;
printf("*******************Internal EEPROM test***************\r\n\r\n"); //串口提示信息
printf("****************No verification function**************\r\n\r\n");
//printf("page:%d\r\n", page);
memset(&DevInfo_read, 0, sizeof(DevInfo_read)); //清空结构体内容
EEPROM_WriteData(USER_DATA_EEPROM_ADDR, (uint32_t *)&DevInfo_default, page); //内部EEprom写入数据
HAL_Delay(200); //延时
EEPROM_ReadData(USER_DATA_EEPROM_ADDR, (uint32_t *)&DevInfo_read, page); //内部EEprom读取数据
printf("Device_id:0x%08lX, Hardware_Version:0x%08lX, Application_Version:0x%08lX \r\n",
DevInfo_read.Device_id, DevInfo_read.Hardware_Version,
DevInfo_read.Application_Version);
memset(&DevInfo_read, 0, sizeof(DevInfo_read)); //清空结构体内容
EEPROM_WriteData(USER_DATA_EEPROM_ADDR, (uint32_t *)&DevInfo_update, page); //内部EEprom写入数据
HAL_Delay(200); //延时
EEPROM_ReadData(USER_DATA_EEPROM_ADDR, (uint32_t *)&DevInfo_read, page); //内部EEprom读取数据
printf("Device_id:0x%08lX, Hardware_Version:0x%08lX, Application_Version:0x%08lX \r\n",
DevInfo_read.Device_id, DevInfo_read.Hardware_Version,
DevInfo_read.Application_Version);
// EEPROM_ReadData(USER_DATA_EEPROM_ADDR, (uint32_t *)&DevInfo_read, page); //内部EEprom读取数据
// printf("Device_id:0x%08lX, Hardware_Version:0x%08lX, Application_Version:0x%08lX \r\n",
// DevInfo_read.Device_id, DevInfo_read.Hardware_Version,
// DevInfo_read.Application_Version);
printf("\r\n****************Add validation function**************\r\n\r\n");
memset(&DevInfo_read, 0, sizeof(DevInfo_read)); //清空结构体内容
EEPROM_WRITE_Verify_CHECK(USER_DATA_EEPROM_ADDR, (uint32_t *)&DevInfo_default, page); //内部EEprom写入数据
HAL_Delay(200); //延时
EEPROM_Read_Verify_CHECK(USER_DATA_EEPROM_ADDR, (uint32_t *)&DevInfo_read, page); //内部EEprom读取数据
printf("Device_id:0x%08lX, Hardware_Version:0x%08lX, Application_Version:0x%08lX \r\n",
DevInfo_read.Device_id, DevInfo_read.Hardware_Version,
DevInfo_read.Application_Version);
memset(&DevInfo_read, 0, sizeof(DevInfo_read)); //清空结构体内容
EEPROM_WRITE_Verify_CHECK(USER_DATA_EEPROM_ADDR, (uint32_t *)&DevInfo_update, page); //内部EEprom写入数据
HAL_Delay(200); //延时
EEPROM_Read_Verify_CHECK(USER_DATA_EEPROM_ADDR, (uint32_t *)&DevInfo_read, page); //内部EEprom读取数据
printf("Device_id:0x%08lX, Hardware_Version:0x%08lX, Application_Version:0x%08lX \r\n",
DevInfo_read.Device_id, DevInfo_read.Hardware_Version,
DevInfo_read.Application_Version);
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* 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_USART1_UART_Init();
/* USER CODE BEGIN 2 */
test_eeprom();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_Delay(1000);
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLLMUL_8;
RCC_OscInitStruct.PLL.PLLDIV = RCC_PLLDIV_2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1;
PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
四、运行结果
1、无校验运算
2、校验运算运行结果
传送门->代码
五、总结
好了,就介绍到此,通过该案例,可以在一些产品上做掉电保存功能,和一些数据保存等功能。