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

基于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的图:


+ C% W" ]: Z& B1 T

微信图片_20230612172451.png

: f! @4 }9 b% t+ @" z! ]- x$ K3 \

微信图片_20230612172454.png

# ^" D. E6 m$ ?. c" m; g/ _4 y

然后来看个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       :


. A( }* k0 I5 M& Y& Y# Z, p

  1. #define FLASH_SIZE                (uint32_t)((*((uint32_t *)FLASHSIZE_BASE)&0xFFFF) * 1024U)
    : i3 ^# ?% n  K3 I. J
  2. #define FLASH_PAGE_SIZE           ((uint32_t)128U)  /*!< FLASH Page Size in bytes */
复制代码
6 L2 ^) F) c) o0 X0 l* b4 k

对于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意为“字节”,是计算机文件大小的基本计算单位;

3 h( B, r: W4 I% {+ s
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,因为怕有时候数据不够放……


1 d6 U+ t$ G6 Y1 o2.1 读取函数
  1. //读取指定地址的半字(16位数据)
    ; @" c9 [( p+ j+ o( s
  2. uint16_t FLASH_ReadHalfWord(uint32_t address)
    ' }6 ]) P, Q' w0 M& f! j
  3. {5 X4 a  Y' |& M+ R) f$ R6 l+ s
  4.   return *(__IO uint16_t*)address; # j/ G, C$ T+ w8 C& q
  5. }% L# V# u% @, E- R# ?6 Z! Q& C
  6.   [; c; l+ V+ N2 y' f" p
  7. //读取指定地址的全字(32位数据)
    5 z& n2 Q, Y: s( b8 _* x
  8. uint32_t FLASH_ReadWord(uint32_t address)- |# x$ _3 ?3 _1 \$ O4 J  j
  9. {. F$ H; V8 h  @
  10.   return *(__IO uint32_t*)address;
    : _. M/ H" ?7 Y, _& r/ a7 \
  11. }
复制代码

) ?8 C% v) z* W+ k4 x
& f; r' p# ?  O% M; q

简单测试一下:

  1. u32 read_data1=0XFFFFFFFF;: t6 c$ ?" _$ B
  2. u32 read_data2=0XFFFFFFFF;8 T( b' P$ v" \5 |7 V1 a
  3. ...
    - ~; m% i; |# j& W) r
  4. read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);2 Z+ q. j& z$ F3 B* O
  5. printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);) |; l: g8 P! l2 |
  6. read_data2 = FLASH_ReadWord(DATA_EEPROM_START_ADDR + EEPROM_PAGE_SIZE);
    ' r( C" L/ G& R! o  [8 s
  7. printf("the EEPROM sceond page test data is: 0x %x \r\n",read_data2);
复制代码
8 |$ L$ v4 C. D. z9 x) x

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


& m% o& L% y2 y9 n' N2.2   EEPROM写函数

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

  1. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Unlock(void);9 T; @9 I7 @: W
  2. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Lock(void);% D$ b) N# G- i4 A7 K

  3. 6 s) n( S$ j7 _( j
  4. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Erase(uint32_t Address);+ A& Q8 ]! z) e' v$ ?
  5. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data);
复制代码

/ b; l: j9 y( J3 t

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

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

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

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

  1. HAL_FLASHEx_DATAEEPROM_Unlock();
    * ~8 p, s5 h& `8 \
  2. HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, DATA_EEPROM_START_ADDR, write_data1);) W- v0 t& u" ?) a: L; W$ ~
  3. HAL_FLASHEx_DATAEEPROM_Lock();
    ) J+ A1 }0 U, L  s# S2 [) U
  4. ...
    & i! O" j# M9 X6 W( a( }
  5. if(btn_getState(&K1_BUTTON_150mS) == BTN_EDGE2){( ~# M5 j$ {/ i' X/ x, R- g
  6.         printf(" K1 150ms button!,EEPROM_Erase test\r\n");
    + R! v" h  A# h' R1 E: I
  7.         HAL_FLASHEx_DATAEEPROM_Unlock();
      c/ I4 ^# A$ V( ]9 Z7 |
  8.         HAL_FLASHEx_DATAEEPROM_Erase(DATA_EEPROM_START_ADDR+4);
    # ]3 h3 S3 L8 p3 n, u) y( t
  9.         HAL_FLASHEx_DATAEEPROM_Lock();
    7 c) m2 i( e' Q0 U; g
  10.         HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);+ m  P9 R& u: s7 T7 ^
  11.     }8 e/ G0 [# k3 p4 M2 V
  12. ...
    3 V* q% Y0 U! ^" V8 J! I3 ~
  13. if(btn_getState(&K2_BUTTON_150mS) == BTN_EDGE2){) \- s& ~5 F2 T9 \9 d! a2 I2 U& N
  14.         printf(" K2 150ms button!EEPROM_read test\r\n");
    5 n1 V+ G9 o' c9 V: C# C  G9 \. F5 |
  15.         read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);  ?1 p7 R7 E  j2 ^8 f0 o. T: e
  16.         printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);) Q+ j( N: C4 f! F, \# \- q, E
  17.     }
复制代码
3 C( Y3 U8 B& ]! e: r& E

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

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


4 Y4 k  ?% q3 y写入问题说明修改

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();
    9 o0 D( N3 |2 U4 L$ n& {
  2.   HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, DATA_EEPROM_START_ADDR, 0X88);( N7 p9 B) p  {8 O6 U! Z7 H
  3.   HAL_FLASHEx_DATAEEPROM_Lock();
      a. X5 v$ g' p* b
  4.   read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);* I+ t% b- \& o" u8 k
  5.   printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);
复制代码
' ^, n$ E6 v, ^' Q$ A! p' B% p- G

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

  1. void MY_DATAEEPROM_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data)   4 `( K# \% k& ~+ l
  2. {% G, _* Q! w7 K7 b. E* G
  3. HAL_FLASHEx_DATAEEPROM_Unlock();        
    * z4 E2 [; }2 \
  4.     HAL_FLASHEx_DATAEEPROM_Program(TypeProgram, Address, Data);
      v  ~& g% X1 g* o: ^
  5. HAL_FLASHEx_DATAEEPROM_Lock();
    5 I: b$ k$ S7 A- r1 Z
  6. }
复制代码

4 d+ c) }- N4 T/ k* Z

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

# w/ T5 d5 }5 c7 H3 B1 ?
2.3   Flash写函数

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

  1. /* IO operation functions *****************************************************/0 h: P/ O+ N; V
  2. HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data);
    . k( e2 d  a0 G
  3. HAL_StatusTypeDef HAL_FLASH_Program_IT(uint32_t TypeProgram, uint32_t Address, uint32_t Data);
    9 l0 y9 b) A# u

  4. 8 I" C; w+ l; X9 Z& U+ i* t: H. c
  5. /* FLASH IRQ handler function */
    / G7 X1 M$ a: ^; ~% L  W- F
  6. void       HAL_FLASH_IRQHandler(void);0 u5 {) I  h5 k& o0 C" ]$ e- P
  7. /* Callbacks in non blocking modes */
    6 G* c( W2 @6 X8 U+ l3 x
  8. void       HAL_FLASH_EndOfOperationCallback(uint32_t ReturnValue);
    * I" l" `3 P8 l
  9. void       HAL_FLASH_OperationErrorCallback(uint32_t ReturnValue);- P# |; ?7 V, L: S  W0 [
  10. ! B* G+ x! {/ _* `
  11. /**
    ) J0 Y0 p7 D6 G' k- O! C$ m- f
  12.   * @}
    * v" Z1 z) \" {4 l) p
  13.   */
    ( l8 q3 ~( U- z1 P* e% I

  14. 8 _, I; Q( h5 F
  15. /** @addtogroup FLASH_Exported_Functions_Group2
    * p! a2 T9 |* f& }( L" }
  16.   * @{
    $ a, K% y: v  w4 b. ?3 a
  17.   */
    / p: r, }* e3 _8 Y0 l0 m
  18. /* Peripheral Control functions ***********************************************/' J. W+ U: Z) U: P
  19. HAL_StatusTypeDef HAL_FLASH_Unlock(void);
    . p6 D) W. }; O; N
  20. HAL_StatusTypeDef HAL_FLASH_Lock(void);/ @( ^, h. M. C
  21. HAL_StatusTypeDef HAL_FLASH_OB_Unlock(void);: w7 ~9 F6 V4 t, l# f7 k" g; t
  22. HAL_StatusTypeDef HAL_FLASH_OB_Lock(void);
    % ~4 r3 H$ r1 l: K9 C4 S1 a1 g
  23. HAL_StatusTypeDef HAL_FLASH_OB_Launch(void);
复制代码
" O8 S$ |" D- m# O

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


% T3 R$ I, }, d) b

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


$ b# h. p9 C) I. r
  1. HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data)3 w/ t8 R$ _5 z; a
  2. {
    & k9 d0 S( w5 A" L6 a
  3.   HAL_StatusTypeDef status = HAL_ERROR;3 s  z8 p8 O" z
  4.   4 w: _: G% n0 E  T
  5.   /* Process Locked */
    " M- _) |) B1 k5 q' |
  6.   __HAL_LOCK(&pFlash);9 r# F+ ^. n0 z- }$ k
  7. ! ], w: p( `0 L2 w, l" T' _  [4 R
  8.   /* Check the parameters */
    ( d3 D7 }- q% w  @$ z
  9.   assert_param(IS_FLASH_TYPEPROGRAM(TypeProgram));
    / Q9 d) S7 N# F6 ], b# M
  10.   assert_param(IS_FLASH_PROGRAM_ADDRESS(Address));
    ) v* D( C. I0 ?2 S$ D7 G
  11. * F9 F0 g5 Y. b' p5 _
  12.   /* Wait for last operation to be completed */
    3 _5 f( y& U: a9 F1 ]+ u
  13.   status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);" a( K) W7 t$ \
  14.   : V. n! a9 F6 F
  15.   if(status == HAL_OK)6 S1 _. P& S$ N. b
  16.   {$ h: x' ~" ?2 ~+ z! V
  17.     /* Clean the error context */1 A- o6 Y- `2 h( Y& z6 m
  18.     pFlash.ErrorCode = HAL_FLASH_ERROR_NONE;8 E/ ~# R& l0 O
  19. 2 H- `$ R" E, \
  20.     /*Program word (32-bit) at a specified address.*/! I8 O: [' _! N4 y. }- L7 ^& ^
  21.     *(__IO uint32_t *)Address = Data;( Z' L- }5 A7 w5 U( Q) x& s
  22. ( v+ B2 C. e/ W3 @& @, A- K" K
  23.     /* Wait for last operation to be completed */
    2 n7 c5 J3 m  D
  24.     status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
      ?- ]% ?& i% ?3 [4 B1 r
  25.   }
    - F- Y; Y" j$ c: s# _

  26. + J" f8 j( y0 ~/ i) W" {
  27.   /* Process Unlocked */
    7 l% V+ N2 s$ J# ^: \/ Z
  28.   __HAL_UNLOCK(&pFlash);, d/ M9 o3 B" e$ J! @8 Y
  29. $ a% ~0 l3 \4 ~9 s# }
  30.   return status;
    # r8 z3 [9 C4 y  ^8 _' M+ h; i
  31. }
    ( f- N  e5 W/ D# \4 M5 U& R1 Z
复制代码
0 o4 M! e5 K. |, ]6 u& c; m+ w- p* M

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

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

  1. #define ADDR_FLASH_PAGE_505      0X08000000 + 128*504   //
    / v6 i7 T: Y0 V" ?  k, {  L
  2. #define ADDR_FLASH_PAGE_506      0X08000000 + 128*505   //: c9 t1 R- A  o! q
  3. #define ADDR_FLASH_PAGE_507      0X08000000 + 128*506   //$ Q0 t; }1 |( F  O3 Y
  4. #define ADDR_FLASH_PAGE_508      0X08000000 + 128*507   //3 x) N2 G* p0 r, Y# p
  5. #define ADDR_FLASH_PAGE_509      0X08000000 + 128*508   //$ C; R' A. y6 F! y* m6 y  [
  6. #define ADDR_FLASH_PAGE_510      0X08000000 + 128*509   //
    : n4 h( W$ t. q; o
  7. #define ADDR_FLASH_PAGE_511      0X08000000 + 128*510   //3 a8 S4 o' C, i: N5 o
  8. #define ADDR_FLASH_PAGE_512      0X08000000 + 128*511   //最后一页
复制代码

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

  1. void MY_DATAFLASH_Program(uint32_t Address, uint32_t Data)   
    6 W( ]/ n$ {" ?2 _6 Y
  2. {
    + }0 z* j7 `8 W. T
  3. HAL_FLASH_Unlock();        
    * D; A! X0 u0 J' `/ q
  4.   if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){
    6 z  r% `- T; R$ ]6 [
  5.     printf("write data 0x%x OK\n", Data);' Q3 R- U/ e2 m/ A6 C
  6.   }
    : [. D# Q) q* W- _
  7.   else{
    6 \1 C0 A! h4 y7 b- {# M  i* K) g1 p
  8.     printf("failed!!!\n");
    $ B% O: q3 U- e7 L( d# G
  9.   }
    4 b' ]1 z* [& X- V+ O9 ~
  10. HAL_FLASH_Lock();
    0 x2 v& l& v" z" d' ~8 A
  11. }
复制代码

) c" A+ `& ^* l

测试一下;

  1.   MY_DATAFLASH_Program(ADDR_FLASH_PAGE_512,write_data2);9 N% s. z; @7 ?) e
  2.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_512);' r/ Y* a9 r7 D9 E" Z
  3.   printf("the ADDR_FLASH_PAGE_512 is: 0x %x \r\n",read_data1);
复制代码
- K' y- X0 }* D) Q2 H4 F

在没有写入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)   
    8 Y2 C+ ^% E4 p' f% u
  2. {  U; N( x$ X, b. Y
  3.   FLASH_EraseInitTypeDef EraseInitStruct;8 ^, Q* l- h4 w4 u# @7 N9 O
  4.   uint32_t checkdata;! f5 A) X1 e1 A( Y  `
  5.   uint32_t PAGEError = 0;4 P, _4 t! S8 ~( ?& C% r
  6.   checkdata = FLASH_ReadWord(Address);
    " n, k3 x) _$ A# p' i- [3 y- Q
  7. HAL_FLASH_Unlock();
    0 B7 T! X3 `. |) \
  8.   /*如果是0,直接写*/: b2 c3 ^  V+ Z% x9 g
  9.   if(checkdata == 0){       + e: X2 t7 b7 y
  10.     if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){$ f* C% Y1 ?& R4 y# K* ~3 a' D3 Q2 @! n
  11.       printf("write data 0x%x OK\n", Data);
    $ Z2 ^+ j# l- M0 H# J$ t4 X
  12.     }* D% E0 t% _9 X) b. U, ~: c% z
  13.     else{* A6 T# C0 }; i1 [: v, C( U- x! |
  14.       printf("failed!!!\n");
    , X' M: O* `& T6 [% q- y
  15.     }
    , N9 K3 P( i  m, P" y% X# G
  16.   }
    ) O. W$ K* w1 i0 H6 x% Z, u
  17.   /*否则擦除再写*/2 v( w6 G- B- z4 |; I) L; P! C
  18.   else{4 A& s9 x& ?5 V) {+ n4 ^
  19.      __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);8 H% n# G4 l% J. Z* O
  20. ) N/ o( t- c5 H* @( U  P: Z% J
  21.       EraseInitStruct.TypeErase   = FLASH_TYPEERASE_PAGES; // 刷除方式9 r6 p. P4 M5 h% N4 _
  22.    EraseInitStruct.PageAddress = Address; // 起始地址
    ; ?+ C- M: d+ S. U8 R4 r, c
  23.       EraseInitStruct.NbPages = 1;
    ( i7 B! N1 d$ _( {, A

  24. 3 J: J. {3 w: u7 W2 x5 u$ C
  25.       if (HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError) != HAL_OK)6 Y# p9 \% `, j6 h1 U% [
  26.       {
    - \: B: i9 F" k4 W
  27.         // 如果刷除错误
    - T5 Z* ^: J: a' b
  28.         printf("\r\n FLASH Erase Fail\r\n");" l& J  g5 P" ~+ g/ k6 o
  29.         printf("Fail Code:%d\r\n",HAL_FLASH_GetError());
    ; C" R2 r+ e5 T0 f0 a( S
  30.         printf("Fail Page:%d\r\n",PAGEError);& _9 Y5 G4 C6 E5 s2 z
  31.       }
    . E" A0 d& |6 o

  32. ( L  ?& j  m0 d1 i
  33.       if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){- \  _! F# ?' B# O% N
  34.       printf("write data 0x%x OK\n", Data);
    1 x% j. g4 z4 J- L
  35.       }- G8 B; k, T/ h
  36.       else{7 \+ Q" Q% |9 q* Z) U4 Q' g- _* X
  37.         printf("failed!!!\n");
    ( G( u2 H8 N8 z- w4 {5 Z
  38.       }! r2 g, u# |! q2 D5 s: y! D
  39. " |* O" S! S. ~* ]: U
  40.   }/ r+ q# A) n2 R/ B/ `% [
  41. HAL_FLASH_Lock();
    + B7 r. Q  j  m9 x6 p
  42. }
复制代码

$ ?8 D: k" b9 T# C

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

  1. u32 write_data1 = 0X12345678;
    , f, n8 N; e7 S
  2. u32 write_data2 = 0X87654321;
    / J, p: @# x( k9 _+ S* i6 t! x
  3. u32 write_data3 = 0XFFFFFFFF;8 s2 P0 ~$ B4 L. G! b; n

  4. $ N8 P6 \1 z9 F) D- h3 L
  5. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_508 + 124,0X508508FF);) H% `- C5 R% l( t) Z
  6. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509,write_data3);% C* F/ \5 A9 |( W; J$ K9 P: j$ k
  7. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509+4,write_data2);
    : m4 `- r9 G' {: u6 q! I
  8. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509+8,write_data1);4 v; [0 A# n. L' R7 q, @. X
  9. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_510,0X510510FF); - |! j3 A. @; S1 A
  10. 9 s, y1 d0 U/ H& G, ^$ T* U4 x
  11. read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_508 + 124);
    / d( q# S% d- H$ Z$ u' k
  12. printf("the ADDR_FLASH_PAGE_508 last is: 0x %x \r\n",read_data1);
    . U# x2 b$ r5 [0 M4 J
  13.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509);# T8 k" M% @: r, Z+ ]) o' }& V! Y
  14.   printf("the ADDR_FLASH_PAGE_509 1 is: 0x %x \r\n",read_data1);
    - T; O7 x4 G+ C; M  F
  15.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 4);
    - U2 r1 y  e/ t7 ?
  16.   printf("the ADDR_FLASH_PAGE_509 2 is: 0x %x \r\n",read_data1);
    ! Q; _! B" b& ]2 [( t
  17.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 8);
    + T# R( V7 d: u" b# k# ]
  18.   printf("the ADDR_FLASH_PAGE_509 3 is: 0x %x \r\n",read_data1);( t# q6 }5 k# J" d8 q- {- D/ A: c
  19.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_510);) Z) }5 i- r# p# a; n4 a4 e
  20.   printf("the ADDR_FLASH_PAGE_510 first is: 0x %x \r\n",read_data1);4 z( u* W! W9 {" b
  21. & c6 r* g6 j( }$ L, C0 h
  22. if(btn_getState(&K2_BUTTON_150mS) == BTN_EDGE2){) n, P1 S5 n  N
  23.         printf(" K2 150ms button!EEPROM_read test\r\n");& B: X* v: T' a6 v% w  i8 j
  24.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_508 + 124);% z  T2 t' W+ e) e2 x) T3 a$ |
  25.         printf("the ADDR_FLASH_PAGE_508 last is: 0x %x \r\n",read_data1);
    " D  T1 M+ W0 w9 F
  26.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509);
    ! Z; ~! c# k5 e" N  p2 ~8 i
  27.         printf("the ADDR_FLASH_PAGE_509 1 is: 0x %x \r\n",read_data1);
    0 z9 |* z; B- K1 O5 C  y3 A$ ?
  28.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 4);  T8 h- z3 _5 C' m3 U! t( b
  29.         printf("the ADDR_FLASH_PAGE_509 2 is: 0x %x \r\n",read_data1);
    3 W) A3 ~* E# t
  30.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 8);
    & `0 w4 [" l! I2 c
  31.         printf("the ADDR_FLASH_PAGE_509 3 is: 0x %x \r\n",read_data1);
    9 M- U$ m! c' O- m4 ?
  32.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_510);1 q- Q1 F& x' K; B" J- W9 a
  33.         printf("the ADDR_FLASH_PAGE_510 first is: 0x %x \r\n",read_data1);
    2 ^) T$ [: D1 t& i) G5 h

  34. 9 E+ ]& [  w+ o3 v8 }
  35.     }& H7 s9 p/ S$ x" n3 h) h3 A5 E
  36. if(btn_getState(&K1_BUTTON_150mS) == BTN_EDGE2){
    $ ~) A3 }" Y% q& @# I( m
  37.         // printf(" K1 150ms button!,EEPROM_Erase test\r\n");% n9 ^: t, V* w9 |0 A, t
  38.         // MY_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, DATA_EEPROM_START_ADDR, write_data1);
    3 A0 g* X/ i1 S/ c+ Q- h
  39.         printf(" K1 150ms button!,flash write test\r\n");! ~9 X0 L7 a3 C, M# J; t5 o
  40.         MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509,0X33333333);) h0 A4 U2 \, C
  41.         HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);( J( Y2 u# L" X  t4 `: p! G- w
  42.     }
复制代码

0 |( X% i& p  U1 i9 ~$ z( I9 b

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


2 V# r; E* B4 n; {. I

微信图片_20230612172448.png

# T. z' `1 q$ L1 A

微信图片_20230612172355.png


# C: l2 k( v2 O! x4 b6 m  ]

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

) `3 L* p/ a8 b$ d6 i
2.4 读写EEPROM的后续问题

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


/ U" E2 l2 w0 M0 O+ A. q2 I* X

微信图片_20230612172339.png


& u. c" }8 a; W: k: ]6 Q6 Z* p

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


0 {- O' V1 H8 m+ h  [0 j9 Y/ u" l; [1 t2.4.1 问题的出现和解决

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


& [2 [5 p6 m! z9 ]# f- N4 s

微信图片_20230612172215.png

) U$ u' L) ^8 E4 L6 V

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

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

* J& L+ V3 t# r. m

微信图片_20230612172212.png

. o& o) p; v, U

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

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

  1. BlueID_STRUCT test;+ _1 U+ ]1 @6 [, V
  2. ...
    # X) N/ ^: Q# i8 M9 V& u" m
  3. /*% E1 a! \7 n6 \1 r' J% b7 n" A9 i
  4. CHBlueID_STRUCT Flash_PowerOn_BlueCheck()8 H0 e% S* W1 r" q8 A# H# w$ t0 a2 w
  5. {
    7 S$ V, `5 z8 H) C' b. y. [
  6. 6 U# j+ b& ^; H+ x* f% w+ e
  7. CHBlueID_STRUCT PowerOn_ID;
    - F$ v" e9 p0 J# X# }3 u
  8. PowerOn_ID.CH1ID = FLASH_blueIDRead(CH1_ID_ADDR);
    5 L/ u5 ~- C9 j7 A3 l& V" \1 m4 v  ^
  9. PowerOn_ID.CH2ID = FLASH_blueIDRead(CH2_ID_ADDR);5 Z" y. u- g/ T/ O0 r, f' e$ q
  10. PowerOn_ID.CH3ID = FLASH_blueIDRead(CH3_ID_ADDR);# Z5 }  l( g, y  G
  11. PowerOn_ID.CH4ID = FLASH_blueIDRead(CH4_ID_ADDR);3 K2 N7 S5 d* ]
  12. PowerOn_ID.CH5ID = FLASH_blueIDRead(CH5_ID_ADDR);
    6 m* w3 H# O$ u
  13. PowerOn_ID.CH6ID = FLASH_blueIDRead(CH6_ID_ADDR);
    9 {/ L1 G2 ~! K' p% `
  14. PowerOn_ID.CH7ID = FLASH_blueIDRead(CH7_ID_ADDR);
    5 m* s; t1 m2 ^# M: P
  15. PowerOn_ID.CH8ID = FLASH_blueIDRead(CH8_ID_ADDR);
    ! Y, r1 I: B% t: K3 D( K; o& w( N: ~
  16. PowerOn_ID.CH9ID = FLASH_blueIDRead(CH9_ID_ADDR);
    9 q! U6 s) \7 d5 p  z4 r/ l) i
  17. PowerOn_ID.CH10ID = FLASH_blueIDRead(CH10_ID_ADDR);
      q: G: E2 V5 j

  18. 5 V: H3 l+ K) z% f
  19. return PowerOn_ID;
    ( t% l: i1 u. u' m$ {3 R$ a- C# o' {
  20. }
    + p+ T2 J! X* D$ s3 R1 T
  21. */
      f6 M7 l3 Q9 g# T6 o
  22. BlueChipID = Flash_PowerOn_BlueCheck(); //上电先把ID读出来做比较
    9 t/ Z" H6 }6 z( v# M3 W2 r
  23. //打印一个出来测试,看结果
    3 z) ]* N4 h, [
  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);
    6 R8 u0 n) R% b
  25. ; w: N; ?; n8 O) a5 n
  26. test.ID1= 0XFF; //结构体每个元素是 uint8_t 类型
    + [3 T6 e3 s" r! t& j3 {" f: N
  27. test.ID2= 0XEE;) `. T6 p/ @  k7 o4 p
  28. test.ID3= 0XDD;% y1 @& U- o6 q: o0 T# ?1 v
  29. test.ID4= 0XCC;/ F3 F# ^- j. n* \6 r# b
  30. test.ID5= 0XBB;! d: A. |+ H5 O0 f2 z9 v
  31. test.ID6= 0XAA;
    . L3 s2 b6 |5 m* T

  32. ( I  e+ x# i" N3 e, w2 s. ^
  33. while (1){...}
复制代码
* C( w7 I- m( o/ d# P# i

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


& W) t2 S2 _2 G5 i) p2 W1 j

微信图片_20230612172209.png

( |5 f5 r: L( `

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

* F) I# T1 ]& ^1 p! G

微信图片_20230612172207.png


) o( I& g6 E. x1 q) G

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


! P. y$ v+ s) N, Q

微信图片_20230612172058.png


1 A3 l) ?( ]( ^+ B1 K9 K- L& h8 c6 R

微信图片_20230612172050.png


- Q% o' ~! ~2 v! @

打印的结果:

$ u! L  L/ e% G9 j# g% G7 ?1 c

微信图片_20230612172039.png


! j/ z6 `2 c  m% y2 ~

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

  1. BlueID_STRUCT FLASH_blueIDRead(uint32_t address)
    7 X# ^8 o$ Z7 v8 ]
  2. {; O% \3 m/ M* h  k2 N
  3. BlueID_STRUCT u48id;" D! X5 ?* O: G2 l. n
  4.   // uint16_t temp1,temp2,temp3;  /**(__IO uint16_t*)address; */- Z6 t8 j% T6 y
  5.   // temp1=*(__IO uint16_t*)address;
      q! a6 {3 _3 Y* w
  6. // u48id.ID1 = (uint8_t)(temp1>>8);9 D- q! G: ^* U/ S' |0 Q
  7. // u48id.ID2 = (uint8_t)(temp1&0X00FF);
    . R; e" i4 T9 t: W
  8.   // temp2=*(__IO uint16_t*)(address+2);2 H  z% ^. M% A2 V8 p: M* i0 k* Q
  9. // u48id.ID3 = (uint8_t)(temp2>>8);
    , `' p* f0 O8 ^+ |
  10. // u48id.ID4 = (uint8_t)(temp2&0X00FF);
    9 ]; @8 U4 o5 _7 E
  11. // temp3=*(__IO uint16_t*)(address+4);0 k. N2 y5 s0 ?
  12. // u48id.ID5 = (uint8_t)(temp3>>8);$ L* |' x4 L$ G- n  j" V/ H
  13. // u48id.ID6 = (uint8_t)(temp3&0X00FF); 8 n  e- ^" H( y& m$ S) C0 R, y% C
  14. 1 v. D! s1 N( H( M
  15.   u48id.ID1 =  FLASH_Readbyte(address);
    ) h8 P7 z- e9 {! v- K' W
  16.   u48id.ID2 =  FLASH_Readbyte(address+1);9 o  V5 M- Y% T8 k7 Z: V
  17.   u48id.ID3 =  FLASH_Readbyte(address+2);
    - b7 ~: t4 Q+ s( ~
  18.   u48id.ID4 =  FLASH_Readbyte(address+3);
    ' o9 J: h- R% ]$ U
  19.   u48id.ID5 =  FLASH_Readbyte(address+4);
    . v6 X5 ~" h, W2 S! h, G3 e, v' J
  20.   u48id.ID6 =  FLASH_Readbyte(address+5);' l4 o3 L3 k& S; Y+ Y) ^

  21. & N6 N; R7 q2 e
  22.   return u48id;
    8 D7 I5 g* O( H0 c
  23. }
复制代码
. k2 G3 X8 z  C* E

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


& L" N4 c4 n# E. i

微信图片_20230612172035.png

. X) S( x4 y; D
2.4.2 问题的分析(大小端模式数据格式)

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

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


! l4 S- |$ h! K* s

微信图片_20230612172032.png

. y1 ~0 V/ I" L( ]

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


0 n4 e* {8 F( I& ]

微信图片_20230612172030.png

6 K' j  c0 U4 E! G

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

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

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

# q. |, _4 ~- z9 S* G2 I: b' a' ~4 Q
2.4.3 STM32L071RBT6 EEPROM读写全字问题

/ H; _$ t& V2 u8 x读问题的出现

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

  1. uint32_t  FLASH_ReadWord(uint32_t address)
    5 N5 e* t2 l# M; _1 w1 }' |
  2. {" b0 ?; ?' x1 S" H0 _  I
  3.   return *(__IO uint32_t*)address;
    0 E/ z, q* u4 n
  4. }
复制代码
7 `2 `& E; Q+ o8 Q0 H+ m

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

$ ]( \% M9 b# _& v2 S

微信图片_20230612172026.png

" A% U2 a/ A5 r% a0 ]& d) y

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


; U& j- u5 X9 O3 _

问题的解决:

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

) D' f0 t! A" o, r: j) j

微信图片_20230612172019.png

9 n3 |  B2 P: b2 }# H+ W% |* M/ J

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

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


- _4 k/ u/ |, M

微信图片_20230612172012.png


3 J! Z' y: T1 P/ Y

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

  1. u32 FLASH_ReadEnoceanID(uint32_t address): L( `; [* \& M. v* X
  2. {
    ! ]$ n, y, }0 a  b/ T" R% R* u' r
  3.   u32 keepid;
    ' F, L& Q; n, Y: R
  4.   u8 i;- e5 X2 |* V0 `- v2 m- f
  5.   keepid = FLASH_Readbyte(address);
    ( z3 A. }1 q) n" {$ t, Q5 Q
  6.   i = FLASH_Readbyte(address + 1);+ s* [& s& r* Z
  7.   keepid = (i<<8)|keepid;
    & f, ^3 C4 Z5 {
  8.   i = FLASH_Readbyte(address + 2);- d) _" k, s7 j' b4 \& `3 o3 g
  9.   keepid = (i<<16)|keepid;8 o6 I0 j0 r6 q% ~2 `) r3 @
  10.   i = FLASH_Readbyte(address + 3);
    4 F: }; |  {' _. |  O6 L- y
  11.   keepid = (i<<24)|keepid;+ Y: E- W) ?, a/ j5 _. D& c: T
  12.   return keepid;% ]1 p2 d0 P; B
  13. }
    " {; `+ e$ Y' u" ^! c4 f; X9 c2 H

  14. , o  W2 L/ l& A9 Q5 d
  15. CHID_STRUCT Flash_PowerOn_Check()  G  ~4 z0 ^4 s
  16. {; K  `, `5 g" P$ J4 x3 _

  17. : r+ y  n/ l& E; G/ }
  18. CHID_STRUCT PowerOn_ID;
    " V+ i" @& d6 I( Y& S
  19. PowerOn_ID.CH1ID = FLASH_ReadEnoceanID(CH1_ID_ADDR);
    8 q5 i: \* X/ }3 B& C
  20. PowerOn_ID.CH2ID = FLASH_ReadEnoceanID(CH2_ID_ADDR);
      ~5 X) q, g0 M4 d! y5 }) O$ C
  21. PowerOn_ID.CH3ID = FLASH_ReadEnoceanID(CH3_ID_ADDR);  w( n/ C8 Q& f
  22. PowerOn_ID.CH4ID = FLASH_ReadEnoceanID(CH4_ID_ADDR);7 c( y- R8 [: J
  23. PowerOn_ID.CH5ID = FLASH_ReadEnoceanID(CH5_ID_ADDR);, m( N  p4 b9 c7 ]
  24. PowerOn_ID.CH6ID = FLASH_ReadEnoceanID(CH6_ID_ADDR);
    ' w& a2 d6 k6 o, c
  25. PowerOn_ID.CH7ID = FLASH_ReadEnoceanID(CH7_ID_ADDR);3 f. i5 f9 n# h7 l2 F
  26. PowerOn_ID.CH8ID = FLASH_ReadEnoceanID(CH8_ID_ADDR);/ w- d7 \( s5 P* U
  27. PowerOn_ID.CH9ID = FLASH_ReadEnoceanID(CH9_ID_ADDR);( |, [, {  O; P1 I! V5 N. B
  28. PowerOn_ID.CH10ID = FLASH_ReadEnoceanID(CH10_ID_ADDR);
    ( h7 p8 Z: i5 ^8 r

  29.   J& z  [- y2 o: G: X/ q! u
  30. return PowerOn_ID;( a% j, \, k+ F0 }, |3 l/ M- L
  31. }
复制代码

, a' n( r/ U! O+ M1 t! P4 H

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


7 ]  d) W# s; _/ T写问题的出现

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

一直用的写全字函数为:

  1. FLASH_Status  FLASH_WriteWord(uint32_t Address, uint32_t Data)6 n9 I. H  _2 P- i% B0 H! A
  2. {% y( O( v/ K9 `( U

  3. & w: B- U' \3 `) b  K  T
  4. FLASH_Status i = FLASH_COMPLETE;
    8 _. z. K9 r* q. M; E/ W3 J

  5. * N0 ^/ C5 P7 Q6 I3 m2 W
  6. HAL_FLASHEx_DATAEEPROM_Unlock();  
    ' k  t& p, E  U9 B3 `+ `
  7.   while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, Address, Data) != HAL_OK);
    " j- V" E- s9 m' A8 K: A' z+ R
  8. HAL_FLASHEx_DATAEEPROM_Lock();1 j7 H6 Z+ u2 p6 |1 o8 H

  9. - t+ ~1 O- y9 c: Q- Q* G
  10. return i;# K: Z& k2 u  n. l$ D# J, c6 a' J  u5 B
  11. }
复制代码
5 j8 L  i' B$ T

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

/ A! O8 n# e1 Z1 d* w  u' c# m

微信图片_20230612172009.png

7 E, }2 ?* f) z. _# a+ S

但是使用时候发现:

/ l; D) J7 K. Q$ E% t

微信图片_20230612172006.png


& H& \4 v  I  a: h5 c

问题的解决:

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


/ r; c$ a2 O2 \4 h3 I, G' s

微信图片_20230612172003.png

1 a- P9 K0 f* `, t$ Q

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

  1. FLASH_Status  FLASH_WriteWord(uint32_t Address, uint32_t Data)1 \! r# M. w2 g+ R9 w& l
  2. {
    7 P3 c) M' d0 |2 A# Z2 B& ~( G
  3. " ]1 c1 U" F) J. J$ l$ x) o
  4.   FLASH_Status state = FLASH_COMPLETE;- {# q1 x! t" Z* [' f' _
  5.   u8 i = 0;4 S2 C; A3 ~4 G  ]" V
  6.   u8 writedata[6]={0};
    " o5 m: W/ g& K& N: P8 t
  7. . t( M3 h; J( f% @
  8.   writedata[0] = (u8)Data;! j7 B$ _8 A2 r9 y( T; m1 W
  9.   writedata[1] = (u8)(Data>>8);
    6 w" K+ ^( Q; x! L6 j
  10.   writedata[2] = (u8)(Data>>16);& o' R7 _- u5 [$ i
  11.   writedata[3] = (u8)(Data>>24);
    7 Q8 T6 t; p& V9 E
  12. 2 \# h, a; }5 o  w7 T( U
  13. HAL_FLASHEx_DATAEEPROM_Unlock();  5 b9 C1 }) ^9 O( m+ h, s
  14.   for(i=0; i<4; i++){- J$ Y! P; u+ R
  15.     while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, Address + i, writedata[i]) != HAL_OK);+ v6 J4 f9 s/ b# t/ e# o
  16.   }0 r0 O! y& L8 |  r" T4 o0 t% \
  17. HAL_FLASHEx_DATAEEPROM_Lock();8 A5 D6 I# S' T  w3 H1 U) Z0 |
  18. 0 N) r9 R9 U6 C5 A8 _7 m
  19. return state;* r( S' F  N! u  l
  20. }
复制代码

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

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

2.4.3小结使用的芯片为  STM32L071RBT6

4 e0 A' \6 g5 c: I" }
最后问题的解决

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

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


/ `0 U7 t* v# ?5 T( a7 b' U

微信图片_20230612172000.png

/ _1 M) h  I& _- C; a3 k2 }! _

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

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

$ @) E1 \% x6 k1 ^- ]; g5 i; U& H

微信图片_20230612171954.png


, [8 s+ [7 _6 X! D1 Z

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

0 h" B% t6 P+ f2 O
收藏 评论0 发布时间:2023-6-12 17:30

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版