在FLASH中讀寫結構體
注意事項
程式設計(寫資料)地址要對齊
寫資料時,我們要指定寫入的地址,如果寫入地址為非對齊,則會出現程式設計對齊錯誤。
比如遵循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 !