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

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

$ y9 C, Z& \: X

微信图片_20230612172451.png

# i7 H. d6 l- ?7 E2 U9 o; {5 m

微信图片_20230612172454.png

0 p% E5 q0 R0 {% k

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


  R/ z: ?) l: d0 E# q. j: J

  1. #define FLASH_SIZE                (uint32_t)((*((uint32_t *)FLASHSIZE_BASE)&0xFFFF) * 1024U)  X& G/ t7 h2 A$ Q% ~0 G
  2. #define FLASH_PAGE_SIZE           ((uint32_t)128U)  /*!< FLASH Page Size in bytes */
复制代码

& l  @/ g3 }  q" T4 o, E& [

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

' y# A3 \6 ?9 d8 N( U$ P) J
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,因为怕有时候数据不够放……


$ Y5 T; o  `2 ~1 G; n6 Q2.1 读取函数
  1. //读取指定地址的半字(16位数据)
    , s6 r" F& R; k( f( ~& z8 G( L6 w$ s3 J
  2. uint16_t FLASH_ReadHalfWord(uint32_t address)
    ; J6 K* A2 `" U' `2 x. l
  3. {4 ]! \" f$ b- O+ N! W! d8 C
  4.   return *(__IO uint16_t*)address; * ]( ]+ m1 }  p; K
  5. }
    $ Z" s. O- l! U
  6. . \  z  w+ Y7 J" m8 i1 G
  7. //读取指定地址的全字(32位数据)
    , q1 d4 e! @8 a- M1 h
  8. uint32_t FLASH_ReadWord(uint32_t address)8 u" A- L- _6 u' m& G, L0 G& \% ?
  9. {8 c0 E& C4 D1 F: k$ W3 X4 X
  10.   return *(__IO uint32_t*)address;. Q. l5 i; R  A2 q) ?5 m
  11. }
复制代码

, Z; ~; Q7 q0 M' F+ U5 h
# N4 ^2 q" z# |% w7 m; X- _! z0 j

简单测试一下:

  1. u32 read_data1=0XFFFFFFFF;
    / A: K0 X) G6 S5 ^5 W* ]9 _* M
  2. u32 read_data2=0XFFFFFFFF;
    5 Y% t( k) P. m: T+ ]& a
  3. ...7 C8 M6 ]" k" f, q, O
  4. read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);
    - k; U& I+ G( B) V0 t
  5. printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);& A& ~7 x# h% ]- k' n. D
  6. read_data2 = FLASH_ReadWord(DATA_EEPROM_START_ADDR + EEPROM_PAGE_SIZE);
    4 E! l- l0 v& F; L6 d& k
  7. printf("the EEPROM sceond page test data is: 0x %x \r\n",read_data2);
复制代码

2 |8 e* {2 R& P7 u) N3 h) _, b

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


0 q( Q- y+ q( L6 {5 ~( c1 M  z2.2   EEPROM写函数

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

  1. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Unlock(void);, d- ?! j" R: U
  2. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Lock(void);! [  x* Y2 x9 y! s' z- U

  3. + a! w: M6 E& O( x- ?3 q4 A1 v' p
  4. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Erase(uint32_t Address);& E3 J! ]5 j6 S; R
  5. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data);
复制代码

3 h: \* {( w( j

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

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

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

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

  1. HAL_FLASHEx_DATAEEPROM_Unlock();
    8 Y6 u9 {- A: J0 J" t) w4 v5 T& J
  2. HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, DATA_EEPROM_START_ADDR, write_data1);
    : o% B( `/ X8 Y& ^; x
  3. HAL_FLASHEx_DATAEEPROM_Lock();0 B% Q4 R( W! h8 r, V/ W% J  \
  4. ...8 E: s5 w, |* i; N& K4 c0 J
  5. if(btn_getState(&K1_BUTTON_150mS) == BTN_EDGE2){( w8 \+ p. g; u) }) e* I- F5 i, e
  6.         printf(" K1 150ms button!,EEPROM_Erase test\r\n");
    + ?  ?7 T/ ~# p
  7.         HAL_FLASHEx_DATAEEPROM_Unlock();
    9 o, g6 R5 d3 \' b
  8.         HAL_FLASHEx_DATAEEPROM_Erase(DATA_EEPROM_START_ADDR+4);! Z2 c. Q" ~( G: u! ?2 n* |
  9.         HAL_FLASHEx_DATAEEPROM_Lock();5 c+ _- Q6 W! N2 z- A! a; }
  10.         HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);+ X: @! V; W1 x( R# D
  11.     }
    2 w' }- A4 C6 g0 U
  12. ...: w. h4 I3 ]0 P
  13. if(btn_getState(&K2_BUTTON_150mS) == BTN_EDGE2){
    7 V. h! E1 g8 c+ x1 z
  14.         printf(" K2 150ms button!EEPROM_read test\r\n");* L+ q5 }8 |- v. |  u" H
  15.         read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);
    7 l* c0 y" q1 j
  16.         printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);
    2 `5 a" n+ ^  r$ |
  17.     }
复制代码
" K* Y9 ]- _; Z. K, D

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

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


' r* R' b) @, w写入问题说明修改

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();
    % a. }$ B2 q3 u9 Y
  2.   HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, DATA_EEPROM_START_ADDR, 0X88);2 P, W9 g/ \9 Y& e# s
  3.   HAL_FLASHEx_DATAEEPROM_Lock();; K8 D, s" E4 m6 i0 a: [
  4.   read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);" h8 U* X2 b& f! ?8 C! ^
  5.   printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);
复制代码
: e$ D5 `( R/ L7 |8 D+ F9 [

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

  1. void MY_DATAEEPROM_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data)   
      L  B7 g5 j/ k4 ^, ?
  2. {3 P3 n  S& p: A2 J& D1 I2 n( F
  3. HAL_FLASHEx_DATAEEPROM_Unlock();        : i2 q% ?. j& D" J1 e
  4.     HAL_FLASHEx_DATAEEPROM_Program(TypeProgram, Address, Data);
    # W3 O1 `) n3 o) O/ a
  5. HAL_FLASHEx_DATAEEPROM_Lock();
    ( d# L9 Z  g0 ^" r  \+ }- `! Z) h% T
  6. }
复制代码
, q! v3 X7 \6 Z

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


& j  O1 q# l' }4 O3 B0 H0 X( D2.3   Flash写函数

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

  1. /* IO operation functions *****************************************************/9 s! s7 k& A2 {
  2. HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data);
    8 c$ t+ T8 T2 q
  3. HAL_StatusTypeDef HAL_FLASH_Program_IT(uint32_t TypeProgram, uint32_t Address, uint32_t Data);/ {4 ?$ K. ~/ }  J: T' l
  4. ; x# ~3 D; j- R  }
  5. /* FLASH IRQ handler function */( k% P( a4 X% o* K( V, J
  6. void       HAL_FLASH_IRQHandler(void);8 z9 U( Y( l6 ~3 z
  7. /* Callbacks in non blocking modes */) Z& r: N- K; w8 g6 G& ?7 t/ o
  8. void       HAL_FLASH_EndOfOperationCallback(uint32_t ReturnValue);
    8 R" V2 w+ G; K  ]% {$ e$ r: g3 |4 B
  9. void       HAL_FLASH_OperationErrorCallback(uint32_t ReturnValue);
      p9 H1 L% w0 l6 O  `# c

  10. % C4 G& c3 R* e% l
  11. /**
    % [, m8 z- D* ?& i
  12.   * @}+ k  v# ?1 x! Z5 L# n" e5 M8 z
  13.   */
      i6 G* i: n: E: a7 v' m) C6 \

  14. ; l+ m; Q0 m) p! a
  15. /** @addtogroup FLASH_Exported_Functions_Group2
    ; S  |& k2 V: v) Y
  16.   * @{
      W  r& V+ d) W1 m( }+ k
  17.   */
    ) `( o' C) X7 X4 j
  18. /* Peripheral Control functions ***********************************************/
    ( O+ r! G) i- }8 M1 A
  19. HAL_StatusTypeDef HAL_FLASH_Unlock(void);
    , t6 s$ Z: b7 R' {
  20. HAL_StatusTypeDef HAL_FLASH_Lock(void);: D4 t& u4 C6 H2 J3 e
  21. HAL_StatusTypeDef HAL_FLASH_OB_Unlock(void);$ v1 ]! l6 }6 n) Y. K+ [6 u1 [; [( A9 x
  22. HAL_StatusTypeDef HAL_FLASH_OB_Lock(void);7 w- L+ a3 `; A# x* v8 M
  23. HAL_StatusTypeDef HAL_FLASH_OB_Launch(void);
复制代码

* ?  J  {) b+ r8 U

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

: u" ~+ \& Z$ s8 o0 v, R

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


& \9 U  {/ N& r
  1. HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data)
    2 {& g' d" r+ e
  2. {
    $ a, |% Q7 a& l0 B0 r# d
  3.   HAL_StatusTypeDef status = HAL_ERROR;8 t8 I- Q6 P3 J$ V! T, i
  4.   ' [3 p' J7 ?: X, S" n% d% O( T- O( P
  5.   /* Process Locked */
    % e5 G* Q7 q1 u3 {
  6.   __HAL_LOCK(&pFlash);1 R- e$ T. A9 T* ]
  7. , O: y* v- R/ ]! t6 ^
  8.   /* Check the parameters */# [% ~4 i  Q  v. I
  9.   assert_param(IS_FLASH_TYPEPROGRAM(TypeProgram));. t. a9 D% x9 i) ~9 I; M' U
  10.   assert_param(IS_FLASH_PROGRAM_ADDRESS(Address));
    ! W2 ?, Z# A' n' y  H

  11. 7 D) t9 j. ]; i6 D! w
  12.   /* Wait for last operation to be completed */9 A9 T; x8 M2 b9 y# E8 g
  13.   status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);' ^' X9 m5 x( w$ ^' k1 \
  14.   
    ' y* f! c+ Y" S/ z" }
  15.   if(status == HAL_OK)
    ; I4 m- v. U% U, u# I) W* K
  16.   {8 H, N4 ]4 \7 D" s6 y* k+ B7 X
  17.     /* Clean the error context */( j; K6 o# V, y: K+ f4 O( F  ]1 c: _" u
  18.     pFlash.ErrorCode = HAL_FLASH_ERROR_NONE;
    & s, D" U/ T# \* f& f# z/ @* u

  19. " w/ l# P* g* R: v! o1 s
  20.     /*Program word (32-bit) at a specified address.*/
    ' `% Q4 f# s# {& A4 n9 f# T  t
  21.     *(__IO uint32_t *)Address = Data;+ L; s  S/ D* v- s1 p4 [4 }
  22. + G( ]3 v2 l8 ]7 o# [
  23.     /* Wait for last operation to be completed */
    1 B7 S2 n, L) A8 L, r
  24.     status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);& O6 ]/ }3 ~% }3 T
  25.   }
    / Y+ R1 _2 E6 J- U1 r3 g/ M

  26. # H+ R& j. `# P: x
  27.   /* Process Unlocked */
    1 Q+ M( d5 l; X
  28.   __HAL_UNLOCK(&pFlash);4 P9 h0 I6 n7 Z+ R

  29. 4 V' o4 g3 ^' k
  30.   return status;' K, x2 _  V* {. Y+ g7 ^8 z
  31. }
    ( v. i* h, ~1 c' g/ _( j( z" g
复制代码

/ C9 u5 a7 z0 m: u; P+ E

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

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

  1. #define ADDR_FLASH_PAGE_505      0X08000000 + 128*504   //# x5 F7 t7 p; H  W* R
  2. #define ADDR_FLASH_PAGE_506      0X08000000 + 128*505   //
    + y$ I0 j! ?" Z6 F; e8 M2 r' V4 y
  3. #define ADDR_FLASH_PAGE_507      0X08000000 + 128*506   //
    / x/ s' v& X+ @& @3 u: g
  4. #define ADDR_FLASH_PAGE_508      0X08000000 + 128*507   //
    ) l# \4 u; [$ H8 P3 [* p( U8 u
  5. #define ADDR_FLASH_PAGE_509      0X08000000 + 128*508   //$ w! K9 \( {. F2 H8 Y, c- E: b
  6. #define ADDR_FLASH_PAGE_510      0X08000000 + 128*509   //
    . M. k4 a* l  i5 ]
  7. #define ADDR_FLASH_PAGE_511      0X08000000 + 128*510   //7 B. ?9 B7 \. I+ G2 G, S0 X
  8. #define ADDR_FLASH_PAGE_512      0X08000000 + 128*511   //最后一页
复制代码

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

  1. void MY_DATAFLASH_Program(uint32_t Address, uint32_t Data)   " p5 q  b. y9 X8 e2 N
  2. {
    8 w& q% V1 u( _0 s% G
  3. HAL_FLASH_Unlock();        
    0 i% }3 |% x7 H, z. T$ n2 _
  4.   if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){
    % L: }6 n& @9 C0 b) B
  5.     printf("write data 0x%x OK\n", Data);. w6 ]2 |5 r' o5 p
  6.   }, J, c, Q+ z- o' _. M7 K$ V1 Y
  7.   else{9 m5 k0 g0 r( ]% B% }7 D
  8.     printf("failed!!!\n");$ a+ @1 s% y/ f5 l4 @9 m
  9.   }
    1 r& p7 u5 x* [/ v: e! m2 h  s/ e' m
  10. HAL_FLASH_Lock();" B, B: F( g  Y# L: N
  11. }
复制代码
; ^1 B- W/ j" A' _

测试一下;

  1.   MY_DATAFLASH_Program(ADDR_FLASH_PAGE_512,write_data2);
    5 N: l5 ^' K- M4 L, e% ?5 a
  2.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_512);- }+ b- s/ {5 U% F7 j. X1 x
  3.   printf("the ADDR_FLASH_PAGE_512 is: 0x %x \r\n",read_data1);
复制代码

) O% ~6 m5 y* y+ q

在没有写入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)   * U9 `+ g, Y! n
  2. {
    ! ]! W# v0 m# B& m
  3.   FLASH_EraseInitTypeDef EraseInitStruct;
    ! l: l3 n/ h. m/ n. {, J
  4.   uint32_t checkdata;, K& p7 a$ f" I7 B. Q/ o
  5.   uint32_t PAGEError = 0;
    ; }$ w8 N# t0 Q$ m, k+ j
  6.   checkdata = FLASH_ReadWord(Address);
    ( S" i( H6 L. _* @
  7. HAL_FLASH_Unlock(); 6 t: U  c* X4 S* V7 c( ^9 ]- k
  8.   /*如果是0,直接写*/% ?9 Q+ c  h7 Q6 G  m2 B8 j
  9.   if(checkdata == 0){      
    8 U' g0 G9 W1 n9 y, x
  10.     if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){) A$ K) m* I$ h, T3 |
  11.       printf("write data 0x%x OK\n", Data);
    7 Q5 x; h% s! g! P$ b; `
  12.     }
    % \1 x1 D. x* T( {$ k9 I
  13.     else{
    ( @. M( I- ~5 v% x
  14.       printf("failed!!!\n");0 v, P& T" q  v; ~; f
  15.     }8 @* ~1 m; K6 ~. g# t
  16.   }
    + r9 O! _6 Z. |
  17.   /*否则擦除再写*/( Y1 M: j3 U0 }1 U( _$ f4 T
  18.   else{1 X# p$ T: b3 N7 d7 x9 X6 r- m
  19.      __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);( K( e* |. @7 V0 {
  20. $ R0 s! _' C6 g, Y* J
  21.       EraseInitStruct.TypeErase   = FLASH_TYPEERASE_PAGES; // 刷除方式! F. C' W# n" V6 F9 `  M0 [
  22.    EraseInitStruct.PageAddress = Address; // 起始地址
    4 `) K* ?: m4 h6 i- j
  23.       EraseInitStruct.NbPages = 1;
      C8 q+ O0 E4 o  v3 t
  24. 8 U3 h: N6 _/ {/ Q
  25.       if (HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError) != HAL_OK)- O8 e+ r' L- W- q; E* D" P
  26.       {
    6 V- C9 ?% _+ R3 b( Z" g) T
  27.         // 如果刷除错误! Z. M8 `, F7 {
  28.         printf("\r\n FLASH Erase Fail\r\n");
    - C. n9 h# e6 m: H
  29.         printf("Fail Code:%d\r\n",HAL_FLASH_GetError());
    8 L. u+ h$ g  G, f+ t
  30.         printf("Fail Page:%d\r\n",PAGEError);
    % Z) L% Q$ z& a) S% J0 i$ n: z
  31.       }% C. i3 s7 I+ B: C/ D, R1 I; z4 w& ^

  32. 0 W/ J- f8 ?- G2 ~
  33.       if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){+ y: R; l! \) ^- S- B. Q
  34.       printf("write data 0x%x OK\n", Data);
    8 d. O* S7 Z+ P" Z$ ~' Y
  35.       }
    & V# m% D2 y4 j0 V$ a
  36.       else{! {; E/ q7 A* P
  37.         printf("failed!!!\n");9 z. h; ~8 _9 j, b2 r
  38.       }
    $ R2 R4 q8 m* O& u

  39. " r: b0 i3 x0 C0 _
  40.   }
    + e2 Z" D- D4 `" g; o
  41. HAL_FLASH_Lock();/ z9 }. D  B. T, |& u& Z$ U6 R
  42. }
复制代码

7 W! T- T' b+ F

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

  1. u32 write_data1 = 0X12345678;! T8 c" [0 h& K* v! [( H  U3 n% V- S
  2. u32 write_data2 = 0X87654321;( O6 l7 E# t# {- ]9 N$ e4 u
  3. u32 write_data3 = 0XFFFFFFFF;
    ( H+ M, _% u- E1 C
  4. 1 c% W( e7 X% N1 s, M
  5. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_508 + 124,0X508508FF);2 c( ~9 ~- f! ^4 g& h1 c6 o
  6. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509,write_data3);
    # I- C; w, h. Y& ^( J' V
  7. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509+4,write_data2);8 i6 g6 U( J# u. \: x
  8. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509+8,write_data1);
    9 @: ]$ L5 ~$ I$ h6 P
  9. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_510,0X510510FF);
    8 H$ S. S* ?& I; o+ w3 K
  10. 5 q# |0 _4 `" m' f
  11. read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_508 + 124);
    : [  E+ z: Z4 K( U
  12. printf("the ADDR_FLASH_PAGE_508 last is: 0x %x \r\n",read_data1);  [* A( c" M) t- S3 e. H: N0 T$ i
  13.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509);/ I7 u0 t5 O  l2 W2 ^- s5 ?+ G
  14.   printf("the ADDR_FLASH_PAGE_509 1 is: 0x %x \r\n",read_data1);
    / M' P% h5 g+ R, W+ R% X  b( `
  15.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 4);$ u3 ]- }0 R4 r. f+ m! W" m
  16.   printf("the ADDR_FLASH_PAGE_509 2 is: 0x %x \r\n",read_data1);7 @0 Z. r8 D, {2 W  {" U4 |3 k7 Q
  17.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 8);
    : y  k* [& o7 d" s
  18.   printf("the ADDR_FLASH_PAGE_509 3 is: 0x %x \r\n",read_data1);2 O& Z% I  d, A' v" C" k- c
  19.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_510);
    8 f. ?4 L  f5 w1 @) B2 _7 M: Z
  20.   printf("the ADDR_FLASH_PAGE_510 first is: 0x %x \r\n",read_data1);
    ' V: S6 H# {+ E2 C6 F% j' s# J7 V

  21. , U% z4 }) b% Y0 h7 c- J5 \
  22. if(btn_getState(&K2_BUTTON_150mS) == BTN_EDGE2){
    2 `8 U0 M- B7 ?& _
  23.         printf(" K2 150ms button!EEPROM_read test\r\n");
    " C6 D* W/ e, D0 ]( b# n+ H
  24.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_508 + 124);# d! R% j( e7 Z' i/ S
  25.         printf("the ADDR_FLASH_PAGE_508 last is: 0x %x \r\n",read_data1);
    - b& [8 Z3 c4 j
  26.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509);. D6 h# l# |0 D8 r7 b2 ]
  27.         printf("the ADDR_FLASH_PAGE_509 1 is: 0x %x \r\n",read_data1);, }8 Q9 v3 t% y0 P8 o6 j
  28.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 4);
    1 N2 D. A. e2 B! k. h5 K, k
  29.         printf("the ADDR_FLASH_PAGE_509 2 is: 0x %x \r\n",read_data1);* @6 h: I6 @) R# ^3 ~) h
  30.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 8);; J+ k. H. T; }8 |( \9 b, {8 {* U
  31.         printf("the ADDR_FLASH_PAGE_509 3 is: 0x %x \r\n",read_data1);2 W  e8 G" a0 r! A. q0 q* n
  32.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_510);
    # L2 m  a' H2 z- h! g
  33.         printf("the ADDR_FLASH_PAGE_510 first is: 0x %x \r\n",read_data1);
    - I4 x& n, a  ^, W

  34. 9 P6 b4 _$ `$ k. y2 p, l: S
  35.     }- m9 W# b) i$ F
  36. if(btn_getState(&K1_BUTTON_150mS) == BTN_EDGE2){
    : F! g1 k* X  k8 k; y/ q
  37.         // printf(" K1 150ms button!,EEPROM_Erase test\r\n");. m: G3 _/ [% n! j# j9 p9 p
  38.         // MY_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, DATA_EEPROM_START_ADDR, write_data1);
    4 `" E* Z4 G! k( C$ V
  39.         printf(" K1 150ms button!,flash write test\r\n");
    / a& q% M1 M5 r2 h4 N/ r% q' t
  40.         MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509,0X33333333);
    6 O1 z! x( y5 M$ b5 k% l+ I& n5 M! \
  41.         HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);' s2 B! E: S5 [5 U$ g4 L6 L
  42.     }
复制代码
$ A& N( y5 b& F

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

9 Q# g# O1 t) N( b& Y

微信图片_20230612172448.png

) n. C4 ^/ v1 r9 B8 ~

微信图片_20230612172355.png


5 {* v& ~- j% W3 n2 Y$ o  H

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

. I9 _+ l9 Q9 A" d1 M2 {$ L
2.4 读写EEPROM的后续问题

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


6 i$ p9 ?6 Z4 z

微信图片_20230612172339.png


1 f# R& X. \- N# [

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

7 ?5 {7 N0 S4 p( I9 |+ s
2.4.1 问题的出现和解决

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

5 R0 {. m8 T. O. z# G7 @% z

微信图片_20230612172215.png

$ P4 n, u! Z3 ]4 w9 N

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

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

4 D- W/ s3 n. _" x

微信图片_20230612172212.png

8 e6 ]7 V2 x6 z' u. R) ~

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

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

  1. BlueID_STRUCT test;
    8 ]7 o% w/ M+ q- U' @5 J
  2. ...7 A7 c: x% P5 j9 @% s! ]5 i
  3. /*
    9 Q( p) `0 _/ d  `. t' b/ q( _& h9 {
  4. CHBlueID_STRUCT Flash_PowerOn_BlueCheck()
    8 D. r' J# c. d
  5. {
    & B' o' ?& e, o

  6. 4 D7 \+ Q, R) \% y: T5 G9 o
  7. CHBlueID_STRUCT PowerOn_ID;
      b+ l; B$ g8 ]+ j6 e; V. P
  8. PowerOn_ID.CH1ID = FLASH_blueIDRead(CH1_ID_ADDR);' i9 C9 I3 A) k( B( A+ U$ y- A# w
  9. PowerOn_ID.CH2ID = FLASH_blueIDRead(CH2_ID_ADDR);
    + [7 ^/ E! v) W; W8 O9 `2 T! t
  10. PowerOn_ID.CH3ID = FLASH_blueIDRead(CH3_ID_ADDR);
    $ ^; e6 S8 h5 K* s3 L- i: X$ v' V  Q
  11. PowerOn_ID.CH4ID = FLASH_blueIDRead(CH4_ID_ADDR);# @& X! l  H, @* B" u3 W
  12. PowerOn_ID.CH5ID = FLASH_blueIDRead(CH5_ID_ADDR);% p) E; Z1 B. Z3 J. e2 b+ r
  13. PowerOn_ID.CH6ID = FLASH_blueIDRead(CH6_ID_ADDR);# b( R9 }: h" @  W: c
  14. PowerOn_ID.CH7ID = FLASH_blueIDRead(CH7_ID_ADDR);
    , f; ?# Z6 S- o4 F
  15. PowerOn_ID.CH8ID = FLASH_blueIDRead(CH8_ID_ADDR);
    # h& M! o( Q7 d* n
  16. PowerOn_ID.CH9ID = FLASH_blueIDRead(CH9_ID_ADDR);
    8 S) v; z# s. e( o7 m8 t3 u
  17. PowerOn_ID.CH10ID = FLASH_blueIDRead(CH10_ID_ADDR);
    * _, x4 u# t! @) a$ {5 X/ i

  18. ) U7 O# w5 `2 r. d
  19. return PowerOn_ID;- K9 b* W' x' C# [$ r% ]  \1 J' W
  20. }
    2 N2 D! c, t" f1 _, b
  21. */
    4 p5 _6 Q9 z: O2 B+ y
  22. BlueChipID = Flash_PowerOn_BlueCheck(); //上电先把ID读出来做比较7 n  _1 J0 A4 P+ s% ]. k3 w1 M
  23. //打印一个出来测试,看结果
    ) S, D2 t5 ]  m0 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);
    3 U# z2 Q% x3 M6 }, E0 t1 u
  25. $ Q  k# ~9 l! l' R0 _
  26. test.ID1= 0XFF; //结构体每个元素是 uint8_t 类型
    + O8 u' N' H9 R) P$ s) p  }$ M$ B1 ~$ Q
  27. test.ID2= 0XEE;2 b" @, p- e2 `: @  H& c3 }  H4 \
  28. test.ID3= 0XDD;
      |3 @  q' D% p9 Z5 V$ s# d
  29. test.ID4= 0XCC;
    & Z3 x' ^' I' }7 s+ S5 t/ _+ M
  30. test.ID5= 0XBB;
    : Y- h4 `- Z3 l: n4 j
  31. test.ID6= 0XAA;. a) q/ \- J6 l

  32. ) {1 N5 c, A# Z" s4 [# k
  33. while (1){...}
复制代码

7 m" I/ t5 p/ P6 `* |' K7 R4 |

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


' g0 R% O, v  _) I

微信图片_20230612172209.png


" y2 j- L' r/ i4 w" U

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


* s5 j4 O; U7 H0 t# n' @1 {

微信图片_20230612172207.png

5 A, C3 a. x7 ]: O5 y) J6 y

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

+ Z% S7 M4 ?: V7 @; I$ a3 E6 X

微信图片_20230612172058.png

. m% _( m" d" i) z8 y' T

微信图片_20230612172050.png


6 @5 }* T$ |" j/ ]$ V5 t6 E

打印的结果:

% Z3 k8 D$ \- K' @1 @, N( C4 _

微信图片_20230612172039.png


& w0 q, @- H' X# U" v2 K( @& ]# C% E5 O

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

  1. BlueID_STRUCT FLASH_blueIDRead(uint32_t address)
    8 u* S) a* e9 P! s
  2. {
    8 K! O* q9 x; D
  3. BlueID_STRUCT u48id;" H8 j/ H, X3 u% V
  4.   // uint16_t temp1,temp2,temp3;  /**(__IO uint16_t*)address; *// V3 J1 ]* W* _9 M9 S
  5.   // temp1=*(__IO uint16_t*)address; $ E( }$ @; k( h! q3 H; g7 W
  6. // u48id.ID1 = (uint8_t)(temp1>>8);7 R' r5 y9 B$ f
  7. // u48id.ID2 = (uint8_t)(temp1&0X00FF);( J8 \+ c( n+ @* k' w- D
  8.   // temp2=*(__IO uint16_t*)(address+2);6 B% a! Y( U, |6 w
  9. // u48id.ID3 = (uint8_t)(temp2>>8);5 ^1 |) _% r4 G8 C5 P
  10. // u48id.ID4 = (uint8_t)(temp2&0X00FF);
    - t9 A( Q0 u. r; d  f! {! d4 l
  11. // temp3=*(__IO uint16_t*)(address+4);. \1 r1 s) q( k; n# d- `! s
  12. // u48id.ID5 = (uint8_t)(temp3>>8);! R5 b/ q; I5 m+ H$ b# J
  13. // u48id.ID6 = (uint8_t)(temp3&0X00FF);
    - n2 I( }9 }& w2 m

  14. - Q$ |# F+ I0 H% d6 ?
  15.   u48id.ID1 =  FLASH_Readbyte(address);! m5 {; W! B# c# i) [  ]
  16.   u48id.ID2 =  FLASH_Readbyte(address+1);* w3 |5 D* j! O3 D; r, q
  17.   u48id.ID3 =  FLASH_Readbyte(address+2);0 L  O, |; @, _+ n% b
  18.   u48id.ID4 =  FLASH_Readbyte(address+3);. i& _3 u0 k8 O* y% [: s
  19.   u48id.ID5 =  FLASH_Readbyte(address+4);+ L) Q% ~8 f" i9 D! a2 ~$ Y: P# T
  20.   u48id.ID6 =  FLASH_Readbyte(address+5);1 c& B( ~7 l* M

  21. & J+ a9 ]5 k9 ?$ A% c' `
  22.   return u48id;% M5 t" K# D9 e& g
  23. }
复制代码
- C( H! k  e5 n2 d" b6 M

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

3 n) ?* `( j3 W6 B% ?: S

微信图片_20230612172035.png

5 |, m# R9 o( w2 s2 d6 K
2.4.2 问题的分析(大小端模式数据格式)

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

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


2 P; \7 y( A$ N7 @. m8 q

微信图片_20230612172032.png


: V: f. _! p  b& P

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

" K( Z7 C* F+ v& r5 W

微信图片_20230612172030.png


! T4 ]0 ~1 |9 M/ W% g. w7 I% |

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

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

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

) ^4 p4 }, R* j+ W
2.4.3 STM32L071RBT6 EEPROM读写全字问题
5 q- Z& P' o1 z: L$ @" ^& x" p
读问题的出现

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

  1. uint32_t  FLASH_ReadWord(uint32_t address)- y- @9 u3 g4 K4 _4 q! }- U
  2. {+ {- J! D+ w0 X* {8 P' q( @
  3.   return *(__IO uint32_t*)address;! Q2 A4 a( K* `' a  d& ^/ Q
  4. }
复制代码
+ @/ C: c" i5 F' V% B5 l7 j' V! U

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

& g6 B9 m1 N9 V& Q  `! M' K

微信图片_20230612172026.png

2 [% }5 ~2 p& h8 W

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


# W) L, [3 y; t( n0 i2 K0 U! C0 q

问题的解决:

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

$ E- Z: a+ e3 P

微信图片_20230612172019.png

! q6 w$ D/ R- \& x: I! T; J

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

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


5 N+ L& u' A. @5 g

微信图片_20230612172012.png


8 u( H* ^" S* u8 E6 E" P: K% e

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

  1. u32 FLASH_ReadEnoceanID(uint32_t address)
      t5 ]- ^& a. `. H; \0 a6 H9 {
  2. {, t7 x# ^/ P! Q# \1 C! s# o
  3.   u32 keepid;: b! J  R. @3 [" r5 O+ Y
  4.   u8 i;2 a1 I. p7 V5 b( A! i% j. ^4 N
  5.   keepid = FLASH_Readbyte(address);# z$ Q$ I7 ]! R5 h
  6.   i = FLASH_Readbyte(address + 1);# ~: ^3 V% H) E8 T( j/ ~7 O, T
  7.   keepid = (i<<8)|keepid;; d+ v) t: W' W! `$ s+ W
  8.   i = FLASH_Readbyte(address + 2);! d) E; I3 R7 ^' k/ n0 S5 t! u
  9.   keepid = (i<<16)|keepid;. A( _- Q! [4 e# G! U& ~" w0 F4 B
  10.   i = FLASH_Readbyte(address + 3);7 b$ J' Z* r( A: r3 y! n
  11.   keepid = (i<<24)|keepid;1 _8 @, o4 d6 u8 i0 R9 V
  12.   return keepid;
    4 L9 c% G/ G# ~; C' e% Y
  13. }
    - |( v% a' u% \& _+ U9 e% v
  14. " @, s8 {' \2 }
  15. CHID_STRUCT Flash_PowerOn_Check()" R9 x0 r0 A, r* p4 z( c9 j
  16. {
    " e$ E  y3 ~* l! h% n
  17. # p% W; {6 I8 ]& w4 }. o2 `. _- E* W
  18. CHID_STRUCT PowerOn_ID;
    2 F; D3 y- `; `& r
  19. PowerOn_ID.CH1ID = FLASH_ReadEnoceanID(CH1_ID_ADDR);
      {% a9 A6 L# C0 \6 O  `! @" Q
  20. PowerOn_ID.CH2ID = FLASH_ReadEnoceanID(CH2_ID_ADDR);; E- L1 k; l* @( ^, P
  21. PowerOn_ID.CH3ID = FLASH_ReadEnoceanID(CH3_ID_ADDR);
    6 w6 u  a; E5 Z* x5 {: H7 `
  22. PowerOn_ID.CH4ID = FLASH_ReadEnoceanID(CH4_ID_ADDR);- h( j5 @* U: h! w( L0 |
  23. PowerOn_ID.CH5ID = FLASH_ReadEnoceanID(CH5_ID_ADDR);
    $ V5 k' W5 F  r/ {. k
  24. PowerOn_ID.CH6ID = FLASH_ReadEnoceanID(CH6_ID_ADDR);' H& }) y6 x' b5 {8 M
  25. PowerOn_ID.CH7ID = FLASH_ReadEnoceanID(CH7_ID_ADDR);
    , M  Z0 R( C5 V2 c' q8 K
  26. PowerOn_ID.CH8ID = FLASH_ReadEnoceanID(CH8_ID_ADDR);
    9 w9 y- B* X- D7 v) \5 t
  27. PowerOn_ID.CH9ID = FLASH_ReadEnoceanID(CH9_ID_ADDR);  j$ S* @1 `5 `) u6 X
  28. PowerOn_ID.CH10ID = FLASH_ReadEnoceanID(CH10_ID_ADDR);6 }# t. j! `9 U) Q; [9 e6 l8 Q- q
  29. # g, ^/ q; P( \. K. }! {. Y
  30. return PowerOn_ID;+ }! P! E. L5 _, v- C, f
  31. }
复制代码
3 T) s/ ?5 |* g/ L7 v+ [9 L! a4 g

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

9 V+ \1 Q0 p$ U! H, _& R
写问题的出现

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

一直用的写全字函数为:

  1. FLASH_Status  FLASH_WriteWord(uint32_t Address, uint32_t Data)$ y8 W, m0 E8 D- _' i2 T3 H
  2. {: ]: B" S( k' y0 m4 X: s+ Q' _

  3. / P" L; i( z9 E+ F  {
  4. FLASH_Status i = FLASH_COMPLETE;( o3 {+ H1 y, g& Y! k0 |

  5. 0 i% U2 F& L3 D7 i* ^3 ]  `
  6. HAL_FLASHEx_DATAEEPROM_Unlock();  3 K+ Q$ c, ~; S. M) A
  7.   while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, Address, Data) != HAL_OK);
    & Y5 P. f% v. r( L7 F) f0 }& G
  8. HAL_FLASHEx_DATAEEPROM_Lock();
    ) }. c! `: A- t
  9. + v' L' y/ L' ~; I0 Z" M  Z6 {" c
  10. return i;
    * z* v; J, w/ \
  11. }
复制代码

& A( }2 X4 j) O$ t) o# A2 {

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

2 H/ u% C$ c6 J1 X- ?2 Y% J" x5 X

微信图片_20230612172009.png


' z2 O& {; z5 @( b' E" N7 B

但是使用时候发现:

6 J1 ~7 E% Q" I1 L9 J# |

微信图片_20230612172006.png

6 T2 P% k& f5 w2 l& o% l

问题的解决:

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

- }4 m' Y/ l7 P; N8 [

微信图片_20230612172003.png

) l, J7 s1 _6 Z5 r5 N- _: v4 y

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

  1. FLASH_Status  FLASH_WriteWord(uint32_t Address, uint32_t Data)
    " p8 W, j9 V! [9 o  V
  2. {
    5 |( J# S" s/ w' X- Y

  3. * ?1 C& ?' Y+ d4 e5 [
  4.   FLASH_Status state = FLASH_COMPLETE;
    0 j7 b. z0 m. |+ M6 K
  5.   u8 i = 0;
    2 e! O: L- Z4 S$ o
  6.   u8 writedata[6]={0};
    ! L+ v- y1 |; X8 o
  7. ) F! H, Q) \; R! U
  8.   writedata[0] = (u8)Data;
    & {6 K0 v; J& D8 X& P2 s
  9.   writedata[1] = (u8)(Data>>8);4 Z. G. y9 N# _$ K0 k0 g
  10.   writedata[2] = (u8)(Data>>16);+ ^) r0 G- Q( i# r8 L6 a5 B
  11.   writedata[3] = (u8)(Data>>24);
    ' Q9 m. ~8 g7 ?3 V

  12. + T! y9 ?; P% ?0 J: ]" k
  13. HAL_FLASHEx_DATAEEPROM_Unlock();  + }- v9 X, p8 R$ Q0 p& y" T+ r! n/ m
  14.   for(i=0; i<4; i++){% d8 I# \- N/ {6 w1 V6 B+ Y! ^; y
  15.     while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, Address + i, writedata[i]) != HAL_OK);
    0 Y* d: F. E( c0 S. T, J) n1 U
  16.   }
    7 j2 ^; G  V* d4 z& q) c
  17. HAL_FLASHEx_DATAEEPROM_Lock();
    * ^) X4 P' B; l! H6 }. }2 G9 S
  18. + L  ~8 P& ~% i6 O6 m4 n" m) v2 y9 o
  19. return state;
    # c" n- }1 P# B) L9 C+ D
  20. }
复制代码

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

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

2.4.3小结使用的芯片为  STM32L071RBT6

6 _5 Z9 e4 C/ t1 N
最后问题的解决

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

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

9 E/ w' J7 ^! U# D/ P# B0 U

微信图片_20230612172000.png


/ r7 R5 k7 q- s$ ~  v- l

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

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

# ~6 ?& D2 ~. G  i' g% J- q

微信图片_20230612171954.png


: m3 l+ C+ g& {9 H8 T/ z

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

2 K+ l6 C# M  w2 Q
收藏 评论0 发布时间:2023-6-12 17:30

举报

0个回答

所属标签

相似技术帖

官网相关资源

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