STM32L0系列EEPROM中結構體的讀取

路安達發表於2021-09-22

STM32L0中操作EEPROM本來參考了上篇操作FLASH的方法,多多少少都有些問題。我覺得可能是結構體在轉換成其他變數的時候出了問題。

比如下面這段程式碼,在Windows上可以正常執行(使用g++編譯),但是在微控制器上就會卡死。

typedef struct
{
    uint8_t IDD;
    uint8_t zero[4];
    uint8_t dutyCorr[4];
} usrflash;

usrflash eepromDat = {.IDD = 1U, .zero = {0}, .dutyCorr = {0}};

int main()
{
    float zerof = 3.14;
    uint8_t zeroc[4] = {0};
    float zero = 0;
    eepromDat.zero[0] = *((uint8_t *)&zerof + 0);
    eepromDat.zero[1] = *((uint8_t *)&zerof + 1);
    eepromDat.zero[2] = *((uint8_t *)&zerof + 2);
    eepromDat.zero[3] = *((uint8_t *)&zerof + 3);
    zero = *(float *)(&eepromDat)->zero;
    printf("\r\nzero=%f\r\n\n", zero);
    return 0;
}

這段程式碼的大義是將 zerof 這個變數打散儲存在結構體的一個陣列裡,然後再還原出來。其在Windows上毫無問題,但若要再微控制器上執行就必須建立一箇中間變數將eepromDat.zero[0~3]的值先賦給中間變數再利用中間變數還原那個浮點數。總的來說就是微控制器,結構體,不行

	float zerof = 3.14;
	uint8_t p_time[4] = {0};
	float zero = 0;
	eepromDat.zero[0] = *((uint8_t *)&zerof + 0);
	eepromDat.zero[1] = *((uint8_t *)&zerof + 1);
	eepromDat.zero[2] = *((uint8_t *)&zerof + 2);
	eepromDat.zero[3] = *((uint8_t *)&zerof + 3);
	p_time[0] = eepromDat.zero[0];
	p_time[1] = eepromDat.zero[1];
	p_time[2] = eepromDat.zero[2];
	p_time[3] = eepromDat.zero[3];
	zero = *(float*)p_time;
	printf("\r\nzero=%f\r\n\n", zero);

造成這種問題的原因我也沒想明白,但我隱約覺得這事沒準和編譯器有關係,也許ARM-GCC可以解決。這是我第一次對ARM-CC提出質疑。

將結構體與字串進行相互轉換

為了應對以上的問題,我想到的辦法是乾脆先將結構體資料轉換成字串,再將字串儲存進 EEPROM 就要方便的多了。這樣只各需要一個在 EEPROM 中讀寫字串的函式就可以同時操作字串和結構體了。

而且字串是 char* , 其是由位元組組成的,又因為byte「位元組」是記憶體定址和存取的最小單位,最起碼這樣看上去要安全一些。

對於結構體的要求

若想將結構體順利的準換成字串,對於結構體還是有一定要求的,一般要求結構體的形式如下

typedef struct
{
		uint8_t IDD;
		float zero;
		float dutyCorr;
		...............
} usrflash;

其中第一個成員變數的變數名和變數型別千萬不要修改,因為在將結構體轉換為字串是是要以它的第一個成員變數的地址作為開頭的。

將結構體轉換成字串的方法

其實就是指標的靈活運用

// eepromDat 是 usrflash 型別的結構體

uint8_t *p_temp = (uint8_t *)malloc(sizeof(usrflash));
for (uint8_t i = 0; i < sizeof(usrflash); i++)
{
    p_temp[i] = *((uint8_t *)((&(&eepromDat)->IDD) + i));
}
free(p_temp);

將字串還原為結構體的方法

// eepromDat2 是 usrflash 型別的結構體

uint8_t *q_temp = (uint8_t *)malloc(sizeof(usrflash));
for (uint8_t i = 0; i < sizeof(usrflash); i++)
{
    *(uint8_t *)(&((&eepromDat2)->IDD) + i) = *(q_temp + i);
}
free(q_temp);

以字串作為中間量的EEPROM中結構體的讀寫操作

    /* 向 EEPROM 中寫結構體 ------------------------------------------- */
    // 待寫入的結構體
    usrflash eepromDat;
    // 為中間快取(字串)分配記憶體
    uint8_t *p_temp = (uint8_t *)malloc(sizeof(usrflash));
    // 將結構體轉換成字串
    for (uint8_t i = 0; i < sizeof(usrflash); i++)
    {
        p_temp[i] = *((uint8_t *)((&(&eepromDat)->IDD) + i));
    }
    // 將字串寫入 EEPROM
    FLASH_EEPROM_Write_string(EEPROM_BASE_ADDR, p_temp, sizeof(usrflash));
    // 釋放記憶體
    free(p_temp);

    /* 從 EEPROM 中讀結構體 ------------------------------------------- */
    // 待存入的結構體
    usrflash eepromDat2;
    // 為中間快取(字串)分配記憶體
    uint8_t *q_temp = (uint8_t *)malloc(sizeof(usrflash));
    // 從 EEPROM 中讀取字串
    FLASH_EEPROM_Read_string(EEPROM_BASE_ADDR, q_temp, sizeof(usrflash));
    // 將字串轉換為結構體
    for (uint8_t i = 0; i < sizeof(usrflash); i++)
    {
        *(uint8_t *)(&((&eepromDat2)->IDD) + i) = *(q_temp + i);
    }
    // 釋放記憶體
    free(q_temp);

EEPROM 函式。在別的檔案中摘錄

 void FLASH_EEPROM_Write_string(uint32_t addr, uint8_t * p_temp, uint16_t len)
    {
        HAL_FLASHEx_DATAEEPROM_Unlock();
        for (uint16_t i = 0; i < len; i++)
        {
            HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, addr + i, *((uint8_t *)(p_temp + i)));
        }
        HAL_FLASHEx_DATAEEPROM_Lock();
    }
    void FLASH_EEPROM_Read_string(uint32_t addr, uint8_t * q_temp, uint16_t len)
    {
        for (uint16_t i = 0; i < len; i++)
        {
            *((uint8_t *)(q_temp + i)) = *(uint8_t *)(addr + i);
        }
    }

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

相關文章