你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

基于STM32L051测试Flash和EEPROM的读写

[复制链接]
攻城狮Melo 发布时间:2023-6-12 17:30

本文测试 L051 flash的读写,用来处理以后应用中的掉电数据保存。

1、STM32L051内部存储模块地址范围

开始找到F103的FLASH图复习了一遍,然后L051C8T6,64KB的flash,  然后我惊奇的发现还有2KB的EEPROM。发现L051系列的地址与F103完全不同,F103的flash每页的长度有1KB(小容量<=64KB)和2KB(大容量128KB起)查看各种资料, 查了2个小时, 还是不知道L051的flash 每页长度是 128Byte 还是256Byte????????????????????????还是请教了一下大佬,发现直接在J-Flash中可以找到答案,先上个F103的图:


% ?8 p4 V7 F0 G" r

微信图片_20230612172451.png

6 y  {3 E) e  ~$ A

微信图片_20230612172454.png


: `! f9 {. Y' h6 M& V

然后来看个L051的图:图中64KB 的flash 和2KB的EEPROM都能都明显的看出地址,flash 512页,每页128bytes,EEPROM只有4页,每页512bytes.知道了基本的地址,就可以操作起来了。

最后还需要确定的一点事,最小擦除单元是128bytes,还是256bytes,按以前的认知,删除是按照一个Sector擦除的,也就是128bytes,但是我查看了一些网上的例子和资料,有的是说256bytes,所以后面需要自己确定一下

其实在HAL库的 stm32l0xx_hal_flash.h 文件中有过 FLASH_PAGE_SIZE    的定义,就是128bytes       :


+ g0 C* ]' H) f9 d3 K

  1. #define FLASH_SIZE                (uint32_t)((*((uint32_t *)FLASHSIZE_BASE)&0xFFFF) * 1024U)
    0 l0 g0 g/ d: S+ V
  2. #define FLASH_PAGE_SIZE           ((uint32_t)128U)  /*!< FLASH Page Size in bytes */
复制代码

9 X: C+ y2 B" E/ v2 D' d

对于flash的操作,有一些基础知识补充一下:

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必须字,读 字节、半字、字都支持。(这句话也是错误的,这是以前哪里看到的,实际测试写可以根据字,半字,字节来写)

一些基本概念:定义字是根据处理器的特性决定的。首先ARM是32bit处理器,所以它的字是32bit的。半字自然就是16bit;字节不论在哪个CPU上都是8bit。1 Byte = 8 bits(即 1B=8b) 1 KB = 1024 Bytes Bit意为“位”或“比特”,是计算机运算的基础,属于二进制的范畴;Byte意为“字节”,是计算机文件大小的基本计算单位;

; m: \3 ?# g; Y5 A! e/ @' Q: }
2、读写函数的设计

HAL库中肯定是有对flash和EEPROM进行操作的函数,我们这里新建一个stml0_flash.c 和stml0_flash.h 函数分别放在对应位置,进行自己的函数设计。库中Flash与EEPROM的函数看样子是分别放在 stm32l0xx_hal_flash.c 和 stm32l0xx_hal_flash_ex.c 中的,我们先使用EEPROM,因为提供EEPROM,就是让用户可以保存一些掉电后的数据的嘛,测试完EEPROM,再去测试下flash,因为怕有时候数据不够放……


7 G7 V) z1 Q/ z1 w2.1 读取函数
  1. //读取指定地址的半字(16位数据)
    ' ?7 D. Z1 t$ c& {' `, o
  2. uint16_t FLASH_ReadHalfWord(uint32_t address)$ Y' o- Y1 s! Y# N: Y
  3. {
    + `" l4 g; h, X- m. d* x6 z
  4.   return *(__IO uint16_t*)address; , Z& k( [2 E' k$ q. N1 @8 F
  5. }7 s" c$ ?3 F: K& R( [- a* b
  6. 5 ~( n5 \3 F+ z! e" v
  7. //读取指定地址的全字(32位数据)
    : f/ d: g0 O9 r  ~. p/ ]! w
  8. uint32_t FLASH_ReadWord(uint32_t address)2 [+ Q  l/ w' L! T4 o* N
  9. {- I* {) @3 @2 w' P3 l# d. F0 l
  10.   return *(__IO uint32_t*)address;+ _% U$ r, u. C
  11. }
复制代码

3 W2 F# i/ @  i- L' ^  a; G) u# Z# `( w

简单测试一下:

  1. u32 read_data1=0XFFFFFFFF;
    + Z( F8 O/ z' \0 T* F( [1 ^
  2. u32 read_data2=0XFFFFFFFF;; X) A& D. Y: G9 h$ P+ Z* _: L2 s
  3. ...
    & g0 p* E  U' }
  4. read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);, z/ h) O. N: M6 V$ o3 T- t
  5. printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);
    . R3 X7 x3 d  P# z
  6. read_data2 = FLASH_ReadWord(DATA_EEPROM_START_ADDR + EEPROM_PAGE_SIZE);
    " `- [! X0 E- T7 L6 R6 E
  7. printf("the EEPROM sceond page test data is: 0x %x \r\n",read_data2);
复制代码
  ^3 j: V9 {" G0 \. M/ v- a* y

没有写入数据读取的值应该都是0。

; W+ y6 O# r' D% K* _+ ]
2.2   EEPROM写函数

对EEPROM的写函数:stm32l0xx_hal_flash_ex.h中函数如下:

  1. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Unlock(void);
    6 Q: l; W7 h" e( q/ ^
  2. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Lock(void);
    & I) v* u: ^; n+ C- s0 Z1 k

  3. % f9 ]" f7 e. x& L8 P0 ~# {
  4. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Erase(uint32_t Address);
    * ]: Q0 H/ a& R0 V" n
  5. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data);
复制代码
+ z2 A. z) @- y4 U+ i

通过函数看来,可以直接用,但是这里有一个问题

需要测试一下,擦除是否会擦除整个扇区,有待验证!!

答:EEPROM的擦除可以直接擦除某个地址的字,不会擦除整个片区

EEPROM的操作相对Flash,比较简单,直接使用HAL库中的函数即可完成

  1. HAL_FLASHEx_DATAEEPROM_Unlock();
    6 i# w! ?! i, _6 ?  S% \) s( v- J
  2. HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, DATA_EEPROM_START_ADDR, write_data1);" [# b& H/ R9 r. r& Q& p0 `
  3. HAL_FLASHEx_DATAEEPROM_Lock();( p, T, {, z# U6 v9 \
  4. ...
    7 m8 c$ R$ P4 J2 }% o
  5. if(btn_getState(&K1_BUTTON_150mS) == BTN_EDGE2){5 h8 V. M9 _. g- l5 p
  6.         printf(" K1 150ms button!,EEPROM_Erase test\r\n");. [# {; c; P) `  K8 B( t
  7.         HAL_FLASHEx_DATAEEPROM_Unlock();% J' Z; w# @- c1 P. d2 C5 J% {- m+ M
  8.         HAL_FLASHEx_DATAEEPROM_Erase(DATA_EEPROM_START_ADDR+4);
    ) ~7 W  g  f' b/ {
  9.         HAL_FLASHEx_DATAEEPROM_Lock();
    3 W7 X7 [* \8 ~4 j4 g* i
  10.         HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
    9 V- A/ D  e, @
  11.     }
    . s6 B: [2 I% f
  12. ...& ^' ^$ m7 H! t6 Z* h( f
  13. if(btn_getState(&K2_BUTTON_150mS) == BTN_EDGE2){5 t/ \7 v* d1 i% }
  14.         printf(" K2 150ms button!EEPROM_read test\r\n");4 Y# [: h# n4 v5 |& K
  15.         read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);
    : s. J) f2 ~+ Q7 k# N/ i0 w1 p7 y
  16.         printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);5 P  [9 Y" {/ p5 ]) q$ x* ~" _
  17.     }
复制代码
: v' W0 j* N3 r5 l

按照上面的例子,擦除DATA_EEPROM_START_ADDR+4的地址不会影响DATA_EEPROM_START_ADDR地址的开始写入的数据 写入一样,如果不在意以前的数据,直接写入就可以。 总结来说EEPROM的使用还是很好用而且简单的。而且EEPROM是可以按照字节,半字,全字写入的,测试了一下,是右对齐

右对齐什么意思呢,打个比方就是如果在地址 addr 中,本来写入全字 0x12345678 然后直接在EEPROM 的 addr 这个地址,写入半字 0xFFFF, 再读取全字的话,addr 地址的全字就变成了 0x1234FFFF  ,这个具体的为什么在地址写入半字,不会直接从地址开始占用2个字节,是因为地址的类型为 uint32_t ,所以该地址就是 4个字节类型的数。


9 S. ^1 }7 R  a! _0 n( b4 L" t写入问题说明修改

2021/11/23 修改说明 上面一段的解释有误,这里修改一下,不是因为地址类型为uint32_t,地址类型永远是这个,是因为定义的数据类型为uint32_t ,然后STM32又是小端模式,所以保存的方式是从地址的最后开始保存,4个字节的全字,第一个字节放在地址开始+4 的位置,第二个字节放在地址开始+3的位置,所以如果调用HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, DATA_EEPROM_START_ADDR, write_data1);关键在于FLASH_TYPEPROGRAMDATA_WORD以全字方式写入半字,那么内核会自动分配4个字节的宽度,半字的第一个字节放在写入地址+4 的位置,第二个字节放在写入地址+3的位置,所以导致了上面的结果

  1.   HAL_FLASHEx_DATAEEPROM_Unlock();4 {1 }5 E0 y: d6 P3 P
  2.   HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, DATA_EEPROM_START_ADDR, 0X88);" o2 p( W' N! B" E% P6 {$ K' [4 T
  3.   HAL_FLASHEx_DATAEEPROM_Lock();5 X; ~' U% }7 `& u% y8 i
  4.   read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);' K$ f0 u) u$ e4 q# o9 F
  5.   printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);
复制代码

; |' n& P8 \2 u7 V. F  ~

最后在 stml0_flash.h 中把函数完善声明一下,使得主函数中的程序更加简洁。

  1. void MY_DATAEEPROM_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data)   9 i  a* b7 N/ o6 U% S" C( V# S; m7 A
  2. {- M7 b$ ~; J( z0 z
  3. HAL_FLASHEx_DATAEEPROM_Unlock();        + e1 z2 i$ f& S: i, [6 d
  4.     HAL_FLASHEx_DATAEEPROM_Program(TypeProgram, Address, Data);+ N1 J) S- n. Q
  5. HAL_FLASHEx_DATAEEPROM_Lock();9 C0 _# X+ T; W# k
  6. }
复制代码
4 S* o+ X$ u: ?6 F5 G/ X

那么L051 的EEPROM的测试就到这里,其实有EEPROM,在项目中的保存数据的功能就问题不大了,但是我们既然开始了,就把L051 Flash的读写也测试一下。

- N- H. {' _* B1 `" Z
2.3   Flash写函数

Flash的读取其实和EEPROM一样,主要是写函数,来看一下stm32l0xx_hal_flash.h 中有哪些函数

  1. /* IO operation functions *****************************************************/4 r9 {! A3 ]& U
  2. HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data);
    , m  L1 V/ V7 }/ t. o5 c7 G# X
  3. HAL_StatusTypeDef HAL_FLASH_Program_IT(uint32_t TypeProgram, uint32_t Address, uint32_t Data);' I9 F5 x& h1 i+ ]- A

  4. & m6 J$ Q2 v2 `/ z1 K
  5. /* FLASH IRQ handler function */
    % {5 e9 Q" m$ z; s$ v7 G/ e
  6. void       HAL_FLASH_IRQHandler(void);
    # k- o. q: G' ?
  7. /* Callbacks in non blocking modes */5 ?$ O- u. |$ K3 R
  8. void       HAL_FLASH_EndOfOperationCallback(uint32_t ReturnValue);/ c% j3 @0 K$ _- i7 W( R
  9. void       HAL_FLASH_OperationErrorCallback(uint32_t ReturnValue);' x9 g1 d* Y8 _+ a
  10. 8 _* T) Z: d+ f3 y* j: c
  11. /**
    3 N% {+ ~# X6 q1 q% ^" B
  12.   * @}
    9 M( g) m. l/ V) I9 N1 V
  13.   */
    ( {, }7 G3 p$ I; c! [" h

  14.   A8 w6 t. n+ v" N7 |
  15. /** @addtogroup FLASH_Exported_Functions_Group24 R) H7 ]+ f4 V" Q+ i1 v' W. O" k
  16.   * @{2 t% w. X. i% e0 K5 i; r
  17.   */
    # A/ L4 y( j$ s1 M0 o, p
  18. /* Peripheral Control functions ***********************************************/+ U" h3 Y: G' M4 h3 |
  19. HAL_StatusTypeDef HAL_FLASH_Unlock(void);
    - |! r' ^4 e+ I0 b
  20. HAL_StatusTypeDef HAL_FLASH_Lock(void);
    2 O* d9 M* q. i  f8 ~; V* D& }
  21. HAL_StatusTypeDef HAL_FLASH_OB_Unlock(void);! z, u# h) Y; D, h* P4 g$ n- D$ h& I
  22. HAL_StatusTypeDef HAL_FLASH_OB_Lock(void);
    % H, m/ _; G! S8 A% p
  23. HAL_StatusTypeDef HAL_FLASH_OB_Launch(void);
复制代码
( f( F2 k7 W9 o& N, f+ F8 B3 q

有点忙,Flash的后面再也,看了几个demo,只需要做几个测试就可以;


+ Z, z6 S6 w+ ^; x$ Q# N

2021.8.5  今天有空来接着测试一下L051  Flash的读写,看了下HAL_FLASH_Program函数:

* ^, r( y1 q3 ?! U1 @2 V# ~
  1. HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data)& B4 T! e! S8 K
  2. {
    9 L$ L3 B# C5 o4 s. y* T5 g
  3.   HAL_StatusTypeDef status = HAL_ERROR;# e4 |0 N4 U) E. L
  4.   
    , n1 J1 L1 k  K- u
  5.   /* Process Locked */# d# b$ R3 E2 t* ]1 R
  6.   __HAL_LOCK(&pFlash);7 F% b7 O. Z' ?
  7. # b( v" n8 ~3 L" g  V4 H( S
  8.   /* Check the parameters */
    " t/ Y" |0 `3 g# H5 ]8 m0 o
  9.   assert_param(IS_FLASH_TYPEPROGRAM(TypeProgram));# B) e- f/ h  z6 S' R% O- l
  10.   assert_param(IS_FLASH_PROGRAM_ADDRESS(Address));- x: P; x6 R2 \: x
  11. # U/ \* p7 R1 X$ ?1 d" N9 f
  12.   /* Wait for last operation to be completed */- X; D% V; ^! V* x5 v9 Y
  13.   status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
    & X% G% e" R) p+ c4 r8 X. U
  14.   / S* a8 K' R9 _, p* c9 y' ~
  15.   if(status == HAL_OK)
    , e9 e9 _* k& }
  16.   {
    2 P% {/ O: v8 C* h( p
  17.     /* Clean the error context */0 J% X1 f' t: A  ]: ~8 X
  18.     pFlash.ErrorCode = HAL_FLASH_ERROR_NONE;0 r; n7 Y/ r& {4 B5 ^% J' k
  19. % U: l! X: k3 E3 ^. O
  20.     /*Program word (32-bit) at a specified address.*/
    % I/ L; Y- S7 U! Y# x) y
  21.     *(__IO uint32_t *)Address = Data;
    5 l) A5 `6 ?" F: F; e
  22. ) \) P; g( J* [9 ]7 h* B1 G
  23.     /* Wait for last operation to be completed */
    ' x+ i& I8 \& W$ I/ z( m
  24.     status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
    ) S. _0 h5 ?7 C4 z
  25.   }
    $ p4 h, r8 i. W/ i9 c
  26. 8 U" v; p; a. d2 F: T# w
  27.   /* Process Unlocked */  [! d1 h. Y  F) D1 L8 ]
  28.   __HAL_UNLOCK(&pFlash);
    9 r% {* j: H: I  f& d1 t

  29. : R) ]5 k/ j5 E6 m) m, @3 q) O- j
  30.   return status;
    - E) v8 N0 E+ B( V
  31. }- s! D& Z  G1 [
复制代码

, T) j8 [9 T6 E7 f: s# i- F

这里也再次说明了,L051的写必须以字的方式写入。

不管了,先测试一下,不擦除直接写入,这里先定义一下写入的地址,前面我们已经知道了L051 flash一共 512页,每页128bytes,所以我们直接拿最后面的几页来测试

  1. #define ADDR_FLASH_PAGE_505      0X08000000 + 128*504   //
    % o" D' F0 W- d7 U/ m. K
  2. #define ADDR_FLASH_PAGE_506      0X08000000 + 128*505   //0 o4 p5 X9 u7 |/ h1 [; t7 T3 m, V
  3. #define ADDR_FLASH_PAGE_507      0X08000000 + 128*506   //. g/ {9 X9 F1 \' T: M) @
  4. #define ADDR_FLASH_PAGE_508      0X08000000 + 128*507   //- B  T" P" S" ]- ]: Q
  5. #define ADDR_FLASH_PAGE_509      0X08000000 + 128*508   //
    # R7 u, ~5 Y  N; T
  6. #define ADDR_FLASH_PAGE_510      0X08000000 + 128*509   //
    - B8 `+ i; x/ O* X; n: ]* B, P
  7. #define ADDR_FLASH_PAGE_511      0X08000000 + 128*510   //$ J7 C: \$ V2 X8 ?0 `3 X% A5 X' T$ k
  8. #define ADDR_FLASH_PAGE_512      0X08000000 + 128*511   //最后一页
复制代码

开始先不擦除,直接在最后一页写入一个字读取一下试试,整理一下写入函数:

  1. void MY_DATAFLASH_Program(uint32_t Address, uint32_t Data)   3 C+ n/ N" K2 I* A3 }7 H  z
  2. {* e; R! r4 z" M; x  l4 N
  3. HAL_FLASH_Unlock();        
    " s! q6 t( A2 T/ i5 ?" d% M
  4.   if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){
    1 {4 K$ n" a4 _6 j3 K! |
  5.     printf("write data 0x%x OK\n", Data);
    , h- Y1 K1 `7 R6 x
  6.   }
    0 g" Y$ i' ^% n6 }/ c, i
  7.   else{0 }2 i5 T: Z" F0 o/ F2 Q
  8.     printf("failed!!!\n");
    / {# [5 P' J7 K/ L# \
  9.   }
    & l3 a1 R' a; u  i
  10. HAL_FLASH_Lock();
    ' A, `* O  f* v/ q% J" i
  11. }
复制代码
# B8 {  O9 X% [0 h' \2 _

测试一下;

  1.   MY_DATAFLASH_Program(ADDR_FLASH_PAGE_512,write_data2);& U' n$ b+ l; X# T8 Z$ B
  2.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_512);8 @* I5 D- E7 _* }) \$ p; ^
  3.   printf("the ADDR_FLASH_PAGE_512 is: 0x %x \r\n",read_data1);
复制代码
! l+ B& R  p9 J2 A

在没有写入flash之前,该地址读出来的数据为0,写入后读出来是正常写入的数据这里有个疑问,按理来说,flash存储器有个特点,就是只能写0,不能写1,所以flash的写入,比需先擦除,或者至少检查一下该数据区是否可以写入,但是L051 怎么初始的时候读出来是0? 难道L051的有区别,需要测试一下

先在一个地址写0XFFFFFFFF ,然后写完了再写一次别的数据看看能否直接写入,结果是写入了0XFFFFFFFF ,不能继续直接写数据,说明,估计L051是相反的,这里具体是不是我只看测试结果,结论的话我自己知道就可以应用,希望有权威大神指正。

这里我们得用到一个关键的函数 ,这个函数是在stm32l0xx_hal_flash_ex.c 这个文件中的,是flash的擦除函数:HAL_StatusTypeDef HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *PageError)

所以这里我们知道了以后,可以优化一下写入函数,我们项目中用到的是可以直接对某个地址的写入,然后也不需要保存,此页其他的数据,所以我们把函数改成如下:

  1. void MY_DATAFLASH_Program(uint32_t Address, uint32_t Data)   
    # _+ p# W; L. ~, y
  2. {7 T/ i$ P. `0 W. V: c
  3.   FLASH_EraseInitTypeDef EraseInitStruct;# |5 e& J* T+ v+ A* r; w& e
  4.   uint32_t checkdata;/ i5 d) j% i. Z3 N. _0 J
  5.   uint32_t PAGEError = 0;
    # l2 z4 r. v9 c, B, w/ q
  6.   checkdata = FLASH_ReadWord(Address);7 Z3 d- ?& Q) ]9 t- P
  7. HAL_FLASH_Unlock();
    6 A* v& q, q  j1 K
  8.   /*如果是0,直接写*/
    " f/ v/ f3 m( I* ~  [. X1 s
  9.   if(checkdata == 0){      
    & m+ i$ v% D! h
  10.     if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){3 |  v6 h4 y4 S# g0 F8 _3 W
  11.       printf("write data 0x%x OK\n", Data);
    : `, F8 c" ?/ ]
  12.     }. v1 _7 F. v2 u$ b% \
  13.     else{
    0 ^& g" N. c9 T
  14.       printf("failed!!!\n");7 g) o. U. n& v. ~' b! V' Z+ U
  15.     }3 C0 @  A3 B  ~) _) v$ B; a# e6 `
  16.   }
    0 N. _# i: O% c4 y! u8 a4 f* g  T
  17.   /*否则擦除再写*/5 G- k4 |" }. b( x0 }7 n  D* g2 f
  18.   else{
      {# }) C4 |: c' X  b
  19.      __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);
    + _. K6 H- a) v

  20. " s* l, b+ d7 U
  21.       EraseInitStruct.TypeErase   = FLASH_TYPEERASE_PAGES; // 刷除方式3 ~5 T1 w5 ?6 g3 g9 }% d/ \6 m. e: h
  22.    EraseInitStruct.PageAddress = Address; // 起始地址2 ^3 |' W: ^" X+ j0 v! B
  23.       EraseInitStruct.NbPages = 1;9 ^+ F( A  `7 E/ t$ {8 U( `

  24. 4 c$ N  W7 p% |9 _+ |4 J7 r
  25.       if (HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError) != HAL_OK)
    - V  w7 ]8 L* ~+ A9 z
  26.       {. z5 o4 l7 @8 p3 ~$ W$ I* r( _- _6 e% f
  27.         // 如果刷除错误
    3 C; y7 t$ c: g$ N( @% R
  28.         printf("\r\n FLASH Erase Fail\r\n");3 c7 N: z2 `. D$ s1 Y0 a
  29.         printf("Fail Code:%d\r\n",HAL_FLASH_GetError());
    6 D4 h* ]4 v+ ~7 V
  30.         printf("Fail Page:%d\r\n",PAGEError);
    . s) Q# Q+ s! O$ v' p( Y" W& `
  31.       }
    4 r, G, R' G6 n8 m2 ?
  32. . i5 C1 @& h7 P1 a1 Z, }% t
  33.       if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){
    ( p1 O+ T! k% Q  \" S, A4 w
  34.       printf("write data 0x%x OK\n", Data);
    . k1 `% b- g5 e4 F& N4 j
  35.       }' \) \, n) z" o: G/ ~
  36.       else{/ [: W# O' L# s9 A6 U
  37.         printf("failed!!!\n");
    ! J/ Y( U0 @5 n: ~% W/ L$ Q
  38.       }
    " R1 B8 V3 H/ W. \% d% K- [

  39. 1 p( w) S& N/ C3 l. u; Q
  40.   }
    5 l4 [. C6 G, }8 L" D* N
  41. HAL_FLASH_Lock();7 i- ?* M2 R6 ^; M- F
  42. }
复制代码

0 k# w' l! y9 _& V) F$ x2 t

自己修改了一个函数, 改成这样以后,就能直接在想写入的地方写入数据了,到这里,flash足够我项目中的使用了。但是还有最后一个疑问,就是擦除的一页到底是不是128bytes我来验证一下。

  1. u32 write_data1 = 0X12345678;/ ]; _, C/ ^6 ?% H( t8 G) i
  2. u32 write_data2 = 0X87654321;) j3 g0 M+ x! ~( X
  3. u32 write_data3 = 0XFFFFFFFF;, p9 P" G: G: Z! d0 L$ S7 \9 r6 _

  4. 2 d2 O+ e# Y' U4 z3 H; n9 E
  5. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_508 + 124,0X508508FF);
    9 |0 n: j9 a# L
  6. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509,write_data3);& B4 G: Z) X/ o( J, H
  7. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509+4,write_data2);
    3 t+ c! I9 C& S6 d, P8 v8 B
  8. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509+8,write_data1);8 {' H1 \. n7 _1 N( T, N. w7 f; k3 V
  9. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_510,0X510510FF);
    ( s/ o  M* M5 t/ V/ n( ]" |
  10. 9 o8 y1 f3 o9 h/ D
  11. read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_508 + 124);
    ) `# m- D) z! u8 v
  12. printf("the ADDR_FLASH_PAGE_508 last is: 0x %x \r\n",read_data1);, b/ T$ L+ Y" k+ c; j3 ~+ \8 g
  13.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509);* r7 y! K; Y4 f& K. j! y. C
  14.   printf("the ADDR_FLASH_PAGE_509 1 is: 0x %x \r\n",read_data1);% i( p  m2 G2 G  t/ t0 d
  15.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 4);
    5 h1 g* L$ r, u0 k
  16.   printf("the ADDR_FLASH_PAGE_509 2 is: 0x %x \r\n",read_data1);3 r1 g  c9 n+ i9 R+ V( I7 G
  17.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 8);$ [& L7 s8 r1 T5 ^: m( x8 _7 f
  18.   printf("the ADDR_FLASH_PAGE_509 3 is: 0x %x \r\n",read_data1);
    & c2 H7 b1 i& K8 ^
  19.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_510);
    , F8 ?/ O  A& h/ `3 Y, j
  20.   printf("the ADDR_FLASH_PAGE_510 first is: 0x %x \r\n",read_data1);' e" I) z4 |5 ~; w/ [+ ?6 l

  21. 5 a% x# o) v4 Z0 w/ w4 H; V
  22. if(btn_getState(&K2_BUTTON_150mS) == BTN_EDGE2){2 T/ l: a+ Y/ {# m  j$ y; H( |( M
  23.         printf(" K2 150ms button!EEPROM_read test\r\n");
    , q" w! k  P( b& u  }! q2 W
  24.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_508 + 124);& I6 A, v$ s9 b1 h
  25.         printf("the ADDR_FLASH_PAGE_508 last is: 0x %x \r\n",read_data1);
    5 }) \- Q3 Y5 A9 i. F
  26.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509);
    1 d5 m& k! i, G$ a0 h( h4 K& K( d" }
  27.         printf("the ADDR_FLASH_PAGE_509 1 is: 0x %x \r\n",read_data1);8 W) u5 A9 C& d# t; o
  28.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 4);8 L+ l2 m7 `9 ~% ~1 X
  29.         printf("the ADDR_FLASH_PAGE_509 2 is: 0x %x \r\n",read_data1);
    % l, b$ r  k2 S+ Q6 y2 T
  30.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 8);
    8 g5 W( f; ^; N1 f( t
  31.         printf("the ADDR_FLASH_PAGE_509 3 is: 0x %x \r\n",read_data1);7 O, Z: {; x2 a4 u8 Y
  32.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_510);' ~7 N' Q" H' T7 q3 G- \4 Z8 T
  33.         printf("the ADDR_FLASH_PAGE_510 first is: 0x %x \r\n",read_data1);. T3 M1 G- H5 o6 M9 V4 L

  34. 5 q1 }( ]( {  m! ?( J$ h
  35.     }- N$ {' X) x% K3 P8 c- x4 ]8 O
  36. if(btn_getState(&K1_BUTTON_150mS) == BTN_EDGE2){
    & p5 x& c& i/ o2 u. w
  37.         // printf(" K1 150ms button!,EEPROM_Erase test\r\n");
      G+ T( Q- p5 S: g2 m/ r) {
  38.         // MY_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, DATA_EEPROM_START_ADDR, write_data1);0 q& N# P3 p, z, y7 I
  39.         printf(" K1 150ms button!,flash write test\r\n");
    , Y* ]6 M1 [8 D" V% M
  40.         MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509,0X33333333);
    : u, o% @  r$ e
  41.         HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
    $ D% L( O; i0 K1 e/ S, o# K3 B9 h
  42.     }
复制代码

7 q2 [' C3 o$ C# ~9 I9 a( k5 J# N

因为地址位置如果有数据的话会擦除一页再写入,所以我们看了一下508页最后一个地址,和510页的第一个地址数据,对509页的数据进行了操作,结果发现不会改变508 和510的数据,509页的数据会全部清除!

$ h) ?3 V; p4 z% R8 F

微信图片_20230612172448.png

% y4 x5 r+ b' n

微信图片_20230612172355.png

) c0 K0 x1 ~" L# `

所以得出的结论显而易见,flash擦除按照一页一页擦除, 在L051上面一页为128bytes,而且擦除后数据全部为 0;


; E4 m/ p- P0 g, A; U2.4 读写EEPROM的后续问题

最近有一个项目,因为缺货 STM32L051R系列缺货,买了 STM32L071RBT6 替代:


0 Q3 Q. s% `' ?% @

微信图片_20230612172339.png


6 s; Q7 W; R* y0 ~

看了一下地址,其实我们上面所有的测试代码基本都是可以直接用的,代码直接用STM32L051的代码也是可以的,实际项目中,还是使用EEPROM比较方便,所以在使用EEPROM的时候,发现了一个问题(并不是换了芯片的问题),还是数据读取和写入的问题。

5 a# ]; Y. u$ P2 a6 f
2.4.1 问题的出现和解决

下面程序的硬件平台是 STM32L071RBT6,首先在程序中,有一个写ID的函数:

  k+ q2 R/ n0 f3 Q

微信图片_20230612172215.png


8 s9 Y& E, n4 x' q4 W  `

写入IO是通过字节的方式 byte  写入的,写了6个字节(蓝牙设备的ID)。

然后最初读取的函数用的是:


" \4 L3 Y5 z0 k& F3 f

微信图片_20230612172212.png


# J2 f: e  z) Y) z3 ?* s. s: h

使用这个读ID的函数,问题就出来了。上图代码中,我读取ID使用的是半字读取,处理方式是把读到的半字前面8位给ID1, 后面8位给ID2, 但是测试中发现 数据读出来与想要的相反,什么意思呢,看下面的测试说明:

上电打印出EEPROM中读取的一个地址的,每个ID(每个ID是uint8_t类型)的数据,在代码开始定义了测试数据:

  1. BlueID_STRUCT test;; L) h: r$ _" ]
  2. ...8 L5 [7 r5 q" R  A
  3. /*
    4 a" k8 ^7 d6 k  C# K; A. e6 j
  4. CHBlueID_STRUCT Flash_PowerOn_BlueCheck(), E, H1 F% J# t1 g1 l' R8 c
  5. {
    - u5 q. s% ]+ k6 Z
  6. , q" U3 p! t: o* J
  7. CHBlueID_STRUCT PowerOn_ID;
    6 G5 Z* ^# u( b( o# u. s' J
  8. PowerOn_ID.CH1ID = FLASH_blueIDRead(CH1_ID_ADDR);7 O" L# k9 ]" f; O
  9. PowerOn_ID.CH2ID = FLASH_blueIDRead(CH2_ID_ADDR);
      Y& b! R5 x# F( S
  10. PowerOn_ID.CH3ID = FLASH_blueIDRead(CH3_ID_ADDR);
    ) }. s5 C; U% B
  11. PowerOn_ID.CH4ID = FLASH_blueIDRead(CH4_ID_ADDR);' t. m8 r; T+ l9 y
  12. PowerOn_ID.CH5ID = FLASH_blueIDRead(CH5_ID_ADDR);4 ~9 k9 r% `/ Q& j. _, @7 r& {# t
  13. PowerOn_ID.CH6ID = FLASH_blueIDRead(CH6_ID_ADDR);8 d5 f9 M( R" A7 Z$ W  S
  14. PowerOn_ID.CH7ID = FLASH_blueIDRead(CH7_ID_ADDR);
    ; n; L( S: [7 {+ P& E* W! }- E
  15. PowerOn_ID.CH8ID = FLASH_blueIDRead(CH8_ID_ADDR);  |. w8 p" t8 e& ~+ l4 A& m
  16. PowerOn_ID.CH9ID = FLASH_blueIDRead(CH9_ID_ADDR);
    1 F- z& L3 t/ [, H2 `% O
  17. PowerOn_ID.CH10ID = FLASH_blueIDRead(CH10_ID_ADDR);
    7 m. J# J: b$ I, s$ Z
  18.   U& R! S8 w  L1 W# Q# d' V. l
  19. return PowerOn_ID;
    ; F' x/ D) z; A
  20. }! ?; p( A/ R% a& b+ K7 W0 _2 `( S, w
  21. */
    5 m; |6 J/ {  @4 F$ S! V1 I. ?! N
  22. BlueChipID = Flash_PowerOn_BlueCheck(); //上电先把ID读出来做比较
    : c, N9 G5 }% h( Q; n# i3 c
  23. //打印一个出来测试,看结果
    + m% H- ~, `/ w" Q$ P
  24. 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);
    ; A2 C# M% T: s) K! p) {" G6 G$ Q

  25. ) p4 z1 C. `5 r$ g* t0 a" v! a" ?" q
  26. test.ID1= 0XFF; //结构体每个元素是 uint8_t 类型: v' F* i2 r& O' r: ~! W* J
  27. test.ID2= 0XEE;$ t1 P/ |$ t5 o/ A
  28. test.ID3= 0XDD;# o4 a# ?6 e4 B7 K
  29. test.ID4= 0XCC;
      A" O% L" A5 U, B+ ?2 Y7 W
  30. test.ID5= 0XBB;
    6 M3 Y# |( Y$ D7 I9 q
  31. test.ID6= 0XAA;
    8 ]; b& W2 x0 A# ^/ I& q/ \- c

  32. # Y: I! k; d7 W2 b6 q: b
  33. while (1){...}
复制代码

/ d2 k8 n9 _0 q3 r+ |# n

在程序中通过操作,写入测试数据,调用上面提到过的写ID的函数,写ID的函数是每个字节每个字节写的:

* q  Z  J$ L$ J: P

微信图片_20230612172209.png


5 M: r8 s( v5 N# a  i  d1 c

按我们希望的结果来说,读出来按照顺序打印,应该是:0xff,0xee,0xdd,0xcc,0xbb,0xaa 。测试实际上是:


. m$ s1 F8 `  S: F* i( S* c

微信图片_20230612172207.png


  e9 D4 _/ x* v, j$ k3 V3 G

为了确实是读的问题还是写的问题,添加了读字节的函数:


7 l; w2 _; k# C. d7 h* Y) w+ X

微信图片_20230612172058.png

2 l& w9 ^" C, X: ^7 o' x/ o

微信图片_20230612172050.png

, N% M  @6 P8 w' M  ~" Z

打印的结果:


3 ~& y/ g3 O# B! S

微信图片_20230612172039.png


% Y/ V' C) ?% o! Y: v

说明确实是ID的读取函数出的问题,是因为使用的半字读取,问题处理不麻烦,我们把读取函数修改一下:

  1. BlueID_STRUCT FLASH_blueIDRead(uint32_t address)
    . E$ x+ Q" p+ E5 ]0 i* z
  2. {
    ' t* D' s7 H8 Q) F8 _
  3. BlueID_STRUCT u48id;
    1 H. M% n5 O3 z- Z( {$ B
  4.   // uint16_t temp1,temp2,temp3;  /**(__IO uint16_t*)address; */* h& m6 G) J: [7 \* k" |4 U7 \' f
  5.   // temp1=*(__IO uint16_t*)address; 5 ]" w4 ~1 {# Q# Y: F5 ?
  6. // u48id.ID1 = (uint8_t)(temp1>>8);; X/ p9 I1 O' [* w
  7. // u48id.ID2 = (uint8_t)(temp1&0X00FF);
    ) w6 _: x- L; ?# X8 }% J
  8.   // temp2=*(__IO uint16_t*)(address+2);* z+ k* i% m. L& c" f% M1 q
  9. // u48id.ID3 = (uint8_t)(temp2>>8);
    $ P2 m: }8 d' T; v  e, ?3 Y
  10. // u48id.ID4 = (uint8_t)(temp2&0X00FF);
    2 y! U& T8 v' A' ^( y
  11. // temp3=*(__IO uint16_t*)(address+4);
    ) s+ ?1 @1 E% ^& g. e/ O4 B+ B* B
  12. // u48id.ID5 = (uint8_t)(temp3>>8);
      B# ^+ k$ j" q; I6 {" @3 v' U
  13. // u48id.ID6 = (uint8_t)(temp3&0X00FF); + M6 n, _# N9 z4 }' t# A/ p" |, s
  14. : [3 q# }$ h+ W# Y+ s' E
  15.   u48id.ID1 =  FLASH_Readbyte(address);
    : y! p. L! }/ x, G, g
  16.   u48id.ID2 =  FLASH_Readbyte(address+1);; J1 D7 r) D+ F, U- J* G9 g
  17.   u48id.ID3 =  FLASH_Readbyte(address+2);
    2 P+ ^% V/ Q& J: h; y
  18.   u48id.ID4 =  FLASH_Readbyte(address+3);4 J7 j4 V" C' M& e
  19.   u48id.ID5 =  FLASH_Readbyte(address+4);1 p4 F5 ]) V$ H/ ^! T3 t& q- |
  20.   u48id.ID6 =  FLASH_Readbyte(address+5);
    + }+ u# ~/ _  r% Z1 |; n1 ]& [
  21. : j6 e% K1 c1 Z: l, f" h7 D
  22.   return u48id;
    ' _  ], H' n6 ?! n
  23. }
复制代码

0 ?3 C  C4 t: p* Q5 m- b# U

测试结果才正常了,如下图:

! c+ ?+ e" d: }* G' Y% Y

微信图片_20230612172035.png


+ g, V9 z) Z4 B+ _1 F) J3 j& _2.4.2 问题的分析(大小端模式数据格式)

出现上面的问题,其实是和我们经常说的大端模式和小端模式有关的。

STM32使用的是小端模式,简单介绍一下大端模式小端模式数据存放的方式,如下图:


, }. J. B+ @$ W0 ]4 I

微信图片_20230612172032.png

1 b5 c6 i' @/ n! D7 L' [! P) d

知道上面的知识,我们在开始的读取函数中是直接读取的半字(__IO uint16_t*)address; ,但是我们写入的时候是一个字节一个字节写入,上面的例子所以我们内存中的数据实际上如下图所示:


! N( X1 ~  y9 d8 g5 ]

微信图片_20230612172030.png


; a! a0 w+ g2 ?* q

使用(__IO uint16_t*)address; 去读取,读取出来的数值一个uint16_t类型的数值:

假设是 a,a=*(__IO uint16_t*)addr; 会有 a = 0xEEFF; 所以高8位变成了 0xEE。OK!解释清楚!问题解决!

至此,我们基本上把STM32L051 的EEPROM 和Flash 功能都测试过了,把工程中需要用到的功能做了测试,也学到了一些新的知识,还是实践出结论啊,当初没有自己测试之前看了网上的有关类似的介绍,还是很多误解,这下全部清晰了。


% A  Y( Y- J/ J5 i4 Q2.4.3 STM32L071RBT6 EEPROM读写全字问题
" ?9 E- g1 ~* O0 R' I
读问题的出现

前面其实测试过,读取全字是可以的,直接使用return *(__IO uint32_t*)address;:便可以读到该地址的全字:

  1. uint32_t  FLASH_ReadWord(uint32_t address)
    / f3 J& w  M, F( N
  2. {0 p6 r* H) n: y& A4 o
  3.   return *(__IO uint32_t*)address;; i! a7 k* \8 g- f
  4. }
复制代码
0 u2 A# I! e' R& J- k1 E' n

所以在后面使用过程中,有这么一个函数:


) K+ U* ]: w5 n5 q6 ^. w

微信图片_20230612172026.png

; W- C& i3 t. k  Q- V: g( R# W

一开始还真的不知道哪里出了问题?折腾了好一阵才发现调用函数读取ID时就会卡死。


% t& P$ p! j7 q/ _( o

问题的解决:

因为还有另外一个蓝牙版本的产品,如果是蓝牙设备的ID,因为蓝牙设备的ID是6位的,所以当时读取蓝牙的ID的时候使用的是(蓝牙版本是没问题的):

$ h6 s) T" n5 L  @/ B

微信图片_20230612172019.png


0 k+ m/ E" ]0 t. }9 ~/ |& F- ^

其实折腾了好一会儿,后来想着蓝牙是读一个字节,要不要试着把 全字 分为 4个字节,单独读取试一试?

于是学蓝牙把程序分为4个字节读取:


' z) I* C1 N3 [! Q* D5 `

微信图片_20230612172012.png


# Q: `7 j) t9 M7 v4 z- h& @

代码也放一下,方便复制:

  1. u32 FLASH_ReadEnoceanID(uint32_t address)6 p( D2 m$ x4 r2 A* |7 Y, O
  2. {
    " R8 ^  G# h0 f; n. {+ k4 f% L
  3.   u32 keepid;
    1 n8 B% P4 u# D6 o0 F, w/ a
  4.   u8 i;
    6 i' b+ Z  L# d' |7 T& @+ [, G
  5.   keepid = FLASH_Readbyte(address);
    5 ^5 t  \( L( ?- N* H+ h" ?* ?
  6.   i = FLASH_Readbyte(address + 1);
    3 i' e5 u4 O9 f
  7.   keepid = (i<<8)|keepid;" d1 l5 `; y2 M% p+ ^, X& o
  8.   i = FLASH_Readbyte(address + 2);( g7 _0 J4 l  p' ^+ r0 G
  9.   keepid = (i<<16)|keepid;1 r7 R3 D: |5 ]
  10.   i = FLASH_Readbyte(address + 3);
    " D2 x3 N# t# z
  11.   keepid = (i<<24)|keepid;; K5 g. O0 q! l3 R
  12.   return keepid;4 M- k& b- n  ?  T. M
  13. }. K, h) w9 q8 t6 Z5 I

  14. # a. c- q7 E1 H5 P
  15. CHID_STRUCT Flash_PowerOn_Check()
    $ X  g, m2 D* |! u7 V& _+ o% B
  16. {& a$ x+ k! z& o$ U" Y7 Z$ Z7 r
  17. 1 v7 r2 s1 z) X9 u3 [! p
  18. CHID_STRUCT PowerOn_ID;
    ' {$ G# @8 u, z8 w# }" ^7 z8 P
  19. PowerOn_ID.CH1ID = FLASH_ReadEnoceanID(CH1_ID_ADDR);
    6 P. f0 W# R5 Y4 x+ P2 P4 w
  20. PowerOn_ID.CH2ID = FLASH_ReadEnoceanID(CH2_ID_ADDR);
    # N+ z# X6 S$ A% a, V# I2 {
  21. PowerOn_ID.CH3ID = FLASH_ReadEnoceanID(CH3_ID_ADDR);  w* g. k1 \' ^
  22. PowerOn_ID.CH4ID = FLASH_ReadEnoceanID(CH4_ID_ADDR);" |: C% t- n+ B6 F6 c  O
  23. PowerOn_ID.CH5ID = FLASH_ReadEnoceanID(CH5_ID_ADDR);
    1 Y  b" ], V3 d
  24. PowerOn_ID.CH6ID = FLASH_ReadEnoceanID(CH6_ID_ADDR);
    4 \  i& K$ |) ^* z9 O" W
  25. PowerOn_ID.CH7ID = FLASH_ReadEnoceanID(CH7_ID_ADDR);. v. V* P+ x+ Y) W. }
  26. PowerOn_ID.CH8ID = FLASH_ReadEnoceanID(CH8_ID_ADDR);
    4 l6 h" r( V6 V: k2 Z+ O# ^7 r
  27. PowerOn_ID.CH9ID = FLASH_ReadEnoceanID(CH9_ID_ADDR);5 t  s. V# k& \: L! o
  28. PowerOn_ID.CH10ID = FLASH_ReadEnoceanID(CH10_ID_ADDR);
    ' N' G) }2 N: |: E3 u& P
  29. % |( h0 F3 U# f7 r7 J& M
  30. return PowerOn_ID;: Q8 d( z: G" M
  31. }
复制代码
7 p. v# q4 _! v

测试一下,发现就好了,至于原因,目前还不知道为什么……(最后问题解决有说明,内存字节对齐问题)


/ ?* b. R: ?, ~5 ~! ~6 A* N6 s写问题的出现

本来以为解决了上面问题OK了,可是后面测试的时候发现写的时候也有问题:

一直用的写全字函数为:

  1. FLASH_Status  FLASH_WriteWord(uint32_t Address, uint32_t Data)
    : I0 u* m2 x) `, Z& x5 V7 D; T
  2. {# l/ K0 k& A" Q+ A
  3. $ x" p7 A: @; T
  4. FLASH_Status i = FLASH_COMPLETE;( r& g' P8 ^8 W% z: r  S

  5. ; Y& c9 z% m9 n" F+ N
  6. HAL_FLASHEx_DATAEEPROM_Unlock();  7 w( G, K' r& L4 q  Z: _8 F
  7.   while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, Address, Data) != HAL_OK);5 t! F; M7 ?9 \. r
  8. HAL_FLASHEx_DATAEEPROM_Lock();
    % h3 C: {/ H- z! P& F
  9. # W+ B, @3 ]/ e$ S
  10. return i;! x* o: g4 H  y' e7 \3 _
  11. }
复制代码

1 R5 c9 K: l% Y  g0 x

在程序中会调用此函数进行 ID 保存:


; O+ E& M+ |! ?! V# n# V

微信图片_20230612172009.png


; T& l; k- q3 j7 Q: t) `% ~

但是使用时候发现:


: N7 F* G3 }) s6 o; F8 d" S

微信图片_20230612172006.png


- C$ v; l  {  g) S

问题的解决:

其实这个问题也莫名其妙,真是说不出来为什么,估计是得详细的查看数据手册,但是还是 因为 在蓝牙的版本上面没有此类问题:

. Q. w1 A7 k* U7 d& R' u3 m

微信图片_20230612172003.png

' k1 G3 ]+ D0 N% l0 _

所以这里还是尝试改成 以 字节 方式写入:

  1. FLASH_Status  FLASH_WriteWord(uint32_t Address, uint32_t Data)
    " Q0 e. b% n4 Y& [6 C3 J0 t6 X
  2. {
    * G6 w- ~- X- e* Y

  3. . H4 s& I7 ^" V) H4 w% q
  4.   FLASH_Status state = FLASH_COMPLETE;, H0 j# o7 c# D: \& w/ u
  5.   u8 i = 0;* w. c& h2 k% S' U& r' E- ?
  6.   u8 writedata[6]={0};
    9 l: I  H  @5 \8 i3 v

  7. ! `+ v- v$ {  N  T. Z. S$ T
  8.   writedata[0] = (u8)Data;" a9 V# m; y( U- ]2 F2 X' s
  9.   writedata[1] = (u8)(Data>>8);9 K' A& @7 W+ o. b8 @) e  ?- v& \
  10.   writedata[2] = (u8)(Data>>16);5 n" ]& \) F3 G
  11.   writedata[3] = (u8)(Data>>24);+ T4 a: \/ E' A- D: ~% D

  12. # ^( U: b0 U5 x/ x- W3 D
  13. HAL_FLASHEx_DATAEEPROM_Unlock();  7 O) Q3 v5 H' ^* B) H7 K, M
  14.   for(i=0; i<4; i++){& a1 g! v; \& \3 u3 R* F
  15.     while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, Address + i, writedata[i]) != HAL_OK);* Q; Y' s6 Z1 }7 d
  16.   }0 t/ S+ R0 Q4 O1 j) n! \0 e
  17. HAL_FLASHEx_DATAEEPROM_Lock();
    % P, I. n: F& j9 _& Z; ]  i- R

  18. 7 D9 [& Q, t( p$ _# R
  19. return state;& ?0 P. A8 t2 K) w: Q4 v
  20. }
复制代码

使用此种方式写入,就不会出现问题!

其实也可以尝试修改地址,使得成为 4 的倍数,可能也不会出问题,这里就不测试了(最后问题解决有还是测试了,内存字节对齐问题)

2.4.3小结使用的芯片为  STM32L071RBT6

% T. x/ m* G9 `! t. {6 ]. b
最后问题的解决

先直接说结论,就是EEPROM地址定义的问题,应该是4字节对齐(4的整数倍),读取全字的操作才能正常!

上面 STM32L071RBT6 EEPROM 读写全字问题的关键在于,存储地址的定义上,如上面一张图所示:(我在EEPROM区域定义了10个地址,用来存放无线设备的ID数据,如果是蓝牙芯片,那么ID为6个字节,如果是Enocean芯片,那么ID为4个字节,为了保持代码的统一,我在使用保存4个字节的ID数据的地址定义时候沿用的是蓝牙的 EEPROM区域定义)


$ \- s2 |# z4 }* @! `  ^( t

微信图片_20230612172000.png

) s" w; y& \/ t) G

那么正如我图中猜想的一样,蓝牙的 ID 6个字节,我是都是通过一个字节一个字节操作,组合起来进行的,所以一切正常。但是对于 4个字节的 ID ,期初是用的 全字的方式,就出问题了,换成一个字节一个字节的操作,看上去是解决问题了。

但是实际上多了一些隐藏问题,暂时也说不清楚,在产品使用的时候,读写ID还是会有莫名其妙的问题,最终还是对当初的这个猜想,地址是不是也需要4字节对齐?进行了修改测试,于是乎,对于 4 个字节 ID的处理,地址改成:


. }3 N6 c1 w  a& o# b- r" e  g

微信图片_20230612171954.png


$ E& k3 V6 I# l* Y1 X5 d

把地址修改成 4  的倍数以后,上面的读取全字的两个函数便可以正常使用,而不会出上面莫名其妙的问题。


; H  n9 M- w5 @; a0 [9 q" ^# o
收藏 评论0 发布时间:2023-6-12 17:30

举报

0个回答
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版