在FLASH中讀寫結構體


在FLASH中讀寫結構體

⚠ 注意事項

  1. 編程(寫數據)地址要對齊

    寫數據時,我們要指定寫入的地址,如果寫入地址為非對齊,則會出現編程對齊錯誤。
    比如遵循32位(4字節)地址對齊,你的地址只能是4的倍數。0x08001000正確,0x08001001錯誤。
    不同型號對齊寬度可能不同,有的32位、有的128位等,可通過“取余”判斷地址。

    比如我遇到在 EEPROM 中寫一個結構體時,下面這種會有問題,最后一個數據會寫入失敗。將 uint8_t ID; 改為 uint32_t ID; 則正常。

    typedef struct
    {
    		// uint8_t ID;
        	uint32_t ID;
    		float zero;
    		float dutyCorr;
    		float fittingCorr;
     		float initialTemp;
    } usrflash;
    

main.c

usrflash dtl645Config = {0};
...
// 讀
FLASH_EEPROM_Read_struct(0x0801FC00, &dtl645Config);
// 寫
FLASH_EEPROM_Write_struct(0x0801FC00, &dtl645Config);

flash.h

#ifndef __FLASH_H
#define __FLASH_H

#ifdef __cplusplus
extern "C"
{
#endif

#include "main.h"

    // 在 FLASH 寫入的結構體變量的類型。
    // 第一個成員變量的變量名(ID)不要隨便改;
    // 變量類型也千萬別瞎改。
    typedef struct
    {
        uint8_t ID;              // ID
        uint8_t addr[6];         // 終端地址
        uint8_t baud;            // 波特率
        uint8_t residualCurrent; // 剩余電流值
        uint8_t warningValue;    // 剩余電流預警值(百分比)
        uint8_t actionValue;     // 剩余電流動作值
        uint8_t lndt;            // 極限不驅動時間
        uint16_t trippingTimes;  // 跳閘次數mi
        uint8_t password[3];     // 密碼
        uint8_t sSwitch;         // 動作狀態(0為斷開,1為閉合)(分閘/合閘)
        uint8_t sStatus;         // 運行狀態(0為正常,1為警告,2為越限)
    } usrflash;

    // 在FLASH中寫一個字(32bit)
    void FLASH_EEPROM_Write(uint32_t a, uint32_t n);
    // 從FLASH中讀取一個字(32bit)
    uint32_t FLASH_EEPROM_Read(uint32_t addr);
    // 在FLASH中寫一個結構體(usrflash 類型的結構體)
    void FLASH_EEPROM_Write_struct(uint32_t addr, usrflash *userinfo);
    // 從FLASH中讀取一個結構體(usrflash 類型的結構體)
    void FLASH_EEPROM_Read_struct(uint32_t addr, usrflash *userinfo);

#ifdef __cplusplus
}
#endif

#endif /* __FLASH_H */

flash.c

//***************************************************************************************************
// flash.c	串口相關功能實現
//

// Includes
// ******************************************************************************************
#include "flash.h"

//***************************************************************************************************
// 在FLASH中寫一個字(32bit)
// 傳入參數為寫入的地址和要寫入的數據, 寫入數據以字為單位,每個字占32bit即4Byte.
// 擦除時將擦除此地址所在的一整頁。
//
void FLASH_EEPROM_Write(uint32_t addr, uint32_t n)
{
    // FLASH 擦除操作 --------------------------------------------------------------/
    HAL_FLASH_Unlock(); //解鎖
    uint32_t PageError = 0; // 如果出現錯誤這個變量會被置為出錯的FLASH地址
    FLASH_EraseInitTypeDef EraseInitStruct; // 定義結構體
    EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; // Flash執行頁面只做擦除操作
    EraseInitStruct.PageAddress = addr; // 要擦除的地址
    EraseInitStruct.NbPages = 1; // 要擦除的頁數
    if (HAL_FLASHEx_Erase(&EraseInitStruct, &PageError) == HAL_OK)
        ; // 擦除此地址所在的一整頁
    // FLASH 寫入操作 --------------------------------------------------------------/
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr, n); // 向FLASH中寫入
    HAL_FLASH_Lock(); // 鎖住FLASH
}

//***************************************************************************************************
// 從FLASH中讀取一個字(32bit)
// 入口參數為要讀取的FLASH的地址,返回值為uint32_t變量
//
uint32_t FLASH_EEPROM_Read(uint32_t addr)
{
    uint32_t pValue = *(__IO uint32_t*)(addr);
    return pValue;
}

//***************************************************************************************************
// 在FLASH中寫一個結構體(usrflash 類型的結構體,這個類型在 flash.h 中定義的).
// 傳入參數為要寫入的地址和 usrflash 型結構體變量的取地址。
// 擦除時將擦除此地址所在的一整頁。
// STM32F103C8 的 FLASH 中每頁的大小為 1k, 寫入時需注意數據量不要太大。
//
void FLASH_EEPROM_Write_struct(uint32_t addr, usrflash* userinfo)
{
    // FLASH 擦除操作 --------------------------------------------------------------/
    HAL_FLASH_Unlock(); //解鎖
    uint32_t PageError = 0; // 如果出現錯誤這個變量會被置為出錯的FLASH地址
    FLASH_EraseInitTypeDef EraseInitStruct; // 定義結構體
    EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; // Flash執行頁面只做擦除操作
    EraseInitStruct.PageAddress = addr; // 要擦除的地址
    EraseInitStruct.NbPages = 1; // 要擦除的頁數
    if (HAL_FLASHEx_Erase(&EraseInitStruct, &PageError) == HAL_OK); // 擦除此地址所在的一整頁
    // FLASH 寫入操作 --------------------------------------------------------------/
    for (uint8_t i = 0; i < sizeof(usrflash); i += 4) {
        if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr + i, *((__IO uint32_t*)(&userinfo->ID + i))) != HAL_OK) //這里的數據由於是結構體,需要從里面取值,一定要以第一個成員的地址開始偏移,不能使用結構體自身的地址來偏移,否則數據會出錯
        {
            HAL_FLASH_Lock();
            return;
        }
    }
    HAL_FLASH_Lock();
}

//***************************************************************************************************
// 從FLASH中讀取一個結構體(usrflash 類型的結構體,這個類型在 flash.h 中定義的).
// 入口參數為要讀取的 FLASH 的地址和接收數據的 usrflash 型結構體變量的取地址。無返回值。
// 為了避免數據出錯,讀操作一定要和寫操作對應,寫是按雙字寫,讀就要按雙字讀,否則就需要解決大小端的問題。
// 
void FLASH_EEPROM_Read_struct(uint32_t addr, usrflash* userinfo)
{
    for (uint8_t i = 0; i < sizeof(usrflash); i += 4) {
        *((uint32_t*)(&userinfo->ID + i)) = *(__IO uint32_t*)(addr + i); //注意賦值的左邊,必須要用結構體第一個成員的地址來偏移,雙字偏移量是8
    }
    //這樣獲取的結構體內容,可以直接通過結構體變量或者結構體指針來訪問了.
}

禁止轉載到CSDN !
禁止轉載到CSDN !
禁止轉載到CSDN !


免責聲明!

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



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