STM32 internal Flash uses Erase Leveling

STM32 internal Flash uses Erase Leveling

1. Design Ideas

  • Chip: STM32F103ZET6

  • Software: STM32 CubeHAL

  • Flash Page Size: 2K

  • Using Flash Page 60 (0x0801E000-0x0801E800 (excluded) to test the wear balance algorithm

  • Data frame format: | 0x5A|data1|data2|0xA5|

  • The experimental Flash storage model is as follows:

  • First, erase Page 60, after erasing the data in Page 60 pages are all 1.
  • Each time the data is written, the numerical FF (not written data) is searched sequentially, and the data is written after it is found. At the same time, the previous valid data is set to 0. If FF is not found, it indicates that the data is full, the whole page will be erased and the data will be written from scratch.
  • Each time the data is read out, the value 5A (valid data) is searched sequentially, and then four bytes are read out. If the value 5A is not found, the error code 0 is returned.

2. Implementation code

There are three files, flash_wear_leveling.h, flash_wear_leveling.c and example.c, flash_wear_leveling.h and flash_wear_leveling.c.
The implementation of wear equalization algorithm, example.c, is an example.

Visit my Gist directly to get the latest code

  • flash_wear_leveling.h
#ifndef __FLASH_H__
#define __FLASH_H__

#include "stm32f1xx_hal.h"

#define FLASH_START_ADDRESS 0x0801E000
#define PAGE_SIZE               (uint32_t)FLASH_PAGE_SIZE  /* Page size */

#define WT_GET_NEW_ADDR (1<<0)
#define WT_GET_USED_ADDR (1<<1)

typedef union
{
	uint32_t data;
	uint8_t buff[4];
}flash_pack_u;

typedef struct
{
	uint32_t flash_start_address;
	uint32_t current_addr;
	uint32_t new_addr;
	uint16_t page_size;
	flash_pack_u buff;
}flasher_t;

#define newFlasher(start_address,page_size){ \
		start_address,start_address,start_address,page_size,{0} \
}

typedef union
{
	uint8_t data[2];
	uint16_t data16;
}writer_u;

void flash_init(void);
void easer_flash(void);
void find_new_entry(void);
uint32_t find_used_entry(void);
void write_word_to_flash(writer_u writer);

#endif

  • flash_wear_leveling.c
#include <drv_flash.h>
extern void FLASH_PageErase(uint32_t PageAddress);

static Flasher flasher = newFlasher(FLASH_START_ADDRESS,PAGE_SIZE);

void easer_flash(void)
{
	/* -1- Unlock the Flash Bank Program Erase controller */
	  HAL_FLASH_Unlock();

	  /* -2- Clear All pending flags */
	  __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_WRPERR | FLASH_FLAG_PGERR);

	  /* -3- erase the FLASH pages */
	  FLASH_PageErase(FLASH_START_ADDRESS);
	  FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
	  CLEAR_BIT(FLASH->CR, FLASH_CR_PER);

	  /* -5- Lock the Flash Bank Program Erase controller */
	  HAL_FLASH_Lock();
}


void find_new_entry(void)
{
	while(flasher.current_addr < flasher.flash_start_address+flasher.page_size)
	{
		flasher.buff.data = read_word_from_flash(flasher.current_addr);
		if(flasher.buff.buff[0] == 0xFF)
		{
			flasher.new_addr = flasher.current_addr;
			return;
		}

		flasher.current_addr += 4;
	}

	if(flasher.current_addr >= flasher.flash_start_address+flasher.page_size)
	{
		easer_flash();
		flasher.current_addr = flasher.flash_start_address;
		flasher.new_addr = flasher.flash_start_address;
	}
}

uint32_t find_used_entry(void)
{
	while(flasher.current_addr < flasher.flash_start_address+flasher.page_size)
	{
		flasher.buff.data = read_word_from_flash(flasher.current_addr);
		if(flasher.buff.buff[0] == 0x5A)
		{
			return flasher.buff.data;
		}

		flasher.current_addr += 4;
	}
	return 0;
}

void write_word_to_flash(writer_u writer)
{
	flash_pack_u buf;
	buf.buff[0] = 0x5A;
	buf.buff[1] = writer.data[0];
	buf.buff[2] = writer.data[1];
	buf.buff[3] = 0xA5;

	find_new_entry();

	HAL_FLASH_Unlock();

	if(flasher.new_addr-4 >= flasher.flash_start_address)
	{
		HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,flasher.new_addr-4,0x00);
	}
	HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,flasher.new_addr,buf.data);

	HAL_FLASH_Lock();
}

  • example.c
/*
 * First, make sure that the Flash area you want to operate on is just erased (all 1). To achieve this, you can just call easer_flash() function to erase a specific flash page.
 * Of course, you need to call the flash_init() function to set the first address of the flash page.
 * Data frame format is | 0x5A | data1 | data2 | 0xA5|
 * Each time the data is written, the numerical FF (not written data) is searched sequentially, and the data is written after it is found. At the same time, the previous valid data is set to 0. If FF is not found, it indicates that the data is full, the whole page will be erased and the data will be written from scratch.
 * Each time the data is read out, the value 5A (valid data) is searched sequentially, and then four bytes are read out. If the value 5A is not found, the error code 0 is returned.
 * 
 */

int main()
{
  // write data into flash
  writer_u writer;
  writer.data[0] = 0x11;
  writer.data[1] = 0x22;
  write_word_to_flash(writer);

  // then read it from flash and print it
  flash_pack_u flash_pack;
  flash_pack.data = find_used_entry();
  usart1_printf("%x  %x\n",flash_pack.buff[1]);
  usart1_printf("%x  %x\n",flash_pack.buff[2]);
}

Posted by webwired on Thu, 03 Oct 2019 16:24:37 -0700