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));
}