本文讨论如何使用flash模拟eeprom(基于STM32 HAL库),本例使用的MCU是STM32F103TB。
IDE平台:IAR EWARM7.60
用到的资源:STM32Cube_FW_F1_V1.4.0库,emulation_ee.c/h,app_eeprom.c/h,main.c;
emulation_ee模块封装了flash模拟eeprom的所有细节,提供了3个用户API,该模块从基于stm32标准外设库的eeprom模块修改而来。
实例工程下载:http://download.csdn.net/detail/qiezhihuai/9593794
一、首先介绍模块使用到的STM32 HAL库API
1、HAL_FLASHEx_Erase()API,可以对flash进行按页或块擦除
原型:HAL_StatusTypeDef HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *PageError);
所在文件:stm32f1xx_hal_flash_ex.h
使用方法:首先定义一个FLASH_EraseInitTypeDef类型的实例,一个page_error变量,例:
uint32_t page_error = 0;
FLASH_EraseInitTypeDeferase_initstruct =
{
.TypeErase = FLASH_TYPEERASE_PAGES,/* 擦除方式,这里选择按页擦除 */
.NbPages = 1,/* 页数量,模块中只对单页进行操作 */
.PageAddress = PAGE0_BASE_ADDRESS/* 被擦除页的起始地址,默认为PAGE0的地址,使用中需要对此成员进行更新 */
}
现在对PAGE1进行擦除:
erase_initstruct.PageAddress = PAGE1_BASE_ADDRESS; /* 设定擦除地址 */
HAL_FLASHEx_Erase(&erase_initstruct,&page_error);/* 操作成功,该API返回HAL_OK */
2、HAL_FLASH_Program()API,对flash进行编程
原型:HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data);
所在文件:stm32fxx_hal_flash.h
例:HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWOR,PAGE1_BASE_ADDRESS,VALID_PAGE);/* 按半字编程 */
3、HAL_FLASH_Unlock()API,在调用flash编程函数之前调用,用于解锁flash
4、HAL_FLASH_Lock()API,执行完编程API后调用,锁flash,避免误操作
例:
HAL_FLASH_Unlock();
ee_readvariable(TEMP_EE_ADDR,&temp_value);
HAL_FLASH_Lock();
二、emulation_ee.h
#ifndef _EMULATION_EE_H
#define _EMULATION_EE_H
/* Includes ------------------------------------------------------------------*/
/* 这里使用的stm32f103tb,所以包含stm32f1xx.h,如果使用的是不是f1系列,则应修改为相应的头文件包含 */
#include "stm32f1xx.h"
/* Exported constants --------------------------------------------------------*/
/* low-density(16 to 32k) and medium-density(64 to 128k) device,page size = 1kbyte */
/* 页大小,根据片上flash大小来设定该值,低中密度的也大小 = 1kbytes */
#define PAGE_SIZE (uint16_t)0x400
/* high-density(256 to 512k) and XL-density(512 - 1024k),page size = 2kbyte */
//#define PAGE_SIZE (uint16_t)0x800
/* EEPROM start address in Flash,对应page63 */
#define EEPROM_START_ADDRESS ((uint32_t)0x08010000) /* EEPROM emulation start address:
after 64KByte of used Flash memory */
/* Pages 0 and 1 base and end addresses */
#define PAGE0_BASE_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + 0x000))
#define PAGE0_END_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + (PAGE_SIZE - 1)))
#define PAGE1_BASE_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + PAGE_SIZE))
#define PAGE1_END_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + (2 * PAGE_SIZE - 1)))
/* Used Flash pages for EEPROM emulation */
#define PAGE0 ((uint16_t)0x0000)
#define PAGE1 ((uint16_t)0x0001)
/* No valid page define */
#define NO_VALID_PAGE ((uint16_t)0x00AB)
/* Page status definitions */
#define ERASED ((uint16_t)0xFFFF) /* PAGE is empty */
#define RECEIVE_DATA ((uint16_t)0xEEEE) /* PAGE is marked to receive data */
#define VALID_PAGE ((uint16_t)0x0000) /* PAGE containing valid data */
/* Valid pages in read and write defines */
#define READ_FROM_VALID_PAGE ((uint8_t)0x00)
#define WRITE_IN_VALID_PAGE ((uint8_t)0x01)
/* Page full define */
#define PAGE_FULL ((uint8_t)0x80)
/* Variables' number,用户根据要保存的数据量来确定 */
#define NUMB_OF_VAR ((uint8_t)0x02)
/* 模块初始化API,使用之前用户必须首先调用该函数进行初始化操作 */
uint16_t ee_init(void);
/* 从flash读取一个数据 */
uint16_t ee_readvariable(uint16_t virt_addr, uint16_t* data);
/* 对flash写入一个数据 */
uint16_t ee_writevariable(uint16_t virt_addr, uint16_t data);
#endif
#include "emulation_ee.h"
uint16_t data_var = 0;
uint32_t cur_wr_address; /* current W/R address */
uint32_t page_error = 0;
FLASH_EraseInitTypeDef erase_initstruct =
{
.TypeErase = FLASH_TYPEERASE_PAGES,
.NbPages = 1,
.PageAddress = PAGE0_BASE_ADDRESS
};
extern uint16_t virt_add_var_tab[NUMB_OF_VAR]; /* 在app_eeprom.c中定义 */
static uint16_t __ee_init(void);
static HAL_StatusTypeDef ee_format(void);
static uint16_t ee_findvalidpage(uint8_t operation);
static uint16_t ee_verifypagefullwritevariable(uint16_t virt_addr,uint16_t data);
static uint16_t ee_pagetransfer(uint16_t virt_addr,uint16_t data);
static uint16_t init_cur_wr_address(void);
/* init emulation_ee moudle */
uint16_t ee_init(void)
{
uint16_t flash_status;
flash_status = __ee_init();
init_cur_wr_address();
return flash_status;
}
/**
* @brief Restore the pages to a known good state in case of page's status
* corruption after a power loss.
* @param None.
* @retval - Flash error code: on write Flash error
* - FLASH_COMPLETE: on success
*/
uint16_t __ee_init(void)
{
uint16_t page_status0 = 6,page_status1 = 6;
uint16_t var_idx = 0;
uint16_t eeprom_status = 0, read_status = 0;
int16_t x = -1;
uint16_t flash_status;
/* get page0 actual status */
page_status0 = (*(__IO uint16_t*)PAGE0_BASE_ADDRESS);
/* get page1 actual status */
page_status1 = (*(__IO uint16_t*)PAGE1_BASE_ADDRESS);
switch(page_status0)
{
case ERASED:
if(page_status1 == VALID_PAGE) /* Page0 erased, Page1 valid */
{
erase_initstruct.PageAddress = PAGE0_BASE_ADDRESS;
flash_status = HAL_FLASHEx_Erase(&erase_initstruct,&page_error);
if(flash_status != HAL_OK)
return flash_status;
}
else if(page_status1 == RECEIVE_DATA)
{
erase_initstruct.PageAddress = PAGE0_BASE_ADDRESS;
flash_status = HAL_FLASHEx_Erase(&erase_initstruct,&page_error);
if(flash_status != HAL_OK)
return flash_status;
flash_status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD,
PAGE1_BASE_ADDRESS,
VALID_PAGE);
if(flash_status != HAL_OK)
return flash_status;
}
else
{
flash_status = ee_format();
if(flash_status != HAL_OK)
return flash_status;
}
break;
case RECEIVE_DATA:
if(page_status1 == VALID_PAGE)
{
for(var_idx =0;var_idx format eeprom */
{
flash_status = ee_format();
if(flash_status != HAL_OK)
return flash_status;
}
else if(page_status1 == ERASED)
{
/* ERASE PAGE1 */
erase_initstruct.PageAddress = PAGE1_BASE_ADDRESS;
flash_status = HAL_FLASHEx_Erase(&erase_initstruct,&page_error);
if(flash_status != HAL_OK)
return flash_status;
}
else
{
for(var_idx=0;var_idx (page_start_address +2))
{
address_value = (*(__IO uint16_t*)address);
if(address_value == virt_addr)
{
*data = (*(__IO uint16_t*)(address-2));
read_status = 0;
break;
}else{
address = address - 4;
}
}
return read_status;
}
/**
* @brief Verify if active page is full and Writes variable in EEPROM.
* @param VirtAddress: 16 bit virtual address of the variable
* @param Data: 16 bit data to be written as variable value
* @retval Success or error status:
* - FLASH_COMPLETE: on success
* - PAGE_FULL: if valid page is full
* - NO_VALID_PAGE: if no valid page was found
* - Flash error code: on write Flash error
*/
static uint16_t ee_verifypagefullwritevariable(uint16_t virt_addr,uint16_t data)
{
uint16_t status = HAL_OK;
uint16_t validpage = PAGE0;
uint32_t page_end_address = 0x080107FF;
/* get valid page for write operation */
validpage = ee_findvalidpage(WRITE_IN_VALID_PAGE);
if(validpage == NO_VALID_PAGE)
return NO_VALID_PAGE;
/* get the valid page end address */
page_end_address = (uint32_t)((EEPROM_START_ADDRESS-2) + (uint32_t)((1+validpage)*PAGE_SIZE));
/* GET the active page address starting from begining */
while(cur_wr_address < page_end_address){
if((*(__IO uint32_t*)cur_wr_address) == 0xFFFFFFFF)
{
status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD,
cur_wr_address,
data);
if(status != HAL_OK)
return status;
status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD,
cur_wr_address +2,
virt_addr);
cur_wr_address = cur_wr_address + 4;
return status;
}else
{
cur_wr_address += 4;
}
}
return PAGE_FULL;
}
/**
* @brief Find valid Page for write or read operation
* @param Operation: operation to achieve on the valid page.
* This parameter can be one of the following values:
* @arg READ_FROM_VALID_PAGE: read operation from valid page
* @arg WRITE_IN_VALID_PAGE: write operation from valid page
* @retval Valid page number (PAGE0 or PAGE1) or NO_VALID_PAGE in case
* of no valid page was found
*/
uint16_t ee_findvalidpage(uint8_t operation)
{
uint16_t page_status0 = 6,page_status1 = 6;
/* get page0 actual status */
page_status0 = (*(__IO uint16_t*)PAGE0_BASE_ADDRESS);
/* get page1 actual status */
page_status1 = (*(__IO uint16_t*)PAGE1_BASE_ADDRESS);
/* write or read operation */
switch(operation)
{
case WRITE_IN_VALID_PAGE:
if(page_status1 == VALID_PAGE)
{
if(page_status0 == RECEIVE_DATA){
return PAGE0;
}else{
return PAGE1;
}
}
else if(page_status0 == VALID_PAGE)
{
if(page_status1 == RECEIVE_DATA){
return PAGE1;
}else{
return PAGE0;
}
}
else
{
return NO_VALID_PAGE;
}
case READ_FROM_VALID_PAGE:
if(page_status0 == VALID_PAGE)
{
return PAGE0;
}
else if(page_status1 == VALID_PAGE)
{
return PAGE1;
}
else
{
return NO_VALID_PAGE;
}
default:
return PAGE0;
}
}
uint16_t init_cur_wr_address(void)
{
uint16_t validpage = PAGE0;
uint32_t page_endaddress;
/* get valid page for write opteration */
validpage = ee_findvalidpage(WRITE_IN_VALID_PAGE);
/* check if there is no valid page */
if(validpage == NO_VALID_PAGE)
{
cur_wr_address = (uint32_t)(EEPROM_START_ADDRESS + (uint32_t)(validpage*PAGE_SIZE));
return NO_VALID_PAGE;
}
cur_wr_address = (uint32_t)(EEPROM_START_ADDRESS + (uint32_t)(validpage*PAGE_SIZE));
page_endaddress = (uint32_t)((EEPROM_START_ADDRESS-2) + (uint32_t)((1+validpage)*PAGE_SIZE));
while(cur_wr_address < page_endaddress)
{
if((*(__IO uint32_t*)cur_wr_address) == 0xFFFFFFFF){
return HAL_OK;
}else{
cur_wr_address = cur_wr_address + 4;
}
}
return PAGE_FULL;
}
/**
* @brief Transfers last updated variables data from the full Page to
* an empty one.
* @param VirtAddress: 16 bit virtual address of the variable
* @param Data: 16 bit data to be written as variable value
* @retval Success or error status:
* - FLASH_COMPLETE: on success
* - PAGE_FULL: if valid page is full
* - NO_VALID_PAGE: if no valid page was found
* - Flash error code: on write Flash error
*/
uint16_t ee_pagetransfer(uint16_t virt_addr,uint16_t data)
{
uint16_t status = HAL_OK;
uint32_t new_page_address = 0x080103FF,old_page_address = 0x08010000;
uint16_t validpage = PAGE0,varidx = 0;
uint16_t ee_status = 0,read_status = 0;
validpage = ee_findvalidpage(READ_FROM_VALID_PAGE);
if(validpage == PAGE1)
{
new_page_address = PAGE0_BASE_ADDRESS;
old_page_address = PAGE1_BASE_ADDRESS;
}
else if(validpage == PAGE0)
{
new_page_address = PAGE1_BASE_ADDRESS;
old_page_address = PAGE0_BASE_ADDRESS;
}
else
{
return NO_VALID_PAGE;
}
status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD,
new_page_address,
RECEIVE_DATA);
if(status != HAL_OK)
return status;
init_cur_wr_address();
ee_status = ee_verifypagefullwritevariable(virt_addr,data);
if(ee_status != HAL_OK)
return ee_status;
for(varidx = 0;varidx
/*
********************************************************************************
* Filename : app_eeprom.h
* Version : V1.00
* Programmer(s) : qiezhihuai
********************************************************************************
*/
#ifndef _APP_EEPROM_H
#define _APP_EEPROM_H
#ifdef __cplusplus
extern "C"{
#endif
#include "emulation_ee.h"
/* 虚拟地址宏 */
/* ee init flag,if value = 0 or 0xffff do init virtAddVarTab,else if don't init */
#define EE_READ_STATUS_VIRT_ADDR virt_add_var_tab[0] /* 第一次上电初始化标识,0表示成功初始化完成,1表示没有进行过初始化 */
#define TEMP_EE_ADDR virt_add_var_tab[1] /* 虚拟地址1中存储的是 温度数据 */
/* 初始化API */
void app_ee_init(void);
#ifdef __cplusplus
}
#endif
#endif
/*
********************************************************************************
*
*
* Filename : app_eeprom.c
* Version : V1.00
* Programmer(s) : qiezhihuai
********************************************************************************
*/
#include "app_eeprom.h"
/* Virtual address defined by the user: 0xFFFF value is prohibited */
uint16_t virt_add_var_tab[NUMB_OF_VAR] = {0x0001, 0x0002}; /* 1个数据对应1个虚拟地址 */
/* 第一次上电初始化函数 */
static void virt_add_tab_init(void)
{
uint16_t read_status;
uint16_t init_flag = 0;
read_status = ee_readvariable(EE_READ_STATUS_VIRT_ADDR,&init_flag);
if(read_status == 0) /* 找到了此地址,说明已经初始化过了 */
return;
/* 没有找到 EE_INIT_FLAG_EE_ADDR 这个地址,说明之前没有执行过初始化操作,执行第一次写入操作*/
ee_writevariable(EE_READ_STATUS_VIRT_ADDR,0); /* write 0 to virture addr EE_INIT_FLAG_EE_ADDR */
ee_readvariable(EE_READ_STATUS_VIRT_ADDR,&init_flag);
if(init_flag == 1) /* 对其他虚拟地址已经写入过数据 */
return;
/* 没有对其他的虚拟地址写入过数据,执行初始化 */
ee_writevariable(EE_READ_STATUS_VIRT_ADDR,1); /* set init_flag = 1 */
/* 初始化temp_val */
ee_writevariable(TEMP_EE_ADDR,32000);
}
void app_ee_init(void)
{
HAL_FLASH_Unlock(); /* 对flash编程前,必须解锁flash */
ee_init();
virt_add_tab_init();
HAL_FLASH_Lock(); /* 编程结束后,必须锁定flash */
}
/********************** END OF FILE ******************************************/
函数保存数据。
/* Includes ------------------------------------------------------------------*/
#include "stm32f1xx.h"
#include "iwdg.h"
#include "spi.h"
#include "usart.h"
#include "gpio.h"
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
extern uint16_t virt_add_var_tab[NUMB_OF_VAR]; /* 引用app_eeprom.c 中的变量 */
int main(void)
{
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* Configure the system clock */
SystemClock_Config();
/* Initialize all configured peripherals */
MX_GPIO_Init();
// MX_IWDG_Init();
MX_SPI1_Init();
MX_USART1_UART_Init();
/* init eeprom */
app_ee_init();
/* read temp_value from eeprom */
HAL_FLASH_Unlock();
ee_readvariable(TEMP_EE_ADDR,&temp_value);
HAL_FLASH_Lock();
/* update DAC */
START_SPI1();
HAL_SPI_Transmit(&hspi1,(uint8_t *)&temp_value,1,5000);
STOP_SPI1();
while (1)
{
if(uart1_ready == SET)
{
uart1_ready = RESET;
uart1_parser();
}
if(t_flag == SET)
{
t_flag = RESET;
/* Update DAC out */
START_SPI1();
HAL_SPI_Transmit(&hspi1,(uint8_t *)&temp_value,1,5000);
STOP_SPI1();
}
}
}
/** System Clock Configuration
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_LSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = 16;
RCC_OscInitStruct.LSIState = RCC_LSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV2;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0);
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
/* SysTick_IRQn interrupt configuration */
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/