日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

STM32L051测试 (四、Flash和EEPROM的读写)

發(fā)布時(shí)間:2024/8/1 编程问答 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 STM32L051测试 (四、Flash和EEPROM的读写) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
..添加目錄欄目 2021/11/23 ..修改文中以前的問(wèn)題,增加后續(xù)問(wèn)題說(shuō)明(數(shù)據(jù)存儲(chǔ)大小端模式) 2021/11/23 ..增加后續(xù)問(wèn)題2.4.3(STM32L071RBT6 EEPROM讀寫全字問(wèn)題) 2021/12/27 ..更新一下STM32L071 EEPROM讀寫全字問(wèn)題最后解決說(shuō)明 2022/3/24

目錄

  • 前言
  • 1、STM32L051內(nèi)部存儲(chǔ)模塊地址范圍
  • 2、讀寫函數(shù)的設(shè)計(jì)
    • 2.1 讀取函數(shù)
    • 2.2 EEPROM寫函數(shù)
      • 寫入問(wèn)題說(shuō)明修改
    • 2.3 Flash寫函數(shù)
    • 2.4 讀寫EEPROM的后續(xù)問(wèn)題
      • 2.4.1 問(wèn)題的出現(xiàn)和解決
      • 2.4.2 問(wèn)題的分析(大小端模式數(shù)據(jù)格式)
      • <font color=#FF0033>2.4.3 STM32L071RBT6 EEPROM讀寫全字問(wèn)題
        • 讀問(wèn)題的出現(xiàn)
        • 寫問(wèn)題的出現(xiàn)
        • <font color=#FF0033>最后問(wèn)題的解決
  • 總結(jié)

前言

今天開(kāi)始測(cè)試L051 flash的讀寫。

本系列博文目錄:

STM32L051測(cè)試 (一、使用CubeMX生成工程文件 — ST系列芯片通用)
STM32L051測(cè)試 (二、開(kāi)始添加需要的代碼)
STM32L051測(cè)試 (三、I2C協(xié)議設(shè)備的添加測(cè)試)

1、STM32L051內(nèi)部存儲(chǔ)模塊地址范圍

開(kāi)始找到F103的FLASH圖復(fù)習(xí)了一遍,然后L051C8T6,64KB的flash, 然后我驚奇的發(fā)現(xiàn)還有2KB的EEPROM。

發(fā)現(xiàn)L051系列的地址與F103完全不同,F103的flash每頁(yè)的長(zhǎng)度有1KB(小容量<=64KB)和2KB(大容量128KB起)查看各種資料,
查了2個(gè)小時(shí),

還是不知道L051的flash 每頁(yè)長(zhǎng)度是 128Byte 還是256Byte????????????????????????

還是請(qǐng)教了一下大佬,發(fā)現(xiàn)直接在J-Flash中可以找到答案,先上個(gè)F103的圖:

然后來(lái)看個(gè)L051的圖:

圖中64KB 的flash 和2KB的EEPROM都能都明顯的看出地址,flash 512頁(yè),每頁(yè)128bytes,EEPROM只有4頁(yè),每頁(yè)512bytes.知道了基本的地址,就可以操作起來(lái)了。

最后還需要確定的一點(diǎn)事,最小擦除單元是128bytes,還是256bytes,按以前的認(rèn)知,刪除是按照一個(gè)Sector擦除的,也就是128bytes,但是我查看了一些網(wǎng)上的例子和資料,有的是說(shuō)256bytes,所以后面需要自己確定一下。

其實(shí)在HAL庫(kù)的 stm32l0xx_hal_flash.h 文件中有過(guò) FLASH_PAGE_SIZE 的定義,就是128bytes :

#define FLASH_SIZE (uint32_t)((*((uint32_t *)FLASHSIZE_BASE)&0xFFFF) * 1024U) #define FLASH_PAGE_SIZE ((uint32_t)128U) /*!< FLASH Page Size in bytes */

對(duì)于flash的操作,有一些基礎(chǔ)知識(shí)補(bǔ)充一下:

Read interface organized by word, half-word or byte in every area
? Programming in the Flash memory performed by word or half-page
? Programming in the Option bytes area performed by word
? Programming in the data EEPROM performed by word, half-word or byte
? Erase operation performed by page (in Flash memory, data EEPROM and Option
bytes)

STM32L051寫Flash必須字,讀 字節(jié)、半字、字都支持。(這句話也是錯(cuò)誤的,這是以前哪里看到的,實(shí)際測(cè)試寫可以根據(jù)字,半字,字節(jié)來(lái)寫)。

一些基本概念:

定義字是根據(jù)處理器的特性決定的。

首先ARM是32bit處理器,所以它的字是32bit的。半字自然就是16bit;
字節(jié)不論在哪個(gè)CPU上都是8bit。

1 Byte = 8 bits(即 1B=8b)
1 KB = 1024 Bytes

Bit意為“位”或“比特”,是計(jì)算機(jī)運(yùn)算的基礎(chǔ),屬于二進(jìn)制的范疇;

Byte意為“字節(jié)”,是計(jì)算機(jī)文件大小的基本計(jì)算單位;

2、讀寫函數(shù)的設(shè)計(jì)

HAL庫(kù)中肯定是有對(duì)flash和EEPROM進(jìn)行操作的函數(shù),我們這里新建一個(gè)stml0_flash.c 和stml0_flash.h 函數(shù)分別放在對(duì)應(yīng)位置,進(jìn)行自己的函數(shù)設(shè)計(jì)。

庫(kù)中Flash與EEPROM的函數(shù)看樣子是分別放在
stm32l0xx_hal_flash.c 和 stm32l0xx_hal_flash_ex.c 中的,我們先使用EEPROM,因?yàn)樘峁〦EPROM,就是讓用戶可以保存一些掉電后的數(shù)據(jù)的嘛。

測(cè)試完EEPROM,再去測(cè)試下flash,因?yàn)榕掠袝r(shí)候數(shù)據(jù)不夠放……

2.1 讀取函數(shù)

//讀取指定地址的半字(16位數(shù)據(jù)) uint16_t FLASH_ReadHalfWord(uint32_t address) {return *(__IO uint16_t*)address; }//讀取指定地址的全字(32位數(shù)據(jù)) uint32_t FLASH_ReadWord(uint32_t address) {return *(__IO uint32_t*)address; }

簡(jiǎn)單測(cè)試一下:

u32 read_data1=0XFFFFFFFF; u32 read_data2=0XFFFFFFFF; ... read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR); printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1); read_data2 = FLASH_ReadWord(DATA_EEPROM_START_ADDR + EEPROM_PAGE_SIZE); printf("the EEPROM sceond page test data is: 0x %x \r\n",read_data2);

沒(méi)有寫入數(shù)據(jù)讀取的值應(yīng)該都是0。

2.2 EEPROM寫函數(shù)

對(duì)EEPROM的寫函數(shù):
stm32l0xx_hal_flash_ex.h中函數(shù)如下:

HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Unlock(void); HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Lock(void);HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Erase(uint32_t Address); HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data);

通過(guò)函數(shù)看來(lái),可以直接用,但是這里有一個(gè)問(wèn)題

需要測(cè)試一下,擦除是否會(huì)擦除整個(gè)扇區(qū),有待驗(yàn)證!!

答:EEPROM的擦除可以直接擦除某個(gè)地址的字,不會(huì)擦除整個(gè)片區(qū)

EEPROM的操作相對(duì)Flash,比較簡(jiǎn)單,直接使用HAL庫(kù)中的函數(shù)即可完成

HAL_FLASHEx_DATAEEPROM_Unlock(); HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, DATA_EEPROM_START_ADDR, write_data1); HAL_FLASHEx_DATAEEPROM_Lock(); ... if(btn_getState(&K1_BUTTON_150mS) == BTN_EDGE2){printf(" K1 150ms button!,EEPROM_Erase test\r\n");HAL_FLASHEx_DATAEEPROM_Unlock();HAL_FLASHEx_DATAEEPROM_Erase(DATA_EEPROM_START_ADDR+4);HAL_FLASHEx_DATAEEPROM_Lock();HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);} ... if(btn_getState(&K2_BUTTON_150mS) == BTN_EDGE2){printf(" K2 150ms button!EEPROM_read test\r\n");read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);}

按照上面的例子,擦除DATA_EEPROM_START_ADDR+4的地址不會(huì)影響DATA_EEPROM_START_ADDR地址的開(kāi)始寫入的數(shù)據(jù)
寫入一樣,如果不在意以前的數(shù)據(jù),直接寫入就可以。

總結(jié)來(lái)說(shuō)EEPROM的使用還是很好用而且簡(jiǎn)單的。

而且EEPROM是可以按照字節(jié),半字,全字寫入的,測(cè)試了一下,是右對(duì)齊

右對(duì)齊什么意思呢,打個(gè)比方就是如果在地址 addr 中,本來(lái)寫入全字 0x12345678 然后直接在EEPROM 的 addr 這個(gè)地址,寫入半字 0xFFFF, 再讀取全字的話,addr 地址的全字就變成了 0x1234FFFF ,這個(gè)具體的為什么在地址寫入半字,不會(huì)直接從地址開(kāi)始占用2個(gè)字節(jié),是因?yàn)榈刂返念愋蜑?uint32_t ,所以該地址就是 4個(gè)字節(jié)類型的數(shù)。

寫入問(wèn)題說(shuō)明修改

2021/11/23 修改說(shuō)明 上面一段的解釋有誤,這里修改一下,不是因?yàn)榈刂奉愋蜑閡int32_t,地址類型永遠(yuǎn)是這個(gè),是因?yàn)槎x的數(shù)據(jù)類型為uint32_t ,然后STM32又是小端模式,所以保存的方式是從地址的最后開(kāi)始保存,4個(gè)字節(jié)的全字,第一個(gè)字節(jié)放在地址開(kāi)始+4 的位置,第二個(gè)字節(jié)放在地址開(kāi)始+3的位置,所以如果調(diào)用HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, DATA_EEPROM_START_ADDR, write_data1);

關(guān)鍵在于FLASH_TYPEPROGRAMDATA_WORD以全字方式寫入半字,那么內(nèi)核會(huì)自動(dòng)分配4個(gè)字節(jié)的寬度,半字的第一個(gè)字節(jié)放在寫入地址+4 的位置,第二個(gè)字節(jié)放在寫入地址+3的位置,所以導(dǎo)致了上面的結(jié)果

HAL_FLASHEx_DATAEEPROM_Unlock();HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, DATA_EEPROM_START_ADDR, 0X88);HAL_FLASHEx_DATAEEPROM_Lock();read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);

最后在 stml0_flash.h 中把函數(shù)完善聲明一下,使得主函數(shù)中的程序更加簡(jiǎn)潔。

void MY_DATAEEPROM_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data) {HAL_FLASHEx_DATAEEPROM_Unlock(); HAL_FLASHEx_DATAEEPROM_Program(TypeProgram, Address, Data);HAL_FLASHEx_DATAEEPROM_Lock(); }

那么L051 的EEPROM的測(cè)試就到這里,其實(shí)有EEPROM,在項(xiàng)目中的保存數(shù)據(jù)的功能就問(wèn)題不大了,但是我們既然開(kāi)始了,就把L051 Flash的讀寫也測(cè)試一下。

2.3 Flash寫函數(shù)

Flash的讀取其實(shí)和EEPROM一樣,主要是寫函數(shù),來(lái)看一下stm32l0xx_hal_flash.h 中有哪些函數(shù)

/* IO operation functions *****************************************************/ HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data); HAL_StatusTypeDef HAL_FLASH_Program_IT(uint32_t TypeProgram, uint32_t Address, uint32_t Data);/* FLASH IRQ handler function */ void HAL_FLASH_IRQHandler(void); /* Callbacks in non blocking modes */ void HAL_FLASH_EndOfOperationCallback(uint32_t ReturnValue); void HAL_FLASH_OperationErrorCallback(uint32_t ReturnValue);/*** @}*//** @addtogroup FLASH_Exported_Functions_Group2* @{*/ /* Peripheral Control functions ***********************************************/ HAL_StatusTypeDef HAL_FLASH_Unlock(void); HAL_StatusTypeDef HAL_FLASH_Lock(void); HAL_StatusTypeDef HAL_FLASH_OB_Unlock(void); HAL_StatusTypeDef HAL_FLASH_OB_Lock(void); HAL_StatusTypeDef HAL_FLASH_OB_Launch(void);

有點(diǎn)忙,Flash的后面再也,看了幾個(gè)demo,只需要做幾個(gè)測(cè)試就可以;

2021.8.5 今天有空來(lái)接著測(cè)試一下L051 Flash的讀寫,看了下HAL_FLASH_Program函數(shù):

HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data) {HAL_StatusTypeDef status = HAL_ERROR;/* Process Locked */__HAL_LOCK(&pFlash);/* Check the parameters */assert_param(IS_FLASH_TYPEPROGRAM(TypeProgram));assert_param(IS_FLASH_PROGRAM_ADDRESS(Address));/* Wait for last operation to be completed */status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);if(status == HAL_OK){/* Clean the error context */pFlash.ErrorCode = HAL_FLASH_ERROR_NONE;/*Program word (32-bit) at a specified address.*/*(__IO uint32_t *)Address = Data;/* Wait for last operation to be completed */status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);}/* Process Unlocked */__HAL_UNLOCK(&pFlash);return status; }

這里也再次說(shuō)明了,L051的寫必須以字的方式寫入。

不管了,先測(cè)試一下,不擦除直接寫入,這里先定義一下寫入的地址,前面我們已經(jīng)知道了L051 flash一共 512頁(yè),每頁(yè)128bytes,所以我們直接拿最后面的幾頁(yè)來(lái)測(cè)試

#define ADDR_FLASH_PAGE_505 0X08000000 + 128*504 // #define ADDR_FLASH_PAGE_506 0X08000000 + 128*505 // #define ADDR_FLASH_PAGE_507 0X08000000 + 128*506 // #define ADDR_FLASH_PAGE_508 0X08000000 + 128*507 // #define ADDR_FLASH_PAGE_509 0X08000000 + 128*508 // #define ADDR_FLASH_PAGE_510 0X08000000 + 128*509 // #define ADDR_FLASH_PAGE_511 0X08000000 + 128*510 // #define ADDR_FLASH_PAGE_512 0X08000000 + 128*511 //最后一頁(yè)

開(kāi)始先不擦除,直接在最后一頁(yè)寫入一個(gè)字讀取一下試試,整理一下寫入函數(shù):

void MY_DATAFLASH_Program(uint32_t Address, uint32_t Data) {HAL_FLASH_Unlock(); if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){printf("write data 0x%x OK\n", Data);}else{printf("failed!!!\n");}HAL_FLASH_Lock(); }

測(cè)試一下;

MY_DATAFLASH_Program(ADDR_FLASH_PAGE_512,write_data2);read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_512);printf("the ADDR_FLASH_PAGE_512 is: 0x %x \r\n",read_data1);

在沒(méi)有寫入flash之前,該地址讀出來(lái)的數(shù)據(jù)為0,寫入后讀出來(lái)是正常寫入的數(shù)據(jù)

這里有個(gè)疑問(wèn),按理來(lái)說(shuō),flash存儲(chǔ)器有個(gè)特點(diǎn),就是只能寫0,不能寫1,所以flash的寫入,比需先擦除,或者至少檢查一下該數(shù)據(jù)區(qū)是否可以寫入,但是L051 怎么初始的時(shí)候讀出來(lái)是0? 難道L051的有區(qū)別,需要測(cè)試一下

先在一個(gè)地址寫0XFFFFFFFF ,然后寫完了再寫一次別的數(shù)據(jù)看看能否直接寫入,結(jié)果是寫入了0XFFFFFFFF ,不能繼續(xù)直接寫數(shù)據(jù),說(shuō)明,估計(jì)L051是相反的,這里具體是不是我只看測(cè)試結(jié)果,結(jié)論的話我自己知道就可以應(yīng)用,希望有權(quán)威大神指正。

這里我們得用到一個(gè)關(guān)鍵的函數(shù) ,這個(gè)函數(shù)是在stm32l0xx_hal_flash_ex.c 這個(gè)文件中的,是flash的擦除函數(shù):

HAL_StatusTypeDef HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *PageError)

所以這里我們知道了以后,可以優(yōu)化一下寫入函數(shù),我們項(xiàng)目中用到的是可以直接對(duì)某個(gè)地址的寫入,然后也不需要保存,此頁(yè)其他的數(shù)據(jù),所以我們把函數(shù)改成如下:

void MY_DATAFLASH_Program(uint32_t Address, uint32_t Data) {FLASH_EraseInitTypeDef EraseInitStruct;uint32_t checkdata;uint32_t PAGEError = 0;checkdata = FLASH_ReadWord(Address);HAL_FLASH_Unlock(); /*如果是0,直接寫*/if(checkdata == 0){ if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){printf("write data 0x%x OK\n", Data);}else{printf("failed!!!\n");}}/*否則擦除再寫*/else{__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; // 刷除方式EraseInitStruct.PageAddress = Address; // 起始地址EraseInitStruct.NbPages = 1;if (HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError) != HAL_OK){// 如果刷除錯(cuò)誤printf("\r\n FLASH Erase Fail\r\n");printf("Fail Code:%d\r\n",HAL_FLASH_GetError());printf("Fail Page:%d\r\n",PAGEError);}if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){printf("write data 0x%x OK\n", Data);}else{printf("failed!!!\n");}}HAL_FLASH_Lock(); }

自己修改了一個(gè)函數(shù), 改成這樣以后,就能直接在想寫入的地方寫入數(shù)據(jù)了,到這里,flash足夠我項(xiàng)目中的使用了。但是還有最后一個(gè)疑問(wèn),就是擦除的一頁(yè)到底是不是128bytes我來(lái)驗(yàn)證一下。

u32 write_data1 = 0X12345678; u32 write_data2 = 0X87654321; u32 write_data3 = 0XFFFFFFFF;MY_DATAFLASH_Program(ADDR_FLASH_PAGE_508 + 124,0X508508FF); MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509,write_data3); MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509+4,write_data2); MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509+8,write_data1); MY_DATAFLASH_Program(ADDR_FLASH_PAGE_510,0X510510FF); read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_508 + 124);printf("the ADDR_FLASH_PAGE_508 last is: 0x %x \r\n",read_data1);read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509);printf("the ADDR_FLASH_PAGE_509 1 is: 0x %x \r\n",read_data1);read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 4);printf("the ADDR_FLASH_PAGE_509 2 is: 0x %x \r\n",read_data1);read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 8);printf("the ADDR_FLASH_PAGE_509 3 is: 0x %x \r\n",read_data1);read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_510);printf("the ADDR_FLASH_PAGE_510 first is: 0x %x \r\n",read_data1);if(btn_getState(&K2_BUTTON_150mS) == BTN_EDGE2){printf(" K2 150ms button!EEPROM_read test\r\n");read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_508 + 124);printf("the ADDR_FLASH_PAGE_508 last is: 0x %x \r\n",read_data1);read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509);printf("the ADDR_FLASH_PAGE_509 1 is: 0x %x \r\n",read_data1);read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 4);printf("the ADDR_FLASH_PAGE_509 2 is: 0x %x \r\n",read_data1);read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 8);printf("the ADDR_FLASH_PAGE_509 3 is: 0x %x \r\n",read_data1);read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_510);printf("the ADDR_FLASH_PAGE_510 first is: 0x %x \r\n",read_data1);} if(btn_getState(&K1_BUTTON_150mS) == BTN_EDGE2){// printf(" K1 150ms button!,EEPROM_Erase test\r\n");// MY_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, DATA_EEPROM_START_ADDR, write_data1);printf(" K1 150ms button!,flash write test\r\n");MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509,0X33333333);HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);}

因?yàn)榈刂肺恢萌绻袛?shù)據(jù)的話會(huì)擦除一頁(yè)再寫入,所以我們看了一下508頁(yè)最后一個(gè)地址,和510頁(yè)的第一個(gè)地址數(shù)據(jù),對(duì)509頁(yè)的數(shù)據(jù)進(jìn)行了操作,結(jié)果發(fā)現(xiàn)不會(huì)改變508 和510的數(shù)據(jù),509頁(yè)的數(shù)據(jù)會(huì)全部清除!


所以得出的結(jié)論顯而易見(jiàn),flash擦除按照一頁(yè)一頁(yè)擦除,
在L051上面一頁(yè)為128bytes,而且擦除后數(shù)據(jù)全部為 0;

2.4 讀寫EEPROM的后續(xù)問(wèn)題

最近有一個(gè)項(xiàng)目,因?yàn)槿必?STM32L051R系列缺貨,買了 STM32L071RBT6 替代:

看了一下地址,其實(shí)我們上面所有的測(cè)試代碼基本都是可以直接用的,代碼直接用STM32L051的代碼也是可以的,實(shí)際項(xiàng)目中,還是使用EEPROM比較方便,所以在使用EEPROM的時(shí)候,發(fā)現(xiàn)了一個(gè)問(wèn)題(并不是換了芯片的問(wèn)題),還是數(shù)據(jù)讀取和寫入的問(wèn)題。

2.4.1 問(wèn)題的出現(xiàn)和解決

下面程序的硬件平臺(tái)是 STM32L071RBT6,首先在程序中,有一個(gè)寫ID的函數(shù):


寫入IO是通過(guò)字節(jié)的方式 byte 寫入的,寫了6個(gè)字節(jié)(藍(lán)牙設(shè)備的ID)。

然后最初讀取的函數(shù)用的是:

使用這個(gè)讀ID的函數(shù),問(wèn)題就出來(lái)了。

上圖代碼中,我讀取ID使用的是半字讀取,處理方式是把讀到的半字前面8位給ID1, 后面8位給ID2, 但是測(cè)試中發(fā)現(xiàn)數(shù)據(jù)讀出來(lái)與想要的相反,什么意思呢,看下面的測(cè)試說(shuō)明:

上電打印出EEPROM中讀取的一個(gè)地址的,每個(gè)ID(每個(gè)ID是uint8_t類型)的數(shù)據(jù),在代碼開(kāi)始定義了測(cè)試數(shù)據(jù):

BlueID_STRUCT test; ... /* CHBlueID_STRUCT Flash_PowerOn_BlueCheck() {CHBlueID_STRUCT PowerOn_ID;PowerOn_ID.CH1ID = FLASH_blueIDRead(CH1_ID_ADDR);PowerOn_ID.CH2ID = FLASH_blueIDRead(CH2_ID_ADDR);PowerOn_ID.CH3ID = FLASH_blueIDRead(CH3_ID_ADDR);PowerOn_ID.CH4ID = FLASH_blueIDRead(CH4_ID_ADDR);PowerOn_ID.CH5ID = FLASH_blueIDRead(CH5_ID_ADDR);PowerOn_ID.CH6ID = FLASH_blueIDRead(CH6_ID_ADDR);PowerOn_ID.CH7ID = FLASH_blueIDRead(CH7_ID_ADDR);PowerOn_ID.CH8ID = FLASH_blueIDRead(CH8_ID_ADDR);PowerOn_ID.CH9ID = FLASH_blueIDRead(CH9_ID_ADDR);PowerOn_ID.CH10ID = FLASH_blueIDRead(CH10_ID_ADDR);return PowerOn_ID; } */ BlueChipID = Flash_PowerOn_BlueCheck(); //上電先把ID讀出來(lái)做比較 //打印一個(gè)出來(lái)測(cè)試,看結(jié)果 printf("BlueChipID.CH1ID is 0x%x,0x%x,0x%x,0x%x,0x%x,0x%x\r\n",BlueChipID.CH1ID.ID1,BlueChipID.CH1ID.ID2,BlueChipID.CH1ID.ID3,BlueChipID.CH1ID.ID4,BlueChipID.CH1ID.ID5,BlueChipID.CH1ID.ID6);test.ID1= 0XFF; //結(jié)構(gòu)體每個(gè)元素是 uint8_t 類型 test.ID2= 0XEE; test.ID3= 0XDD; test.ID4= 0XCC; test.ID5= 0XBB; test.ID6= 0XAA;while (1){...}

在程序中通過(guò)操作,寫入測(cè)試數(shù)據(jù),調(diào)用上面提到過(guò)的寫ID的函數(shù),寫ID的函數(shù)是每個(gè)字節(jié)每個(gè)字節(jié)寫的:

按我們希望的結(jié)果來(lái)說(shuō),讀出來(lái)按照順序打印,應(yīng)該是:0xff,0xee,0xdd,0xcc,0xbb,0xaa 。測(cè)試實(shí)際上是:

為了確實(shí)是讀的問(wèn)題還是寫的問(wèn)題,添加了讀字節(jié)的函數(shù):

打印的結(jié)果:

說(shuō)明確實(shí)是ID的讀取函數(shù)出的問(wèn)題,是因?yàn)槭褂玫陌胱肿x取,問(wèn)題處理不麻煩,我們把讀取函數(shù)修改一下:

BlueID_STRUCT FLASH_blueIDRead(uint32_t address) {BlueID_STRUCT u48id;// uint16_t temp1,temp2,temp3; /**(__IO uint16_t*)address; */// temp1=*(__IO uint16_t*)address; // u48id.ID1 = (uint8_t)(temp1>>8);// u48id.ID2 = (uint8_t)(temp1&0X00FF);// temp2=*(__IO uint16_t*)(address+2);// u48id.ID3 = (uint8_t)(temp2>>8);// u48id.ID4 = (uint8_t)(temp2&0X00FF);// temp3=*(__IO uint16_t*)(address+4);// u48id.ID5 = (uint8_t)(temp3>>8);// u48id.ID6 = (uint8_t)(temp3&0X00FF); u48id.ID1 = FLASH_Readbyte(address);u48id.ID2 = FLASH_Readbyte(address+1);u48id.ID3 = FLASH_Readbyte(address+2);u48id.ID4 = FLASH_Readbyte(address+3);u48id.ID5 = FLASH_Readbyte(address+4);u48id.ID6 = FLASH_Readbyte(address+5);return u48id; }

測(cè)試結(jié)果才正常了,如下圖:

2.4.2 問(wèn)題的分析(大小端模式數(shù)據(jù)格式)

出現(xiàn)上面的問(wèn)題,其實(shí)是和我們經(jīng)常說(shuō)的大端模式和小端模式有關(guān)的。

STM32使用的是小端模式,簡(jiǎn)單介紹一下大端模式小端模式數(shù)據(jù)存放的方式,如下圖:

知道上面的知識(shí),我們?cè)陂_(kāi)始的讀取函數(shù)中是直接讀取的半字(__IO uint16_t*)address; ,但是我們寫入的時(shí)候是一個(gè)字節(jié)一個(gè)字節(jié)寫入,上面的例子所以我們內(nèi)存中的數(shù)據(jù)實(shí)際上如下圖所示:

使用(__IO uint16_t*)address; 去讀取,讀取出來(lái)的數(shù)值一個(gè)uint16_t類型的數(shù)值:

假設(shè)是 a,a=*(__IO uint16_t*)addr; 會(huì)有 a = 0xEEFF; 所以高8位變成了 0xEE。 OK! 解釋清楚!問(wèn)題解決!

至此,我們基本上把STM32L051 的EEPROM 和Flash 功能都測(cè)試過(guò)了,把工程中需要用到的功能做了測(cè)試,也學(xué)到了一些新的知識(shí),還是實(shí)踐出結(jié)論啊,當(dāng)初沒(méi)有自己測(cè)試之前看了網(wǎng)上的有關(guān)類似的介紹,還是很多誤解,這下全部清晰了。

2.4.3 STM32L071RBT6 EEPROM讀寫全字問(wèn)題

讀問(wèn)題的出現(xiàn)

前面其實(shí)測(cè)試過(guò),讀取全字是可以的,直接使用return *(__IO uint32_t*)address;:便可以讀到該地址的全字:

uint32_t FLASH_ReadWord(uint32_t address) {return *(__IO uint32_t*)address; }

所以在后面使用過(guò)程中,有這么一個(gè)函數(shù):

一開(kāi)始還真的不知道哪里出了問(wèn)題?折騰了好一陣才發(fā)現(xiàn)調(diào)用函數(shù)讀取ID時(shí)就會(huì)卡死。

問(wèn)題的解決:

因?yàn)檫€有另外一個(gè)藍(lán)牙版本的產(chǎn)品,如果是藍(lán)牙設(shè)備的ID,因?yàn)樗{(lán)牙設(shè)備的ID是6位的,所以當(dāng)時(shí)讀取藍(lán)牙的ID的時(shí)候使用的是(藍(lán)牙版本是沒(méi)問(wèn)題的):

其實(shí)折騰了好一會(huì)兒,后來(lái)想著藍(lán)牙是讀一個(gè)字節(jié),要不要試著把 全字 分為 4個(gè)字節(jié),單獨(dú)讀取試一試?

于是學(xué)藍(lán)牙把程序分為4個(gè)字節(jié)讀取:

代碼也放一下,方便復(fù)制:

u32 FLASH_ReadEnoceanID(uint32_t address) {u32 keepid;u8 i;keepid = FLASH_Readbyte(address);i = FLASH_Readbyte(address + 1);keepid = (i<<8)|keepid;i = FLASH_Readbyte(address + 2);keepid = (i<<16)|keepid;i = FLASH_Readbyte(address + 3);keepid = (i<<24)|keepid;return keepid; }CHID_STRUCT Flash_PowerOn_Check() {CHID_STRUCT PowerOn_ID;PowerOn_ID.CH1ID = FLASH_ReadEnoceanID(CH1_ID_ADDR);PowerOn_ID.CH2ID = FLASH_ReadEnoceanID(CH2_ID_ADDR);PowerOn_ID.CH3ID = FLASH_ReadEnoceanID(CH3_ID_ADDR);PowerOn_ID.CH4ID = FLASH_ReadEnoceanID(CH4_ID_ADDR);PowerOn_ID.CH5ID = FLASH_ReadEnoceanID(CH5_ID_ADDR);PowerOn_ID.CH6ID = FLASH_ReadEnoceanID(CH6_ID_ADDR);PowerOn_ID.CH7ID = FLASH_ReadEnoceanID(CH7_ID_ADDR);PowerOn_ID.CH8ID = FLASH_ReadEnoceanID(CH8_ID_ADDR);PowerOn_ID.CH9ID = FLASH_ReadEnoceanID(CH9_ID_ADDR);PowerOn_ID.CH10ID = FLASH_ReadEnoceanID(CH10_ID_ADDR);return PowerOn_ID; }

測(cè)試一下,發(fā)現(xiàn)就好了,至于原因,目前還不知道為什么……(最后問(wèn)題解決有說(shuō)明,內(nèi)存字節(jié)對(duì)齊問(wèn)題)

寫問(wèn)題的出現(xiàn)

本來(lái)以為解決了上面問(wèn)題OK了,可是后面測(cè)試的時(shí)候發(fā)現(xiàn)寫的時(shí)候也有問(wèn)題:

一直用的寫全字函數(shù)為:

FLASH_Status FLASH_WriteWord(uint32_t Address, uint32_t Data) {FLASH_Status i = FLASH_COMPLETE;HAL_FLASHEx_DATAEEPROM_Unlock(); while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, Address, Data) != HAL_OK);HAL_FLASHEx_DATAEEPROM_Lock();return i; }

在程序中會(huì)調(diào)用此函數(shù)進(jìn)行 ID 保存:

但是使用時(shí)候發(fā)現(xiàn):


問(wèn)題的解決:

其實(shí)這個(gè)問(wèn)題也莫名其妙,真是說(shuō)不出來(lái)為什么,估計(jì)是得詳細(xì)的查看數(shù)據(jù)手冊(cè),但是還是 因?yàn)?在藍(lán)牙的版本上面沒(méi)有此類問(wèn)題:

所以這里還是嘗試改成 以 字節(jié) 方式寫入:

FLASH_Status FLASH_WriteWord(uint32_t Address, uint32_t Data) {FLASH_Status state = FLASH_COMPLETE;u8 i = 0;u8 writedata[6]={0};writedata[0] = (u8)Data;writedata[1] = (u8)(Data>>8);writedata[2] = (u8)(Data>>16);writedata[3] = (u8)(Data>>24);HAL_FLASHEx_DATAEEPROM_Unlock(); for(i=0; i<4; i++){while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, Address + i, writedata[i]) != HAL_OK);}HAL_FLASHEx_DATAEEPROM_Lock();return state; }

使用此種方式寫入,就不會(huì)出現(xiàn)問(wèn)題!

其實(shí)也可以嘗試修改地址,使得成為 4 的倍數(shù),可能也不會(huì)出問(wèn)題,這里就不測(cè)試了(最后問(wèn)題解決有還是測(cè)試了,內(nèi)存字節(jié)對(duì)齊問(wèn)題)

2.4.3小結(jié)使用的芯片為 STM32L071RBT6

最后問(wèn)題的解決

先直接說(shuō)結(jié)論,就是EEPROM地址定義的問(wèn)題,應(yīng)該是4字節(jié)對(duì)齊(4的整數(shù)倍),讀取全字的操作才能正常!

上面 STM32L071RBT6 EEPROM 讀寫全字問(wèn)題的關(guān)鍵在于,存儲(chǔ)地址的定義上,如上面一張圖所示:

(我在EEPROM區(qū)域定義了10個(gè)地址,用來(lái)存放無(wú)線設(shè)備的ID數(shù)據(jù),如果是藍(lán)牙芯片,那么ID為6個(gè)字節(jié),如果是Enocean芯片,那么ID為4個(gè)字節(jié),為了保持代碼的統(tǒng)一,我在使用保存4個(gè)字節(jié)的ID數(shù)據(jù)的地址定義時(shí)候沿用的是藍(lán)牙的 EEPROM區(qū)域定義)

那么正如我圖中猜想的一樣,藍(lán)牙的 ID 6個(gè)字節(jié),我是都是通過(guò)一個(gè)字節(jié)一個(gè)字節(jié)操作,組合起來(lái)進(jìn)行的,所以一切正常。

但是對(duì)于 4個(gè)字節(jié)的 ID ,期初是用的 全字的方式,就出問(wèn)題了,換成一個(gè)字節(jié)一個(gè)字節(jié)的操作,看上去是解決問(wèn)題了。

但是實(shí)際上多了一些隱藏問(wèn)題,暫時(shí)也說(shuō)不清楚,在產(chǎn)品使用的時(shí)候,讀寫ID還是會(huì)有莫名其妙的問(wèn)題,最終還是對(duì)當(dāng)初的這個(gè)猜想,地址是不是也需要4字節(jié)對(duì)齊?進(jìn)行了修改測(cè)試,于是乎,對(duì)于 4 個(gè)字節(jié) ID的處理,地址改成:

把地址修改成 4 的倍數(shù)以后,上面的讀取全字的兩個(gè)函數(shù)便可以正常使用,而不會(huì)出上面莫名其妙的問(wèn)題。

總結(jié)

在STM32L0 系列讀取 EEPROM 的時(shí)候,需要注意:

字節(jié)操作,傳入合理范圍內(nèi)的任何地址參數(shù)都可以;

半字操作,地址需要 2 字節(jié)對(duì)齊,就是 2的倍數(shù);

全字操作,地址需要 4 字節(jié)對(duì)齊,就是 4 的倍數(shù);

總結(jié)

以上是生活随笔為你收集整理的STM32L051测试 (四、Flash和EEPROM的读写)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。