STM32f030f4p6 內部flash 打包讀寫


最近做到的項目在運行需要把一組uint8_t(unsigned char)的數據進行掉電儲存,想到單片機STM32f030f4p6內部flash可以直接由程序操作,寫了以下代碼用於uint8_t數據打包保存和讀取。

 



1、程序清單 與 測試結果

本程序包含5個文件,分別是:

1、Flash.c:內部flash讀取儲存相關函數

2、Flash.h:flash頭文件

3、USART1.c:STM32F030F4P6的串口驅動,串口僅用於打印數據觀察。

4、USART1.h:串口頭文件

5、main.c:防止程序主入口

 

1、Flash.c

#include "Flash.h"

#include "USART1.h"
/*******************************************************************************
* Function Name  : doseFlashHasPackedMessage
* Description    : Does flash has packed messages   
* Input          : None
* Output         : 
* Return         : ture/false
*******************************************************************************/
bool doseFlashHasPackedMessage(void)
{
    uint16_t length;
    uint16_t getHead;    

    /*Is head matched*/ 
    getHead = (uint16_t)(*(uint16_t*)(STM32F0xx_FLASH_PAGE15_STARTADDR ));      
    if( EEPPROM_PACKAGEHEAD != getHead )
    {
        return false;
    }
    
    /*Is length zero*/
    length = (*(uint16_t*)(STM32F0xx_FLASH_PAGE15_STARTADDR+2));
    if( 0 == length)
    {
        return false;
    }
    
    return true;
}
/*******************************************************************************
* Function Name  : getValuablePackedMessageLengthofFlash
* Description    : Get valuable packed message length of flash 
* Input          : None
* Output         : 
* Return         : valuable length
*******************************************************************************/
uint16_t getValuablePackedMessageLengthofFlash( void )
{
    uint16_t length;
         
    /*Is head matched*/       
    if( EEPPROM_PACKAGEHEAD != (*(uint16_t*)(STM32F0xx_FLASH_PAGE15_STARTADDR )) )
    {
        return 0;
    }
    
    /*Get length*/
    length = (uint16_t)(*(uint16_t*)(STM32F0xx_FLASH_PAGE15_STARTADDR+2));   
    
    return length;
}
/*******************************************************************************
* Function Name  : readPackedMessageFromFlash
* Description    : Read packed message form flash
* Input          : buff:point to first location of received buffer.length:Maxmum length of reception
* Output         : 
* Return         : reception length
*******************************************************************************/
uint16_t readPackedMessageFromFlash( uint8_t *buff , uint16_t length)
{
    int i;
    uint16_t getLength;
    
    if( !doseFlashHasPackedMessage() )
        return 0;
    
    /*Get valuable length*/
    getLength = getValuablePackedMessageLengthofFlash();
    
    /*Read out message*/
    for(i=0;i<MIN(getLength,length);i++)
    {
        buff[i]= *(uint8_t*)(STM32F0xx_FLASH_PAGE15_STARTADDR+4+i);
    }     
    
    return MIN(getLength,length);
}
/*******************************************************************************
* Function Name  : isItOddNumber
* Description    : is input data an odd number?
* Input          : number:input data
* Output         : 
* Return         : true/false
*******************************************************************************/
bool isItOddNumber(uint16_t number)
{
    if(0 != number%2)
    {
        return true;
    }
    return false;
}
/*******************************************************************************
* Function Name  : Flash_eeprom_WriteWithPacked
* Description    : Write a group of datas to flash.
* Input          : buff:pointer of first data, length: write length
* Output         : 
* Return         : true/false
*******************************************************************************/
bool writeMessageToFlash( uint8_t *buff , uint16_t length)
{
    uint16_t temp;
    int i;
    
    /*Protection*/
    if( (length+4) > STM32F0xx_PAGE_SIZE )
    {
        return false;
    }
    
    FLASH_Unlock(  );    

    /*Clear all flags*/
    FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPERR );
    
    /*Erase first . Do not rember.*/
    if(FLASH_COMPLETE != FLASH_ErasePage(STM32F0xx_FLASH_PAGE15_STARTADDR))//°üº¬Á˵ȴýbusy
    {
        return false;
    }
    
    /*Write head*/
    FLASH_ProgramHalfWord( STM32F0xx_FLASH_PAGE15_STARTADDR, EEPPROM_PACKAGEHEAD );
    /*Write length*/
    FLASH_ProgramHalfWord( STM32F0xx_FLASH_PAGE15_STARTADDR+2 , length );
    
    
    /*Write datas*/
    for(i=0 ;i<length/2 ;i++)
    {
        temp = buff[2*i]|(uint16_t)buff[2*i+1]<<8;
        FLASH_ProgramHalfWord( STM32F0xx_FLASH_PAGE15_STARTADDR+4+2*i , temp);
    }  
    if( isItOddNumber(length) )//Write one more if length is odd number.
    {        
        temp = (uint16_t)buff[length-1];
        FLASH_ProgramHalfWord( STM32F0xx_FLASH_PAGE15_STARTADDR+4+(length-1) , temp);
    }

    
    /*Read out and check*/
    for(i=0 ;i<length ;i++)
    {
        if( *(uint8_t*)(STM32F0xx_FLASH_PAGE15_STARTADDR+4+i) != buff[i] )
        {
            FLASH_Lock();
            return false;
        }
    }    
    
    FLASH_Lock();
    return true;    
}


/*******************************************************************************
* Function Name  : flashReadWriteTest
* Description    : Flash read write test.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void flashReadWriteTest( void ) 
{
    #define testReadWriteNumber  200
    uint8_t buff_write[testReadWriteNumber]={0};
    uint8_t buff_read[testReadWriteNumber]={0};
    uint16_t length;
    int i;
    
    for( i=0;i<testReadWriteNumber;i++)
    {
        buff_write[i]=i;
    }
    
    writeMessageToFlash( buff_write , testReadWriteNumber);
    length = readPackedMessageFromFlash( buff_read , testReadWriteNumber);   
    printf("length=%d\r\n",length);
    for(i=0;i<length;i++)
    {
        printf("read[%d]=%d\r\n",i,buff_read[i]);
    }
    
    while(1);    
}
View Code

2、Flash.h

#ifndef __FLASH_H
#define __FLASH_H

#include "stm32f0xx.h"
#include <stdbool.h>

//Message head
#define EEPPROM_PACKAGEHEAD 0xAA55//

//Flash page head
#define STM32F0xx_PAGE_SIZE 0x400
#define STM32F0xx_FLASH_PAGE0_STARTADDR 0x8000000
#define STM32F0xx_FLASH_PAGE1_STARTADDR (STM32F0xx_FLASH_PAGE0_STARTADDR+STM32F0xx_PAGE_SIZE)
#define STM32F0xx_FLASH_PAGE2_STARTADDR (STM32F0xx_FLASH_PAGE0_STARTADDR+2*STM32F0xx_PAGE_SIZE)
#define STM32F0xx_FLASH_PAGE3_STARTADDR (STM32F0xx_FLASH_PAGE0_STARTADDR+3*STM32F0xx_PAGE_SIZE)
#define STM32F0xx_FLASH_PAGE4_STARTADDR (STM32F0xx_FLASH_PAGE0_STARTADDR+4*STM32F0xx_PAGE_SIZE)
#define STM32F0xx_FLASH_PAGE5_STARTADDR (STM32F0xx_FLASH_PAGE0_STARTADDR+5*STM32F0xx_PAGE_SIZE)
#define STM32F0xx_FLASH_PAGE6_STARTADDR (STM32F0xx_FLASH_PAGE0_STARTADDR+6*STM32F0xx_PAGE_SIZE)
#define STM32F0xx_FLASH_PAGE7_STARTADDR (STM32F0xx_FLASH_PAGE0_STARTADDR+7*STM32F0xx_PAGE_SIZE)
#define STM32F0xx_FLASH_PAGE8_STARTADDR (STM32F0xx_FLASH_PAGE0_STARTADDR+8*STM32F0xx_PAGE_SIZE)
#define STM32F0xx_FLASH_PAGE9_STARTADDR (STM32F0xx_FLASH_PAGE0_STARTADDR+9*STM32F0xx_PAGE_SIZE)
#define STM32F0xx_FLASH_PAGE10_STARTADDR (STM32F0xx_FLASH_PAGE0_STARTADDR+10*STM32F0xx_PAGE_SIZE)
#define STM32F0xx_FLASH_PAGE11_STARTADDR (STM32F0xx_FLASH_PAGE0_STARTADDR+11*STM32F0xx_PAGE_SIZE)
#define STM32F0xx_FLASH_PAGE12_STARTADDR (STM32F0xx_FLASH_PAGE0_STARTADDR+12*STM32F0xx_PAGE_SIZE)
#define STM32F0xx_FLASH_PAGE13_STARTADDR (STM32F0xx_FLASH_PAGE0_STARTADDR+13*STM32F0xx_PAGE_SIZE)
#define STM32F0xx_FLASH_PAGE14_STARTADDR (STM32F0xx_FLASH_PAGE0_STARTADDR+14*STM32F0xx_PAGE_SIZE)
#define STM32F0xx_FLASH_PAGE15_STARTADDR (STM32F0xx_FLASH_PAGE0_STARTADDR+15*STM32F0xx_PAGE_SIZE)


#define MIN(A,B) (A<B?A:B)


void flashReadWriteTest( void ) ;

#endif
View Code

3、USART1.c

#include "USART1.h"


void USART1_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

    GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_1);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_1);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    USART_InitStructure.USART_BaudRate = 115200;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode =  USART_Mode_Tx;

    USART_Init(USART1, &USART_InitStructure);
    USART_Cmd(USART1, ENABLE);



}


void USART1_IRQHandler(void)
{


}


/**
  * @brief  Retargets the C library printf function to the USART.
  * @param  None
  * @retval None
  */
PUTCHAR_PROTOTYPE
{
    /* Place your implementation of fputc here */
    /* e.g. write a character to the USART */
    USART_SendData(USART1, (uint8_t) ch);

    /* Loop until the end of transmission */
    while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
    {}

    return ch;
}
View Code

4、USART1.h

#ifndef __BSP_USART1_H
#define __BSP_USART1_H

#include "stm32f0xx.h"
#include <stdio.h>

#ifdef __GNUC__
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
   set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */

void USART1_Init(void);

#endif
View Code

5、main.c

#include "stm32f0xx.h"
#include "USART1.h"
#include "Flash.h"
int main(void)
{
    USART1_Init();    
    flashReadWriteTest();
    while(1)
    {
    }
}
View Code




 測試結果:

第54-199省略..

 





 

2、程序詳解

2.1、內存結構:

STM32F0xx的flash結構如下:最多具有64頁,每頁1KByte大小。

我使用的STM32F030F4P6 flash區域有16K,所以實際上只有0-15頁,本程序中把需要保存的數據數據存放在最后一頁(第15頁)中。

 

2.2、定義數據包結構

為了保證儲存和讀出flash的數據是正確的,本程序將寫入flash數據分為3個區域

  1. 報頭區:寫入數據的時候,將第15頁的第1、2字節寫入0xaa55,在讀出的時候如果此位置不是0xaa55,則表示這段數據數據無效,不是由自己存入的數據或者程序出現了異常;
  2. 長度:寫入數據的時候,第15頁的第3、4字節寫入有效數據的長度,讀出這個字段,就知道上次自己一共存入了多少數據;
  3. 數據段:從第15頁的第5字節開始,全部字節(1020byte)用於儲存uint8_t類型的數據;

 

2.3、寫操作

寫操作有如下步驟:

  • 保護:由於我們只有1Kbyte空間,出去4個字節的報頭和長度,實際只能存儲1020個u8類型,寫太多返回失敗。
  • 解flash鎖
  • 頁擦除
  • 寫入數據包
  • 上鎖
/*******************************************************************************
* Function Name  : Flash_eeprom_WriteWithPacked
* Description    : Write a group of datas to flash.
* Input          : buff:pointer of first data, length: write length
* Output         : 
* Return         : true/false
*******************************************************************************/
bool writeMessageToFlash( uint8_t *buff , uint16_t length)
{
    uint16_t temp;
    int i;
    
    /*Protection*/
    if( (length+4) > STM32F0xx_PAGE_SIZE )
    {
        return false;
    }
    
    FLASH_Unlock(  );    

    /*Clear all flags*/
    FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPERR );
    
    /*Erase first . Do not rember.*/
    if(FLASH_COMPLETE != FLASH_ErasePage(STM32F0xx_FLASH_PAGE15_STARTADDR))//°üº¬Á˵ȴýbusy
    {
        return false;
    }
    
    /*Write head*/
    FLASH_ProgramHalfWord( STM32F0xx_FLASH_PAGE15_STARTADDR, EEPPROM_PACKAGEHEAD );
    /*Write length*/
    FLASH_ProgramHalfWord( STM32F0xx_FLASH_PAGE15_STARTADDR+2 , length );
    
    
    /*Write datas*/
    for(i=0 ;i<length/2 ;i++)
    {
        temp = buff[2*i]|(uint16_t)buff[2*i+1]<<8;
        FLASH_ProgramHalfWord( STM32F0xx_FLASH_PAGE15_STARTADDR+4+2*i , temp);
    }  
    if( isItOddNumber(length) )//Write one more if length is odd number.
    {        
        temp = (uint16_t)buff[length-1];
        FLASH_ProgramHalfWord( STM32F0xx_FLASH_PAGE15_STARTADDR+4+(length-1) , temp);
    }

    
    /*Read out and check*/
    for(i=0 ;i<length ;i++)
    {
        if( *(uint8_t*)(STM32F0xx_FLASH_PAGE15_STARTADDR+4+i) != buff[i] )
        {
            FLASH_Lock();
            return false;
        }
    }    
    
    FLASH_Lock();
    return true;    
}

 

2.3.1、解鎖操作:

用戶手冊描述如下:

在(芯片)重置過后,為了防止多余的擦寫操作flash會被保護。除了OBL_LAUNCH位用於重載option bit,FLASH_CR寄存器的其他部分都不可以訪問。

需要把以下兩個解鎖序列寫入FLASH_KEY寄存器,才能訪問FLASH_CR:

  • 0x45670123
  • 0xCDEF89AB

檢索官方庫定位FLASH_Unlock()這個函數,它的底層實現與文檔描述相符,另外增加了防止重復解鎖的操作,直接使用FLASH_Unlock()這個函數即可以完成解鎖。

 

 

2.3.2、頁擦除

用戶手冊描述如下:

 

1、通過檢查FLASH_SR寄存器的BSY位來確認flash沒有使用

2、把FLASH_CR寄存器的PER位置SET

3、通過編寫FLASH_AR寄存器來確認要擦除的頁

4、將FLASH_CR寄存器的STRT位置SET

5、等待BSY位rst

6、檢查FLASH_SR寄存器中的EOP標記(成功會置SET)(流程圖沒有、庫函數沒檢查)

7、清除EOP標記(流程圖沒有、庫函數沒清除)

 

檢索官方庫定位到FLASH_ErasePage(uint32_t Page_Address)這個函數,它的底層實現與文檔流程圖描述相符,值得注意的是,手冊描述中6、7步並沒有出現在手冊的流程圖和庫函數中(庫版本V1.2.0)。但在使用中沒有出現問題,這里先記錄看以后使用中會不會出現問題

 

 

 

2.3.3、往flash中寫入數據

用戶手冊描述如下

  1、通過檢查FLASH_SR寄存器的BSY位來確認flash沒有使用

  2、設置FLASH_CR寄存器的PG位

  3、在目標地址上寫入半個word的數據

  4、等BSY位reset

  5、檢查FLASH_SR寄存器的EOP標記(SET表示成功)(流程圖沒有出現、庫函數中沒檢查)

 檢索官方庫定位到FLASH_ErasePage(uint32_t Page_Address)這個函數,它的底層實現與文檔流程圖描述相符,同樣地,庫函數沒有檢查第五步的EOP標記

 

 

2.3.4、上鎖操作

根據手冊提示,定位到庫函數void FLASH_Lock(void)。只要置位LOCK位就上鎖了。

 

 

 




 




 

2.4、讀操作  

程序如下

/*******************************************************************************
* Function Name  : readPackedMessageFromFlash
* Description    : Read packed message form flash
* Input          : buff:point to first location of received buffer.length:Maxmum length of reception
* Output         : 
* Return         : reception length
*******************************************************************************/
uint16_t readPackedMessageFromFlash( uint8_t *buff , uint16_t length)
{
    int i;
    uint16_t getLength;
    
    if( !doseFlashHasPackedMessage() )
        return 0;
    
    /*Get valuable length*/
    getLength = getValuablePackedMessageLengthofFlash();
    
    /*Read out message*/
    for(i=0;i<MIN(getLength,length);i++)
    {
        buff[i]= *(uint8_t*)(STM32F0xx_FLASH_PAGE15_STARTADDR+4+i);
    }     
    
    return MIN(getLength,length);
}

 

根據用戶手冊提示,讀操作是可以直接取址的,所以讀操作實際只有如下一句只要用uint8_t類型取出目標地址再取值就可以了。

 

但是,我們的數據是打包的(詳見2.2),所以還需要:

1、根據報頭判斷是不是有效數據

2、根據長度判斷要讀取多少數據

3、最后才是讀出數據

 

2.4.1、判斷數據有效性:

我們通過報頭來判斷數據是不是自己寫入的。

也就是判斷flash第15頁的第1、2個字節是不是0xaa55,如果不是,那這段數據是無效的。

另外再判斷一下長度字段,如果長度等於0,也就是后面沒有數據,那這段數據也是無效的。

 

/*******************************************************************************
* Function Name  : doseFlashHasPackedMessage
* Description    : Does flash has packed messages   
* Input          : None
* Output         : 
* Return         : ture/false
*******************************************************************************/
bool doseFlashHasPackedMessage(void)
{
    uint16_t length;
    uint16_t getHead;    

    /*Is head matched*/ 
    getHead = (uint16_t)(*(uint16_t*)(STM32F0xx_FLASH_PAGE15_STARTADDR ));      
    if( EEPPROM_PACKAGEHEAD != getHead )
    {
        return false;
    }
    
    /*Is length zero*/
    length = (*(uint16_t*)(STM32F0xx_FLASH_PAGE15_STARTADDR+2));
    if( 0 == length)
    {
        return false;
    }
    
    return true;
}

 

2.4.2、根據長度讀出數據:

 

在讀Flash程序中,getLength是從flash中讀出的長度,length是我們指定的最大讀取長度。如果getLength大於我們指定讀取長度,很可能會造成溢出,所以兩者取小的一個防止溢出。

 





 

到此為止程序寫完了,要是有什么錯誤或不足的地方還望各位大佬指出,要是使用中出現什么BUG也希望大家回來反饋一下,十分感謝

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM