目录

 

概述

一、使用方法

二、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>&copy; 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、校验运算运行结果

 

传送门->代码

 

五、总结

      好了,就介绍到此,通过该案例,可以在一些产品上做掉电保存功能,和一些数据保存等功能。

 

 

 

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐