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

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

% U3 y+ g- G1 Y( B) f4 y7 U

微信图片_20230612172451.png


0 |, n2 {, E% w9 x; N* }3 |

微信图片_20230612172454.png

( H2 ?" A4 K; A

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

0 ?/ z& |  A* P, U

  1. #define FLASH_SIZE                (uint32_t)((*((uint32_t *)FLASHSIZE_BASE)&0xFFFF) * 1024U)
      k' L! n+ \/ o0 O( B
  2. #define FLASH_PAGE_SIZE           ((uint32_t)128U)  /*!< FLASH Page Size in bytes */
复制代码

# s  p8 Y* d: j" P0 d5 X9 Q  [. i

对于flash的操作,有一些基础知识补充一下:

Read interface organized by word, half-word or byte in every area • Programming in the Flash memory performed by word or half-page • Programming in the Option bytes area performed by word • Programming in the data EEPROM performed by word, half-word or byte • Erase operation performed by page (in Flash memory, data EEPROM and Option bytes)

STM32L051写Flash必须字,读 字节、半字、字都支持。(这句话也是错误的,这是以前哪里看到的,实际测试写可以根据字,半字,字节来写)

一些基本概念:定义字是根据处理器的特性决定的。首先ARM是32bit处理器,所以它的字是32bit的。半字自然就是16bit;字节不论在哪个CPU上都是8bit。1 Byte = 8 bits(即 1B=8b) 1 KB = 1024 Bytes Bit意为“位”或“比特”,是计算机运算的基础,属于二进制的范畴;Byte意为“字节”,是计算机文件大小的基本计算单位;

2 |3 n2 O4 Q' Z8 w7 P
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,因为怕有时候数据不够放……

% b8 O! z. X1 w1 ]& d) u
2.1 读取函数
  1. //读取指定地址的半字(16位数据)" P! Q  W4 p$ U+ y' v; ?
  2. uint16_t FLASH_ReadHalfWord(uint32_t address)
    $ g( \5 L  R8 G/ V# B  @9 j
  3. {* Q2 P8 N5 \7 t3 h7 j  o5 g8 Z4 @4 s
  4.   return *(__IO uint16_t*)address; 8 C& }  u$ `/ ?. W8 y! G
  5. }
    - {9 u% ]/ R- a+ k* X" M* P) @
  6. " Z/ \' F" C' f9 t8 w4 d) t+ V5 d
  7. //读取指定地址的全字(32位数据)
    9 M, V8 Z4 r4 G6 K7 _
  8. uint32_t FLASH_ReadWord(uint32_t address)& @+ u+ G) \/ v4 N3 N3 \7 E6 R
  9. {
    ) h* x* ?/ ?5 G! h8 q4 Q
  10.   return *(__IO uint32_t*)address;
    4 c" l, }$ g( y9 n3 r, @
  11. }
复制代码

+ }( m6 }% B2 D% l3 @0 ^* t, ]* _" E9 H  n* n5 K4 F

简单测试一下:

  1. u32 read_data1=0XFFFFFFFF;' a% e' `! M/ V4 ^1 E
  2. u32 read_data2=0XFFFFFFFF;6 q* {$ b4 M; {' J  L1 `
  3. ...
    ) Y, g7 o: m. @. l* F  f
  4. read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);/ d7 M. X, K" y$ a$ l/ R8 v
  5. printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);
    1 J" Q6 C5 [) N! H
  6. read_data2 = FLASH_ReadWord(DATA_EEPROM_START_ADDR + EEPROM_PAGE_SIZE);6 z9 f0 @% S# U( o- b
  7. printf("the EEPROM sceond page test data is: 0x %x \r\n",read_data2);
复制代码

7 I3 l- C3 L' v

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

/ V# m3 z: i! a' U' k
2.2   EEPROM写函数

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

  1. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Unlock(void);- K0 S! ]" G5 _2 j5 P6 e" v3 q8 W5 U
  2. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Lock(void);
    0 h" P2 u$ }+ a

  3. 2 ^; g3 @7 A. o
  4. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Erase(uint32_t Address);
    # j3 E8 ]2 e3 R5 M# F: ]) x/ e
  5. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data);
复制代码

/ V; e. P  Z5 a. z

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

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

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

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

  1. HAL_FLASHEx_DATAEEPROM_Unlock();
    7 D2 u2 {; J6 b( P
  2. HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, DATA_EEPROM_START_ADDR, write_data1);
    . P7 W& u9 _6 U( a
  3. HAL_FLASHEx_DATAEEPROM_Lock();% D; [4 _* L9 [& q1 }' N) o
  4. ...9 I) z( M6 K0 X4 b% I# y
  5. if(btn_getState(&K1_BUTTON_150mS) == BTN_EDGE2){3 F1 ]& f) r4 G0 V1 \
  6.         printf(" K1 150ms button!,EEPROM_Erase test\r\n");! I+ Z2 u" Z' a% l  L
  7.         HAL_FLASHEx_DATAEEPROM_Unlock();
    $ `* \; J/ A. o  d6 s8 S
  8.         HAL_FLASHEx_DATAEEPROM_Erase(DATA_EEPROM_START_ADDR+4);
    . ]* ^2 K+ E0 E- _" A, \8 r6 a) W1 T' C2 U
  9.         HAL_FLASHEx_DATAEEPROM_Lock();
    8 W9 R( ]" d; u
  10.         HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
    6 @8 s1 U! t  P: C
  11.     }! L  r' O- W8 o* w
  12. ...
    7 ~( a8 P3 t$ ~" I: W
  13. if(btn_getState(&K2_BUTTON_150mS) == BTN_EDGE2){
    ) g! Q  X3 o) r( b
  14.         printf(" K2 150ms button!EEPROM_read test\r\n");
    3 r! Y7 J5 k, x% G
  15.         read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);
    % i% O: l# c/ ~2 G" j/ @( }
  16.         printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);  R- [: U- _3 `: H$ ^6 y$ N3 ~
  17.     }
复制代码

! K6 M' z& [# O* n' d* R/ D7 t

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

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

3 g8 W3 f% a/ }5 a4 Z, t6 E4 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();
    / W7 n' m, U* |" J& O- ]
  2.   HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, DATA_EEPROM_START_ADDR, 0X88);; u, H) \( X4 F6 N' Y
  3.   HAL_FLASHEx_DATAEEPROM_Lock();( D5 m. F+ s; m5 M% `" \2 n4 a" v
  4.   read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);3 j7 j6 }, p1 @$ M9 I/ d: k
  5.   printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);
复制代码

$ C( i) X2 h7 N8 ?) @, A; S2 m" {

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

  1. void MY_DATAEEPROM_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data)   
    , c& L- M9 c* j& h+ l
  2. {1 k/ _: o, }" l# h0 G
  3. HAL_FLASHEx_DATAEEPROM_Unlock();        
    / y- p; ~7 u' U
  4.     HAL_FLASHEx_DATAEEPROM_Program(TypeProgram, Address, Data);: O) g0 F- |9 w1 c
  5. HAL_FLASHEx_DATAEEPROM_Lock();
    9 o8 l# `! N! X& A' L" U8 Y
  6. }
复制代码

  D8 S% H8 f3 _* t* j; g/ h5 H

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


4 N) M9 r3 W" A2.3   Flash写函数

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

  1. /* IO operation functions *****************************************************/
    ( Z5 x3 z. n0 i: V7 @2 h1 j
  2. HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data);
    ( v; x  v" B% V
  3. HAL_StatusTypeDef HAL_FLASH_Program_IT(uint32_t TypeProgram, uint32_t Address, uint32_t Data);$ Y$ O2 M% {4 K4 g0 v
  4. : v( ]. z4 r# n' h
  5. /* FLASH IRQ handler function */
    ' j7 w3 H3 g/ t+ y; v; Q( r
  6. void       HAL_FLASH_IRQHandler(void);
    8 d1 A2 D# Z1 n6 v6 j
  7. /* Callbacks in non blocking modes */
    5 h7 {- m: |4 o, R, e
  8. void       HAL_FLASH_EndOfOperationCallback(uint32_t ReturnValue);9 b4 M+ ?- ^( o! Q1 G- d" W" R
  9. void       HAL_FLASH_OperationErrorCallback(uint32_t ReturnValue);6 A7 u" o% Z0 i. \& G6 h* P

  10. ) _" V( x9 x( K% u9 b
  11. /**
    3 `5 }) P+ s2 P: B& {
  12.   * @}+ ]$ w; j, H, u5 C/ u( O% t! U
  13.   */
    - n  ?: N' l1 v& X

  14. & \( q2 @$ H# E  a' e- Y
  15. /** @addtogroup FLASH_Exported_Functions_Group2& ^+ ]% z; A: X( F2 c
  16.   * @{
    + k: M/ K- x/ p6 @! r% J; _
  17.   */
    8 |) g3 L. u3 V$ K% A  I4 F
  18. /* Peripheral Control functions ***********************************************/3 d1 o% z, R6 l" U7 e3 y
  19. HAL_StatusTypeDef HAL_FLASH_Unlock(void);9 m& x' Z$ x. ]( C
  20. HAL_StatusTypeDef HAL_FLASH_Lock(void);) F) @: a& B4 K- I" L
  21. HAL_StatusTypeDef HAL_FLASH_OB_Unlock(void);( c6 k1 w" A0 U6 k2 Q% v
  22. HAL_StatusTypeDef HAL_FLASH_OB_Lock(void);
    ; O& G/ p9 Z6 ], O
  23. HAL_StatusTypeDef HAL_FLASH_OB_Launch(void);
复制代码

" o# H# |% ~! v, V

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


( M4 o( m1 }, o4 v% ?

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


! F) j- y  k6 \  M
  1. HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data)3 n6 e) p2 L5 J* \+ \. g
  2. {
      ^3 F" \. a3 u% C
  3.   HAL_StatusTypeDef status = HAL_ERROR;  f3 k" i% I# f4 q) ]' \- N2 N
  4.     ]/ r; Z# k" X! [
  5.   /* Process Locked */
    " V6 Z) ]5 b: P" D; O9 D) I$ c
  6.   __HAL_LOCK(&pFlash);
    7 j; E3 U4 R% X% t

  7. 5 j6 J# K2 B* w- G: D5 N
  8.   /* Check the parameters */
    : V) o2 _0 Y- [3 m3 P  O$ l
  9.   assert_param(IS_FLASH_TYPEPROGRAM(TypeProgram));  ]$ d7 q; {6 Q) G& X7 i5 _( S
  10.   assert_param(IS_FLASH_PROGRAM_ADDRESS(Address));) d0 _7 k: J& e+ E, {

  11. 6 w5 |0 U- l( }7 H
  12.   /* Wait for last operation to be completed */
    7 w8 T4 X# y& B/ h
  13.   status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
    - x/ q; V, y- j) C( A
  14.   
    ( X  @# k: T( a1 T( O7 e; e9 d
  15.   if(status == HAL_OK)* A- h8 z' P: p$ o( j
  16.   {, f! H+ J$ N% [5 P. w! s$ f
  17.     /* Clean the error context */* H# g& P4 f6 w& |" q4 `
  18.     pFlash.ErrorCode = HAL_FLASH_ERROR_NONE;
    " T) `; B, K5 {& y5 E4 N) g

  19. + x1 M7 b' s, Z8 y$ T
  20.     /*Program word (32-bit) at a specified address.*/$ U" s6 C: p* j5 y0 m% v
  21.     *(__IO uint32_t *)Address = Data;* I$ |  _# ~, V4 @; K5 g, w8 U) _# }
  22. 5 C+ k6 z) ^0 v# T) z
  23.     /* Wait for last operation to be completed */& s7 P# d$ Y2 i) n+ Y# e( e9 O
  24.     status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
    ' Q+ j0 p( }$ F' t
  25.   }
    " J& o& W5 V" \
  26. 5 E' H2 g% z! V4 R! ^2 w1 U
  27.   /* Process Unlocked */
    9 Q7 S% ~5 V0 Y* D8 B/ o
  28.   __HAL_UNLOCK(&pFlash);/ o7 ~$ F  |: x" Z+ ]  a) S' _
  29. 3 C* y; u% W: @7 Z" {3 u
  30.   return status;
    1 G9 B/ {: u' j. x5 x, C
  31. }
    2 S+ M8 U) W6 s" z
复制代码

% \8 M& s' e/ O* o, i

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

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

  1. #define ADDR_FLASH_PAGE_505      0X08000000 + 128*504   //
    5 \7 [& w& z- E) {/ L' y+ E
  2. #define ADDR_FLASH_PAGE_506      0X08000000 + 128*505   //6 d7 _% E1 V7 M4 Q; a! _1 B
  3. #define ADDR_FLASH_PAGE_507      0X08000000 + 128*506   //
    ' Y. v% ]- J+ L9 ^; x
  4. #define ADDR_FLASH_PAGE_508      0X08000000 + 128*507   //
    ( j/ ?' [  z' o# Q- {
  5. #define ADDR_FLASH_PAGE_509      0X08000000 + 128*508   //$ {) D) N2 U# k- G! K
  6. #define ADDR_FLASH_PAGE_510      0X08000000 + 128*509   //. |0 @* g/ O$ y9 x
  7. #define ADDR_FLASH_PAGE_511      0X08000000 + 128*510   //# X, s9 R- K$ F$ S
  8. #define ADDR_FLASH_PAGE_512      0X08000000 + 128*511   //最后一页
复制代码

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

  1. void MY_DATAFLASH_Program(uint32_t Address, uint32_t Data)   
    5 F& h" h$ c( c
  2. {
    * H6 _1 T7 e$ h$ e
  3. HAL_FLASH_Unlock();        
    7 W# V7 y# i9 V" D1 [9 y
  4.   if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){
    2 X9 d6 _' P; ]1 p, b" ^  a* Q
  5.     printf("write data 0x%x OK\n", Data);1 b5 N* t) I# s# s0 T
  6.   }) B( p1 j* P" p( e: p7 u
  7.   else{" M& q# P2 Q- f! C2 L& H
  8.     printf("failed!!!\n");$ k. P% o8 D" z, s
  9.   }% [3 \& \' C6 v  }% N
  10. HAL_FLASH_Lock();# ~* N. J2 {. z0 ~8 a
  11. }
复制代码

* e& l! u) I( z* W

测试一下;

  1.   MY_DATAFLASH_Program(ADDR_FLASH_PAGE_512,write_data2);
    : d$ k* L2 Z2 K& n  H
  2.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_512);9 o8 Y6 A- z4 T8 H
  3.   printf("the ADDR_FLASH_PAGE_512 is: 0x %x \r\n",read_data1);
复制代码
5 t0 J; f1 r# {* F6 ]3 {2 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)   / E9 I1 p3 B" C/ t
  2. {; |6 G; h) v( x+ u+ v: k
  3.   FLASH_EraseInitTypeDef EraseInitStruct;, C$ g# f+ f) c
  4.   uint32_t checkdata;
    + y, N6 |) p2 C% B0 ?
  5.   uint32_t PAGEError = 0;) A; n  w. _; L) P! m
  6.   checkdata = FLASH_ReadWord(Address);
    1 E4 z; B& K* N* |
  7. HAL_FLASH_Unlock();
    . |0 T3 O8 m; W  M
  8.   /*如果是0,直接写*/
    0 C& _& ?) B. y/ T3 M
  9.   if(checkdata == 0){      
    1 ]/ G+ ^7 s# |+ A7 G1 f' l
  10.     if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){
    0 ~* W$ {' `  ^* o4 K$ p9 K6 k
  11.       printf("write data 0x%x OK\n", Data);
    8 J4 g7 ~+ k2 H) E7 h) b  `: H' Q  f
  12.     }- O  c/ s( t$ e+ p
  13.     else{
    5 N- \2 y" W  @7 R' f6 {( G
  14.       printf("failed!!!\n");# Q( `* Q4 p" {. O" Y7 |
  15.     }
    2 i, {2 H" ^$ K6 c6 T9 U/ L) |. d4 E
  16.   }
    6 e* j5 t: q  I/ h) q2 I
  17.   /*否则擦除再写*/3 m' W( y" O: [& ?$ q4 ~
  18.   else{
    3 y0 a, v) T! F& H
  19.      __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);# n1 q6 Y3 `3 z4 m

  20. 2 l, ^+ G4 c% H% E- u
  21.       EraseInitStruct.TypeErase   = FLASH_TYPEERASE_PAGES; // 刷除方式: R/ D: A/ c6 Y5 l! a7 C
  22.    EraseInitStruct.PageAddress = Address; // 起始地址8 I! X% q3 y( J: \7 W
  23.       EraseInitStruct.NbPages = 1;
    : c5 E6 l: i+ U/ e  A8 [6 T

  24. * Z/ ]+ |9 C& w9 B+ u8 q
  25.       if (HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError) != HAL_OK)
    ; @! j* n5 a; \) E+ j! s
  26.       {
    " Y' c# ^( P# k& P
  27.         // 如果刷除错误8 W; q7 l: o1 k: v$ d
  28.         printf("\r\n FLASH Erase Fail\r\n");8 E8 y9 X$ t/ V+ A' c! M/ J  Q! Z& d
  29.         printf("Fail Code:%d\r\n",HAL_FLASH_GetError());2 _* E) e; k+ A2 h2 u
  30.         printf("Fail Page:%d\r\n",PAGEError);
    / M( D2 K) r9 O$ S3 h
  31.       }
    6 Q8 V1 J0 U) Z* n5 O& x
  32. 4 q* t  B3 x0 y4 V+ }3 V- _
  33.       if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){3 d( R( z/ W  q, j
  34.       printf("write data 0x%x OK\n", Data);
    8 J2 @/ e1 w& U! ^- b, f2 H- |7 Z
  35.       }
    ! f! a- B$ }+ [  o9 C  f3 V
  36.       else{
    : y7 i* S) O. d( `" X& e% ]! D
  37.         printf("failed!!!\n");; W  x7 S1 Y; y0 Y* i
  38.       }
    $ E- J  X- e, {7 h/ S1 x
  39. 6 x% a, f; A; h, h4 F2 T
  40.   }
    " J2 ~. X9 S6 W# m( J% z' o" I6 t
  41. HAL_FLASH_Lock();
    2 d' X* n3 z0 y7 T
  42. }
复制代码

. A5 t( |" ~9 P9 A8 r  f

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

  1. u32 write_data1 = 0X12345678;
    , Q' i/ |8 Z0 ?! }. m: F
  2. u32 write_data2 = 0X87654321;1 Q) F" R- H2 g
  3. u32 write_data3 = 0XFFFFFFFF;
    % P( S  b. ~* u( x; `
  4. - H1 r& i5 b# f9 E3 }# r' W) Q
  5. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_508 + 124,0X508508FF);
    0 \  O% Q) `& S. G2 j! w
  6. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509,write_data3);7 ^6 _( c; m1 z# Q6 a, i
  7. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509+4,write_data2);& y  h$ `. G( x6 v+ s- Y8 S) z4 o' U/ K8 B
  8. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509+8,write_data1);
    8 i$ M6 c3 c- y8 |* X  b# B
  9. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_510,0X510510FF);
    ( t/ \; V( N( a: a
  10. 0 V: F- `, }2 O0 T0 w8 _
  11. read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_508 + 124);
    $ O, _% D7 {$ ^$ W7 A' f
  12. printf("the ADDR_FLASH_PAGE_508 last is: 0x %x \r\n",read_data1);
    0 H, s, G. D" x
  13.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509);
    7 H# Z6 {5 f$ d7 w) x. d
  14.   printf("the ADDR_FLASH_PAGE_509 1 is: 0x %x \r\n",read_data1);
    & i) T8 l. l4 U1 z/ v" ]8 e) D
  15.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 4);% L7 b4 V# ?7 H6 {8 o9 N+ f: A  C
  16.   printf("the ADDR_FLASH_PAGE_509 2 is: 0x %x \r\n",read_data1);
    # H& L; y8 |) h" s, S
  17.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 8);
    % q( d2 m+ y- i& X* C5 y1 ~
  18.   printf("the ADDR_FLASH_PAGE_509 3 is: 0x %x \r\n",read_data1);7 s& T& f* {1 v. O$ A/ e0 p
  19.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_510);
    8 O1 H! `3 ~1 A( W/ \0 _/ J# a4 `, n
  20.   printf("the ADDR_FLASH_PAGE_510 first is: 0x %x \r\n",read_data1);! }" v6 m1 G/ z8 ^' d& J& z4 |2 C
  21. " S: J9 z  i0 i3 Z7 ~
  22. if(btn_getState(&K2_BUTTON_150mS) == BTN_EDGE2){7 l! g, b* v0 w2 c% B3 M
  23.         printf(" K2 150ms button!EEPROM_read test\r\n");) k, V) o$ {0 X$ y9 O, E  o
  24.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_508 + 124);5 X# j! h1 i' ~" I6 R
  25.         printf("the ADDR_FLASH_PAGE_508 last is: 0x %x \r\n",read_data1);" A; x& h1 e  N; {
  26.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509);
    8 R) U# {0 g& K0 H9 W) ]
  27.         printf("the ADDR_FLASH_PAGE_509 1 is: 0x %x \r\n",read_data1);* n9 X5 n1 W$ e$ w9 t" B4 k
  28.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 4);
    6 {$ r- `) u8 Y
  29.         printf("the ADDR_FLASH_PAGE_509 2 is: 0x %x \r\n",read_data1);2 m# w2 [( l, l
  30.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 8);
      }7 O2 Z7 ?% ^% n4 t! e
  31.         printf("the ADDR_FLASH_PAGE_509 3 is: 0x %x \r\n",read_data1);
    , {, x. U0 S1 O( }" I
  32.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_510);* ^$ @% v3 D3 A: F
  33.         printf("the ADDR_FLASH_PAGE_510 first is: 0x %x \r\n",read_data1);
    ) Z  c" Z, X7 x3 O4 i4 K; m6 Z

  34. : |/ j, _% }# }; U0 i5 j6 T$ M
  35.     }) @$ B/ Q- A! B  P1 g
  36. if(btn_getState(&K1_BUTTON_150mS) == BTN_EDGE2){, v8 v1 o. N% H" _  s2 b2 v
  37.         // printf(" K1 150ms button!,EEPROM_Erase test\r\n");( c/ c+ V6 U# I$ V5 F
  38.         // MY_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, DATA_EEPROM_START_ADDR, write_data1);
    3 \+ T" O& ]. d1 e4 i
  39.         printf(" K1 150ms button!,flash write test\r\n");: k4 _3 Q# W! u. z
  40.         MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509,0X33333333);9 e+ o2 q& b- b8 B) J* o
  41.         HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
    1 v$ a* _+ o( S9 s
  42.     }
复制代码
9 Q' d' W/ @1 ~$ k  ^" b; {) l

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


* h4 e3 z: i: n1 A* l! |

微信图片_20230612172448.png

9 \* u" N+ o2 K% ]" i

微信图片_20230612172355.png

# v' {- t# D, A) Z  Z8 p. ]0 ]) G& B

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


* T  }4 k& b& g% T- |2.4 读写EEPROM的后续问题

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

1 T0 |# F( S+ S5 R/ q, B, t7 a

微信图片_20230612172339.png


7 a, O& o- N# g' I6 L

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

  S* }8 O/ n, s, L" Y
2.4.1 问题的出现和解决

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


2 H6 P; G7 T; M, z: s5 V

微信图片_20230612172215.png


- f* \% P! f' U* s

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

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


, ]+ p. F) u/ ?* c. e

微信图片_20230612172212.png


5 Y& s" U& D: j4 t

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

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

  1. BlueID_STRUCT test;4 [0 k, I6 S" u- r( ^
  2. ...9 X4 j/ C7 G. y7 U! [3 J
  3. /*) O: R* p; d" U' J9 l
  4. CHBlueID_STRUCT Flash_PowerOn_BlueCheck()& i3 ?( `4 W% m8 h* h; a
  5. {; W# n% D% C. [6 A2 b% T
  6. " v  m. L# O( L& N
  7. CHBlueID_STRUCT PowerOn_ID;8 I, {& X1 r2 W  u# y' Q8 c7 ^
  8. PowerOn_ID.CH1ID = FLASH_blueIDRead(CH1_ID_ADDR);
    . D" o" X7 k# B7 K0 M
  9. PowerOn_ID.CH2ID = FLASH_blueIDRead(CH2_ID_ADDR);
    : I  {! W/ y1 f: e( |. X! d
  10. PowerOn_ID.CH3ID = FLASH_blueIDRead(CH3_ID_ADDR);
    $ d; |5 I* L% _* v- C1 d0 @( v
  11. PowerOn_ID.CH4ID = FLASH_blueIDRead(CH4_ID_ADDR);. j" Y( @( S8 C$ `
  12. PowerOn_ID.CH5ID = FLASH_blueIDRead(CH5_ID_ADDR);
    0 T. {. F% m, I# s, B
  13. PowerOn_ID.CH6ID = FLASH_blueIDRead(CH6_ID_ADDR);
    ) H+ j& K* @! t/ t3 E
  14. PowerOn_ID.CH7ID = FLASH_blueIDRead(CH7_ID_ADDR);+ x. @4 V# l, c8 |# C7 x0 m
  15. PowerOn_ID.CH8ID = FLASH_blueIDRead(CH8_ID_ADDR);# ^, G* e8 g) G: V; O
  16. PowerOn_ID.CH9ID = FLASH_blueIDRead(CH9_ID_ADDR);& p8 o1 ~+ H" x. m; E7 L# G
  17. PowerOn_ID.CH10ID = FLASH_blueIDRead(CH10_ID_ADDR);+ M4 B+ \, q- V% E" h

  18. 7 j! D# a2 C% a
  19. return PowerOn_ID;
    ! {8 e' M1 N- b, x! ]
  20. }0 S% h7 @& \% h
  21. */) t! p( D9 L2 `
  22. BlueChipID = Flash_PowerOn_BlueCheck(); //上电先把ID读出来做比较
    " z/ J0 u  _" E" e
  23. //打印一个出来测试,看结果
    3 w1 K6 N& q- X4 q
  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);; [0 a3 Z! _/ V/ H% S
  25. / V  A/ w/ e1 p; |
  26. test.ID1= 0XFF; //结构体每个元素是 uint8_t 类型7 o- T4 c; Q: e! {$ ~$ V
  27. test.ID2= 0XEE;: z* j2 j: M! k4 N: h4 K# Z
  28. test.ID3= 0XDD;
    ! D# ?: u# Z% e5 {2 N
  29. test.ID4= 0XCC;
    3 i4 T+ r( h8 N8 k  r6 ^! f
  30. test.ID5= 0XBB;7 b$ [% I/ ^' u+ ^# b4 N( ~
  31. test.ID6= 0XAA;: Q. I, X- q2 Y

  32. 8 \% ^! Y. K3 J8 e/ C
  33. while (1){...}
复制代码

0 s. S; d$ E' t: k! h4 o) y4 O+ r

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

0 P5 Z. y% L% Q' C

微信图片_20230612172209.png


" ]  L9 u. m( D; ~% k7 J

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

/ f, \+ w& @% r. ?

微信图片_20230612172207.png

2 Z; \5 k" M6 m: b$ w) }1 q

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


1 ?8 f) c0 N" U: N

微信图片_20230612172058.png

7 \! G% p! e" v* J

微信图片_20230612172050.png


' w. _6 w' i4 C

打印的结果:

4 s5 [  c; M: P; g' j

微信图片_20230612172039.png

. U  _. X; T- J9 x% |& ~0 E) ?

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

  1. BlueID_STRUCT FLASH_blueIDRead(uint32_t address)8 I/ @" y( ]' w  F
  2. {  n6 Q7 ^3 h8 h" F. W3 x' R0 C
  3. BlueID_STRUCT u48id;
    7 [! I7 n, e; B" a( C! M% W) c# x
  4.   // uint16_t temp1,temp2,temp3;  /**(__IO uint16_t*)address; */% h' d# K5 m. U, J/ h
  5.   // temp1=*(__IO uint16_t*)address; - w8 k5 {& y( w, X" o5 v2 ?
  6. // u48id.ID1 = (uint8_t)(temp1>>8);
    1 w7 Y* x. i) p  ?2 F
  7. // u48id.ID2 = (uint8_t)(temp1&0X00FF);  C1 G. Z8 L& k' v) ?
  8.   // temp2=*(__IO uint16_t*)(address+2);
    8 K! t. e3 v8 h0 C( ^2 [
  9. // u48id.ID3 = (uint8_t)(temp2>>8);0 ?) @8 j* M! M6 [
  10. // u48id.ID4 = (uint8_t)(temp2&0X00FF);
    : r& d% K  e* p
  11. // temp3=*(__IO uint16_t*)(address+4);
    + V6 l; ]7 C3 R2 r& c+ {
  12. // u48id.ID5 = (uint8_t)(temp3>>8);7 A  H$ k, K/ l, t
  13. // u48id.ID6 = (uint8_t)(temp3&0X00FF); 6 _: k% b/ u7 y+ R, x
  14. 7 g3 T5 k1 s) v  y
  15.   u48id.ID1 =  FLASH_Readbyte(address);% _6 I# |6 J+ }3 t
  16.   u48id.ID2 =  FLASH_Readbyte(address+1);
    1 P. O6 K1 l' ~% t+ V
  17.   u48id.ID3 =  FLASH_Readbyte(address+2);
    3 n1 o2 W5 ~) Y( {
  18.   u48id.ID4 =  FLASH_Readbyte(address+3);( q! h7 E6 R2 s1 o2 K+ {; J
  19.   u48id.ID5 =  FLASH_Readbyte(address+4);, v0 W1 o" I4 ]4 a) A- r
  20.   u48id.ID6 =  FLASH_Readbyte(address+5);! f1 E' D9 ^) x# i0 h
  21.   `2 l+ ?+ z2 o& W+ a  S6 f# M
  22.   return u48id;
    3 f" O6 M/ s6 {( {0 a8 L
  23. }
复制代码

. d5 ?, z5 A7 `! b# q& |6 j& d$ W

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


" L" Y( k% h7 o* a( ]* _: f6 B( Y5 {

微信图片_20230612172035.png

; z3 g6 w* o! W3 K7 m% F. N# @; I
2.4.2 问题的分析(大小端模式数据格式)

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

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


4 y2 H3 G* o2 I, ?& I

微信图片_20230612172032.png

0 l( G& t$ q) }! ~2 [

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


5 J- u$ N1 U3 a& z9 }8 s7 D" ~

微信图片_20230612172030.png


6 g1 j3 a( o' o' t

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

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

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


/ z( F! d  f1 I, F2.4.3 STM32L071RBT6 EEPROM读写全字问题
# ~6 ]0 r" s5 g
读问题的出现

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

  1. uint32_t  FLASH_ReadWord(uint32_t address)1 H5 d: z5 N2 K3 a4 G9 H5 E
  2. {' n" U) o" L8 ~$ ?0 @4 K
  3.   return *(__IO uint32_t*)address;
    : V4 A: L3 y- \) z6 y+ t9 S+ m
  4. }
复制代码

! A) n8 E0 W9 V' P/ W3 O

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


; h6 U  N2 J( e8 R$ F

微信图片_20230612172026.png


) D+ g: y3 z- L6 w1 E

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

7 v1 I7 N, F! Z8 }8 h0 n( H

问题的解决:

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

' o, k# L" _! T* f. Y( x

微信图片_20230612172019.png

$ v. J5 U5 h$ @! _

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

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

. `9 x. w. p5 ~# o2 O8 ]

微信图片_20230612172012.png

# s0 l# r" A  f; p: g0 Y3 g

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

  1. u32 FLASH_ReadEnoceanID(uint32_t address)( \6 t: a0 p/ q$ z* X  F  r) _, m
  2. {& a' \, Y* B+ g
  3.   u32 keepid;( F; q3 N! _( L, Z$ R$ z
  4.   u8 i;, x$ Y3 B9 F5 i. ~
  5.   keepid = FLASH_Readbyte(address);
    3 C* r5 F  W! A& z
  6.   i = FLASH_Readbyte(address + 1);9 t8 {% c) S) K. `+ N2 d, Z' }$ U" H
  7.   keepid = (i<<8)|keepid;
    * y9 ]4 U' H2 {
  8.   i = FLASH_Readbyte(address + 2);
    ' B/ W& h2 I3 f3 X- F9 U
  9.   keepid = (i<<16)|keepid;
    ( ^( N+ r6 {& i0 W8 k1 ^1 ?0 w
  10.   i = FLASH_Readbyte(address + 3);+ Q* Q4 u# N7 ?1 L4 m. _
  11.   keepid = (i<<24)|keepid;* }% ~* `  h  f6 f7 G
  12.   return keepid;- s- ^3 d+ y9 X5 A! N% H
  13. }5 S. T$ N1 d$ l1 a9 u0 U0 g* G
  14. 4 O" ?4 }1 V! U: }2 {
  15. CHID_STRUCT Flash_PowerOn_Check()
    - N8 ]1 p+ Q* }4 J/ Y$ x
  16. {7 m# X9 U+ X2 l" T, _4 @. N( B

  17. $ z2 \: Q& X9 ~# H6 c
  18. CHID_STRUCT PowerOn_ID;3 t1 A( j5 @' D
  19. PowerOn_ID.CH1ID = FLASH_ReadEnoceanID(CH1_ID_ADDR);
    4 x2 P: k" r$ e8 r4 g5 @2 b9 ~
  20. PowerOn_ID.CH2ID = FLASH_ReadEnoceanID(CH2_ID_ADDR);
    - q6 G/ e( J- ?" p; p! ]
  21. PowerOn_ID.CH3ID = FLASH_ReadEnoceanID(CH3_ID_ADDR);, s4 m3 r/ c  T6 y
  22. PowerOn_ID.CH4ID = FLASH_ReadEnoceanID(CH4_ID_ADDR);; s5 o2 N) k- w+ S
  23. PowerOn_ID.CH5ID = FLASH_ReadEnoceanID(CH5_ID_ADDR);
    $ B) A6 ]9 K8 k# E$ l# {( B
  24. PowerOn_ID.CH6ID = FLASH_ReadEnoceanID(CH6_ID_ADDR);
    % W) ?4 \' Y( N; Q- `' a
  25. PowerOn_ID.CH7ID = FLASH_ReadEnoceanID(CH7_ID_ADDR);! z( G+ Y! C% I* d/ K
  26. PowerOn_ID.CH8ID = FLASH_ReadEnoceanID(CH8_ID_ADDR);3 m4 K; ~6 e3 S2 B
  27. PowerOn_ID.CH9ID = FLASH_ReadEnoceanID(CH9_ID_ADDR);
    1 u3 M6 g. R; S, J9 `# c$ Y: w
  28. PowerOn_ID.CH10ID = FLASH_ReadEnoceanID(CH10_ID_ADDR);
    " h: i# c/ k1 t, L0 W& s$ x& T. L
  29. 1 F3 o9 z$ Y3 D( ^# N2 {! E
  30. return PowerOn_ID;4 f" v. n! j: Q+ j/ k7 E
  31. }
复制代码
6 m0 l6 @" h) G- t  x4 i% F4 Y

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

' `; ]* }) ]/ g/ P9 c  F+ w6 ?
写问题的出现

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

一直用的写全字函数为:

  1. FLASH_Status  FLASH_WriteWord(uint32_t Address, uint32_t Data)
    0 j' l5 ?- i2 p) {- e
  2. {
    ' w( e) ^9 I3 J) L+ T% F0 {+ i
  3. & _* R' u- n) u5 G/ M4 ?
  4. FLASH_Status i = FLASH_COMPLETE;3 g6 J% H9 ^5 }; D. H
  5. $ C/ ~4 b7 h1 Z. f
  6. HAL_FLASHEx_DATAEEPROM_Unlock();  5 N0 Z7 V4 w, v- \: a
  7.   while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, Address, Data) != HAL_OK);0 k* K$ U4 G" `" \; B6 H3 y
  8. HAL_FLASHEx_DATAEEPROM_Lock();
    , v, g% U9 w7 l

  9. 7 k7 r7 W) `+ w0 A% J* K) z
  10. return i;
    " b0 d3 ~! D8 D: j) e  l! v
  11. }
复制代码
! m$ r# q7 d! U

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


- e8 E& R  m. w* w6 X$ l

微信图片_20230612172009.png


+ z6 o( D8 @3 j

但是使用时候发现:

; F' c% g( |5 D3 g. g

微信图片_20230612172006.png

* F$ b9 e7 t0 I1 K/ M, T/ }1 U) _

问题的解决:

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

5 |6 m" i: }" k' _

微信图片_20230612172003.png

; E$ `+ L& T; C1 \0 b& v

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

  1. FLASH_Status  FLASH_WriteWord(uint32_t Address, uint32_t Data)
    & I% W- o: N$ t4 K! r
  2. {+ V" V& J' B( s7 b. h% j% G- O  ^

  3. / g4 B4 H- ^( F/ O
  4.   FLASH_Status state = FLASH_COMPLETE;
    8 k9 x! V8 Y3 W+ G7 _+ _! e- H9 t
  5.   u8 i = 0;
    1 B/ K% w0 N  t5 N
  6.   u8 writedata[6]={0};
    1 D) t& x6 M/ i: r

  7.   Q# S- \- s$ F: n, B
  8.   writedata[0] = (u8)Data;
    # A+ D5 @4 X9 k$ n0 d; V
  9.   writedata[1] = (u8)(Data>>8);9 j3 M- j  O' [8 C/ o8 T
  10.   writedata[2] = (u8)(Data>>16);
    . P  b: S3 K4 P5 M! s( c3 E" i
  11.   writedata[3] = (u8)(Data>>24);
    8 o5 ^9 n9 W8 E) }% ~- b
  12. $ h  M  G) l- e' k0 b3 j
  13. HAL_FLASHEx_DATAEEPROM_Unlock();  + M# l8 l) ^( B0 r
  14.   for(i=0; i<4; i++){
    3 \' `( j( f5 X) b6 ?
  15.     while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, Address + i, writedata[i]) != HAL_OK);5 Z/ S% p- N4 [9 |8 I& S) E
  16.   }
    ! J2 Y: S1 V5 @9 Z& s
  17. HAL_FLASHEx_DATAEEPROM_Lock();
    3 R" K6 e0 |$ z  p( V5 a( L

  18. : \8 b0 d9 y; `& s- T, W
  19. return state;0 u- [4 ~) V, ~" D
  20. }
复制代码

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

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

2.4.3小结使用的芯片为  STM32L071RBT6


& I6 V# l3 ]' o最后问题的解决

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

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

, }0 r! Y* I% K  |/ I7 f

微信图片_20230612172000.png

) P6 c: Q. |2 K; S

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

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


0 J3 M) y4 ~7 j

微信图片_20230612171954.png

' ^6 r! z3 Z) z5 D! W! Q" J

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

- x0 ]4 i$ e7 ?! I
赞 收藏 评论0 发布时间:2023-6-12 17:30

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版