STM32 內部Flash帶緩存讀寫 程序源碼 [已驗證]

STM32 內部Flash帶緩存讀寫 程序源碼

由於STM32 系列Flash的Erase操作很多是按Page擦除,如果往Flash寫入的數據或餘數不夠一個整Page,那麼該Page其他數據也會被擦除掉。
所以這種情況想保持其他未操作數據不變的話,就要做緩存讀寫,既將要擦除Page的數據全讀出來,與新寫入的數據Merge後,再寫入。這一塊目前ST沒有給出Demo,需要現寫。

在這裏插入圖片描述
注意到Programming in the Flash memory performed by word or half-page 所以,Write API使用了按Word寫入

測試程序使用的單片機爲STM32L0系列,PageSize爲128B,
在這裏插入圖片描述
Memory map如下:
在這裏插入圖片描述

下面是我以前寫的API,首先是相關宏定義:

0 Macro

讀寫API內部調用的是STM32xx_hal_flash.c中的HAL_FLASH_Program API,如果使用不一樣的Hal庫,可以直接替換下方的Flash_WriteOneWord宏定義:

#define  TRUE    1
#define  FALSE   0

typedef unsigned char   BOOL;       /* boolean data */
typedef unsigned char   FLAG;       /* boolean data */
typedef unsigned char   U8BIT;      /* unsigned 8 bit data */
typedef unsigned short  U16BIT;     /* unsigned 16 bit data */
typedef unsigned long   U32BIT;     /* unsigned 32 bit data */
typedef unsigned long   WORD;       /* unsigned 32 bit data */

#define PAGE_SIZE              FLASH_PAGE_SIZE  

#define Flash_WriteOneWord(a, b)	    HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, a, b)
#define Flash_WriteOneHalfWord(a, b)	HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, a, b)

1.Flash_Erase

Erase主要負責寫入之前清空的操作,由Wirte API判斷要Erase多少Page

/*****************************************************************************
 @Name : Flash_Erase
 @Brief : Erase the whole page by NumtoErase
 @Param In : U32BIT StartAddr         
                     uint16_t PageNumtoErase  
 @Param Out : 
 @Return : TURE for Success, FALSE for Failed
 
History      :
  1.Date   : Tuesday, June 04, 2018
    Author   : Howard Xue

*****************************************************************************/
BOOL Flash_Erase(uint32_t StartAddr, uint16_t PageNumtoErase)
{

	if(HAL_FLASH_Unlock() != HAL_OK)
		return FALSE;
	
	uint32_t PageError_Tmp = 0;
	FLASH_EraseInitTypeDef Eras;
	Eras.NbPages = PageNumtoErase;
	Eras.PageAddress = StartAddr;
	Eras.TypeErase = FLASH_TYPEERASE_PAGES;
	
	if(HAL_FLASHEx_Erase(&Eras , &PageError_Tmp) != HAL_OK)
		return FALSE;


	if(HAL_FLASH_Lock() != HAL_OK)
		  return FALSE;

	return TRUE;


}

2. Flash_Read_Byte

/*****************************************************************************
@Name : Flash_Read_Byte
@Brief : Read Internal Falsh data by address
@Param In : uint32_t StartAddr         
                      uint8_t  *ReadBuf
                      uint32_t NumToRead
@Param Out :   uint8_t  *ReadBuf
@Return : TURE for Success, FALSE for Failed
 
History      :
  1.Date   : Tuesday, June 04, 2018
    Author   : Howard Xue

*****************************************************************************/
BOOL Flash_Read_Byte(uint32_t StartAddr, uint8_t  *ReadBuf, uint32_t NumToRead)
{
	if(NumToRead== 0)
		return FALSE;
		
	uint32_t DataNum = 0;
	uint32_t ReadAddress = StartAddr;
	
	while(DataNum < NumToRead)
	{
		*(ReadBuf + DataNum) = *(__IO uint8_t  *)ReadAddress++;
		DataNum++;
	}
		return TRUE;
}

3.Flash_Write_WithBuffer

BufferWrite其實就是先根據要寫多少數據,算出相應需Erase的page數,之後將這些Page的數據全讀出來,與新要寫入的數據合併覆蓋後,再寫入。 這樣就實現除了要寫入的地址數據,其他的數據不被Erase更改。

注意Wite傳入參數如果是U8的需要強制轉換成U32

/*****************************************************************************
@Name : Flash_Write_WithBuffer
@Brief : Read Buffer from Flash ,Then Merge New Data and Write
@Param In : uint32_t StartAddr         
                      uint32_t  *pBufferToWrite
                      uint32_t NumToWrite
@Param Out :   uint32_t  *pBufferToWrite
@Return : TURE for Success, FALSE for Failed
 
History      :
  1.Date   : Tuesday, June 04, 2018
    Author   : Howard Xue

*****************************************************************************/
BOOL Flash_Write_WithBuffer(uint32_t StartAddr, uint32_t *pBufferToWrite, uint32_t NumToWrite)
{

	uint8_t PageNumtoErase = 1;//default is erase 1 page.
    uint32_t i;
	uint32_t ReadBuffer_Length = 0;
//	uint8_t ReadBuffer[FLASH_PAGE_SIZE] = {0};

	if(NumToWrite == 0)
		return FALSE;
	
	//GetPageNum to erase
	if(NumToWrite > FLASH_PAGE_SIZE)
	{
		PageNumtoErase = NumToWrite/FLASH_PAGE_SIZE + 1; 
	}

	ReadBuffer_Length = PageNumtoErase*FLASH_PAGE_SIZE;

	U32BIT *pReadBuffer = (U32BIT *)calloc(1, ReadBuffer_Length);
	U32BIT *pReadBufferCount = pReadBuffer;
	
	//1.Read orginal data from flash
	Flash_Read_Byte(StartAddr,(U8BIT *)pReadBuffer,ReadBuffer_Length);

	//2.Merge New Data for Write
	memcpy(pReadBufferCount,pBufferToWrite,NumToWrite);

	//3.Erase whole page
	if(Flash_Erase(StartAddr, PageNumtoErase) != TRUE)
		return FALSE;

	//4.Write Buffer to Flash, Unlock -> write -> lock
	if(HAL_FLASH_Unlock() != HAL_OK)
		return FALSE;
		
    for ( i = 0 ; i < (ReadBuffer_Length/4) ; i++ )
    {
        if(Flash_WriteOneWord(StartAddr + i * sizeof(uint32_t ), *pReadBufferCount) != HAL_OK)
			return FALSE;
		
		pReadBufferCount++;
    }

	 
	if(HAL_FLASH_Lock() != HAL_OK)
		return FALSE;

	 free(pReadBuffer);
	 return TRUE;
	

}

4.Flash_Write_NoBuffer

/*****************************************************************************
@Name : Flash_Write_NoBuffer
@Brief : Write Flash without Buffer
@Param In : uint32_t StartAddr         
                      uint32_t  *pBufferToWrite
                      uint32_t NumToWrite
@Param Out :   uint32_t  *pBufferToWrite
@Return : TURE for Success, FALSE for Failed
 
History      :
  1.Date   : Tuesday, June 04, 2018
    Author   : Howard Xue

*****************************************************************************/
BOOL Flash_Write_NoBuffer(uint32_t StartAddr, uint32_t *pBufferToWrite, uint32_t NumToWrite)
{

	uint32_t PageNumtoErase = 1;//default
    uint32_t i;

	if(NumToWrite == 0)
		return FALSE;
	
	//GetPageNum to erase
	if(NumToWrite > FLASH_PAGE_SIZE)
	{
		PageNumtoErase = NumToWrite/FLASH_PAGE_SIZE + 1; 
	}
	
	//Erase whole page
	if(Flash_Erase(StartAddr, PageNumtoErase) != TRUE)
		return FALSE;
	
		//4.Write Buffer to Flash, Unlock -> write -> lock
	if(HAL_FLASH_Unlock() != HAL_OK)
		return FALSE;
		
    for ( i = 0 ; i < (NumToWrite/4) ; i++ )
    {
		if(Flash_WriteOneWord(StartAddr + i * sizeof(uint32_t ), *pBufferToWrite) != HAL_OK)
			return FALSE;
			
        pBufferToWrite++;
    }

	 	if(HAL_FLASH_Lock() != HAL_OK)
		  return FALSE;

		return TRUE;

}

5. Test

Test程序中,先Flash_Write_NoBuffer將Data全部寫入Flash,然後更改幾個下次要寫入的數值,只寫入更改的數據,這樣FlashBufferWrite就會僅更新更改的數值,其他數值不變(如果不使用BufferWrite,Flash同一Page其他數據都會被Erase)
注意Wite傳入參數如果是U8的需要強制轉換成U32





void FlashTest()
{
	U8BIT Data_W[64] = {0xFA,0xFA,0xFE,0xFE,0xFA,0xFA,0xFE,0xFE,0xFA,0xFA,0xFE,0xFE,0xFA,0xFA,0xFE,0xFE,0xFA,0xFA,0xFE,0xFE,0xFA,0xFA,0xFE,0xFE};
	U8BIT Data_R[64] = {0};
    U32BIT FLASH_DATAZONE_BASE_ADDR = 0x08028000; //Address for test

	Flash_Read_Byte(FLASH_DATAZONE_BASE_ADDR, Data_R,sizeof(Data_R));

	Flash_Write_NoBuffer(FLASH_DATAZONE_BASE_ADDR,(U32BIT *)&Data_W[0],sizeof(Data_W));

	Flash_Read_Byte(FLASH_DATAZONE_BASE_ADDR, Data_R,sizeof(Data_R));

	Data_W[0] = 0;
	Data_W[1] = 1;
	//Merge new data and write, ohter data will contained
	Flash_Write_WithBuffer(FLASH_DATAZONE_BASE_ADDR,(U32BIT *)&Data_W[0],4);
	
	Flash_Read_Byte(FLASH_DATAZONE_BASE_ADDR, Data_R,sizeof(Data_R));
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章