STM系列 Flash操作

STM系列的Flash操作方法及誤操作筆記


1、flash操作方法


1.1 對stm8的flash操作

#define BaseCfgaddr (uint32*)0x4000

/*
 * 函數名稱    :   WriteAddrToFlash
 * 函數功能    :   數據寫入Flash
 * 輸入參數    :
 * 輸出參數    :   無
 * 返 回 值    :   0或1
 * 其它說明    :   無
 * */
uint8 SYS_WriteDataToFlash(uint8* Data,uint32* addr,uint16Len)  
{      
    if(!Data)  
    return 0;           
    /*操作EEPROM,先進行解鎖*/  
    FLASH_DUKR = 0xAE; /* 注意這裏不能斷點調試,否則會造成內部不同步,FLASH解鎖失敗*/
    FLASH_DUKR = 0x56;  
    FLASH_CR2 = 0x00;  
    FLASH_NCR2 = 0xFF;  
    if(!(FLASH_IAPSR & 0x08)) /* 檢測對應的位是否解鎖  */
    return 0;      
    memcpy(addr,Data,Len);  
    FLASH_IAPSR = (ST_UINT8)(~0x08); /* 重新上鎖  */ 
    return 1;  

按字節操作即每次寫入一位

 SYS_WriteDataToFlash(Buf,BaseCfgaddr,WriteCfgSize);


1.2 stm32f的flash操作。

使用stm32f0k4

/*
 * 函數名稱    :   WriteAddrToFlash
 * 函數功能    :   數據寫入Flash
 * 輸入參數    :
 * 輸出參數    :   無
 * 返 回 值    :   0或1
 * 其它說明    :   
                              寫入一個配置,每次寫只用某一頁開始寫,不可一頁中從起始地址寫之後又從中間地址寫入。
                              寫第二個模塊配置時,空間配置從下一頁開始寫。否則會擦除之前的頁,寫之前會先擦除該頁。
 * */
uint8 Sys_WriteDataToFlash(uint8* pData,uint32 Address,uint32 Len)  
{      
    if(!pData)  
    return 0;  
    uint32_t SLen = 0;
    uint32_t Wdata; 
    FLASH_Unlock();

    FLASH_ErasePage(Address); // 寫之前擦除要寫入的整頁
    FLASH_ClearFlag(FLASH_FLAG_BSY|FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPERR); 
    /* Clear pending flags (if any) */  
    do
    {
        memcpy(&Wdata, pData, 4);// 
        FLASH_ProgramWord(Address,Wdata);
        Address += 4; 
        pData += 4;
        SLen += 4;
    }while(SLen<Len);
    FLASH_Lock();
    return 1;
}


/*
 * 函數名稱    :   Sys_ReadFlashToBuf
 * 函數功能    :   從flash中讀出數據到buf查看
 * 輸入參數    :
 * 輸出參數    :   無
* 返 回 值    :
 * 其它說明    :   
 * */
void Sys_ReadFlashToBuf(uint8* buf,uint32 addr,uint16 len)
{
    while(len--)
    {
        *buf = *(uint8*)addr;
         addr++;
         buf++;
    }
}


32位機一個字爲4個字節,庫函數提供兩種形式,一種是半字寫入另一種是一個字寫入,以上使用的是一個字寫入的方式

實際操作過程中是將一個結構體內容寫入flash,先將該結構體以字節對其,將該結構體指向buf存儲,再將該buf按字節寫入flash。

讀取時將內容讀到buf,在將該buf轉爲結構體,即可準確提取出結構體中定義的值


2、操作中所犯錯誤及注意點


1、內存地址

      錯誤:從0x08000000基地址開始操作flash,一執行讀寫flash,程序就卡死。

      要操作flash的地址,如果程序是存儲在main flash memory中開始執行,進入用戶程序,我們操作flash就不能從flash基地址開始操作,這部分已經被應用程序佔用。

      其操作flash地址值要大於本代碼所佔用FLASH的大小+0x08000000 

      用戶程序代碼佔用flash大小可從編譯時候看出來

        8 394 bytes of readonly  code memory 

       84 bytes of readonly  data memory 

       2 828 bytes of readwrite data memory 

        #define FLASH_BASE_ADDR                       (uint32_t)(0x08000000u)
        #define FLASH_STARTADDR_PAGE11    (FLASH_BASE_ADDR + 1024 * 11)

計算可寫在第幾頁,最好的方法是從最後一頁開始寫,根據手冊瞭解flash大小,頁數,勿超出flash大小,否則易出現HardFault_Handler錯誤

2、將字轉換爲按字節寫入

       錯誤: 寫入的數據與實際的數據值不同

       從cotex-m0的庫函數來看,對flash的操作時以字或者半字來操作。實際使用操作多按1個字節操作,造成不對齊。

        uint32_t Wdata; 

        memcpy(&Wdata, pData, 4);// 
        FLASH_ProgramWord(Address,Wdata);
        Address += 4; 
        pData += 4;
        固按以上操作,將4個字節轉換爲按字寫入,統一了使用的所有工程,再將地址按四個字節偏移。


3、宏定義地址

       錯誤:flash讀取時數據出錯,內容指向錯誤。

       SYS_BASE_CFG_T *pCfg = (SYS_BASE_CFG_T *) FLASH_STARTADDR_PAGE11;

       操作是將flash頁11的地址讀取出來,指向轉爲結構體SYS_BASE_CFG_T 形式讀取數據

       但在讀取就出錯,程序運行到該句即出現內存操作錯誤中斷產生

        void HardFault_Handler(void)
        {
             while (1)
             {
             }
        }

        從運行參數上看,當運行到該句,地址指向並不是預先設計好的11頁地址

        #define FLASH_STARTADDR_PAGE11    FLASH_BASE_ADDR + 1024 * 11

        由於宏定義加操作沒有加括號,導致實際偏移值出現錯誤。宏定義式子爲防錯誤一定要加括號

         #define FLASH_STARTADDR_PAGE11    (FLASH_BASE_ADDR + 1024 * 11)

4、HardFault_Handler  

        該中斷產生的一般條件是內存非法操作,包括數組越界、內存溢出、指針地址錯誤、野指針存在

        在stm8中當內存非法操作時mcu內部會產生復位,從復位源可查看爲非法操作符復位  

5、頁大小

        flash操作擦除時可以以頁爲單位擦除或者以整片flash擦除。

        在使用flash時,如果需要寫入兩組不同的數據並且在一頁以內,建議直接使用一頁一組數據,由於內存夠大而且防止誤操作到其他組數據。

        M3或者M0內核大容量與小容量的flash的頁大小有所區別,小容量以1024字節 1k爲一頁,大容量以2048字節2k 爲一頁。所以操作具體內存時以

         (FLASH_BASE_ADDR + 1024 * 11)形式,更直觀看出數據位置,便於IDE調試查看。


對結構體的flash寫入寫出

 

uint16 ReadToFlash(uint8 *pCfg)
{
    CfgInfo.pBaseCfg = (BASE_CFG_T *)pCfg;
    return sizeof(BASE_CFG_T);
}

void ReadAllCfg(void)
{
    uint8 *pRead = (uint8*)FLASH_STARTADDR_PAGE11; // 轉化爲unsigned char 按字節操作 32位一次偏移4字節
    uint32 CfgLen = 0;
    
    CfgLen = ReadToFlash(pRead );
    pRead += CfgLen;
    CfgInfo.SysReadCfgSize += CfgLen;
}

uint16 WriteToFlash(uint8 *pCfg)
{
     CfgInfo.pBaseCfg = (BASE_CFG_T *)pCfg;
     CfgInfo.pBaseCfg->WriteFlashFlag = CFG_FLASH_FLAG;
     CfgInfo.pBaseCfg->FeedWatDogTime = 200;
     return sizeof(BASE_CFG_T);
}

static void WriteAllCfg(void)
{
    uint8 *pWrite = CfgInfo.CfgBuf;
    uint32 CfgLen = 0;
    
    CfgLen = WriteToFlash(pWrite );
    pWrite += CfgLen;
    CfgInfo.SysWriteCfgSize += CfgLen;
    Sys_WriteDataToFlash(CfgInfo.CfgBuf,FLASH_STARTADDR_PAGE11,CfgInfo.SysWriteCfgSize);
    Sys_ReadAllCfg();  
}  

void Sys_CfgInit(void)
{
    BASE_CFG_T *pCfg = (BASE_CFG_T *) FLASH_STARTADDR_PAGE11;

 /*在要寫入的一個buf的第一位作爲寫入標誌位,程序下次執行到這兒就不再重寫,而只是讀取這個flash的內容,實現配置的形式操作。*/
    if(pCfg->WriteFlashFlag != CFG_FLASH_FLAG) 

    {
        /* 寫配置到FLASH */
        WriteAllCfg();
    }
    else
    {
        ReadAllCfg();
        memcpy(CfgInfo.CfgBuf, (char *)FLASH_STARTADDR_PAGE11, CfgInfo.SysReadCfgSize);
    }
}     


typedef __packed  struct 
{
    uint8             WriteFlashFlag;    /* FLASH寫標誌 */
    uint8             DebugFalg;           /* 調試使用標誌 */
    uint32           FeedWatDogTime;       
}BASE_CFG_T;            /* 系統基本配置 */

typedef __packed  struct
{
    BASE_CFG_T  *pBaseCfg;       /* 系統基本配置參數 */
    uint8            SysWriteCfgSize;
    uint8            SysReadCfgSize;    
    uint8          CfgBuf[SYS_CFG_SIZE]; /* 所有人的配置文件 */
}CFG_INFO_T; /* 配置配置參數的 */

 CFG_INFO_T        CfgInfo;

#define  CFG_FLASH_FLAG (0x57)

以上內容爲個人使用過程的方法筆記及個人認爲的注意點、知識點.

內容不全面,如之後使用有所補充隨時更新。

如您發現有所問題,希望給我意見。

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