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

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


* o2 N+ }4 ?0 V  t, e: f

微信图片_20230612172451.png

# v3 S( F/ }* Q0 E7 k

微信图片_20230612172454.png

" ^& U* O4 U7 s/ @' h

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


. ?+ \* q) k8 Y, N0 i: _

  1. #define FLASH_SIZE                (uint32_t)((*((uint32_t *)FLASHSIZE_BASE)&0xFFFF) * 1024U)2 q1 w: u0 f) h! P- ?
  2. #define FLASH_PAGE_SIZE           ((uint32_t)128U)  /*!< FLASH Page Size in bytes */
复制代码
. ~& T. f9 A- `8 ^% _  B9 I

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


1 A: r- _. _7 B4 k/ C! |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,因为怕有时候数据不够放……


# f' F& v$ E' b2.1 读取函数
  1. //读取指定地址的半字(16位数据)
    - U1 L, |. _" z8 A* l; M% {
  2. uint16_t FLASH_ReadHalfWord(uint32_t address)
    2 I! y& O0 m& d; O  K, ?! j
  3. {2 [; i2 A5 R" j4 n" Z  G5 n( R
  4.   return *(__IO uint16_t*)address; 3 c/ }+ [: C% D9 }
  5. }
    4 C5 ~/ W+ @+ P( ]5 B; g1 f$ L: X

  6. 9 `1 ]) t, {( U0 h) H4 l& }7 M
  7. //读取指定地址的全字(32位数据)2 d- X. K4 o5 V7 X; B% V. s
  8. uint32_t FLASH_ReadWord(uint32_t address)
    $ X* r: }' c. M( r! O: c
  9. {
    " ~  O& g9 D) u
  10.   return *(__IO uint32_t*)address;0 o! X6 v8 b3 U1 o. L; Q6 S$ t
  11. }
复制代码

2 k, t, F" m0 s/ X/ F) {' ]) y' r6 U
; d2 X. ?8 U2 D- g' N

简单测试一下:

  1. u32 read_data1=0XFFFFFFFF;) D% V% u, p+ @5 m8 O- S4 T
  2. u32 read_data2=0XFFFFFFFF;6 u3 j6 @' O' S& z, T1 M
  3. ...
    0 P5 x+ f4 {! e& W( f$ v
  4. read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);9 b! {& G/ s1 k. N( x
  5. printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);
    : i5 \  C  |+ }5 H/ _! ?  g0 J/ `
  6. read_data2 = FLASH_ReadWord(DATA_EEPROM_START_ADDR + EEPROM_PAGE_SIZE);
    4 n) d& r' I' G/ U" V
  7. printf("the EEPROM sceond page test data is: 0x %x \r\n",read_data2);
复制代码

& W6 [* M- a8 r  g4 G  d

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


$ K4 J: V" _3 w1 {: i2.2   EEPROM写函数

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

  1. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Unlock(void);+ x! Q; s. m+ j$ r9 ^/ Q
  2. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Lock(void);3 V6 p$ m5 P3 P& t! l: N! K
  3. 9 n. f. w; \0 T% j
  4. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Erase(uint32_t Address);
    . ], G2 R' ]7 q$ v
  5. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data);
复制代码
  p$ R$ m! P- o3 A" O4 P

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

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

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

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

  1. HAL_FLASHEx_DATAEEPROM_Unlock();) v* Q/ C+ U. f8 ]0 H, U
  2. HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, DATA_EEPROM_START_ADDR, write_data1);2 q* ~0 J7 V* |: g& |+ p; d. f; d% o
  3. HAL_FLASHEx_DATAEEPROM_Lock();
      u: r3 Q2 M& ]0 P( |  W; I
  4. ...
    # D; L, w9 B& c/ J1 V! a
  5. if(btn_getState(&K1_BUTTON_150mS) == BTN_EDGE2){% S; Q" u1 Y( m% j
  6.         printf(" K1 150ms button!,EEPROM_Erase test\r\n");
    4 x9 X: V$ V! L* Z
  7.         HAL_FLASHEx_DATAEEPROM_Unlock();
    + x5 J  q% d" H. m
  8.         HAL_FLASHEx_DATAEEPROM_Erase(DATA_EEPROM_START_ADDR+4);: c9 g5 T: w5 k5 ]. T) `
  9.         HAL_FLASHEx_DATAEEPROM_Lock();( u9 C* D- o0 z& m
  10.         HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
    ! [3 u- w: s) J7 x
  11.     }$ @( f" P! {# e. {
  12. ...
    $ V2 E: S7 ^' K/ a1 D
  13. if(btn_getState(&K2_BUTTON_150mS) == BTN_EDGE2){
    # h2 V! v. V( S9 G/ e, c, Q# b
  14.         printf(" K2 150ms button!EEPROM_read test\r\n");
    ! X4 F1 K7 M1 q. r, [& M* T- w
  15.         read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);7 n5 Q- v5 t3 B/ K+ U# P7 f
  16.         printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);' g  Q' X, |$ v8 r3 c; l
  17.     }
复制代码
$ C' p& J# q/ w

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

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


2 c% E1 O" o6 p* D$ W# ]& M$ `6 |写入问题说明修改

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();, d+ o+ J/ e2 r+ P! O7 S
  2.   HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, DATA_EEPROM_START_ADDR, 0X88);
    , x+ O! x6 C: t$ B. s2 T. g
  3.   HAL_FLASHEx_DATAEEPROM_Lock();
    7 V: X3 Z9 ?9 r1 |
  4.   read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);
    . P, p8 Q7 t: c
  5.   printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);
复制代码

8 I+ F6 W+ k, _4 l" T

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

  1. void MY_DATAEEPROM_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data)   
    % m+ X: u5 v6 i$ Y0 B
  2. {
    ) B) z- o1 j0 m. O7 S
  3. HAL_FLASHEx_DATAEEPROM_Unlock();        % F, o* I! o) j- s. ^8 `# j
  4.     HAL_FLASHEx_DATAEEPROM_Program(TypeProgram, Address, Data);
    5 Z, M! J3 O3 T- W- L
  5. HAL_FLASHEx_DATAEEPROM_Lock();
    ! {/ t0 C* p9 `$ M8 v: a7 J; u1 I
  6. }
复制代码

, z. n9 r) ?# d. V8 Y7 o9 N& m

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

4 P; @  H- k1 L# V& q, B
2.3   Flash写函数

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

  1. /* IO operation functions *****************************************************/
      E& H  G/ ~$ D; Q
  2. HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data);
    5 Y! ]: }4 p8 A1 H) }" c
  3. HAL_StatusTypeDef HAL_FLASH_Program_IT(uint32_t TypeProgram, uint32_t Address, uint32_t Data);
    & \& W% p8 W" h0 m, U

  4. ) m9 }0 X% \2 G
  5. /* FLASH IRQ handler function */
    1 s# k3 c" Y  Y3 }# Y8 r: p" F
  6. void       HAL_FLASH_IRQHandler(void);
    5 g/ j" W  q5 Q" M0 M2 W, P
  7. /* Callbacks in non blocking modes */3 |0 z; o- B7 _& ?! \
  8. void       HAL_FLASH_EndOfOperationCallback(uint32_t ReturnValue);
    + u# E2 _& `+ j' s: ?6 w  z6 a
  9. void       HAL_FLASH_OperationErrorCallback(uint32_t ReturnValue);' C0 N2 v! |6 o, f

  10. 2 _' H/ [, {% K/ S& h
  11. /**
    " P6 x/ q6 \* `5 o& a/ T- u
  12.   * @}
    8 ~3 v2 f  P$ ?8 {2 s! i& K2 f4 z
  13.   */+ q$ S1 A- i& ~7 o! T) J

  14. ! U" q7 Z. Y' v( x7 X
  15. /** @addtogroup FLASH_Exported_Functions_Group2
    3 @1 X& |4 P8 x( i% |" q! E6 Z9 X
  16.   * @{$ ]& d: h; x, |
  17.   */7 V7 c, l5 L! ]
  18. /* Peripheral Control functions ***********************************************/
    2 _" Y1 p, }) m0 {) \) I
  19. HAL_StatusTypeDef HAL_FLASH_Unlock(void);! X2 x4 E3 i* h) T8 A- U) |& l
  20. HAL_StatusTypeDef HAL_FLASH_Lock(void);
    8 O" F* ]- A1 F" C; l) g/ v5 G1 m
  21. HAL_StatusTypeDef HAL_FLASH_OB_Unlock(void);
    3 b* @6 F$ K: w' Z1 ~( \- t
  22. HAL_StatusTypeDef HAL_FLASH_OB_Lock(void);
    ( J% E& s0 Y3 [
  23. HAL_StatusTypeDef HAL_FLASH_OB_Launch(void);
复制代码
% z. _7 @: E2 D

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


- O4 c7 C, t  `; L4 E) b

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

- p7 U5 A  _- W$ P$ O" g
  1. HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data)/ D& p2 b0 B6 M/ `' ^. R' G0 T* w
  2. {
    4 f* c7 {  O* P' }' @3 Y+ d& Z" }
  3.   HAL_StatusTypeDef status = HAL_ERROR;5 r. l/ W# W1 {
  4.   3 M: U) T6 s+ b5 Q0 s) i
  5.   /* Process Locked */
    " ^) q3 P: |; J2 W+ n! m8 I
  6.   __HAL_LOCK(&pFlash);
    + A" c3 k, ]# L8 E3 T( ?2 q+ w

  7. 9 c& a% J* b" k$ @6 l2 X
  8.   /* Check the parameters */- w9 y8 w2 \6 E4 h
  9.   assert_param(IS_FLASH_TYPEPROGRAM(TypeProgram));
    % I9 Y1 G# E" A6 N
  10.   assert_param(IS_FLASH_PROGRAM_ADDRESS(Address));2 H* J# z' ~+ `5 X

  11. - T/ v5 M  |# c
  12.   /* Wait for last operation to be completed */8 {2 w8 D5 ~$ p' L, B
  13.   status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);9 K4 z% x9 d3 a: f
  14.   
    # O4 g- k, G1 U- I+ q& {1 B3 ^% U
  15.   if(status == HAL_OK). h2 Y  ^" p, |
  16.   {
    8 i9 h/ B  O/ B4 {7 B9 {3 H" D+ a
  17.     /* Clean the error context */
    8 m2 E' b3 I8 S5 J
  18.     pFlash.ErrorCode = HAL_FLASH_ERROR_NONE;, S  R& U$ M+ e. R2 m

  19. 4 E7 ~1 y! G! [+ I0 Y0 h
  20.     /*Program word (32-bit) at a specified address.*/' U- n" l& k7 K% V
  21.     *(__IO uint32_t *)Address = Data;4 a! `. y3 b& g5 H; x5 V9 v! s
  22. ( I/ m  {9 l! }) ~$ i0 o2 B
  23.     /* Wait for last operation to be completed */
      v# E& s# |$ r' a
  24.     status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
    + S4 z6 a2 Y. V0 \6 v
  25.   }
    9 ?9 Z+ ~$ Q3 a$ @" c3 h, @
  26. % X2 \: C* p$ ], |0 n7 B
  27.   /* Process Unlocked */
    # C' B& [0 E# d, u
  28.   __HAL_UNLOCK(&pFlash);" ]" e' O3 r4 w) ]( k
  29. , B: V' P$ c2 d  Y% \2 S, `
  30.   return status;
    . C6 L& K  f" _- R" z2 s
  31. }
    7 o- U8 A% ^+ k% A
复制代码
0 s- b+ Y  K8 W& s( N

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

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

  1. #define ADDR_FLASH_PAGE_505      0X08000000 + 128*504   //4 f( h& O# I% T9 }1 W
  2. #define ADDR_FLASH_PAGE_506      0X08000000 + 128*505   //
    7 D: J* L% w5 G3 a
  3. #define ADDR_FLASH_PAGE_507      0X08000000 + 128*506   //4 v; d8 N. I5 v7 n
  4. #define ADDR_FLASH_PAGE_508      0X08000000 + 128*507   //
    " h, A6 x& a" \# w
  5. #define ADDR_FLASH_PAGE_509      0X08000000 + 128*508   //
    9 e; k. j- v$ p/ c7 c
  6. #define ADDR_FLASH_PAGE_510      0X08000000 + 128*509   //
    ! n% l- i) N8 O* M: E0 i
  7. #define ADDR_FLASH_PAGE_511      0X08000000 + 128*510   //
    / Z* N6 }9 {" M2 R" o: g' o9 [
  8. #define ADDR_FLASH_PAGE_512      0X08000000 + 128*511   //最后一页
复制代码

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

  1. void MY_DATAFLASH_Program(uint32_t Address, uint32_t Data)   
    8 Z9 @$ \' Z2 Q# m8 a( s+ [
  2. {
    " t5 f' L) K. r9 }
  3. HAL_FLASH_Unlock();        
    % d; h% M. F8 @. Q6 C' f& a
  4.   if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){
    # I3 M0 ~) h$ m  y8 R: z7 H
  5.     printf("write data 0x%x OK\n", Data);
    + ]0 ~3 H3 U$ {8 z7 c: p
  6.   }
    ' I. T& g1 W6 n
  7.   else{; u7 Y: e  _5 F/ a
  8.     printf("failed!!!\n");
    + G% ^$ q: G4 B9 c  N
  9.   }
    ' y3 D& A" |6 d, x/ C) U
  10. HAL_FLASH_Lock();% u3 q  K9 D, H% ]
  11. }
复制代码
, x8 }3 q7 h) l/ [- o

测试一下;

  1.   MY_DATAFLASH_Program(ADDR_FLASH_PAGE_512,write_data2);
    8 x3 G/ ?! ^/ M" a( o. F4 N# V
  2.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_512);8 ?, ?- L: f& W0 f) H3 G! `
  3.   printf("the ADDR_FLASH_PAGE_512 is: 0x %x \r\n",read_data1);
复制代码
; k- C# E3 n5 z1 [

在没有写入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)   
    / K, r; x& p% L, {0 B$ ~& U
  2. {
    4 b$ _) g0 X2 v; M5 H2 O
  3.   FLASH_EraseInitTypeDef EraseInitStruct;/ o1 e0 P" I: e) W4 ~$ Q! o/ N
  4.   uint32_t checkdata;1 ?3 V5 {1 l9 w& F* F2 L/ Z$ d
  5.   uint32_t PAGEError = 0;
    0 u0 P" e8 x) C3 X  L3 H7 ^
  6.   checkdata = FLASH_ReadWord(Address);1 \: d% u7 I9 V7 y5 b8 w6 I( m4 M
  7. HAL_FLASH_Unlock();
    # ^0 t& l- R- F8 {4 v3 Z- L8 o* E
  8.   /*如果是0,直接写*/
    6 _# {- ^6 c" q0 O) s3 f" ~
  9.   if(checkdata == 0){       1 m& h+ y+ _' O
  10.     if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){$ N7 q0 ^7 x& T# t+ v5 n& _$ @
  11.       printf("write data 0x%x OK\n", Data);
    / X( b, }1 ~) O; X
  12.     }6 C  H# Z: U8 s6 _1 D5 \, j
  13.     else{! X. H' Q2 \) Q- ?- E
  14.       printf("failed!!!\n");# r- y5 s4 S0 u! X% g9 ]
  15.     }8 Z+ f% j5 P/ }1 K* S) M
  16.   }1 e& v4 v2 Z- m; P5 \7 {& Q
  17.   /*否则擦除再写*/  \2 S# d! d5 j
  18.   else{% g$ r6 {% R& K2 K% F8 o5 K
  19.      __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);# Z. q# ^8 d# P1 o& Y
  20. & W% ^+ k4 I; G( _; I; z
  21.       EraseInitStruct.TypeErase   = FLASH_TYPEERASE_PAGES; // 刷除方式
    , w3 E; R; ^) ^! {
  22.    EraseInitStruct.PageAddress = Address; // 起始地址
    : z' O; g. a! W& J
  23.       EraseInitStruct.NbPages = 1;
    " l6 p8 ]* V$ D* i

  24. ) s- b  U, i! O4 I% T; l0 ~/ v
  25.       if (HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError) != HAL_OK)
    ( ?. S) b" `! p8 d
  26.       {. |/ q. c# d/ c, ^% o
  27.         // 如果刷除错误
    / |8 U) i0 S7 ], M" r7 ^
  28.         printf("\r\n FLASH Erase Fail\r\n");
    9 G/ n% S! h6 r. Q: a  t6 c+ v
  29.         printf("Fail Code:%d\r\n",HAL_FLASH_GetError());
    & e& T- ]" A+ V' S3 t' q. z
  30.         printf("Fail Page:%d\r\n",PAGEError);
    7 L+ q' ~. u$ Z9 p9 f. _' l1 U" Z
  31.       }
    * T& ?+ _: w4 i) E# A# a! {

  32. ' J2 B+ V' ~+ W, U$ j5 j
  33.       if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){% V  ?; J& F1 G& J) R9 N1 h
  34.       printf("write data 0x%x OK\n", Data);
    9 V7 X5 Z& x1 \* @
  35.       }9 D1 q' |9 ?( U8 e
  36.       else{0 C$ s! ~7 }5 L: q  I0 D
  37.         printf("failed!!!\n");
    - L& u  T) A% j/ p+ Y
  38.       }
    3 d- e4 J9 d) L' l1 ?- s4 |( b0 ^

  39. 9 o- W% p6 R0 G$ X' q
  40.   }' P- g  Z1 b' G" @) W
  41. HAL_FLASH_Lock();
      @1 S0 X1 ?* e5 H' J2 {" ^
  42. }
复制代码

! `" i) R$ A2 I3 K: O) u& J6 k  `

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

  1. u32 write_data1 = 0X12345678;# g2 M, |$ O& e+ I% S% j! U
  2. u32 write_data2 = 0X87654321;
    % [2 Z8 o4 M/ T: @  L3 ~, ^
  3. u32 write_data3 = 0XFFFFFFFF;# v7 m2 v# u8 j- E
  4. ) ]! e9 n# R) r  h0 n6 _# Q, t
  5. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_508 + 124,0X508508FF);0 Z! H( e" A5 c
  6. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509,write_data3);
    : N/ S( Z8 |! c
  7. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509+4,write_data2);0 K2 M' N* q! E+ L0 t
  8. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509+8,write_data1);
    ' L6 l, _( r9 A1 n4 e
  9. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_510,0X510510FF);
    $ Q1 m+ I8 m+ z
  10. 9 q% N7 A, E; L/ b1 N: }
  11. read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_508 + 124);. _  l2 y/ k$ n1 E& i3 [3 C
  12. printf("the ADDR_FLASH_PAGE_508 last is: 0x %x \r\n",read_data1);
    7 S  q9 r4 r; v5 \: b1 c
  13.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509);  v) [" u/ R3 I" V: }5 [+ m
  14.   printf("the ADDR_FLASH_PAGE_509 1 is: 0x %x \r\n",read_data1);$ b5 d9 N- n7 [/ s2 v8 W2 C" [! p" T
  15.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 4);
    $ I* S- \4 _0 E# l: ]3 c8 `0 V
  16.   printf("the ADDR_FLASH_PAGE_509 2 is: 0x %x \r\n",read_data1);0 U# ?" f5 A. H4 {( R7 z
  17.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 8);& t5 s1 M) A# Z# |' o) b$ T
  18.   printf("the ADDR_FLASH_PAGE_509 3 is: 0x %x \r\n",read_data1);
    * Q/ n) K) G  s% b
  19.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_510);2 x4 c" `7 M8 C: ]" T, N7 |- ]& X. M
  20.   printf("the ADDR_FLASH_PAGE_510 first is: 0x %x \r\n",read_data1);
    3 k5 _1 [9 [  S/ O# s1 i

  21. * |& \, L/ D* C4 ^: u6 K- H0 I( `2 v# }
  22. if(btn_getState(&K2_BUTTON_150mS) == BTN_EDGE2){2 Z" \) G) {' W6 k- H# P& {9 |
  23.         printf(" K2 150ms button!EEPROM_read test\r\n");
    + N8 {% G0 ]( V+ j
  24.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_508 + 124);' j2 A9 p3 E$ n% u. e( k" t
  25.         printf("the ADDR_FLASH_PAGE_508 last is: 0x %x \r\n",read_data1);
    5 B0 [! E% s) K6 U" h5 H% N+ g* }
  26.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509);
    5 A$ h9 r9 S9 U' k: ~
  27.         printf("the ADDR_FLASH_PAGE_509 1 is: 0x %x \r\n",read_data1);6 u3 g" V: T% m# M& C0 H1 i
  28.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 4);
    $ A* ^5 G' e( ^9 V2 j' y& A8 y6 o% N
  29.         printf("the ADDR_FLASH_PAGE_509 2 is: 0x %x \r\n",read_data1);
    " {4 e0 I  a& O1 V
  30.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 8);& z& t- h/ L% t, q
  31.         printf("the ADDR_FLASH_PAGE_509 3 is: 0x %x \r\n",read_data1);
    7 i# G  t" t( Y$ a4 d
  32.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_510);$ O) I5 Z0 I$ C; r& F  s/ d: i
  33.         printf("the ADDR_FLASH_PAGE_510 first is: 0x %x \r\n",read_data1);, Z, Z0 {! U- l
  34. ( a# ]5 Z7 F+ R
  35.     }! n# P8 {% l( f% R% @
  36. if(btn_getState(&K1_BUTTON_150mS) == BTN_EDGE2){% d9 K1 D1 q- g. N9 B* ~! S( u
  37.         // printf(" K1 150ms button!,EEPROM_Erase test\r\n");
    3 f3 y: x8 ]$ q4 A+ J3 H; \
  38.         // MY_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, DATA_EEPROM_START_ADDR, write_data1);7 O! Y# C; R/ c
  39.         printf(" K1 150ms button!,flash write test\r\n");1 H) e/ \5 S% `) G9 b- p: w
  40.         MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509,0X33333333);/ p, q, b% a- ?7 ]4 [
  41.         HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
    & A3 V6 b* _" w: q
  42.     }
复制代码
8 g# e* X! M$ U0 \- }& k  T

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


. {, _! ~2 E" S0 }

微信图片_20230612172448.png

. j% B7 t+ j4 |# Y. ?: V) @- }

微信图片_20230612172355.png


4 B  J5 ?7 C1 h4 ~- W) C1 a& c

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


  Q2 C/ L' F! q6 S2.4 读写EEPROM的后续问题

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


6 p8 |; R2 o, V' k$ S" l

微信图片_20230612172339.png

) N' b9 }& R& x# j3 A  k5 x

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

  [) g( `: s1 h* m# g+ E6 N; z' S
2.4.1 问题的出现和解决

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

& d# W: G  o: Y

微信图片_20230612172215.png

5 X5 h0 E8 p: C* k  j: k( n; m

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

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

' F; b; E  G" b. X4 l

微信图片_20230612172212.png


$ L) y$ p# Y, I( M

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

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

  1. BlueID_STRUCT test;
    " ]4 W+ ]2 S$ }6 c$ a# O" \3 x
  2. ...* {- U) i8 @# ?) ~, S/ v
  3. /*
    . `2 p  s) b2 f5 |# b' r
  4. CHBlueID_STRUCT Flash_PowerOn_BlueCheck(): n6 E3 h7 m5 @' B1 [7 o
  5. {
    & U, A# Y2 W; K: `) N, ]9 A% l

  6. * ]6 U( f6 S! g0 E% a2 w8 R0 X
  7. CHBlueID_STRUCT PowerOn_ID;
      Q. t0 c  ]& f; ?
  8. PowerOn_ID.CH1ID = FLASH_blueIDRead(CH1_ID_ADDR);5 ~0 X6 [& ], q
  9. PowerOn_ID.CH2ID = FLASH_blueIDRead(CH2_ID_ADDR);
    . c; b" A: P" @+ `- H! B! K
  10. PowerOn_ID.CH3ID = FLASH_blueIDRead(CH3_ID_ADDR);
    $ ~4 d1 I- k2 z3 S
  11. PowerOn_ID.CH4ID = FLASH_blueIDRead(CH4_ID_ADDR);
    # d0 A+ K1 U3 C6 \5 p0 m4 ?
  12. PowerOn_ID.CH5ID = FLASH_blueIDRead(CH5_ID_ADDR);7 r9 ~# _/ t7 @# d: d" Q
  13. PowerOn_ID.CH6ID = FLASH_blueIDRead(CH6_ID_ADDR);1 Y2 k  G. o0 X* _8 z
  14. PowerOn_ID.CH7ID = FLASH_blueIDRead(CH7_ID_ADDR);
    8 ?* Z2 ?# z  x
  15. PowerOn_ID.CH8ID = FLASH_blueIDRead(CH8_ID_ADDR);+ {. f) y, `: e" s
  16. PowerOn_ID.CH9ID = FLASH_blueIDRead(CH9_ID_ADDR);0 ~5 d  t; [, ?1 y2 D. @6 o
  17. PowerOn_ID.CH10ID = FLASH_blueIDRead(CH10_ID_ADDR);
    - E5 n+ U0 G- D7 o% }3 f  b

  18. " l8 m9 K1 K6 @! A- @
  19. return PowerOn_ID;8 Q' Q2 p, S; }* Y/ Y+ F' B8 R4 }
  20. }
    0 r: i! K+ F( z, i' c( c
  21. */
    8 p. i) d! i- D/ I$ U) d6 V
  22. BlueChipID = Flash_PowerOn_BlueCheck(); //上电先把ID读出来做比较
    8 ?8 [. x7 F+ I6 o" f) T* I
  23. //打印一个出来测试,看结果
    + _& Z( H0 U) F  ^6 U1 o$ o3 M
  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 [5 r, f- R0 ~# l' A

  25. / G0 q6 Z4 @  H
  26. test.ID1= 0XFF; //结构体每个元素是 uint8_t 类型
    1 f5 y9 p/ H8 X2 @
  27. test.ID2= 0XEE;6 K& l0 u+ @6 n# M' o* \
  28. test.ID3= 0XDD;( z+ I2 t  u3 i2 b% k4 @
  29. test.ID4= 0XCC;% l$ g* _! a: E
  30. test.ID5= 0XBB;
    , u' a6 f+ O8 ?( T& _
  31. test.ID6= 0XAA;
    9 y$ T( ]* W" f: Z$ F
  32.   b% c) |; w& A$ p7 B; R. G
  33. while (1){...}
复制代码
7 Q9 ]" f# J7 g

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


8 S- ~+ }& S0 P9 g8 d

微信图片_20230612172209.png


/ d/ c$ \/ C' _' A  ]

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

' u: z9 V6 E/ }

微信图片_20230612172207.png

/ A7 j  C* o1 {+ D

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

9 R2 f" a7 W1 o

微信图片_20230612172058.png


9 N9 G5 g7 Z/ ^0 t6 s

微信图片_20230612172050.png

6 U; [- M+ V9 t. k

打印的结果:


2 {( u- |( `6 j; a, e) p& m  d2 |1 d

微信图片_20230612172039.png


+ P7 R9 J# K. w& ~0 ?0 B8 o, w

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

  1. BlueID_STRUCT FLASH_blueIDRead(uint32_t address)
    * o3 a5 }. `+ b( E  k2 @+ I5 k% f2 A
  2. {
    : @6 ?1 y/ ?& S9 ]
  3. BlueID_STRUCT u48id;
    . \7 G  G$ [* D+ Y" T
  4.   // uint16_t temp1,temp2,temp3;  /**(__IO uint16_t*)address; */+ \' A0 i7 _! T$ B6 f
  5.   // temp1=*(__IO uint16_t*)address; / s! j, _% v3 r2 T
  6. // u48id.ID1 = (uint8_t)(temp1>>8);- F5 T6 c+ }; F: ^
  7. // u48id.ID2 = (uint8_t)(temp1&0X00FF);3 z7 E  Q/ h$ O+ X9 _* o1 Y
  8.   // temp2=*(__IO uint16_t*)(address+2);
    % a- J6 x5 }9 u2 Q) K
  9. // u48id.ID3 = (uint8_t)(temp2>>8);7 {9 r7 t/ d& Y  O- j' d
  10. // u48id.ID4 = (uint8_t)(temp2&0X00FF);. d4 z" r# ~: |! G2 M! W8 K
  11. // temp3=*(__IO uint16_t*)(address+4);9 j- @3 E8 @; ~$ L+ k
  12. // u48id.ID5 = (uint8_t)(temp3>>8);
    ! @  ]" a6 v" ^6 N0 N0 c) j& O
  13. // u48id.ID6 = (uint8_t)(temp3&0X00FF);
    ) O) T% B* I5 Q" p1 R7 Z
  14. ) U+ r" a& H0 g+ _; q2 o4 M/ N
  15.   u48id.ID1 =  FLASH_Readbyte(address);  ^* n- o3 l: q0 D5 y! }3 O! {
  16.   u48id.ID2 =  FLASH_Readbyte(address+1);4 g8 u2 [( }; m; m1 a$ W+ ?
  17.   u48id.ID3 =  FLASH_Readbyte(address+2);. X3 J6 p  e/ p: B3 X- |
  18.   u48id.ID4 =  FLASH_Readbyte(address+3);9 i3 c- f* Z7 m0 u# c0 U
  19.   u48id.ID5 =  FLASH_Readbyte(address+4);
      @, e6 l- l) M( a
  20.   u48id.ID6 =  FLASH_Readbyte(address+5);
    ! s8 R  J' f. G" r6 y' T
  21. 3 ^" b' T: `! o8 I& w: r2 ^
  22.   return u48id;  ?7 J7 n. ~7 o' \/ w
  23. }
复制代码
# j5 O$ g; f; F7 Y+ {

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


' x5 X( T+ m, X2 a/ Q

微信图片_20230612172035.png


5 K' l6 c9 `6 x8 S; @* n2.4.2 问题的分析(大小端模式数据格式)

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

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


8 N' X6 M+ [5 U& a

微信图片_20230612172032.png


4 v, c. l2 K5 k  P

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


1 J' o; J% T9 {- R/ u

微信图片_20230612172030.png


: t2 v: y, e& h9 X# i9 V  a# p

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

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

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


, r* R# y# p+ ?+ y8 C2.4.3 STM32L071RBT6 EEPROM读写全字问题
$ W' S- }  ~" o) a! n
读问题的出现

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

  1. uint32_t  FLASH_ReadWord(uint32_t address)3 u% P# D4 A" Q
  2. {( V5 ]5 {+ f, [
  3.   return *(__IO uint32_t*)address;
    ; ^. a+ u1 ], t  i# {$ E
  4. }
复制代码

; |5 K4 }- C* f0 X& [

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

5 d9 z! t3 V: L  Z: X

微信图片_20230612172026.png


0 h0 P1 X# L- P! P: c/ C, C

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

& ~( {( _. I" a

问题的解决:

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


1 U) u* g: F6 f

微信图片_20230612172019.png

( c8 `) [" n) E6 F1 V0 P) a9 ~

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

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

  W. n5 Z3 t0 R7 h

微信图片_20230612172012.png

, W, f) ~9 U" s2 x& |" W

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

  1. u32 FLASH_ReadEnoceanID(uint32_t address)2 p, o2 V( S+ Y- ]
  2. {# \1 e4 m. U0 z8 [, [) Y
  3.   u32 keepid;  Y* w! g% H& s' {% ~/ t
  4.   u8 i;  y! C5 d2 ~% ?2 }$ x- I' F
  5.   keepid = FLASH_Readbyte(address);
    , p& `2 K% }* C
  6.   i = FLASH_Readbyte(address + 1);( N/ l" _/ F8 w7 K& Z2 }
  7.   keepid = (i<<8)|keepid;
    7 ?4 j$ ^0 R/ Y, D  ?& E
  8.   i = FLASH_Readbyte(address + 2);5 j% k* c0 |5 B  ?
  9.   keepid = (i<<16)|keepid;% R+ s& C( |! j1 Y6 b
  10.   i = FLASH_Readbyte(address + 3);- a. l8 f* O4 Q6 M- T9 W
  11.   keepid = (i<<24)|keepid;* c2 ~. ]8 |3 K# ~2 V" }9 C  W+ n/ L
  12.   return keepid;: f8 I6 W+ G  B$ w3 y1 t# A
  13. }' b. z) r) r5 F3 s1 l. U/ N
  14. ! ?7 r! A; P1 ^+ I8 R
  15. CHID_STRUCT Flash_PowerOn_Check()
    7 O) f! n! O7 V) F( J4 D
  16. {
    ! B) t5 j! M( U' Q* ?; ^
  17. 6 Y8 V( L9 n- g5 v) N! m' S
  18. CHID_STRUCT PowerOn_ID;& e" ^1 ?, H8 q; Y
  19. PowerOn_ID.CH1ID = FLASH_ReadEnoceanID(CH1_ID_ADDR);4 v% ~3 }$ t7 H& \% M
  20. PowerOn_ID.CH2ID = FLASH_ReadEnoceanID(CH2_ID_ADDR);
    . V. k1 D, ?$ m
  21. PowerOn_ID.CH3ID = FLASH_ReadEnoceanID(CH3_ID_ADDR);: I$ n8 }+ I$ u) Y3 p; _9 ~- ~: J0 q
  22. PowerOn_ID.CH4ID = FLASH_ReadEnoceanID(CH4_ID_ADDR);& R0 _+ G" \$ n* T: H! N1 M+ i
  23. PowerOn_ID.CH5ID = FLASH_ReadEnoceanID(CH5_ID_ADDR);! f2 |% M* m' ^6 v) |
  24. PowerOn_ID.CH6ID = FLASH_ReadEnoceanID(CH6_ID_ADDR);
    ) }8 d) h* d8 g# ^- B6 d
  25. PowerOn_ID.CH7ID = FLASH_ReadEnoceanID(CH7_ID_ADDR);+ J8 z% \' X* ^; O- q) [5 ?  i
  26. PowerOn_ID.CH8ID = FLASH_ReadEnoceanID(CH8_ID_ADDR);
    $ D3 N% R, m5 }
  27. PowerOn_ID.CH9ID = FLASH_ReadEnoceanID(CH9_ID_ADDR);
    $ \/ t8 I$ n8 E" s+ _! s% [
  28. PowerOn_ID.CH10ID = FLASH_ReadEnoceanID(CH10_ID_ADDR);
    : b. _. i2 X$ @: Y8 g4 k
  29. : m2 `- P6 Y: ]
  30. return PowerOn_ID;3 ~" E5 B- C* L# S
  31. }
复制代码

5 `( _; g: a% L0 n

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

- W* w+ ^4 ~7 M& Y
写问题的出现

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

一直用的写全字函数为:

  1. FLASH_Status  FLASH_WriteWord(uint32_t Address, uint32_t Data)
    ' z6 A0 O7 E8 F7 T7 S
  2. {- A7 h5 t* \) M: v0 v
  3. ' `6 z( w: |6 j1 l9 F
  4. FLASH_Status i = FLASH_COMPLETE;
    + g, X7 P' N6 U- P2 F8 J

  5.   _8 Z/ m1 r/ I% U9 {
  6. HAL_FLASHEx_DATAEEPROM_Unlock();  1 _4 C8 n5 s4 q! l
  7.   while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, Address, Data) != HAL_OK);  D9 F3 b1 [, ?7 P: B6 D9 W
  8. HAL_FLASHEx_DATAEEPROM_Lock();
    $ q1 ~8 L' t$ p2 c. F7 q$ Y
  9. - F# s$ b* R9 D3 }
  10. return i;
    8 A3 X0 N# {0 I/ }' ^
  11. }
复制代码

% y8 u, [5 r1 D9 n

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


2 E; G( \( q& R& M& f! A

微信图片_20230612172009.png

: x8 c! n" I9 h" }8 |4 }8 Z+ }

但是使用时候发现:


8 ]0 j! Y0 \  B( C# }( J# E

微信图片_20230612172006.png


5 x. Y% m  R# {1 |

问题的解决:

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


; j! P+ L( S" n: ~3 Z

微信图片_20230612172003.png

% u" D! |: h+ |# U# w8 W- J% h

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

  1. FLASH_Status  FLASH_WriteWord(uint32_t Address, uint32_t Data)" Q+ Y; [1 w- _; c  f8 J/ X7 J
  2. {+ ]  ?7 j3 J) g. w' x

  3. $ I6 V/ x2 f' v- w
  4.   FLASH_Status state = FLASH_COMPLETE;
    4 U6 A; M5 A  c1 b" z; q1 V* v+ k
  5.   u8 i = 0;' L' z& K3 V: Z+ @" H3 n0 }
  6.   u8 writedata[6]={0};
    2 H6 l  g1 j3 V. N

  7. - P4 ?7 }7 V6 i' Z& E
  8.   writedata[0] = (u8)Data;
      k% y; W  S7 a
  9.   writedata[1] = (u8)(Data>>8);
    4 c  S+ n( l) ]6 ]
  10.   writedata[2] = (u8)(Data>>16);
    0 R3 g$ T* v& e$ M4 g7 ~6 z" ]
  11.   writedata[3] = (u8)(Data>>24);' B% E8 i! S; e- J9 T/ A6 e8 t

  12. 4 {3 y4 F, l3 l- e: j
  13. HAL_FLASHEx_DATAEEPROM_Unlock();  5 n! _# U+ f$ `! X5 [
  14.   for(i=0; i<4; i++){( b  R% a4 ?/ ]( G; ]% P4 w. x4 K
  15.     while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, Address + i, writedata[i]) != HAL_OK);
    " p% O* K1 y. \
  16.   }
    & W9 Y' `7 n# x7 D! y6 [
  17. HAL_FLASHEx_DATAEEPROM_Lock();4 H" m  v- G/ ^+ s* P" |

  18.   R! a5 T# r3 b& @) j% P
  19. return state;
    3 T" K3 y- }* ?  b( Y
  20. }
复制代码

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

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

2.4.3小结使用的芯片为  STM32L071RBT6


- q5 k# z" E9 ]# r8 S最后问题的解决

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

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

7 H; i' Z! X: j- Z+ _

微信图片_20230612172000.png

2 X6 \$ |" z8 n, A* A1 O& G

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

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


  v# S# n  ~: h) M+ x

微信图片_20230612171954.png

# [1 u1 J/ l; B' [

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


* {- ~$ Z! l% r: }- i4 u* d
收藏 评论0 发布时间:2023-6-12 17:30

举报

0个回答

所属标签

相似技术帖

官网相关资源

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