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

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


: i7 h7 s# _3 J+ L2 h6 T

微信图片_20230612172451.png


4 Y' R0 C% h  @6 E! H

微信图片_20230612172454.png


; L) y% r; _8 W) j% `' O' D1 L

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


8 Y% v" H/ ~5 J3 m

  1. #define FLASH_SIZE                (uint32_t)((*((uint32_t *)FLASHSIZE_BASE)&0xFFFF) * 1024U)
    4 Q$ G! @7 L, B4 f* J) i
  2. #define FLASH_PAGE_SIZE           ((uint32_t)128U)  /*!< FLASH Page Size in bytes */
复制代码

# p: K1 U2 s* P  _; z8 [

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

9 C0 x& {7 ~8 I: A/ j! s
2、读写函数的设计

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


! v" S! e& Y  e" R; @2.1 读取函数
  1. //读取指定地址的半字(16位数据)
      V3 `5 k5 ?! u! V5 m
  2. uint16_t FLASH_ReadHalfWord(uint32_t address)+ d" _4 \/ O( s/ `3 |* j* d# `
  3. {
    $ A5 a8 U2 r7 O# X- V/ {9 X$ g
  4.   return *(__IO uint16_t*)address;
    8 H' y! e; q$ _! `8 p
  5. }
    * n. O# i  r+ P
  6. 2 [& ?' k; k4 D6 v$ t
  7. //读取指定地址的全字(32位数据)# Y' x3 V9 V* t# E8 M. P- N6 X+ ^" D
  8. uint32_t FLASH_ReadWord(uint32_t address); D3 s. ?- u7 p0 c: c8 R2 P) s
  9. {# Y/ b- f' Y( q9 [1 A' f
  10.   return *(__IO uint32_t*)address;
    0 o, p5 k: Y2 Q' k
  11. }
复制代码
9 d3 m+ e) P  B$ i% w! h) \
+ A! I5 B5 T+ ^2 y0 B

简单测试一下:

  1. u32 read_data1=0XFFFFFFFF;+ X- A. y& `! r0 G$ T( G
  2. u32 read_data2=0XFFFFFFFF;
    % e# k7 ^+ Y  o- e
  3. ...: m+ F7 z! |1 n) i4 O" I( E
  4. read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);
    7 p( Y( z( s3 C& c/ J3 A0 z7 z0 C
  5. printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);
    1 m/ W# b0 m! |3 |, v6 I" I
  6. read_data2 = FLASH_ReadWord(DATA_EEPROM_START_ADDR + EEPROM_PAGE_SIZE);( m. D7 e. J* P. _" v
  7. printf("the EEPROM sceond page test data is: 0x %x \r\n",read_data2);
复制代码
$ D( t' \9 I* I- V" I+ t

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

) f7 O: X% \8 A: [1 ~
2.2   EEPROM写函数

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

  1. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Unlock(void);
    . C5 S0 d/ G6 p( F# U0 C
  2. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Lock(void);
    # V0 O7 w1 k# D( y+ T  F
  3. % B6 j  h6 R) i" l- N6 f* }
  4. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Erase(uint32_t Address);
    2 H1 i( x! _. [4 O6 T- e  _5 K
  5. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data);
复制代码

& L6 Y( `' b% Q) ?2 Z& @

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

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

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

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

  1. HAL_FLASHEx_DATAEEPROM_Unlock();/ x% o3 f: w; @% T, d# m
  2. HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, DATA_EEPROM_START_ADDR, write_data1);
    ' T. s9 _1 K/ B
  3. HAL_FLASHEx_DATAEEPROM_Lock();
    - ]. `+ _+ V8 Z! ^7 V$ U) C
  4. ...' Y, j' P: |1 U( k: }, }. \6 W
  5. if(btn_getState(&K1_BUTTON_150mS) == BTN_EDGE2){
    # \" r/ ]0 K4 ^; c7 w
  6.         printf(" K1 150ms button!,EEPROM_Erase test\r\n");) H+ M: _  M3 @6 C3 ~9 c, G$ ~
  7.         HAL_FLASHEx_DATAEEPROM_Unlock();3 y& G& O  C; Q6 ]3 P4 D4 a
  8.         HAL_FLASHEx_DATAEEPROM_Erase(DATA_EEPROM_START_ADDR+4);
    ' x0 V2 Z* U& ^9 G0 s
  9.         HAL_FLASHEx_DATAEEPROM_Lock();
    ; m- ^9 h: F* A' y, a
  10.         HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);2 b$ I% s, K3 e
  11.     }" n$ Q3 @( H4 J/ n
  12. ...$ o5 U' O) R8 }7 c
  13. if(btn_getState(&K2_BUTTON_150mS) == BTN_EDGE2){
    * l2 E" ~3 [3 p8 k4 B
  14.         printf(" K2 150ms button!EEPROM_read test\r\n");6 o( D8 n4 P4 }3 G2 J0 ?
  15.         read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);3 q6 Z1 t2 c3 y2 D# p7 f
  16.         printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);
    / |0 A/ c0 |! n( X
  17.     }
复制代码
: w2 J; W: U) g( @! D5 |4 A0 ~0 a

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

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

, a$ v7 C. Q/ Q+ l, ^: B
写入问题说明修改

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();
    % S2 J2 }" @% F- u+ l, m1 a/ T
  2.   HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, DATA_EEPROM_START_ADDR, 0X88);& o- z  ~* E& K& b2 k7 X; r8 z- m$ g
  3.   HAL_FLASHEx_DATAEEPROM_Lock();
    , m' j* s& N5 J% E! q9 z  |* u
  4.   read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);
    2 x+ m( w6 ~! p
  5.   printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);
复制代码

9 n4 g5 o& ^! i+ L. ~9 z

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

  1. void MY_DATAEEPROM_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data)   
      o& T( y( Q) F
  2. {! a  J8 ?: T0 l( ?5 T
  3. HAL_FLASHEx_DATAEEPROM_Unlock();        
    , N6 m! J& s0 C1 t0 m
  4.     HAL_FLASHEx_DATAEEPROM_Program(TypeProgram, Address, Data);
    ; ?: }* W) X8 m3 [- C
  5. HAL_FLASHEx_DATAEEPROM_Lock();& U: q0 R$ A6 N. B: h) E1 o
  6. }
复制代码

1 b+ g& j2 [. h: m+ s

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

3 b( ^- V( @9 c  S
2.3   Flash写函数

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

  1. /* IO operation functions *****************************************************/
    / \7 @; d* f+ P* x
  2. HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data);
    * b$ n5 ?% G" ?& `  X
  3. HAL_StatusTypeDef HAL_FLASH_Program_IT(uint32_t TypeProgram, uint32_t Address, uint32_t Data);# ]5 m! R% D  K# O& m8 S5 F
  4. ' P9 ]3 }" O% N9 K( m# ^+ l6 ]
  5. /* FLASH IRQ handler function */, F7 e! Y$ r, E; i/ w  z9 M: |
  6. void       HAL_FLASH_IRQHandler(void);! a7 H3 i- U( h1 Y/ _0 I
  7. /* Callbacks in non blocking modes */
    . Z  ?+ L& X$ g- {8 d- `7 s
  8. void       HAL_FLASH_EndOfOperationCallback(uint32_t ReturnValue);- T* D8 m% s0 c1 Q
  9. void       HAL_FLASH_OperationErrorCallback(uint32_t ReturnValue);
    4 M6 z) ?( n* b; W0 v8 H" x
  10. 0 r# F3 m3 p, Q* d( u
  11. /**1 \2 R5 N! Y* F: m6 @
  12.   * @}" x( w7 ]9 E, ~
  13.   */
    4 o6 e- ~1 a( Z- ?, r2 |. S: K

  14. & `6 }# W5 Y0 c8 G. L2 t
  15. /** @addtogroup FLASH_Exported_Functions_Group2
    & r" N% g( J: c
  16.   * @{6 i" u2 [" K8 ?6 a5 a; [8 ^" D
  17.   */; ^4 \* V* {; A, l( i
  18. /* Peripheral Control functions ***********************************************/( g* _2 K% O8 ~2 I9 u2 P
  19. HAL_StatusTypeDef HAL_FLASH_Unlock(void);
      P4 L  A& `( n" ~
  20. HAL_StatusTypeDef HAL_FLASH_Lock(void);
    * o* n6 q/ ~3 @* \! A1 R
  21. HAL_StatusTypeDef HAL_FLASH_OB_Unlock(void);
    " A; C$ U( D! e
  22. HAL_StatusTypeDef HAL_FLASH_OB_Lock(void);
    ! k( j9 Z  C. }- Z
  23. HAL_StatusTypeDef HAL_FLASH_OB_Launch(void);
复制代码

- h$ o0 t6 O4 L) h

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

; \5 s+ M- r  N  O

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

  n: L: C8 m8 f: g$ b( u
  1. HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data)* I. j" ~2 a; R  U
  2. {5 \" p% j/ j- J$ a1 \. t
  3.   HAL_StatusTypeDef status = HAL_ERROR;
    % c% j7 e" l9 l" f( q* J
  4.   
    $ @: s! ^$ ?/ F" G+ v" e+ f
  5.   /* Process Locked */% G$ I& O  {( c4 j
  6.   __HAL_LOCK(&pFlash);6 ]9 x/ O5 i. [, @, r) m% p! N  r7 r0 y

  7. % [6 A( b, A% }8 A8 b' y5 s7 }. x, [- m
  8.   /* Check the parameters */" T* x! v9 D- s% q: U, O( v# q% L/ I
  9.   assert_param(IS_FLASH_TYPEPROGRAM(TypeProgram));
    $ B2 y! G" i$ r$ z7 D3 [6 L4 S; s
  10.   assert_param(IS_FLASH_PROGRAM_ADDRESS(Address));
    ' G2 h/ U! K2 f8 E+ ^' {

  11. # u$ r+ O+ o, v/ X& g
  12.   /* Wait for last operation to be completed */
    $ j2 j3 ~! @7 I! b4 [; Z# t
  13.   status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);$ C- k" ?# b# e  u4 I2 s
  14.   ' P8 z4 L: c1 V5 y+ S  c$ _7 p& A
  15.   if(status == HAL_OK)
    ) o( D7 R0 _5 ]8 G; G# a* g7 n
  16.   {
    - F: y. ~. a' ]4 c$ z. ~5 u
  17.     /* Clean the error context */
      E# L# J3 c! v9 c% Q
  18.     pFlash.ErrorCode = HAL_FLASH_ERROR_NONE;3 V7 z+ b% b; b0 f3 [4 [3 ~

  19. # I8 ]0 M: Y: Z4 d0 C; _& y
  20.     /*Program word (32-bit) at a specified address.*/4 X4 ~4 @# x" l8 v
  21.     *(__IO uint32_t *)Address = Data;, k. F5 ^6 m% x" `6 t
  22. 6 n5 Y+ S1 [+ f4 E
  23.     /* Wait for last operation to be completed */
    ! T" L8 I8 p, \: J
  24.     status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);& y7 m+ R$ A4 N, W
  25.   }8 D2 o' E, x: ^5 ^

  26. & m7 c: r* R0 T! b! V: D( @: d
  27.   /* Process Unlocked */& F& [8 T7 S( m2 c
  28.   __HAL_UNLOCK(&pFlash);
    8 z. x+ K9 I$ @7 }
  29. $ `: I2 h9 x- z# ]' h
  30.   return status;
    - Q: i3 M, Q& k/ Z  b  K
  31. }/ R) Q& \) @$ ?$ v9 H# I/ P
复制代码
+ B* G1 e- ^) k' ]& ^8 e1 @: s

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

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

  1. #define ADDR_FLASH_PAGE_505      0X08000000 + 128*504   //
    8 r/ n. a0 C2 k" }
  2. #define ADDR_FLASH_PAGE_506      0X08000000 + 128*505   //
    8 }% i. L. T  X0 p; ^- u
  3. #define ADDR_FLASH_PAGE_507      0X08000000 + 128*506   //) _  V1 D7 A3 V8 D$ |" X
  4. #define ADDR_FLASH_PAGE_508      0X08000000 + 128*507   //
    9 v/ ]+ c4 C$ ?" O# W# I' l
  5. #define ADDR_FLASH_PAGE_509      0X08000000 + 128*508   //$ ~* s- q8 I- _! H
  6. #define ADDR_FLASH_PAGE_510      0X08000000 + 128*509   //, j; B: I/ l6 B' D3 C% {- }2 A
  7. #define ADDR_FLASH_PAGE_511      0X08000000 + 128*510   //
    & ]$ O& @: r9 \  L3 n
  8. #define ADDR_FLASH_PAGE_512      0X08000000 + 128*511   //最后一页
复制代码

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

  1. void MY_DATAFLASH_Program(uint32_t Address, uint32_t Data)   7 p1 o. l  O: B( ^+ `
  2. {
    8 G. ~. t+ N0 s5 B; U/ K
  3. HAL_FLASH_Unlock();        5 ?# G( ~7 D  n5 ?: p( O
  4.   if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){
    - L* Q+ K- H/ b2 a5 L" I8 F0 J& D" Q
  5.     printf("write data 0x%x OK\n", Data);  y4 r0 u. }1 W' o  [1 C
  6.   }
      B7 O, V; e; ~' F/ J( `9 F
  7.   else{" c; m3 |8 U& V( N" y
  8.     printf("failed!!!\n");
    $ {3 n0 l6 q) [. l4 B. p
  9.   }& i: J6 [) X1 G( j, R
  10. HAL_FLASH_Lock();
    : m4 _4 g' x: f0 p5 r
  11. }
复制代码
3 j$ B" \# u2 O* _

测试一下;

  1.   MY_DATAFLASH_Program(ADDR_FLASH_PAGE_512,write_data2);
    + z' [, _# `: e3 Y% ~1 U3 R8 _4 D6 K
  2.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_512);6 _1 N  p# q8 a. P* Q. I$ m% @
  3.   printf("the ADDR_FLASH_PAGE_512 is: 0x %x \r\n",read_data1);
复制代码

+ o+ U0 G& W! r+ l( A: \5 |

在没有写入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)   
    ' j' U. x3 k2 m! i* M$ z9 e
  2. {! ~& G7 y7 G9 @1 m
  3.   FLASH_EraseInitTypeDef EraseInitStruct;
    ) [/ e" L5 W7 X& f/ a) F% S
  4.   uint32_t checkdata;
    2 k7 l( ~8 ?) H, s! I
  5.   uint32_t PAGEError = 0;
    ; E4 d% [. K3 w4 D8 z& T
  6.   checkdata = FLASH_ReadWord(Address);/ E$ A* ]. p6 _$ _& ~, N4 P
  7. HAL_FLASH_Unlock(); 8 r4 H: S$ p" m) P. I
  8.   /*如果是0,直接写*/
    2 A5 f( B0 [0 Q% R6 _! o" w
  9.   if(checkdata == 0){       ( r$ j2 [" X) Y! o! F8 h& c% E" P+ {
  10.     if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){" S! [! d- Q( h! B8 P
  11.       printf("write data 0x%x OK\n", Data);2 P0 g, `" A0 E3 V) T! J' K
  12.     }- G: d3 u( D) t# n1 ]" c
  13.     else{! b$ t$ z- o' b" B4 d8 j. Z7 K
  14.       printf("failed!!!\n");
    & `) X( S2 w$ m3 g; k( n
  15.     }
    3 O/ L1 @! i) p, Q" ?6 K
  16.   }- N$ Z$ V5 ~9 t8 U$ ]) _& ?
  17.   /*否则擦除再写*/
    " z5 `* L* I/ t+ l! q" `
  18.   else{+ h6 [" \/ g; G1 J1 M) w( V! p
  19.      __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);
    & [6 i' T# y' a+ }
  20. ) G# ?! Y9 f, a8 i
  21.       EraseInitStruct.TypeErase   = FLASH_TYPEERASE_PAGES; // 刷除方式" w! @3 v2 E4 E9 I/ K7 Y% c# M
  22.    EraseInitStruct.PageAddress = Address; // 起始地址* O: y0 B+ u- g0 i7 a
  23.       EraseInitStruct.NbPages = 1;! v9 Y9 [% L/ ~
  24. $ p0 l2 F2 n5 x
  25.       if (HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError) != HAL_OK)
    3 d5 r8 {- }' F- m( h" u
  26.       {
    3 |( y! N, S0 Z3 ?, P- s/ [* h
  27.         // 如果刷除错误
    + P9 @* U" p  |+ r( d9 L
  28.         printf("\r\n FLASH Erase Fail\r\n");5 l, Q5 j" p& G
  29.         printf("Fail Code:%d\r\n",HAL_FLASH_GetError());
    . A4 O& q3 ]& [$ z5 K7 T. ^
  30.         printf("Fail Page:%d\r\n",PAGEError);& n( V! u7 B# f# N7 e
  31.       }, u9 U. _6 y( `9 ^5 X
  32. ) ?! J* M  ?# u3 `
  33.       if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){
    6 D" J1 I1 |- x. a
  34.       printf("write data 0x%x OK\n", Data);
    " P# T: p& |4 C
  35.       }
    6 J5 _- A/ L5 `; [9 e
  36.       else{4 T3 u6 j: a% e1 O3 M/ U
  37.         printf("failed!!!\n");1 n" Q7 V- j: b  {' f
  38.       }
    " y7 O$ m. Q6 R% |: b
  39. - G; \% U" a3 p8 I$ I) E
  40.   }
    ) W  D! s3 d7 V( |7 D
  41. HAL_FLASH_Lock();
    # l4 d8 Y8 M+ D$ M* }
  42. }
复制代码

% a5 h9 A: Y& S. `

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

  1. u32 write_data1 = 0X12345678;
    2 b9 b, F% w) Q1 ^+ ^
  2. u32 write_data2 = 0X87654321;/ e% v% c4 n" e0 z. B' z/ [( K% Z  v
  3. u32 write_data3 = 0XFFFFFFFF;
    * ]/ O9 ~' h3 H6 J8 Y3 \

  4. ' j" l1 \; S* \' I  W% @! e3 w* Q" f
  5. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_508 + 124,0X508508FF);/ B3 g5 z" N  @  j+ Y/ y
  6. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509,write_data3);
    + l5 M( C: d) }. s: d
  7. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509+4,write_data2);1 L1 a& Z+ i) q8 A; B
  8. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509+8,write_data1);8 E  q9 O- A: K+ i4 [
  9. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_510,0X510510FF);
    + Z4 i, }0 q/ {! _# h  N
  10. 4 t* v7 K" V+ q3 a# H: Y
  11. read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_508 + 124);. }( l: W5 R5 \5 n8 O* `$ f* o
  12. printf("the ADDR_FLASH_PAGE_508 last is: 0x %x \r\n",read_data1);
    6 C6 l8 y2 h3 I  b: ^. U! ?
  13.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509);
    4 F5 ^! g- y0 z
  14.   printf("the ADDR_FLASH_PAGE_509 1 is: 0x %x \r\n",read_data1);
    0 L' l5 _4 e! f. D9 L& J' Y
  15.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 4);# @3 e& M/ z2 r' ^! y" Q+ B
  16.   printf("the ADDR_FLASH_PAGE_509 2 is: 0x %x \r\n",read_data1);
    + q. Q" l1 _6 C. @) y
  17.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 8);
    " L% y* p% q- [: c
  18.   printf("the ADDR_FLASH_PAGE_509 3 is: 0x %x \r\n",read_data1);
    4 r8 |/ b) n! x" X# u5 k
  19.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_510);
    * [0 g. F( A. f% K
  20.   printf("the ADDR_FLASH_PAGE_510 first is: 0x %x \r\n",read_data1);
    3 z; D* J5 d2 x& r) P/ n

  21. % `- h% K1 a  N$ L
  22. if(btn_getState(&K2_BUTTON_150mS) == BTN_EDGE2){
    & X( U" [' ?' j! \
  23.         printf(" K2 150ms button!EEPROM_read test\r\n");4 G7 D2 V6 e9 C: [$ v& {# f
  24.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_508 + 124);* u; o  f5 }& v  Z# L/ R5 O
  25.         printf("the ADDR_FLASH_PAGE_508 last is: 0x %x \r\n",read_data1);
    2 S: C2 a# S' T9 V
  26.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509);) P: G$ U% P; m. r. w$ X8 r7 T6 M
  27.         printf("the ADDR_FLASH_PAGE_509 1 is: 0x %x \r\n",read_data1);
    $ n: K1 d; Q3 G" }
  28.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 4);
    + ?- S; u4 [, W5 p0 X7 `
  29.         printf("the ADDR_FLASH_PAGE_509 2 is: 0x %x \r\n",read_data1);' t, M8 v# Q+ a
  30.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 8);' U$ I) _, |3 Z2 c
  31.         printf("the ADDR_FLASH_PAGE_509 3 is: 0x %x \r\n",read_data1);
    / m1 s) h1 g! V2 _: E2 f0 h7 d- ^
  32.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_510);
    # _6 b1 m9 t  f1 R( ~+ K; M
  33.         printf("the ADDR_FLASH_PAGE_510 first is: 0x %x \r\n",read_data1);
    - {1 ^3 u3 E/ H% {9 x" ]
  34. 7 D' W! t, T4 e2 ]$ ]* h# p6 [7 S
  35.     }
    ! j/ I, v. T! @: I
  36. if(btn_getState(&K1_BUTTON_150mS) == BTN_EDGE2){
    9 n) g) C/ f: u8 c3 p; B
  37.         // printf(" K1 150ms button!,EEPROM_Erase test\r\n");
    " _7 d+ z- l4 c& l/ ?5 V* r, ~( Z
  38.         // MY_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, DATA_EEPROM_START_ADDR, write_data1);7 D' c; v; o  A& O' C8 i5 T* Q4 z
  39.         printf(" K1 150ms button!,flash write test\r\n");
    % I# c% C1 }0 _. N/ A
  40.         MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509,0X33333333);
    . ?. y9 [3 p  O2 [6 V& ]1 s
  41.         HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
    . X/ ~! Q" K4 p
  42.     }
复制代码
* U7 A1 M& Q2 V" T% S& e

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

8 R8 u2 O) W4 ~

微信图片_20230612172448.png

- c/ e1 q' V; M' \  t# P/ t

微信图片_20230612172355.png

' U4 z3 L' |. {, t; P' v

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


  h* p8 w3 j/ U5 I- H+ c6 ^" \0 m2.4 读写EEPROM的后续问题

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

3 x7 E) S% x+ j/ w' v- M3 f* Q

微信图片_20230612172339.png


6 l+ \- k" m/ n  u6 c

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

: {# e1 X3 j, p+ f) ]
2.4.1 问题的出现和解决

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


2 B# `7 q, N0 ~3 S& t7 l

微信图片_20230612172215.png


7 ~, s) M: `! }; q3 Z

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

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

- [" F8 d. u) x

微信图片_20230612172212.png

% D" d" _9 `% N1 K3 B+ D0 h6 |- U

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

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

  1. BlueID_STRUCT test;
    ) ?" ~; i, B) n3 {; E
  2. .../ q& @3 C1 m* z- _
  3. /*
    8 E8 c+ H' i. O/ N
  4. CHBlueID_STRUCT Flash_PowerOn_BlueCheck(); f/ X! ]. a" \: h2 v" `: ?
  5. {0 N; O" j- O- ~+ z) u: R& r2 ]
  6. 3 G3 p4 }% B6 O
  7. CHBlueID_STRUCT PowerOn_ID;8 `8 i; A# `- q- R) j* t3 V
  8. PowerOn_ID.CH1ID = FLASH_blueIDRead(CH1_ID_ADDR);
    , d  H/ |! n! D! Z
  9. PowerOn_ID.CH2ID = FLASH_blueIDRead(CH2_ID_ADDR);
    9 O  |8 |5 y# k' O# c1 R8 O. u
  10. PowerOn_ID.CH3ID = FLASH_blueIDRead(CH3_ID_ADDR);
    - S& ?0 K7 ]. G5 G, h' H
  11. PowerOn_ID.CH4ID = FLASH_blueIDRead(CH4_ID_ADDR);
    + D& m0 D: O- T  h2 R0 [5 C, H
  12. PowerOn_ID.CH5ID = FLASH_blueIDRead(CH5_ID_ADDR);( ?9 d% u4 S, Y& ~) ~5 W
  13. PowerOn_ID.CH6ID = FLASH_blueIDRead(CH6_ID_ADDR);2 r5 U2 i5 y% e+ q, X
  14. PowerOn_ID.CH7ID = FLASH_blueIDRead(CH7_ID_ADDR);
    ; w6 H+ d. B; E+ U* F8 [
  15. PowerOn_ID.CH8ID = FLASH_blueIDRead(CH8_ID_ADDR);
    7 s/ L! ]& r4 B% ?8 Q
  16. PowerOn_ID.CH9ID = FLASH_blueIDRead(CH9_ID_ADDR);4 A3 k  G$ [/ m) ?7 j9 y
  17. PowerOn_ID.CH10ID = FLASH_blueIDRead(CH10_ID_ADDR);
    - q$ T* y) l' d& @9 L# {6 o7 s
  18. & g, h5 i$ n# \9 v" D
  19. return PowerOn_ID;
    ; ?7 g$ H8 B& h, J
  20. }4 r- q" E0 R5 Q% x' i% n9 \
  21. */; P4 x& ?( Y, W/ I  T& |4 T
  22. BlueChipID = Flash_PowerOn_BlueCheck(); //上电先把ID读出来做比较
    ( R" g" M' k- ]
  23. //打印一个出来测试,看结果
    1 m* r- [1 u6 d' ^$ y! m- A
  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);% a. _% k) N. }# X3 q$ A3 b$ k5 b- e

  25. 7 c. \& b8 n* Y! \9 _
  26. test.ID1= 0XFF; //结构体每个元素是 uint8_t 类型* D- x7 Y% I( o3 y7 P
  27. test.ID2= 0XEE;
    0 q4 l; @* a/ |3 J" i
  28. test.ID3= 0XDD;
    7 f2 `% m$ b4 g, p
  29. test.ID4= 0XCC;! W6 `% k# n0 V1 R9 E
  30. test.ID5= 0XBB;" V' g. s- Y5 b
  31. test.ID6= 0XAA;3 x( O- Q$ F2 f4 L) j- ^: E
  32. 0 j0 q, n% @2 D+ C
  33. while (1){...}
复制代码
$ T5 ]- i% y% S; I7 g, c; x! u  }8 ]

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

8 `) Z) \9 C0 O! m' y+ ?0 K

微信图片_20230612172209.png


. \  ?. S6 e; O' p7 _

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


: Q# N6 H( r* Y* N4 r6 ]2 V1 @, d

微信图片_20230612172207.png

5 j; a0 B$ g5 M9 f

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

! R% @) p+ z) D' t- m( {/ s( Z5 q

微信图片_20230612172058.png


7 k5 ~: q# O. b8 t

微信图片_20230612172050.png

$ a9 r+ A5 E3 \% w* t

打印的结果:

6 z3 u4 `, A0 D! G6 }

微信图片_20230612172039.png


( [2 x3 }$ T, q3 t7 w1 E9 `) ]

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

  1. BlueID_STRUCT FLASH_blueIDRead(uint32_t address)
    & D; w, s& y! q6 |
  2. {* p+ `+ D9 e$ Q! Q
  3. BlueID_STRUCT u48id;
    % a, `4 `8 a: n1 K7 y/ v# O
  4.   // uint16_t temp1,temp2,temp3;  /**(__IO uint16_t*)address; */, d4 b+ ]: c& D! {
  5.   // temp1=*(__IO uint16_t*)address;
    ; ^3 Y4 m: t9 S8 k
  6. // u48id.ID1 = (uint8_t)(temp1>>8);
    - O) U( }" a- s$ \' w: _
  7. // u48id.ID2 = (uint8_t)(temp1&0X00FF);
    * m+ y8 ?* X4 I0 c7 m, b6 ^
  8.   // temp2=*(__IO uint16_t*)(address+2);* Q: w/ w0 U6 R1 t) a
  9. // u48id.ID3 = (uint8_t)(temp2>>8);) \% \1 l- \; _2 h/ R
  10. // u48id.ID4 = (uint8_t)(temp2&0X00FF);/ d- _3 B4 K2 v3 E9 v& j
  11. // temp3=*(__IO uint16_t*)(address+4);1 z' ]) O5 z. W  E2 c7 d7 I
  12. // u48id.ID5 = (uint8_t)(temp3>>8);
    7 o* `+ y! g% o) V9 Y6 x
  13. // u48id.ID6 = (uint8_t)(temp3&0X00FF); 2 V. d# x: N3 P6 ^  w

  14. * o  j! U" e9 p9 W# j- K( N. t) w
  15.   u48id.ID1 =  FLASH_Readbyte(address);
    ) n: c/ i& o( s9 K
  16.   u48id.ID2 =  FLASH_Readbyte(address+1);2 l2 g& p2 Z/ `; f$ W7 t* L
  17.   u48id.ID3 =  FLASH_Readbyte(address+2);
    0 X9 X) Y' \+ C1 f5 c4 k/ q/ Y; j
  18.   u48id.ID4 =  FLASH_Readbyte(address+3);& Q5 a! f  [2 g3 u' I3 t
  19.   u48id.ID5 =  FLASH_Readbyte(address+4);; Q# c( |: @$ i# w6 E
  20.   u48id.ID6 =  FLASH_Readbyte(address+5);# I. ~7 O, d3 Q* r! b. D
  21. ; p$ o4 n  F; H- ?) u0 v
  22.   return u48id;
    / D3 W1 V* X" `; b8 k; N
  23. }
复制代码

( M. v, @& M+ {. d, ]" G4 O

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


% x) E+ A% w1 y! I$ c

微信图片_20230612172035.png


# f) a0 {) r* _! S, B2.4.2 问题的分析(大小端模式数据格式)

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

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

% D/ L# I: a( |( [

微信图片_20230612172032.png

) v- e) J& J3 |, R% E  k6 G% B

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


0 q) u# R2 z! {! a; Y: c' Q/ w

微信图片_20230612172030.png

  ^2 T% |% n" C$ m4 i4 a' M* `4 }4 B

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

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

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

$ n# }7 l. r' m& Z3 x7 I
2.4.3 STM32L071RBT6 EEPROM读写全字问题
5 U3 }! l; B  {5 ~4 C
读问题的出现

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

  1. uint32_t  FLASH_ReadWord(uint32_t address); R2 m. _* L; s
  2. {2 L7 G. N6 [9 n0 m. E
  3.   return *(__IO uint32_t*)address;* r4 f5 |1 H6 @! P3 o0 u6 I# H- [) R' q+ d
  4. }
复制代码

. h% Y4 v- Z0 E+ ]

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


; z; `2 u& e' v  O, s

微信图片_20230612172026.png

* N! C* b3 H2 m# S- ^* L6 t2 Q

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

# j" o) L8 v% e# d0 H, H$ \: k

问题的解决:

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


9 F% @# z6 D- d8 [+ G

微信图片_20230612172019.png

1 p+ x, P# s+ a3 v) N( s# {

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

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

. H: b- O1 X4 s5 w4 U& {

微信图片_20230612172012.png


: P, b$ C$ X  j+ W5 [- m

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

  1. u32 FLASH_ReadEnoceanID(uint32_t address)- T6 n) F7 i! t/ p! P1 O/ S
  2. {1 k1 ^3 @/ {; b
  3.   u32 keepid;
      \. ]3 N/ w. u! R- [% m7 m
  4.   u8 i;
    9 I( f' T/ o- D5 M
  5.   keepid = FLASH_Readbyte(address);: N2 m; O( K, w. x
  6.   i = FLASH_Readbyte(address + 1);% R/ a9 X# h( G7 d5 E8 ?& ~( h, T
  7.   keepid = (i<<8)|keepid;  ?0 R; M: W$ O! t1 D
  8.   i = FLASH_Readbyte(address + 2);9 T4 j8 D& R$ [) |
  9.   keepid = (i<<16)|keepid;% A( F$ p4 B& H" D  E: M
  10.   i = FLASH_Readbyte(address + 3);
    & b( g( u/ h  M2 d
  11.   keepid = (i<<24)|keepid;
    ! `  {5 A; }8 b# {/ L5 s, q, u
  12.   return keepid;
    : D* O& C+ x/ ?$ ~+ ?) v9 U& [) m* x- t
  13. }: l9 e6 z9 k" T/ _

  14. 8 \1 Y* x& \& ]) e6 N+ U7 d
  15. CHID_STRUCT Flash_PowerOn_Check()
    / i9 p* D8 x+ ~8 Q- A
  16. {
    1 B4 Q/ S$ w% w% [/ x/ _

  17. 6 i" [) U7 k3 {. ^5 o% d
  18. CHID_STRUCT PowerOn_ID;
    6 j0 r( D; y0 w/ F6 U
  19. PowerOn_ID.CH1ID = FLASH_ReadEnoceanID(CH1_ID_ADDR);
    / R% P5 N5 ^! s2 _5 ^
  20. PowerOn_ID.CH2ID = FLASH_ReadEnoceanID(CH2_ID_ADDR);
    4 I( ?, x6 v2 A
  21. PowerOn_ID.CH3ID = FLASH_ReadEnoceanID(CH3_ID_ADDR);6 p0 S' }* W" q" D
  22. PowerOn_ID.CH4ID = FLASH_ReadEnoceanID(CH4_ID_ADDR);) }( P: y, Q2 y8 y* z
  23. PowerOn_ID.CH5ID = FLASH_ReadEnoceanID(CH5_ID_ADDR);
    # P; Y* Y+ x- J3 H& M& M: l. ~) y
  24. PowerOn_ID.CH6ID = FLASH_ReadEnoceanID(CH6_ID_ADDR);: t# C3 F, v& U9 H9 B
  25. PowerOn_ID.CH7ID = FLASH_ReadEnoceanID(CH7_ID_ADDR);( j+ d4 L. P1 q; S" ?9 Q6 [
  26. PowerOn_ID.CH8ID = FLASH_ReadEnoceanID(CH8_ID_ADDR);
    # b' `, c/ ]! j+ E% w, J! [4 o, ^
  27. PowerOn_ID.CH9ID = FLASH_ReadEnoceanID(CH9_ID_ADDR);
    ! E3 m0 x2 W# R5 Z  y
  28. PowerOn_ID.CH10ID = FLASH_ReadEnoceanID(CH10_ID_ADDR);" H& a) l) w4 u" D: Q0 h8 O

  29. " _( k* S! K) g) I$ f
  30. return PowerOn_ID;1 C- T/ C1 i& v! b3 l
  31. }
复制代码

( U* L" `. x) |, |! v8 {! e

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


2 S$ |) t1 g- S% G, v; v) Q8 \, V写问题的出现

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

一直用的写全字函数为:

  1. FLASH_Status  FLASH_WriteWord(uint32_t Address, uint32_t Data)% R. g( ]! ]' L! D% N
  2. {
    ! |1 A- M) T: }5 C' ?
  3. 2 d1 _- _9 Y2 \5 n" Y! \
  4. FLASH_Status i = FLASH_COMPLETE;
    ! o* q, k4 l/ C; Y; [$ _* K' s, F
  5. + Z/ C$ H# B1 C, \- q
  6. HAL_FLASHEx_DATAEEPROM_Unlock();  
    2 D4 G  f+ L: ~9 _  _
  7.   while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, Address, Data) != HAL_OK);) V& ?5 j3 G4 S& n- j
  8. HAL_FLASHEx_DATAEEPROM_Lock();
    ' {; n; C$ c% P+ c0 R8 A

  9. , x, H0 a" X5 V
  10. return i;
    * m6 p" x/ [8 `% n1 V; {
  11. }
复制代码
- g% x* \2 I3 b* W( ?) a4 E! x

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

$ l- T+ b' Z' @. f/ b4 k# r

微信图片_20230612172009.png


+ V. G) X! e1 Y1 E) i

但是使用时候发现:

8 N- `* X, h% k  g5 V9 h

微信图片_20230612172006.png

4 ^  i6 o% {  z+ s

问题的解决:

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


" M: ?( H, M: E6 t! p. b

微信图片_20230612172003.png


) O6 {& P' [9 D( I

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

  1. FLASH_Status  FLASH_WriteWord(uint32_t Address, uint32_t Data)
    . R- n. T  R- b) K9 P
  2. {
    2 a+ v! y5 ~, P# p7 \3 e+ D6 D
  3. % Y) O- [; u' e7 r+ C
  4.   FLASH_Status state = FLASH_COMPLETE;" \$ p4 @+ E5 e, M4 }1 }: m* h
  5.   u8 i = 0;1 k/ {7 D' k/ J' }8 R" H/ v7 I5 T
  6.   u8 writedata[6]={0};
    / P8 P5 g& l% [5 _6 z  y8 S0 C4 f

  7. % N) E1 b1 c/ J" i9 z$ P7 N
  8.   writedata[0] = (u8)Data;
    6 a, ^8 X4 |6 x6 a
  9.   writedata[1] = (u8)(Data>>8);
    ) s( W8 K4 E8 M+ D
  10.   writedata[2] = (u8)(Data>>16);
    1 Z3 ?: I% G% W* U& L3 \5 e( D7 Q
  11.   writedata[3] = (u8)(Data>>24);
    . ~: f0 J: Y. H; `' Y
  12. . [/ O6 ^; [% P6 j2 [
  13. HAL_FLASHEx_DATAEEPROM_Unlock();  2 D8 j! C% l  ?! T
  14.   for(i=0; i<4; i++){! _2 M, h0 m" @8 e: q2 l7 U8 a
  15.     while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, Address + i, writedata[i]) != HAL_OK);
    9 d1 Y" s8 m* }; ^
  16.   }
    5 D) I$ V; F& {8 C7 ^/ l0 d" U
  17. HAL_FLASHEx_DATAEEPROM_Lock();- u( `5 W+ q- |$ W2 [3 C

  18. 3 z) c; @: m8 D6 v" S! C
  19. return state;
    . c7 D1 |. M: F, W0 o& e* U. T" ?9 r
  20. }
复制代码

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

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

2.4.3小结使用的芯片为  STM32L071RBT6


+ A4 P+ i2 |* o; Q5 I最后问题的解决

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

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


9 e) d- `; s8 d

微信图片_20230612172000.png

" u8 G1 w- K) m. }7 X

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

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

) \/ A1 x( g! c# m# b5 v9 {

微信图片_20230612171954.png

1 ]! `3 N  T& ~4 t$ A  [

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

- \. f  J0 r8 _' T& M, f9 D
收藏 评论0 发布时间:2023-6-12 17:30

举报

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