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

謝謝您的觀看!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章