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

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

# t7 u: F* H5 d. z" K) \

微信图片_20230612172451.png


, G+ }- I7 H7 U  Q( ?+ `

微信图片_20230612172454.png

& ^! M9 B% w8 h, l' 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       :

1 o" g1 Q, W" I) K+ I

  1. #define FLASH_SIZE                (uint32_t)((*((uint32_t *)FLASHSIZE_BASE)&0xFFFF) * 1024U)
    $ y' _# n; ^9 c- I& D
  2. #define FLASH_PAGE_SIZE           ((uint32_t)128U)  /*!< FLASH Page Size in bytes */
复制代码

* f. s( a* x, `: e. Z4 m& ]

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


- q! w8 s; H6 c2、读写函数的设计

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

2 E" t+ n, n8 {& O4 s
2.1 读取函数
  1. //读取指定地址的半字(16位数据)
    , L9 c' j3 d# R! E9 B6 W
  2. uint16_t FLASH_ReadHalfWord(uint32_t address)
    $ p9 R/ |- E+ G: k; y
  3. {$ j( @6 N3 ]- p3 s. u: p
  4.   return *(__IO uint16_t*)address;
    ) p* c# m$ B# D$ t5 b
  5. }
    7 e, q( N) y7 H6 ]  i# |
  6. 4 I8 l0 @+ ]1 N2 Q4 r' v
  7. //读取指定地址的全字(32位数据)  Y; |6 ?  v* m5 j9 R3 K) h: ~8 ^
  8. uint32_t FLASH_ReadWord(uint32_t address): c5 U! r. ?3 \+ e! Q: E2 K
  9. {* ^* s, W: m" A% M- \- y
  10.   return *(__IO uint32_t*)address;# z  }. a/ r1 ~
  11. }
复制代码

; H! L2 p) s& h% R- b
4 _9 B/ q; J( R7 c

简单测试一下:

  1. u32 read_data1=0XFFFFFFFF;
    7 Y( h6 g, Z  {
  2. u32 read_data2=0XFFFFFFFF;$ Y- Q7 d3 ^7 n' O9 W* B2 B( B/ P
  3. ...' A  l0 q& T0 E4 o4 ]5 F0 s2 ]) r( D
  4. read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);
    : E. p' a# z- P  X# }
  5. printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);
      Y% b5 l( t- _7 N
  6. read_data2 = FLASH_ReadWord(DATA_EEPROM_START_ADDR + EEPROM_PAGE_SIZE);
    ( Z/ q: y- ^/ o5 Z1 w. t, E1 @( L
  7. printf("the EEPROM sceond page test data is: 0x %x \r\n",read_data2);
复制代码

; _: j' o1 f- |6 V. E4 X" y

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

# y8 p' e# [% n8 U5 L
2.2   EEPROM写函数

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

  1. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Unlock(void);
    / g% s' u% @) a" r
  2. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Lock(void);! C) h$ m5 o* V1 M" C1 \
  3. & i& l5 K" T) L* ?. ]; L# m
  4. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Erase(uint32_t Address);! N+ ]! F; O$ g+ f; k
  5. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data);
复制代码
8 H. h9 t  G1 m; [9 e1 b

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

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

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

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

  1. HAL_FLASHEx_DATAEEPROM_Unlock();
    1 N) s0 U- k0 H5 A: h, ~0 U
  2. HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, DATA_EEPROM_START_ADDR, write_data1);9 p% x5 e7 D& q+ E9 M# l+ S+ G+ y
  3. HAL_FLASHEx_DATAEEPROM_Lock();9 O& |; J1 |$ A  L: Y7 b, `: l1 d% y
  4. ...
    6 U* x+ j: G; v7 f
  5. if(btn_getState(&K1_BUTTON_150mS) == BTN_EDGE2){
    ) }5 ]. f6 ?3 I4 t7 m3 r* Q
  6.         printf(" K1 150ms button!,EEPROM_Erase test\r\n");0 F& [* d+ Z' G, {2 X
  7.         HAL_FLASHEx_DATAEEPROM_Unlock();
    - a& v8 l: n# q
  8.         HAL_FLASHEx_DATAEEPROM_Erase(DATA_EEPROM_START_ADDR+4);) c# g% M5 _- X! j
  9.         HAL_FLASHEx_DATAEEPROM_Lock();
    6 M/ p% K( N- Z: s
  10.         HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
    9 {2 j0 I9 L7 N# O, z* ^6 w& R8 b* k# u2 Q
  11.     }
    + M' r$ i8 v) f! H' y; ]1 O
  12. ...
    : j6 @  G" L# M$ n& L8 N6 G1 @
  13. if(btn_getState(&K2_BUTTON_150mS) == BTN_EDGE2){# C9 j7 A- d7 U) b( d; z$ {
  14.         printf(" K2 150ms button!EEPROM_read test\r\n");
    & Z( g0 r7 K% l1 S
  15.         read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);7 \5 |( T  m& i
  16.         printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);+ F. s- T% I& u
  17.     }
复制代码
) W& [7 X9 o- j2 l+ W" |" P& t

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

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

4 T" i) A+ A1 D- j) h
写入问题说明修改

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();
    , _+ i4 i; U  C1 b' u
  2.   HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, DATA_EEPROM_START_ADDR, 0X88);
    * C4 U8 V7 Z  Q+ N
  3.   HAL_FLASHEx_DATAEEPROM_Lock();
    1 Q: _$ l% u! c8 I
  4.   read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);
      I' l0 a$ B9 J$ l% E( n3 y3 D
  5.   printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);
复制代码

* x" v1 n' Y5 s6 j

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

  1. void MY_DATAEEPROM_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data)   3 t/ ?; n1 B# Q" S5 L3 l, y9 {) P
  2. {' M) a$ R2 k6 a) a. v1 [. N; J
  3. HAL_FLASHEx_DATAEEPROM_Unlock();        
    , U3 Q* p. D, t+ c/ T4 E
  4.     HAL_FLASHEx_DATAEEPROM_Program(TypeProgram, Address, Data);, c, b' `5 z% l% O
  5. HAL_FLASHEx_DATAEEPROM_Lock();
    5 h3 a9 k$ D: }
  6. }
复制代码

0 Z* `$ N) J( S. m& u

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

$ m: d# E6 M# ]$ }% ~
2.3   Flash写函数

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

  1. /* IO operation functions *****************************************************/: e3 U* Y7 y1 x0 g
  2. HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data);" i* I! h1 `6 m6 [5 {' J; Q6 E5 B- J, u( G
  3. HAL_StatusTypeDef HAL_FLASH_Program_IT(uint32_t TypeProgram, uint32_t Address, uint32_t Data);8 s. t: Z0 O2 H3 Z$ e( H
  4. 4 r; e3 `7 a, J/ ^8 X
  5. /* FLASH IRQ handler function */6 W- Q/ B- u' D" O9 n* I
  6. void       HAL_FLASH_IRQHandler(void);
    5 V- V( D1 ?! c  [+ J; S3 D8 b
  7. /* Callbacks in non blocking modes */
    $ Q% I. k; X* o( B; O5 T9 S# j4 B) t
  8. void       HAL_FLASH_EndOfOperationCallback(uint32_t ReturnValue);' L1 d( T# l+ d
  9. void       HAL_FLASH_OperationErrorCallback(uint32_t ReturnValue);
    7 [6 }- L% {$ D. k
  10. 7 {0 m$ _3 p; Q) H9 W2 W
  11. /**- D, b8 c4 |2 J2 |
  12.   * @}0 W& t0 m+ s$ }; q: F4 g" P
  13.   */) G3 Y8 p* ~. c& ^( u1 t1 Q

  14. ) c5 f) k* t5 b# x& O$ E
  15. /** @addtogroup FLASH_Exported_Functions_Group2
    2 P: M$ R" C: B7 V  D& X
  16.   * @{6 @! F% j5 ^) F" f6 o# E
  17.   */
    . |2 A; i% X) D
  18. /* Peripheral Control functions ***********************************************/. B; L2 Y! K; l! h, x& \
  19. HAL_StatusTypeDef HAL_FLASH_Unlock(void);
    ; W5 S3 M. M( J/ f/ f
  20. HAL_StatusTypeDef HAL_FLASH_Lock(void);7 Q9 b; q0 e& B9 x2 b/ m' R
  21. HAL_StatusTypeDef HAL_FLASH_OB_Unlock(void);
    % u$ o3 j* q! W1 E8 A) q
  22. HAL_StatusTypeDef HAL_FLASH_OB_Lock(void);7 N! s/ l3 q. T
  23. HAL_StatusTypeDef HAL_FLASH_OB_Launch(void);
复制代码

7 ~1 o+ \+ m: C! D

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

3 G4 h: r$ i8 N( H5 k. `

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


; O+ g# X- E% r* ]7 U
  1. HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data)& v' d/ W1 v  W& O5 H
  2. {
    ) A# {* b: Q; I& [1 g
  3.   HAL_StatusTypeDef status = HAL_ERROR;
    - K% V5 F- `( n% k; m
  4.   
    ! ?4 ~2 c: H" }# g* y) S7 k
  5.   /* Process Locked */
    2 q! r5 Q+ p+ c9 ~2 O  l
  6.   __HAL_LOCK(&pFlash);
    $ a) m. G; d% J- x

  7. 7 q* w% r0 U/ f. `
  8.   /* Check the parameters */
    % g. O* P4 b& t9 ]: e
  9.   assert_param(IS_FLASH_TYPEPROGRAM(TypeProgram));
    % w) o; d! K5 Q. Z
  10.   assert_param(IS_FLASH_PROGRAM_ADDRESS(Address));/ v* h* L; C! q/ U
  11. 8 }: Z2 a4 `4 O: l
  12.   /* Wait for last operation to be completed */) l/ F, q2 ~; q9 G; x3 b
  13.   status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
    ' H& Q2 \5 Z# |
  14.   
    # C* P! ?. n& b
  15.   if(status == HAL_OK)
    . @- }: x1 i9 n/ Z/ h2 U+ m" d
  16.   {6 t0 C* u4 f# j- j4 H2 i& Z
  17.     /* Clean the error context */
    ' K, l/ J9 y6 I7 p
  18.     pFlash.ErrorCode = HAL_FLASH_ERROR_NONE;
    6 W8 I3 v7 n8 r& J* Q$ g- k0 G1 J
  19. , t- [/ v. S) W1 z3 s
  20.     /*Program word (32-bit) at a specified address.*/
    7 T2 ~% @7 i( U, D  N  k0 e
  21.     *(__IO uint32_t *)Address = Data;
    7 \) e9 K' N4 [* P
  22. $ \& ^: @! P8 F# [0 |
  23.     /* Wait for last operation to be completed */8 o, L2 ]2 _2 g$ n; y, K, |) ]# K
  24.     status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
    7 b+ w# n8 x9 a! |6 y/ F
  25.   }1 ]0 d  t) k% L; N7 j
  26. - G5 Y8 `5 b# d& X* c
  27.   /* Process Unlocked */: G! T6 `+ k! k$ {
  28.   __HAL_UNLOCK(&pFlash);
    2 Z. Y7 H& g" K( i( }9 O1 \/ q( p

  29. 4 S, w; F& s' Q. B
  30.   return status;
    ' c" N: |; |) O# a4 u
  31. }
    + T& |6 q. X! i7 R$ _
复制代码

- ~" s7 c! \" T& \( E$ ]

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

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

  1. #define ADDR_FLASH_PAGE_505      0X08000000 + 128*504   //
    * P4 s  q. V! t% g
  2. #define ADDR_FLASH_PAGE_506      0X08000000 + 128*505   //
    5 ?1 d+ v, ]5 Q7 z
  3. #define ADDR_FLASH_PAGE_507      0X08000000 + 128*506   //: d2 @: t% Q$ e* i4 w# T
  4. #define ADDR_FLASH_PAGE_508      0X08000000 + 128*507   //" z$ a/ a, s; b$ V  r% f
  5. #define ADDR_FLASH_PAGE_509      0X08000000 + 128*508   //1 b, [- S0 I- C& f$ o7 Z: x6 F
  6. #define ADDR_FLASH_PAGE_510      0X08000000 + 128*509   //) F" b+ g) ~0 |& I
  7. #define ADDR_FLASH_PAGE_511      0X08000000 + 128*510   //
    5 y! k' B- o  H, u6 L2 `2 |
  8. #define ADDR_FLASH_PAGE_512      0X08000000 + 128*511   //最后一页
复制代码

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

  1. void MY_DATAFLASH_Program(uint32_t Address, uint32_t Data)   9 a$ G9 A% w2 i( [
  2. {, |! J' r2 a* ~7 _# x  s' S
  3. HAL_FLASH_Unlock();        
    8 i( \0 H! V7 {6 Z: B
  4.   if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){$ `1 D7 T6 x) n% }( f
  5.     printf("write data 0x%x OK\n", Data);  \, O; O1 V# e1 W. p
  6.   }
    . g$ d' }, z$ X$ a, c
  7.   else{/ r. L& K! _( H( O1 J
  8.     printf("failed!!!\n");. d) g( }3 B3 N
  9.   }1 v( s, s- V3 J' ?3 Q) Y7 b+ U( _
  10. HAL_FLASH_Lock();
    9 {0 Q& e+ w8 R" T0 O4 r+ l  L
  11. }
复制代码
* J/ e- J+ I- v+ Y, @4 {# b/ @

测试一下;

  1.   MY_DATAFLASH_Program(ADDR_FLASH_PAGE_512,write_data2);
    9 y  x3 x% `5 f& E
  2.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_512);; Y! }1 M5 {, t' C9 A: ?
  3.   printf("the ADDR_FLASH_PAGE_512 is: 0x %x \r\n",read_data1);
复制代码
5 e: O; y2 R4 z9 P

在没有写入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)   
    1 m- h( }( a6 G/ F( \
  2. {+ g0 U4 E' m- N! H8 E
  3.   FLASH_EraseInitTypeDef EraseInitStruct;
    8 R) \8 T. U4 A9 I7 U
  4.   uint32_t checkdata;
    + s0 Y( ]; R3 n9 E
  5.   uint32_t PAGEError = 0;
    ; r! }4 _3 h6 [
  6.   checkdata = FLASH_ReadWord(Address);5 N/ [' M2 y, y* A& Y/ a6 g
  7. HAL_FLASH_Unlock();
    & c, s8 p# |' i  k; a9 h' ?
  8.   /*如果是0,直接写*/
    5 J1 {5 {" R* D( H8 W  D
  9.   if(checkdata == 0){       % o, J  T. @- I# \9 v
  10.     if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){
    * B3 |+ W0 c, A, B+ X  [
  11.       printf("write data 0x%x OK\n", Data);* B; p7 J: K; v! @
  12.     }8 G/ ^! J* T) C: p
  13.     else{- K% M0 |5 w1 s8 N1 I
  14.       printf("failed!!!\n");
    ! Y: B- y# g$ H) q! }
  15.     }
    % y* @  ^' g& }' B$ w1 F
  16.   }
    ' ^$ x* N2 e$ e3 Y2 E0 o+ a
  17.   /*否则擦除再写*/+ X$ ]0 r& N5 X( i
  18.   else{
    3 @) l6 \2 d8 P, f* O! e# `
  19.      __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);0 n, _% C' {  h0 W
  20. / {6 y( w& q) H, j& Z
  21.       EraseInitStruct.TypeErase   = FLASH_TYPEERASE_PAGES; // 刷除方式
    & H% {- k( j$ l
  22.    EraseInitStruct.PageAddress = Address; // 起始地址5 T2 G$ _& k! E! \
  23.       EraseInitStruct.NbPages = 1;# i' `- B5 Q, B, i( ]9 _& Y" N
  24. # X, t7 i- y7 a" Q
  25.       if (HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError) != HAL_OK)) H9 C$ [4 [3 M5 r) o5 }' o0 W3 ?
  26.       {
    5 \1 w. X) i) f" Q" F
  27.         // 如果刷除错误/ V' n9 `" k6 }' J( A
  28.         printf("\r\n FLASH Erase Fail\r\n");" T+ R0 z. ^$ y, o5 N4 I
  29.         printf("Fail Code:%d\r\n",HAL_FLASH_GetError());
    4 z, R# z0 \% O& N* \6 {
  30.         printf("Fail Page:%d\r\n",PAGEError);
    7 x% E$ ?2 n+ o8 C1 _5 C9 [
  31.       }! }8 ^; ^5 Z) i: ]; A
  32. 2 G9 M/ g5 [2 X( w( f! Q: e
  33.       if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){
    * B/ R" A$ V& a! N" y3 U; I9 v: N
  34.       printf("write data 0x%x OK\n", Data);
      P  H5 Z4 j4 T5 D# u. ]# d- c4 Q
  35.       }
    5 H8 p' H0 l7 ], ^
  36.       else{
    4 P# v  _, O' l. i; L  H( E! z
  37.         printf("failed!!!\n");( X8 l0 G% R1 p+ ^9 [( m
  38.       }/ D: [: f1 I! G: x& `
  39. % T" n# |% h+ W* j9 z& z. Q) R+ N- ^
  40.   }0 |: O  \3 ]. k/ K# _, C' P
  41. HAL_FLASH_Lock();6 M$ ^( m3 z9 Y* d5 t! |
  42. }
复制代码
% S1 t5 ~6 T4 P( }& r/ R. K

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

  1. u32 write_data1 = 0X12345678;
    * S# L5 r1 {8 P- H1 ?' S; p
  2. u32 write_data2 = 0X87654321;
    % }; j% R8 E, ]7 F, U1 R
  3. u32 write_data3 = 0XFFFFFFFF;
    ( g. }; j( F6 p% H

  4. , C( x  e! V6 q4 m9 {6 c
  5. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_508 + 124,0X508508FF);
    ! |3 G  y7 y* Y- X/ e. w
  6. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509,write_data3);! j3 T- E1 E: o( @% T
  7. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509+4,write_data2);
    % i# n: Z; ~! P5 |0 o
  8. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509+8,write_data1);1 Q: w1 D: Y2 `4 x# p3 Q9 r
  9. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_510,0X510510FF); $ E; B: B0 W( G  K0 \* y: }  t/ N

  10. ( y' c; B9 v1 {- |7 K+ ^
  11. read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_508 + 124);, `7 P9 }) l8 @6 `& @& {
  12. printf("the ADDR_FLASH_PAGE_508 last is: 0x %x \r\n",read_data1);
    # c' {3 \; l/ N. c1 z
  13.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509);4 r$ O, ]* D' R' L; S
  14.   printf("the ADDR_FLASH_PAGE_509 1 is: 0x %x \r\n",read_data1);
    + R! i" j1 h3 \* u" {
  15.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 4);
    / ]- r# ^3 h& M7 @$ p: t" y7 b
  16.   printf("the ADDR_FLASH_PAGE_509 2 is: 0x %x \r\n",read_data1);
    & I8 `. O4 T$ E9 F* s
  17.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 8);9 Z" ^) w* ]1 \% _) b
  18.   printf("the ADDR_FLASH_PAGE_509 3 is: 0x %x \r\n",read_data1);) Z3 E# W) @% h2 I# I
  19.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_510);4 \9 e5 F' i, |; O* a" |/ p$ E
  20.   printf("the ADDR_FLASH_PAGE_510 first is: 0x %x \r\n",read_data1);/ A' E* d. l/ V9 h) P/ T( Y
  21. : n( C' u# f: _0 V* ]! F' f* |4 S
  22. if(btn_getState(&K2_BUTTON_150mS) == BTN_EDGE2){; ?5 W* Q* ]/ }9 M9 ]9 j2 ~: {: s+ A% ^
  23.         printf(" K2 150ms button!EEPROM_read test\r\n");* M- e, i: Z# ?8 G" b/ q
  24.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_508 + 124);! O! y" q4 T: k$ j
  25.         printf("the ADDR_FLASH_PAGE_508 last is: 0x %x \r\n",read_data1);  d! r" N& e8 w7 U0 Z: ]
  26.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509);
    # B3 e) s2 n4 P" i& w- C
  27.         printf("the ADDR_FLASH_PAGE_509 1 is: 0x %x \r\n",read_data1);
    % W" @/ G, Z" Z6 R$ b1 h0 P
  28.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 4);* X9 u; a3 s( V9 l
  29.         printf("the ADDR_FLASH_PAGE_509 2 is: 0x %x \r\n",read_data1);  A+ ?4 ?( {1 K1 O: m& U9 B' G1 u
  30.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 8);) E# R  l9 O7 V4 Y' ^
  31.         printf("the ADDR_FLASH_PAGE_509 3 is: 0x %x \r\n",read_data1);# @  e- V+ ~: R
  32.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_510);
    - \6 n9 x1 U% {
  33.         printf("the ADDR_FLASH_PAGE_510 first is: 0x %x \r\n",read_data1);
    & U0 t) k3 U! x& X
  34. ( u7 G- U* r% d* X
  35.     }
    7 d+ {7 O; [1 O% c; C# U% {
  36. if(btn_getState(&K1_BUTTON_150mS) == BTN_EDGE2){
    , o0 }% u% Y$ q+ h
  37.         // printf(" K1 150ms button!,EEPROM_Erase test\r\n");0 B1 j# x$ D4 j: y; S  \
  38.         // MY_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, DATA_EEPROM_START_ADDR, write_data1);8 P5 a; N/ }4 E7 k6 C
  39.         printf(" K1 150ms button!,flash write test\r\n");* C  x6 [% ~9 X% i8 l: s- L
  40.         MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509,0X33333333);
    ) s0 f& U5 P- [. ^" I
  41.         HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
    . M, G! l: H4 x+ U7 I
  42.     }
复制代码
- B7 |# l# L  A" g8 M) ^8 I

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

( M( }6 h# _% V$ ?

微信图片_20230612172448.png

6 w1 `" X7 l1 S0 t% ]

微信图片_20230612172355.png


" l2 R- Q. ]# [6 [

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

/ }' H) T+ e; A# d( X
2.4 读写EEPROM的后续问题

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

" T% n, M' ^+ Y6 Z; a3 R; ]% |

微信图片_20230612172339.png


% n( W5 p/ P6 z6 X

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


9 m5 o+ _5 [& j7 |* G4 i# T2.4.1 问题的出现和解决

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

+ l& F. c6 _) B9 o1 r# B. C. G

微信图片_20230612172215.png


3 {- N" i/ E& D. d+ C% r

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

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

1 m8 m! r5 Y6 t' k" t( _

微信图片_20230612172212.png

& y7 W. ?; d9 U  `

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

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

  1. BlueID_STRUCT test;
    " }9 S  A; _4 S# V
  2. ...
    8 b1 \$ ?/ h9 i
  3. /*
    ! K+ d" ~3 L- P$ g2 v9 z; y' J5 Y! [
  4. CHBlueID_STRUCT Flash_PowerOn_BlueCheck()/ c  d) z2 E  O3 S. i8 P
  5. {  h) R: m$ g, r! w4 e- {( q

  6. $ s7 b; c: N7 L6 D+ S+ Z
  7. CHBlueID_STRUCT PowerOn_ID;
    3 R8 ?6 s& _! O, L
  8. PowerOn_ID.CH1ID = FLASH_blueIDRead(CH1_ID_ADDR);+ X* B6 v& e5 l2 `0 c
  9. PowerOn_ID.CH2ID = FLASH_blueIDRead(CH2_ID_ADDR);
    * Q) B7 z' J2 J5 O, U5 ^
  10. PowerOn_ID.CH3ID = FLASH_blueIDRead(CH3_ID_ADDR);
    9 V; X9 T1 v6 @  \) G( t# t  k
  11. PowerOn_ID.CH4ID = FLASH_blueIDRead(CH4_ID_ADDR);3 }3 w( o$ D4 r  C+ V0 ^2 I
  12. PowerOn_ID.CH5ID = FLASH_blueIDRead(CH5_ID_ADDR);9 r; r+ Q: t+ [! R) v4 Y
  13. PowerOn_ID.CH6ID = FLASH_blueIDRead(CH6_ID_ADDR);
    ( }) U# z% G$ b  t* |/ E
  14. PowerOn_ID.CH7ID = FLASH_blueIDRead(CH7_ID_ADDR);
    $ V' T$ P- h2 h: a" x$ r
  15. PowerOn_ID.CH8ID = FLASH_blueIDRead(CH8_ID_ADDR);
      E6 C3 s/ u+ E- M7 ]
  16. PowerOn_ID.CH9ID = FLASH_blueIDRead(CH9_ID_ADDR);1 x1 V6 D$ v$ V) z) R
  17. PowerOn_ID.CH10ID = FLASH_blueIDRead(CH10_ID_ADDR);1 ^& F- j+ n4 h. I* m" k6 o

  18. ) ]/ ?4 r: t8 |3 o: u, {
  19. return PowerOn_ID;# r( {- y7 W- c7 J1 [/ D. T
  20. }* w0 z) h+ O! b) L
  21. */
    # n( S. w9 o( h. `# W, P, _
  22. BlueChipID = Flash_PowerOn_BlueCheck(); //上电先把ID读出来做比较
    3 V" G4 ]7 n/ }" i: M" G
  23. //打印一个出来测试,看结果
    " p( m% Z1 U7 f7 j
  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);
    , |" F" u. z% P3 s, p

  25. 8 T' Z7 G. Y  ~1 X' q) }: D% _; D
  26. test.ID1= 0XFF; //结构体每个元素是 uint8_t 类型7 }" N8 I/ P/ B+ @7 d5 P
  27. test.ID2= 0XEE;4 J- V) P2 v5 i$ O4 V4 w
  28. test.ID3= 0XDD;) u: {" [! K1 x, J9 |- e
  29. test.ID4= 0XCC;
    5 y3 }7 X' O) k  _: w- u
  30. test.ID5= 0XBB;
    $ D" j+ H4 n( {  m1 O
  31. test.ID6= 0XAA;
    3 ]: _" v5 i7 }) N) r- q- ^

  32. 2 r% |) E, i' ?6 ?
  33. while (1){...}
复制代码

- h" ^0 z' s2 ?' S2 i

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

  n# p5 ^9 x' Q+ E, M: i% K3 t1 X

微信图片_20230612172209.png

+ {% w4 ~; T8 ?* y1 b4 u( _3 R

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

2 p2 Q( T- ~7 I$ ]) D  v

微信图片_20230612172207.png

. @% A5 V% O, T4 d+ N" p

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


2 b8 ]8 G+ Z8 X9 w- w  [! C" C

微信图片_20230612172058.png


( W8 D& n* G3 p! U9 r/ n

微信图片_20230612172050.png


8 j- V( P6 z, ^( y0 d3 r3 ?0 x

打印的结果:

1 Y2 d. f! J2 {0 ^$ Q) K

微信图片_20230612172039.png


: c( ?$ W% B( D- v5 D

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

  1. BlueID_STRUCT FLASH_blueIDRead(uint32_t address)
    8 m# T* D6 U8 B$ q  q! ]
  2. {
    * @8 W; l+ `9 R/ \. v; h" Z
  3. BlueID_STRUCT u48id;
    5 X, _, N* h" [. x  O
  4.   // uint16_t temp1,temp2,temp3;  /**(__IO uint16_t*)address; */' h, M7 f4 R6 S0 u, H6 F5 P* A5 ~6 [
  5.   // temp1=*(__IO uint16_t*)address; 6 v( J7 Y! I' p  G  x* w4 N& n7 k# z
  6. // u48id.ID1 = (uint8_t)(temp1>>8);
    9 K' x. [2 ?: G0 h$ L
  7. // u48id.ID2 = (uint8_t)(temp1&0X00FF);  H5 Z' n, v1 h  z% D# ~$ f
  8.   // temp2=*(__IO uint16_t*)(address+2);
    8 _( T! [. S, k8 C) k" K  m8 |8 O: j8 P! K
  9. // u48id.ID3 = (uint8_t)(temp2>>8);0 \+ L8 H" t% G, w
  10. // u48id.ID4 = (uint8_t)(temp2&0X00FF);
      ~0 I! O; f) N- q8 q3 n1 ]
  11. // temp3=*(__IO uint16_t*)(address+4);/ x  ?1 x8 C7 K7 I5 W; Y
  12. // u48id.ID5 = (uint8_t)(temp3>>8);+ q7 u$ Z0 S& f/ I; b' s
  13. // u48id.ID6 = (uint8_t)(temp3&0X00FF); * r4 N% }" p( B% C

  14.   T. T1 ^7 m: g, K6 x, {8 G0 z% H
  15.   u48id.ID1 =  FLASH_Readbyte(address);6 |4 X8 W6 e- R' D2 Y0 T- i
  16.   u48id.ID2 =  FLASH_Readbyte(address+1);
    ) j' p  f+ M, e9 R
  17.   u48id.ID3 =  FLASH_Readbyte(address+2);
    # z  G; `, W( ?; J' P* A( j) [' C
  18.   u48id.ID4 =  FLASH_Readbyte(address+3);
    $ g) g; v$ K2 d8 d9 G& l
  19.   u48id.ID5 =  FLASH_Readbyte(address+4);; l) z4 B& N8 N4 B
  20.   u48id.ID6 =  FLASH_Readbyte(address+5);
    + y& U( O/ z4 t
  21. , Z, G& \. Z  \& r& \& J" ^" o+ E
  22.   return u48id;2 k- r/ M2 y" @) ~! ?) B( U
  23. }
复制代码

7 t. S6 A% f/ a9 q

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


- f& z4 c% E2 p

微信图片_20230612172035.png

, X* C! |* A" @3 z2 {& i& \
2.4.2 问题的分析(大小端模式数据格式)

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

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

: e) k3 s+ s! B

微信图片_20230612172032.png


# G3 }7 Q* N& q! [4 ^% R

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


- l- X, ~1 T# j& N$ X

微信图片_20230612172030.png

7 c1 ~8 r( B* s5 l

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

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

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

5 }1 I# U3 q* _9 y4 H
2.4.3 STM32L071RBT6 EEPROM读写全字问题
3 e" \7 U* z/ ~1 O
读问题的出现

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

  1. uint32_t  FLASH_ReadWord(uint32_t address)7 Y6 V( u, j+ K; N8 Q6 j6 R( Z% k% M# F
  2. {7 i+ z! Y6 X: h3 _
  3.   return *(__IO uint32_t*)address;3 p: L& f% \  }% c( T& ]' V
  4. }
复制代码
1 ]) c, @4 D# j, }

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


: U. D( v8 Y! }$ `

微信图片_20230612172026.png

0 n. A! Q0 Y2 |# X# f9 m

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

2 q. R' F8 R4 b3 t3 R1 [, r  d

问题的解决:

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


! E" C  N% ?! N' _

微信图片_20230612172019.png


* A9 x+ t8 r) Z# v% Y% D6 A

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

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


! C, H8 p6 l1 w

微信图片_20230612172012.png

/ S" Y' C/ Z1 c2 r5 s

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

  1. u32 FLASH_ReadEnoceanID(uint32_t address): P0 T! Q2 k* `7 q" P
  2. {0 z. F* r! l; e
  3.   u32 keepid;
    ; O; y& i- `! c( L: }" J
  4.   u8 i;# C8 T4 V/ s8 }, x" Q
  5.   keepid = FLASH_Readbyte(address);
    * g2 c6 V" C( p3 y
  6.   i = FLASH_Readbyte(address + 1);
      z, f8 ]) h& Z. N7 B  P0 ^+ P
  7.   keepid = (i<<8)|keepid;
    / j% W0 P; p7 d& [. z. S* a
  8.   i = FLASH_Readbyte(address + 2);
    / @& l; ]) j+ L# b2 F
  9.   keepid = (i<<16)|keepid;/ j/ X$ M0 p+ [: D: |
  10.   i = FLASH_Readbyte(address + 3);
    7 S+ i2 B6 v  n! z6 l/ h
  11.   keepid = (i<<24)|keepid;
    ! U+ Y  ^0 _% h: [
  12.   return keepid;# ?; X! j. e7 y
  13. }
    1 R; m- l$ R+ E, Z* C8 k6 R

  14. 9 R" e$ t: d' p0 H+ n
  15. CHID_STRUCT Flash_PowerOn_Check()
    , X2 z! N: C9 I  O- S( ?
  16. {
    1 `4 N8 f! d9 M% I1 J+ s% u5 o
  17. , \; W5 F3 P& D! Z3 M5 T. ]9 e+ P
  18. CHID_STRUCT PowerOn_ID;4 R; i! G* k% q+ x- {& A
  19. PowerOn_ID.CH1ID = FLASH_ReadEnoceanID(CH1_ID_ADDR);
    1 c3 x- _8 Z( Z+ y
  20. PowerOn_ID.CH2ID = FLASH_ReadEnoceanID(CH2_ID_ADDR);" h9 W' b; ^- q1 u) L. E  m
  21. PowerOn_ID.CH3ID = FLASH_ReadEnoceanID(CH3_ID_ADDR);
    5 K1 R4 v9 H: f% [) u
  22. PowerOn_ID.CH4ID = FLASH_ReadEnoceanID(CH4_ID_ADDR);) Q$ F$ U5 _1 R* r6 ^! A2 g
  23. PowerOn_ID.CH5ID = FLASH_ReadEnoceanID(CH5_ID_ADDR);: V0 K7 Z! W6 z
  24. PowerOn_ID.CH6ID = FLASH_ReadEnoceanID(CH6_ID_ADDR);
    : X- P* U9 j- }9 Y# F9 O* w
  25. PowerOn_ID.CH7ID = FLASH_ReadEnoceanID(CH7_ID_ADDR);( O1 h7 Y9 C0 B- D- T9 _
  26. PowerOn_ID.CH8ID = FLASH_ReadEnoceanID(CH8_ID_ADDR);) v2 m! N$ A( R3 |2 T
  27. PowerOn_ID.CH9ID = FLASH_ReadEnoceanID(CH9_ID_ADDR);" j$ [) k! U7 _0 g: h
  28. PowerOn_ID.CH10ID = FLASH_ReadEnoceanID(CH10_ID_ADDR);
    $ w. i& E+ W0 ~+ A: I' c
  29. & S6 {0 G! |/ O  x
  30. return PowerOn_ID;
    0 Y, L6 ]; ~3 ?5 g) h& d( T' t! R! h
  31. }
复制代码

5 f. E7 n4 j) s# A

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


8 Y3 @5 E/ G7 v& a+ B  L. Y6 B写问题的出现

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

一直用的写全字函数为:

  1. FLASH_Status  FLASH_WriteWord(uint32_t Address, uint32_t Data)
    ; }$ o- h: i2 E! \2 D, ]
  2. {" n% O; D) L0 c5 L; B

  3. # c) Z# f' X3 V# H
  4. FLASH_Status i = FLASH_COMPLETE;: b4 C* L5 b- K! ?3 q, I$ S! M
  5. 1 r; D& m- V! Q: n% X
  6. HAL_FLASHEx_DATAEEPROM_Unlock();  
    : ^' p8 ^% H* [# L! E5 e. _- q$ G
  7.   while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, Address, Data) != HAL_OK);  p* |& m. O% V. H! J# c
  8. HAL_FLASHEx_DATAEEPROM_Lock();
    3 `1 J  y; D8 E- m
  9. ; ]% p# w! Q* P# Q5 L
  10. return i;
    & }1 b9 B* E5 C+ o5 Q3 _' a, w9 i
  11. }
复制代码

; e( [0 W" S' I. R, ^

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


/ f, D1 R7 |0 I, p. |

微信图片_20230612172009.png


' n' i! N! R' M: ^5 |. a6 N

但是使用时候发现:


* t7 l  T7 v& F! g( v: g6 t

微信图片_20230612172006.png

/ B& ?  B! e6 I4 I0 x$ X8 x" }

问题的解决:

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


; n- [8 @! l+ f$ `7 `8 z

微信图片_20230612172003.png

* B/ f8 o3 J. |4 \; y+ ]

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

  1. FLASH_Status  FLASH_WriteWord(uint32_t Address, uint32_t Data); f6 q  [1 O3 P; b
  2. {
    ( M) b! O0 k% l* w
  3. 2 ]8 i% q. S) S" U+ W5 C) {. Z. A
  4.   FLASH_Status state = FLASH_COMPLETE;
    ) h" U) N4 ?4 a2 d( Q1 b  I6 b
  5.   u8 i = 0;
    . }+ |% g/ r& o7 }1 ?
  6.   u8 writedata[6]={0};7 Q( `% p6 w; T. c

  7. 1 [5 U" F2 d& X4 ]; h
  8.   writedata[0] = (u8)Data;
    - ]9 `% g) p/ `. ^- i& D
  9.   writedata[1] = (u8)(Data>>8);
    4 S5 C, d" K, }6 }
  10.   writedata[2] = (u8)(Data>>16);
    3 J8 S# t7 H# M5 }3 g( i
  11.   writedata[3] = (u8)(Data>>24);) B/ J' T  v4 O* R* g! A

  12. . [, [1 o) m+ n$ Q6 M
  13. HAL_FLASHEx_DATAEEPROM_Unlock();  % Y4 t- T( J/ U# \5 u
  14.   for(i=0; i<4; i++){: i0 r2 ^# ~  e8 A& o
  15.     while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, Address + i, writedata[i]) != HAL_OK);$ y9 T  s* c, N8 n
  16.   }
    4 V) u8 d" P$ U4 r* O
  17. HAL_FLASHEx_DATAEEPROM_Lock();
    9 [! c, B& I. i  w  K6 A

  18. ) y* r/ o, m7 C# X
  19. return state;, m% T4 t6 @& F5 u
  20. }
复制代码

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

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

2.4.3小结使用的芯片为  STM32L071RBT6

/ X7 R! e1 }* N+ a# N
最后问题的解决

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

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


/ k, V! y6 `4 J, P' j  ^( ]  k( W( @. o

微信图片_20230612172000.png

  t% w+ @" g) m+ H. N* A3 T- }0 T5 p

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

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


% k5 y7 A  k$ l/ I8 [+ y* q

微信图片_20230612171954.png


) G) O+ Q& V' x4 }+ {% a

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

8 O# a# @6 Q3 W3 K
收藏 评论0 发布时间:2023-6-12 17:30

举报

0个回答

所属标签

相似分享

官网相关资源

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