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

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

7 v  l6 K' ~1 V

微信图片_20230612172451.png

8 b& U2 b/ G/ F* B

微信图片_20230612172454.png

8 B* D, v2 B' r0 X- ?

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


+ }3 r8 G! \: }7 l0 M, Z6 r& M

  1. #define FLASH_SIZE                (uint32_t)((*((uint32_t *)FLASHSIZE_BASE)&0xFFFF) * 1024U)5 H1 Z& {( B. P
  2. #define FLASH_PAGE_SIZE           ((uint32_t)128U)  /*!< FLASH Page Size in bytes */
复制代码

# u$ u. E. }. K3 ^( v- q4 |- 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意为“字节”,是计算机文件大小的基本计算单位;

" B+ q/ V6 R* ^& W1 [2 \1 ?
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,因为怕有时候数据不够放……


0 A8 m% k# J( {- [6 {2 g6 a2.1 读取函数
  1. //读取指定地址的半字(16位数据)$ A! r% R& F/ c0 F8 k! m
  2. uint16_t FLASH_ReadHalfWord(uint32_t address)
    ' q0 x+ u$ A; x1 `- m
  3. {0 i1 K; a/ Y$ K3 ~: z
  4.   return *(__IO uint16_t*)address; / z0 s' I0 p. z
  5. }
    6 w5 P  M0 b& Q" f

  6. 3 j, W6 f: [* D# J+ ?# ~
  7. //读取指定地址的全字(32位数据)
    ) R$ b% V" K  z1 D
  8. uint32_t FLASH_ReadWord(uint32_t address)
    $ h$ Q- V7 T3 c4 T# H
  9. {+ n/ v4 J4 s. t5 K
  10.   return *(__IO uint32_t*)address;
    . z* b: F: ?' c+ K) f0 X
  11. }
复制代码

9 h5 P0 u% M6 m3 M4 ?- f1 U5 D) v! l! {

简单测试一下:

  1. u32 read_data1=0XFFFFFFFF;+ [+ K. b* M" d" c& ?9 D, G% Q' n& ^2 {
  2. u32 read_data2=0XFFFFFFFF;
    3 O5 i. J( F+ f! R7 h3 B
  3. .... Y5 H: N8 W( f; Z  V
  4. read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);$ w8 f9 U- a3 d7 t% h
  5. printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);
    # Q' [' F5 ~4 W8 E1 P7 M
  6. read_data2 = FLASH_ReadWord(DATA_EEPROM_START_ADDR + EEPROM_PAGE_SIZE);3 _- x  h& M. L/ T8 U6 ]* b
  7. printf("the EEPROM sceond page test data is: 0x %x \r\n",read_data2);
复制代码
8 A; v( R+ `2 N( q

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


0 w" Q6 r. w/ n0 R2.2   EEPROM写函数

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

  1. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Unlock(void);9 S! Z% t+ t) J$ H( Z- G
  2. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Lock(void);1 [1 G2 t1 {/ M

  3. $ C4 s6 F% v4 D! Y/ n) u7 T2 u
  4. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Erase(uint32_t Address);3 Q+ u. T' c% I; p
  5. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data);
复制代码

. f  ?% O5 x! W' U

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

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

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

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

  1. HAL_FLASHEx_DATAEEPROM_Unlock();: Z' h( O- p( w( Y1 s" G
  2. HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, DATA_EEPROM_START_ADDR, write_data1);) E2 I' H( P$ ^/ s/ c& ^, L7 b: Q
  3. HAL_FLASHEx_DATAEEPROM_Lock();
    # h3 V2 X7 e6 t; A
  4. ...
    " I1 p( t; [6 o6 p. l
  5. if(btn_getState(&K1_BUTTON_150mS) == BTN_EDGE2){# G6 t) p% Y$ b) G3 B, _
  6.         printf(" K1 150ms button!,EEPROM_Erase test\r\n");" r' T9 A7 h7 m% U7 u- t; e% c
  7.         HAL_FLASHEx_DATAEEPROM_Unlock();
    . c: b1 U! d9 i4 O9 l
  8.         HAL_FLASHEx_DATAEEPROM_Erase(DATA_EEPROM_START_ADDR+4);3 o6 W. o% f) J2 ^, u4 H. D7 [2 i
  9.         HAL_FLASHEx_DATAEEPROM_Lock();9 T9 Y2 W1 I% r* P6 l# {  ?7 P! A
  10.         HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);/ u2 n& W# Y4 v6 }2 |! c
  11.     }! f3 [2 v  [  {. u8 E) x
  12. .... ^! Q6 _% ?0 i" J: u- e# b
  13. if(btn_getState(&K2_BUTTON_150mS) == BTN_EDGE2){) Z: w1 W/ j) T; P5 F- A. Y) Z5 f
  14.         printf(" K2 150ms button!EEPROM_read test\r\n");  g( D% S2 D/ M2 b" G
  15.         read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);
    : x2 E7 G4 J5 z" o
  16.         printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);
      w" P7 O* _2 h$ J
  17.     }
复制代码
( _$ B, a* m9 S; C4 f9 U

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

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

+ R. |: ]2 I  q" h9 }. ]
写入问题说明修改

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();) i; |0 R) \/ Z. r6 `% l9 J* I
  2.   HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, DATA_EEPROM_START_ADDR, 0X88);
    . l1 U. A5 K& ^( [9 M; i/ O# _
  3.   HAL_FLASHEx_DATAEEPROM_Lock();
    & O0 W- O5 `  \  [+ n
  4.   read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);6 V; x, }: d- u0 I8 R9 H7 ^
  5.   printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);
复制代码

$ v9 X4 u9 Q( d8 r

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

  1. void MY_DATAEEPROM_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data)   ' u0 R' x& R( }4 {
  2. {
    ) h; [; r% ]* Y* J5 U
  3. HAL_FLASHEx_DATAEEPROM_Unlock();        
    # u* J* m, G1 S7 \: k
  4.     HAL_FLASHEx_DATAEEPROM_Program(TypeProgram, Address, Data);
    9 y0 D! q, o. B4 O
  5. HAL_FLASHEx_DATAEEPROM_Lock();
    2 y* ?! E1 u. ~7 ~+ a$ f* X0 _
  6. }
复制代码

! D9 e, T: w* D) D0 [

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


% P0 S) e8 }4 @! E/ G& P2.3   Flash写函数

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

  1. /* IO operation functions *****************************************************/. [$ G) M" c" F7 l
  2. HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data);
    ( d) j1 Z3 }* E+ g1 S6 t% @$ R2 U. X/ B
  3. HAL_StatusTypeDef HAL_FLASH_Program_IT(uint32_t TypeProgram, uint32_t Address, uint32_t Data);
    . T9 W! c' {- @! x" n
  4. 4 M4 z& c9 D/ U8 r+ t  H, q& X) U
  5. /* FLASH IRQ handler function */8 X  `. E" h& t7 E6 b+ r( `7 S
  6. void       HAL_FLASH_IRQHandler(void);! n2 a; p- Q/ c; ?
  7. /* Callbacks in non blocking modes */
    7 h1 y, h" Z- Z$ J) I
  8. void       HAL_FLASH_EndOfOperationCallback(uint32_t ReturnValue);1 I% v+ K/ T# D/ i7 \$ x
  9. void       HAL_FLASH_OperationErrorCallback(uint32_t ReturnValue);/ c) K; [8 r* _2 \' o$ Z4 }0 `

  10. ; {) l! S8 F/ v6 d) N4 P* Y
  11. /**
    " I, Z4 ?/ e# t; y7 f4 Z! U+ D: h. L; s
  12.   * @}9 N% G% s: G5 W. W) ~2 u
  13.   */
    ) ]* L% |7 ~* m% s0 ]! q

  14. ; n7 a! b" W% [6 S, A8 _+ p
  15. /** @addtogroup FLASH_Exported_Functions_Group2
    + e3 _- S9 |. l; z
  16.   * @{
    & m& c9 Z$ j8 I4 s; ~
  17.   */# _. Q, s0 `5 E# @5 R; D* A
  18. /* Peripheral Control functions ***********************************************/, u- M' q7 G8 P* ?' P
  19. HAL_StatusTypeDef HAL_FLASH_Unlock(void);
    , [2 T+ h( a* G, b" P- O
  20. HAL_StatusTypeDef HAL_FLASH_Lock(void);) K8 \1 {8 h) v, Z
  21. HAL_StatusTypeDef HAL_FLASH_OB_Unlock(void);
    , Q' w8 f* z+ s  m! y9 l9 V
  22. HAL_StatusTypeDef HAL_FLASH_OB_Lock(void);5 Y2 l& K" s- j  L) D" G3 q  I) \
  23. HAL_StatusTypeDef HAL_FLASH_OB_Launch(void);
复制代码

* J5 K$ Z  B: i/ s- p

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


5 _7 `8 |# ~" I/ f8 X% H% f

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


* }! S: O5 j" R4 {' M/ v
  1. HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data)
      I! W  k* n% z0 ~/ _
  2. {& j5 O2 ~+ j) J1 e
  3.   HAL_StatusTypeDef status = HAL_ERROR;
      o- e# |" t  _( Y
  4.   + u3 t) |* u! M+ Z* s& U6 V
  5.   /* Process Locked */$ c* T, h; y8 x$ }0 V7 e
  6.   __HAL_LOCK(&pFlash);4 d" v/ Q! E9 m" x
  7. + G$ K) }% v; Y5 `. K! |+ B
  8.   /* Check the parameters */
    ' S, C: A. L  j* k$ r8 O
  9.   assert_param(IS_FLASH_TYPEPROGRAM(TypeProgram));
    # B2 F# A9 a* ~; f( `2 H4 E
  10.   assert_param(IS_FLASH_PROGRAM_ADDRESS(Address));
    . n  y" I- n9 S# F5 Q& r
  11. / \3 e- v" G+ {+ ~3 c
  12.   /* Wait for last operation to be completed */9 @9 z5 {5 b1 @4 F6 x' K
  13.   status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
    9 i! ]3 \3 ]* R) x( b2 _
  14.   
    8 ]4 G! T0 F: Q; h2 i5 C5 F
  15.   if(status == HAL_OK): Q2 p3 z) M( C( M$ A
  16.   {% s1 f/ F# [8 g3 F
  17.     /* Clean the error context */
    2 w5 G7 R, S' y( w- u
  18.     pFlash.ErrorCode = HAL_FLASH_ERROR_NONE;
    : a4 _; _1 I# B

  19. 5 O, f; d! ~' P
  20.     /*Program word (32-bit) at a specified address.*/- F) E/ |0 Q% D1 n9 S$ B
  21.     *(__IO uint32_t *)Address = Data;
    & D* W" z( o5 s4 d( n- N/ W
  22. 1 R! T! \1 i5 Y0 M
  23.     /* Wait for last operation to be completed */5 `/ W8 q2 \& ^+ [; f9 g5 \
  24.     status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
    - d# R2 o5 s+ w. b: n
  25.   }7 p2 x1 N: H& |6 d% o% I7 g$ J
  26. 2 V- e. E* G2 J5 {
  27.   /* Process Unlocked */
    / R# b2 {9 a4 }2 X7 I1 p) u- E9 n
  28.   __HAL_UNLOCK(&pFlash);
    ' b6 \1 A$ Z" h* F) N

  29. " _5 G# H. m3 X% h4 Z
  30.   return status;
    . H& }) N0 A3 i' ?) o' H0 r
  31. }6 |( l9 W" i; x; ~3 K8 c
复制代码

0 J: ^: u( u/ h9 c/ S; y

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

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

  1. #define ADDR_FLASH_PAGE_505      0X08000000 + 128*504   //; m8 c9 o" J/ q8 a/ _4 J
  2. #define ADDR_FLASH_PAGE_506      0X08000000 + 128*505   //" R% @% V% J/ [) z+ ~9 s% H
  3. #define ADDR_FLASH_PAGE_507      0X08000000 + 128*506   //
    9 y  n9 z: O$ o
  4. #define ADDR_FLASH_PAGE_508      0X08000000 + 128*507   //! Z' e9 b& l  u; A$ J6 _6 \
  5. #define ADDR_FLASH_PAGE_509      0X08000000 + 128*508   //% h3 u' U6 r1 R" M) |- m
  6. #define ADDR_FLASH_PAGE_510      0X08000000 + 128*509   //1 |: ~) D# o7 r: E9 w
  7. #define ADDR_FLASH_PAGE_511      0X08000000 + 128*510   //
    % |1 Y7 {) p" P/ j6 T: \
  8. #define ADDR_FLASH_PAGE_512      0X08000000 + 128*511   //最后一页
复制代码

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

  1. void MY_DATAFLASH_Program(uint32_t Address, uint32_t Data)   * ~& b4 H( w/ i+ c0 o
  2. {- `. O! n3 f4 g7 P7 h7 B" U
  3. HAL_FLASH_Unlock();          b) }6 y' ~) ~0 H( \& }1 U
  4.   if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){
    0 [5 q6 }8 z8 `- v; s& \! M1 ]
  5.     printf("write data 0x%x OK\n", Data);
    0 r, |6 F8 ^# D0 P0 K+ w; Z
  6.   }. z+ L$ Q6 P/ d' i3 M1 E2 Q# p
  7.   else{
    3 J5 q, {5 B& l  ?3 S7 j
  8.     printf("failed!!!\n");
    * i7 V; ~1 Q% Z$ L
  9.   }
    $ {9 c# |4 c1 A. C$ j2 d
  10. HAL_FLASH_Lock();
    : E. f/ Z5 |3 `/ A! e+ \! ?
  11. }
复制代码

+ X9 A7 l+ e% d

测试一下;

  1.   MY_DATAFLASH_Program(ADDR_FLASH_PAGE_512,write_data2);1 `- B) J% T+ X4 d4 w
  2.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_512);: j+ }& R3 u3 p/ R3 O
  3.   printf("the ADDR_FLASH_PAGE_512 is: 0x %x \r\n",read_data1);
复制代码

& [# k) }* P# P4 A4 _2 v

在没有写入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)   
    8 L6 ]" A( J" p" T% I; l
  2. {4 y& Y6 |& Z9 z6 {, V: z
  3.   FLASH_EraseInitTypeDef EraseInitStruct;+ W" v& W0 z% v9 d" M
  4.   uint32_t checkdata;# E1 }: Z7 ?( I$ B+ Y
  5.   uint32_t PAGEError = 0;% ]  c# J4 ?+ ]/ t
  6.   checkdata = FLASH_ReadWord(Address);
    + v( _; A2 I' n( d: O
  7. HAL_FLASH_Unlock(); 9 F2 h( X7 w' C0 j/ w
  8.   /*如果是0,直接写*/
    ! |4 r! S2 @5 B5 d& [. i
  9.   if(checkdata == 0){       # ~% [  R9 P8 O0 r; ], k) a2 p3 y
  10.     if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){
    ; E- |- P8 z( @0 ^
  11.       printf("write data 0x%x OK\n", Data);9 r8 `6 d( l  E" y
  12.     }3 T2 x( c( M. Y
  13.     else{, g4 {6 q6 l0 ^3 R# F) j% u- ^0 v
  14.       printf("failed!!!\n");; M, Z" s4 V) P/ w! c
  15.     }: g+ I8 y( H+ A) |3 g: B
  16.   }
    ' l) {+ {2 N: o' ~. w, B6 Q
  17.   /*否则擦除再写*/3 t1 v! e( G$ z& K% v1 g
  18.   else{% ], `) R6 v9 B4 |2 Q  Z' L( `$ v
  19.      __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);: {; _' B, x/ A8 B( c, W8 `8 ~& v2 Y
  20.   x& V9 Y' n; m0 D# e: L5 J
  21.       EraseInitStruct.TypeErase   = FLASH_TYPEERASE_PAGES; // 刷除方式
    1 n5 a# E4 \* R0 q' X
  22.    EraseInitStruct.PageAddress = Address; // 起始地址4 z3 o% i7 _# `- V9 i% t2 x
  23.       EraseInitStruct.NbPages = 1;
    7 s: D0 ~( O. W% }7 F
  24. ( L" s/ i- @( \
  25.       if (HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError) != HAL_OK)8 C, @: G: q% M# e' d/ }
  26.       {
    5 j+ U" O8 b) ?* y* r$ c
  27.         // 如果刷除错误
    : O+ V5 `- Z+ R
  28.         printf("\r\n FLASH Erase Fail\r\n");- v# G' l* k5 O1 f. h
  29.         printf("Fail Code:%d\r\n",HAL_FLASH_GetError());
    , i2 l5 _& M  R4 A" E
  30.         printf("Fail Page:%d\r\n",PAGEError);
    , }. z3 M( _! p7 @) F
  31.       }+ g- p5 X) L9 A9 n4 s

  32. , d. ^0 T0 `8 F/ E4 E
  33.       if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){) Z3 ^9 \# i# m
  34.       printf("write data 0x%x OK\n", Data);
    . N- d" V! V- G" O
  35.       }
    / m9 J8 h. Y3 a) D; a
  36.       else{
    ( k% D0 U8 I( M; ?
  37.         printf("failed!!!\n");
    3 L" _) G1 k% @$ y7 b2 n
  38.       }
    9 {- C3 @. ?2 G4 Q& |0 t

  39. ! d4 i+ y! A0 c  }/ r% |
  40.   }
    ' i4 `( p0 j* p" N' M( y6 V) }) o
  41. HAL_FLASH_Lock();
    * D9 S' {& ?8 M6 m& w4 S% Q
  42. }
复制代码
" Z) Z! _( C! H7 E

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

  1. u32 write_data1 = 0X12345678;
    . z& k9 O# z! I9 \9 G, ^
  2. u32 write_data2 = 0X87654321;
    ' Z2 z: a( ]% a
  3. u32 write_data3 = 0XFFFFFFFF;
    . X7 n7 w1 P5 c
  4. + C% y2 Q$ M) t+ E) q, g
  5. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_508 + 124,0X508508FF);$ _. X* H, X' ?6 A" T, F
  6. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509,write_data3);
    " Q* ~7 i  s+ W/ ^: r7 J) ?
  7. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509+4,write_data2);
    + @- D4 \2 ~. l- O4 w3 l2 V6 o
  8. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509+8,write_data1);
    6 |* l: e; S4 U/ S' K
  9. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_510,0X510510FF);
    8 y+ Y4 B: V- k; ~* j  _2 E

  10.   `0 g7 l' F2 \7 Y' A
  11. read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_508 + 124);
    6 Q5 m' x. R, i& i% n
  12. printf("the ADDR_FLASH_PAGE_508 last is: 0x %x \r\n",read_data1);8 p8 I6 D( Y; C* e; k& c
  13.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509);
    / E- J6 {. y; G8 {3 I
  14.   printf("the ADDR_FLASH_PAGE_509 1 is: 0x %x \r\n",read_data1);( b: H/ p5 U8 p, Z# A5 d: P+ P
  15.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 4);
    # l6 `1 R7 p, ~; a& u) }! @
  16.   printf("the ADDR_FLASH_PAGE_509 2 is: 0x %x \r\n",read_data1);
    ! n' A3 x  `1 g  W6 y9 r
  17.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 8);
    " j- O- p- A9 ^) y$ ^" a1 L
  18.   printf("the ADDR_FLASH_PAGE_509 3 is: 0x %x \r\n",read_data1);
    ' g' k% @- {3 _0 [8 ?1 m$ g3 Q/ R8 z8 i' E
  19.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_510);/ W2 y* L5 h9 R
  20.   printf("the ADDR_FLASH_PAGE_510 first is: 0x %x \r\n",read_data1);4 Y1 h; p$ {9 l+ _  k

  21. - _- L' ]9 M, o, f6 L4 K5 _
  22. if(btn_getState(&K2_BUTTON_150mS) == BTN_EDGE2){
    8 F1 q/ L" U7 n- n$ I& s
  23.         printf(" K2 150ms button!EEPROM_read test\r\n");
    5 i' V) P$ i0 R5 x' l
  24.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_508 + 124);5 L4 C9 G$ N, z- q  K9 }6 e
  25.         printf("the ADDR_FLASH_PAGE_508 last is: 0x %x \r\n",read_data1);
    % A* x$ L- ^) E1 d1 N
  26.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509);
    $ Y8 ?8 y' u' g% b8 i5 P$ i
  27.         printf("the ADDR_FLASH_PAGE_509 1 is: 0x %x \r\n",read_data1);
    ' y% p5 e. T. d. D
  28.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 4);2 K, B' A+ t! F- }6 M- l
  29.         printf("the ADDR_FLASH_PAGE_509 2 is: 0x %x \r\n",read_data1);
    , `7 }+ s2 s* k, V0 y0 ]7 i
  30.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 8);+ @$ b( u3 ?9 E
  31.         printf("the ADDR_FLASH_PAGE_509 3 is: 0x %x \r\n",read_data1);
    - C( t6 @$ C8 D
  32.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_510);
    ' p# |5 u  _( L3 z8 d5 w. p+ v
  33.         printf("the ADDR_FLASH_PAGE_510 first is: 0x %x \r\n",read_data1);
    - Z! n; d9 r- k+ r7 w

  34.   c/ C. c1 a8 @9 R
  35.     }6 ]9 D6 M" A  |8 O. ~+ W
  36. if(btn_getState(&K1_BUTTON_150mS) == BTN_EDGE2){
    6 x7 e* Z9 L  x2 B3 f
  37.         // printf(" K1 150ms button!,EEPROM_Erase test\r\n");. V5 G6 q% F/ l1 R
  38.         // MY_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, DATA_EEPROM_START_ADDR, write_data1);, L+ G7 W  X" n/ w  t5 i# i6 P
  39.         printf(" K1 150ms button!,flash write test\r\n");& n8 D+ Y3 z# v# R4 Y
  40.         MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509,0X33333333);+ L6 E4 z- E( A! C) U  y
  41.         HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);4 y. [% ~, x8 k/ R9 L
  42.     }
复制代码
, D, y' Z, K: ~* m- G$ r2 l: [

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


" X+ e' l7 Q; _8 v: |4 p

微信图片_20230612172448.png


  Q& f+ P* {& D

微信图片_20230612172355.png


$ u- J0 O9 ^1 E. ^2 E

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

% ?+ E. z3 K% {
2.4 读写EEPROM的后续问题

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


( F4 i! v: ~  A5 m8 i6 d

微信图片_20230612172339.png


+ E  V9 Z+ F" h" R' U% E

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

8 r1 `, l& d  I
2.4.1 问题的出现和解决

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


) p# o& {) ^: j3 r  L; p

微信图片_20230612172215.png

5 O4 n- Z3 q* @& ~1 A

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

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


, M+ U- _5 Q) I. ]8 Q! u  ~' A

微信图片_20230612172212.png

1 H/ G1 S1 U# X6 ]3 o- C! H6 p0 E

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

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

  1. BlueID_STRUCT test;  N4 d7 Y! R$ U3 w3 O
  2. ...7 C  @2 @9 E3 Y* o* s
  3. /*
    ! K+ R* M9 x2 N, X8 c- Z
  4. CHBlueID_STRUCT Flash_PowerOn_BlueCheck()0 i5 N# u/ C& i- u9 v% H- S( c
  5. {4 d# F6 Z7 V( e
  6. 3 t( G" V. n1 {9 M/ a
  7. CHBlueID_STRUCT PowerOn_ID;2 a$ ]7 z' [0 W1 Y4 S
  8. PowerOn_ID.CH1ID = FLASH_blueIDRead(CH1_ID_ADDR);
    ! Q$ K7 K+ [: q! l/ N1 A
  9. PowerOn_ID.CH2ID = FLASH_blueIDRead(CH2_ID_ADDR);7 O4 l8 r2 r! {
  10. PowerOn_ID.CH3ID = FLASH_blueIDRead(CH3_ID_ADDR);4 z8 A8 q' \, L8 x2 v( m0 L
  11. PowerOn_ID.CH4ID = FLASH_blueIDRead(CH4_ID_ADDR);
    0 d) [5 f: t3 G/ d2 ]8 c
  12. PowerOn_ID.CH5ID = FLASH_blueIDRead(CH5_ID_ADDR);
    5 v( J" o8 V. t& \6 f# n# }% t
  13. PowerOn_ID.CH6ID = FLASH_blueIDRead(CH6_ID_ADDR);
      \, J6 B6 p2 \7 t2 U
  14. PowerOn_ID.CH7ID = FLASH_blueIDRead(CH7_ID_ADDR);/ E9 N0 I% w( S% S
  15. PowerOn_ID.CH8ID = FLASH_blueIDRead(CH8_ID_ADDR);, @4 ]& q) k4 k) Y" y. J
  16. PowerOn_ID.CH9ID = FLASH_blueIDRead(CH9_ID_ADDR);( o* G* X- p% r% c) {
  17. PowerOn_ID.CH10ID = FLASH_blueIDRead(CH10_ID_ADDR);4 e/ O' z& }$ s. y7 ~1 H* O' Q9 e3 y2 a
  18.   ~, _  q5 q$ s4 f( ]# r
  19. return PowerOn_ID;
    8 t/ [, ~* O1 B. M# t  u6 J
  20. }+ b' q$ l9 j/ X! @; T  y( t( l
  21. */
    - z  \" |" L* p4 V" k
  22. BlueChipID = Flash_PowerOn_BlueCheck(); //上电先把ID读出来做比较
    ) A# ?5 a$ d0 Q: I& z
  23. //打印一个出来测试,看结果6 B: }9 V) ^& U' J4 g
  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);
    $ d& w* ^( i4 G3 ^2 E1 x  V# S

  25. 5 w4 z; L% n% ?+ v* R3 C
  26. test.ID1= 0XFF; //结构体每个元素是 uint8_t 类型5 `; [' p: z" c8 K; k4 c
  27. test.ID2= 0XEE;; q/ N) t+ M; ^* w- L. m/ V3 C' _
  28. test.ID3= 0XDD;
    3 G( B* U7 v5 J
  29. test.ID4= 0XCC;
    . `( U# @0 W# N! N* o$ J
  30. test.ID5= 0XBB;0 S. Y; m+ ^) f0 J, h
  31. test.ID6= 0XAA;
    0 F7 X3 }5 T* F& u
  32. ) t2 q! V0 K5 F8 k
  33. while (1){...}
复制代码
0 I0 U/ v; _5 e" y2 E$ I# o

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


+ B4 {7 C/ ^; C0 L

微信图片_20230612172209.png


, N9 w# S9 ~7 w  S1 q/ j2 K3 w

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


) t& K8 p6 v1 W% a% ~

微信图片_20230612172207.png


0 j: `2 [! \' T7 h5 H

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


) H6 P8 \& W4 y8 i/ d- t+ B

微信图片_20230612172058.png

. Y8 c, B9 n6 B7 m+ x; A; Z

微信图片_20230612172050.png

6 c& e8 t; v3 |2 `( {

打印的结果:

6 O* f) d) c; W% ]3 R2 t7 C

微信图片_20230612172039.png


5 Y( W0 q# \- r7 k

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

  1. BlueID_STRUCT FLASH_blueIDRead(uint32_t address)& a( C4 k0 [# z6 i' u  y3 {
  2. {! ~0 U- Z1 ^9 v- C7 K0 K! q# \
  3. BlueID_STRUCT u48id;
    1 m5 k0 ?6 }# d- z
  4.   // uint16_t temp1,temp2,temp3;  /**(__IO uint16_t*)address; */
    ( h9 p! A, t  T! Z/ {/ N+ L7 r
  5.   // temp1=*(__IO uint16_t*)address;
    6 v; T) z0 ~0 h; d; J6 r& ?* a. P# F
  6. // u48id.ID1 = (uint8_t)(temp1>>8);0 ]( J: ?+ ^( Q" B$ l
  7. // u48id.ID2 = (uint8_t)(temp1&0X00FF);8 E/ D& \4 x# ~. _
  8.   // temp2=*(__IO uint16_t*)(address+2);% I3 Z# W" ~; ~7 [7 ~) a0 E2 M
  9. // u48id.ID3 = (uint8_t)(temp2>>8);
    - \! w5 b; q  q9 E+ {4 `( U4 S
  10. // u48id.ID4 = (uint8_t)(temp2&0X00FF);% m$ s; G, X, `" _
  11. // temp3=*(__IO uint16_t*)(address+4);: R' z4 Q7 i! \( h
  12. // u48id.ID5 = (uint8_t)(temp3>>8);( K" @' P7 {( J
  13. // u48id.ID6 = (uint8_t)(temp3&0X00FF); ) [" }7 r& y8 n

  14. 0 p* X5 r7 M6 `8 W6 e) E
  15.   u48id.ID1 =  FLASH_Readbyte(address);
    $ q; L. I; |( A) p9 q* H1 y
  16.   u48id.ID2 =  FLASH_Readbyte(address+1);" f$ W* I: }+ R! J: `( H8 D
  17.   u48id.ID3 =  FLASH_Readbyte(address+2);
    3 t1 f& u) T; ~4 R* J2 h
  18.   u48id.ID4 =  FLASH_Readbyte(address+3);4 X1 V, H5 j' }1 C0 e: d" C6 H
  19.   u48id.ID5 =  FLASH_Readbyte(address+4);9 ~$ v8 [2 n4 `$ o% E0 q
  20.   u48id.ID6 =  FLASH_Readbyte(address+5);
    : I4 F1 |' ]7 X/ g; o

  21.   g2 b. }! q2 d0 z: i. @' E$ x
  22.   return u48id;- m5 @  b# I$ q. e" c
  23. }
复制代码
. Y) e9 t' A: v0 B9 g. X% i

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


/ W+ c4 q$ i# S0 D% S! f8 P

微信图片_20230612172035.png


( H5 e+ t# c+ k: Q: m2.4.2 问题的分析(大小端模式数据格式)

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

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


) B6 i$ w1 Q: Y$ p1 M& x

微信图片_20230612172032.png


( ^7 I1 f: o5 S3 b. G

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


1 s, {* {! X4 i, p

微信图片_20230612172030.png

% r9 H4 V' W$ Z0 g. s9 M

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

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

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

% P$ `$ I) v: C8 {& t* m' |
2.4.3 STM32L071RBT6 EEPROM读写全字问题
8 Y6 \; W4 e7 o4 z* L
读问题的出现

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

  1. uint32_t  FLASH_ReadWord(uint32_t address)
    % C" f1 m" Q: w/ @/ @
  2. {& q: f2 f! [1 L1 A
  3.   return *(__IO uint32_t*)address;
    ) t  W- e9 m# Q% D* c
  4. }
复制代码

: k1 X) _0 A! @, R

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


" {6 }5 P7 q: [1 _) c9 K

微信图片_20230612172026.png


4 D7 o3 A. W( A8 m7 B# @6 t

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


2 m+ P4 m( j) L& y' A) n

问题的解决:

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


, Q8 ~! Q( F% D! X

微信图片_20230612172019.png

! j! I' y& w9 m0 {" ]% e

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

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


9 e8 l2 Q0 h( _0 l/ l% G( i

微信图片_20230612172012.png


: Y0 _6 T* g" \5 S' H( F" C2 A

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

  1. u32 FLASH_ReadEnoceanID(uint32_t address)
    $ f8 O+ e; Z* k+ [
  2. {% K& @; F8 S1 O' h& N# K9 y+ f# R
  3.   u32 keepid;
    1 R7 W' u) i0 q
  4.   u8 i;* D' k0 y4 `, J! `  e/ s* ^6 s4 ]
  5.   keepid = FLASH_Readbyte(address);& s7 o+ b2 E6 q, H" {, I
  6.   i = FLASH_Readbyte(address + 1);
    0 F& B7 ~5 q( G" a+ c
  7.   keepid = (i<<8)|keepid;
    2 q) w1 I  y7 F! i
  8.   i = FLASH_Readbyte(address + 2);/ `  R! w, @4 @/ y7 F3 ~9 p; U
  9.   keepid = (i<<16)|keepid;# Y& h/ ~# O) [6 h- T  l
  10.   i = FLASH_Readbyte(address + 3);
    9 \: v7 w8 I( g& R1 a
  11.   keepid = (i<<24)|keepid;
    1 ~9 H6 e- M" M( X" {
  12.   return keepid;6 h/ u2 y% ^+ `5 m3 A9 e$ I
  13. }/ V. j5 T' h# [

  14. 9 c9 M8 |! G8 \) e6 ]' S) x0 ^
  15. CHID_STRUCT Flash_PowerOn_Check()+ n6 Y) \( F* K9 V8 y4 g, G4 }
  16. {& Y2 w3 B, x; ]

  17. ' Z" Q7 J( h+ n
  18. CHID_STRUCT PowerOn_ID;
    2 L, U+ P! P* ?8 Y$ ~# ~( h
  19. PowerOn_ID.CH1ID = FLASH_ReadEnoceanID(CH1_ID_ADDR);/ h9 a# \  N6 m
  20. PowerOn_ID.CH2ID = FLASH_ReadEnoceanID(CH2_ID_ADDR);( K5 y1 C) T/ S. w# I3 j
  21. PowerOn_ID.CH3ID = FLASH_ReadEnoceanID(CH3_ID_ADDR);' e! L- c* Q  p. }8 m
  22. PowerOn_ID.CH4ID = FLASH_ReadEnoceanID(CH4_ID_ADDR);0 Q- }" X1 g4 |0 _. s  E
  23. PowerOn_ID.CH5ID = FLASH_ReadEnoceanID(CH5_ID_ADDR);" `3 G9 Q( v' v3 l8 H' I1 i
  24. PowerOn_ID.CH6ID = FLASH_ReadEnoceanID(CH6_ID_ADDR);
    & t' O+ E! b0 ?& C6 o0 G
  25. PowerOn_ID.CH7ID = FLASH_ReadEnoceanID(CH7_ID_ADDR);
    " f$ d1 K( r  O6 e
  26. PowerOn_ID.CH8ID = FLASH_ReadEnoceanID(CH8_ID_ADDR);
    - X; g/ g- x; I7 ~9 k! G; b( [
  27. PowerOn_ID.CH9ID = FLASH_ReadEnoceanID(CH9_ID_ADDR);
    " S0 @0 T0 W7 J$ c# F3 B; p/ c3 y( N9 L
  28. PowerOn_ID.CH10ID = FLASH_ReadEnoceanID(CH10_ID_ADDR);  T2 X8 M% R7 T4 b0 m, O% a- _
  29. : [4 M9 N5 q/ P- R: s9 E
  30. return PowerOn_ID;
    7 S7 Q0 q8 O' @/ }, f& j% q
  31. }
复制代码
8 ?8 I: |- [. S4 y

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

6 w& o0 Q' C3 {( X, i' v. G) ?
写问题的出现

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

一直用的写全字函数为:

  1. FLASH_Status  FLASH_WriteWord(uint32_t Address, uint32_t Data)5 j1 Y2 D! s4 z' B) l9 |3 a
  2. {
    - T* l% m/ ?# Z- y6 V$ G6 h8 ~

  3. 9 \0 A$ Q3 [8 ?; d2 N+ u2 ?, j" K
  4. FLASH_Status i = FLASH_COMPLETE;* t0 d5 V7 y7 Z1 R  B5 U
  5. 3 y. u) X" X0 ^0 D% \2 N
  6. HAL_FLASHEx_DATAEEPROM_Unlock();  
    7 f& o2 O: `" i, J6 X1 Z; B7 t
  7.   while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, Address, Data) != HAL_OK);
    ) T+ M8 S5 H1 f. l$ d
  8. HAL_FLASHEx_DATAEEPROM_Lock();' x" p  Q4 R$ m

  9. - A$ d% c9 ?! G* h
  10. return i;
    - e' `7 b( N$ O6 E* j( i# G
  11. }
复制代码
  D& G/ Y1 M2 _# b% R0 M7 H

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


( W; c, Z1 o# L/ v0 ]

微信图片_20230612172009.png


8 o8 u+ I* x) I+ @; S

但是使用时候发现:


2 Y# c& `6 p8 t& m% a

微信图片_20230612172006.png


7 c, ~* V9 o7 z5 v/ w6 ^, i

问题的解决:

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

6 l- K- h0 q- [2 ]# n7 T

微信图片_20230612172003.png


" }  w3 u) [3 ~, Y# ]6 v: j0 o6 z

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

  1. FLASH_Status  FLASH_WriteWord(uint32_t Address, uint32_t Data)
    6 D  G/ {+ M7 g! v: F
  2. {  F+ ^' P& w2 Q% I! |/ I- d
  3. - q* P4 A1 ~* j! }
  4.   FLASH_Status state = FLASH_COMPLETE;
    ! O& |. M8 P+ ~, p7 H1 I
  5.   u8 i = 0;
    9 T# ?9 [( C0 l# C$ u6 b
  6.   u8 writedata[6]={0};
    ) ?2 N" e& v2 B* F3 E7 I: i) _
  7. ; j' C9 U3 N( T7 _- U
  8.   writedata[0] = (u8)Data;9 A- Y0 v1 y/ _* u* r
  9.   writedata[1] = (u8)(Data>>8);. r( c' F7 H% |. E1 R+ A# }
  10.   writedata[2] = (u8)(Data>>16);6 Z: L3 C) P$ V# }2 P
  11.   writedata[3] = (u8)(Data>>24);
    3 S1 S8 l4 P  L0 y3 R
  12. ( T) h# u: I. ~& X+ H; C
  13. HAL_FLASHEx_DATAEEPROM_Unlock();  
    % B7 r6 w- c* g: i' [6 b
  14.   for(i=0; i<4; i++){+ N0 L( {0 p. M$ B! b+ i
  15.     while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, Address + i, writedata[i]) != HAL_OK);/ O6 a3 L* W" B  b
  16.   }1 [0 s2 C- x: G8 z; F* E
  17. HAL_FLASHEx_DATAEEPROM_Lock();# q8 Y! U% ]" U) F. C5 C
  18. 3 E2 g5 c4 U! j" c; Z
  19. return state;
    " y  N+ p7 u+ \% S7 G/ G
  20. }
复制代码

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

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

2.4.3小结使用的芯片为  STM32L071RBT6


! \( |# {, q# i: }- B1 O, D3 m0 M最后问题的解决

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

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


& r6 y& p8 L8 u& T

微信图片_20230612172000.png

7 i6 E- y( p" P* w( k) a* C

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

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

% z# A, U+ A: [7 i: ~

微信图片_20230612171954.png


4 r" Y4 }* h. P- V! [

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

2 W8 q- b' f" p( F/ _
收藏 评论0 发布时间:2023-6-12 17:30

举报

0个回答

所属标签

相似分享

官网相关资源

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