基于STM32 HAL库的flash emulation eeprom

本文讨论如何使用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

三、emulation_ee.c


#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

四、app_eeprom.h
定义地址宏,及1个初始化API, app_ee_init()

/*
********************************************************************************
* 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


五、app_eeprom.c


定义了虚拟地址表 virt_add_var_tab ,地址值由用户随意定义,有效范围0x0000 ~ 0x7FFF,代码如下:

/*
********************************************************************************
*
*
* 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 ******************************************/


六、main.c
在main函数中调用 app_ee_init()初始化eeprom,然后调用ee_readvariable() 读取地址0x0002的温度数据。当串口收到上位机发送的温度数据时调用ee_writevariable()

函数保存数据。


/* 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****/

谢谢您的观看!

你可能感兴趣的:(STM32F1)