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

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


2 @6 B5 N5 u% O3 ]/ f

微信图片_20230612172451.png

; I* n( }5 |0 S) f2 B3 D& H

微信图片_20230612172454.png

" j4 ~* x# U; d4 p

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


/ p0 Y5 w" f9 V- b# T5 j

  1. #define FLASH_SIZE                (uint32_t)((*((uint32_t *)FLASHSIZE_BASE)&0xFFFF) * 1024U). Q- _2 W0 A' l; g+ O' h" g$ ^+ C
  2. #define FLASH_PAGE_SIZE           ((uint32_t)128U)  /*!< FLASH Page Size in bytes */
复制代码

/ ^- \, o! y+ D" f2 |2 T' K, T

对于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. }& g2 b3 |" Q+ k' G9 D7 V2、读写函数的设计

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

4 v8 _: d$ C& m
2.1 读取函数
  1. //读取指定地址的半字(16位数据)+ J# P- f. L+ C5 ?! c
  2. uint16_t FLASH_ReadHalfWord(uint32_t address)- g, O( ^' H0 }0 Z" D- Z
  3. {
    4 R9 r9 m7 B% o4 M
  4.   return *(__IO uint16_t*)address; . t. Z( J7 s) _2 s5 Q
  5. }
    % o# s6 I3 ^" M* p; `
  6. ! z+ ?. b" `7 c5 M+ @& t! d7 H
  7. //读取指定地址的全字(32位数据). w" G4 y9 |! M  Q/ c* c
  8. uint32_t FLASH_ReadWord(uint32_t address)! I# _* Y& J2 H) d
  9. {
    ! A7 a3 i: J. l) A
  10.   return *(__IO uint32_t*)address;
    8 P6 f0 A. {0 [- @, y& }, \* R' L
  11. }
复制代码
& R) u" L2 W( A8 y3 R" @  i
# v+ E' D: `( H( M! t

简单测试一下:

  1. u32 read_data1=0XFFFFFFFF;% I6 i5 T7 D% Q, M
  2. u32 read_data2=0XFFFFFFFF;+ Q0 w7 W2 O+ {: `
  3. ...
    . p/ M; f& `8 n* h8 I" U
  4. read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);
    ! q% i# E# O3 \) A0 X' V' w5 ~
  5. printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);- O) k2 }* v# X+ e+ i
  6. read_data2 = FLASH_ReadWord(DATA_EEPROM_START_ADDR + EEPROM_PAGE_SIZE);
    # l" v% b# }% s/ Q- x; R* t4 d
  7. printf("the EEPROM sceond page test data is: 0x %x \r\n",read_data2);
复制代码
" Q3 D3 E" F  o0 h

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

. \6 X+ r* b& X& ]6 p; `( x
2.2   EEPROM写函数

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

  1. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Unlock(void);
    % S0 r9 o. j- g& @' n: y  Y
  2. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Lock(void);
    ) p, B$ m: M" R, Q6 n; j
  3. 2 n! {7 W$ r. G5 @6 [" K& {- n
  4. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Erase(uint32_t Address);
    : V0 ~* d& j- v' B1 B9 w& @% L4 u( m
  5. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data);
复制代码
/ N3 j% ]! V) Y3 K3 F

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

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

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

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

  1. HAL_FLASHEx_DATAEEPROM_Unlock();6 R- d1 C$ s8 C$ N: D9 V  d: M
  2. HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, DATA_EEPROM_START_ADDR, write_data1);9 q: k; o+ @) n5 B
  3. HAL_FLASHEx_DATAEEPROM_Lock();
    0 E8 {' _0 r- e
  4. ...
    5 B- A0 j. S9 e/ W/ ]
  5. if(btn_getState(&K1_BUTTON_150mS) == BTN_EDGE2){
    & [" k6 s8 K6 _! B9 W
  6.         printf(" K1 150ms button!,EEPROM_Erase test\r\n");! A2 g: _5 G3 s3 E
  7.         HAL_FLASHEx_DATAEEPROM_Unlock();( N4 a4 H3 a3 K& O
  8.         HAL_FLASHEx_DATAEEPROM_Erase(DATA_EEPROM_START_ADDR+4);
    2 T4 j2 H& t) E6 p8 m9 H+ F
  9.         HAL_FLASHEx_DATAEEPROM_Lock();8 H. D( m3 Y  q, o0 O
  10.         HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);/ d) @" b- n3 ?6 |& p
  11.     }
    & \7 i4 y1 b5 I# R. W
  12. ...
    . b& H- P- U/ C; v4 b( N- N, N9 y
  13. if(btn_getState(&K2_BUTTON_150mS) == BTN_EDGE2){
    + P$ a' u2 a5 M8 _8 D
  14.         printf(" K2 150ms button!EEPROM_read test\r\n");2 Z, j) x/ @5 y
  15.         read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);
    / S' D5 g$ v( v+ i. ?, W, l
  16.         printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);
    * F/ ~2 E+ a* r1 V
  17.     }
复制代码

! F' |2 E, a* Y  m5 z

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

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

; U. q( c9 ]# ?# O
写入问题说明修改

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();# ~: g+ Y1 `+ @5 s" y
  2.   HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, DATA_EEPROM_START_ADDR, 0X88);. Y, ~2 Z: B8 y7 J
  3.   HAL_FLASHEx_DATAEEPROM_Lock();
    ! a0 p" ^$ B! a+ [
  4.   read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);6 A/ e1 P" [5 x1 Q8 B
  5.   printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);
复制代码

0 _% W  f5 ?2 D0 |+ f+ I, g

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

  1. void MY_DATAEEPROM_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data)   
    * O# z; d1 G, g* u
  2. {' s5 r) c3 j5 q0 ^9 W, p) D
  3. HAL_FLASHEx_DATAEEPROM_Unlock();        ! f8 v3 `. D  R$ u
  4.     HAL_FLASHEx_DATAEEPROM_Program(TypeProgram, Address, Data);* o6 ^, l: L8 N* c6 g* \- Y6 m& b
  5. HAL_FLASHEx_DATAEEPROM_Lock();
    + V( U6 V1 K5 N0 }, \$ \9 T& L
  6. }
复制代码

6 r* E: U# _8 L/ h8 ~

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

4 h- ~& c" Q& |0 `
2.3   Flash写函数

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

  1. /* IO operation functions *****************************************************/
    9 c' D/ C! e, u# X) P9 v, G5 T
  2. HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data);3 @7 Q. ]2 \( y
  3. HAL_StatusTypeDef HAL_FLASH_Program_IT(uint32_t TypeProgram, uint32_t Address, uint32_t Data);% `6 P/ @0 J1 I- Z8 W) S" i
  4. 6 w  G9 s, l% ~! g
  5. /* FLASH IRQ handler function */
    2 A! F# X: @# a/ Z3 A6 V
  6. void       HAL_FLASH_IRQHandler(void);, |( w8 `5 B! T
  7. /* Callbacks in non blocking modes */
      y5 c" s3 p/ q! h' ]+ b
  8. void       HAL_FLASH_EndOfOperationCallback(uint32_t ReturnValue);* s( q" r! [( R! s
  9. void       HAL_FLASH_OperationErrorCallback(uint32_t ReturnValue);
    * i4 Z2 b  Q$ Z- W, j

  10. 1 U& g; g' y  R" X# [3 P
  11. /**
    8 y0 U2 j! e" h. V$ A6 P: W
  12.   * @}# a2 r" [; m/ E* X; x; Y' e
  13.   */! p0 y, K/ l9 |( Z4 T$ v
  14. 8 ]* @$ k% p2 Q0 U. {/ w9 ~2 L( n
  15. /** @addtogroup FLASH_Exported_Functions_Group25 ]4 {4 j+ a) ~
  16.   * @{- C2 E; j! v& w4 k3 W8 `) Z' N2 P
  17.   */. }  h% X' k2 |  E5 J# [6 |% b
  18. /* Peripheral Control functions ***********************************************/
    8 I$ A/ C% D" u2 D* o
  19. HAL_StatusTypeDef HAL_FLASH_Unlock(void);3 J3 V# U3 M! v1 ^! h8 K
  20. HAL_StatusTypeDef HAL_FLASH_Lock(void);: e! F+ ]0 O5 A  M" Z
  21. HAL_StatusTypeDef HAL_FLASH_OB_Unlock(void);" I3 w  u5 q3 u. T1 v. E; N
  22. HAL_StatusTypeDef HAL_FLASH_OB_Lock(void);
    4 K) }9 ~2 g0 J1 Q7 ]: T
  23. HAL_StatusTypeDef HAL_FLASH_OB_Launch(void);
复制代码
5 Y3 Q" w. q' {

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


. s# Q, j) i7 G

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


. g% N" ]! A& D) W
  1. HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data)
    4 ]3 W  @7 {) B& k9 ], s- Q
  2. {
    . y+ S4 X3 A. ]) P3 W9 g
  3.   HAL_StatusTypeDef status = HAL_ERROR;: p3 Q' e1 Z; o+ G/ |' X
  4.   6 i0 w' g& C; s# f) n8 O
  5.   /* Process Locked */
    ) f. L' Z, @+ c; w$ t& C4 H. `9 y
  6.   __HAL_LOCK(&pFlash);
    4 W- e% V9 c+ a7 D9 d
  7. - }7 T! c$ f- M
  8.   /* Check the parameters */; `" p8 H) w! X
  9.   assert_param(IS_FLASH_TYPEPROGRAM(TypeProgram));
    1 ?: B2 D$ F. g8 }
  10.   assert_param(IS_FLASH_PROGRAM_ADDRESS(Address));
      {  H- X* f/ _: f9 x# k. }5 K; W

  11. 9 e% ?+ {4 q- o1 c! |
  12.   /* Wait for last operation to be completed */
    / n+ I' N, [4 X
  13.   status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
    3 Z0 z6 m- R% y4 f) n
  14.   
    2 h7 R" `$ O9 C; }3 t/ E; f9 d
  15.   if(status == HAL_OK). Z8 x# {6 c/ ^  u  H: V7 G% q
  16.   {2 E% \8 H# m6 P' Z$ o; K4 n
  17.     /* Clean the error context */
    5 X- k* M: P: R# B0 W$ s
  18.     pFlash.ErrorCode = HAL_FLASH_ERROR_NONE;" e& z0 C3 D7 s7 [' |# q3 l* M

  19. % w2 O  j" K# y
  20.     /*Program word (32-bit) at a specified address.*/4 J7 D& {# \% e# E5 ^7 |
  21.     *(__IO uint32_t *)Address = Data;
    / B5 e  A/ C6 t) d( U
  22. 6 Y: g! H0 h8 k4 P
  23.     /* Wait for last operation to be completed */
    5 b* g' l8 _. m) m6 m; a% a
  24.     status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);" V/ b3 _' ?) y) \. \
  25.   }# Z: K/ H. b4 R+ M4 L
  26. ! u& N! t* F5 w1 g
  27.   /* Process Unlocked */2 [1 P% l: \2 U' z1 d7 W
  28.   __HAL_UNLOCK(&pFlash);
    $ Y5 r3 H1 o  C; `! X( x, X  d
  29. 3 g3 L2 y$ y, ^- X5 |& _- `5 N' e, s
  30.   return status;" i( H, O3 V, L: W
  31. }% L1 N& {8 ~- G
复制代码
$ b4 }6 x1 P  N- u

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

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

  1. #define ADDR_FLASH_PAGE_505      0X08000000 + 128*504   //5 ~$ S% M3 a" {8 ~# L3 e
  2. #define ADDR_FLASH_PAGE_506      0X08000000 + 128*505   //. n+ d- w4 h4 R$ p" G  P
  3. #define ADDR_FLASH_PAGE_507      0X08000000 + 128*506   //
    4 X# h& ]! J, x1 U& i
  4. #define ADDR_FLASH_PAGE_508      0X08000000 + 128*507   //7 o  T4 r7 f$ }5 S, Z! U
  5. #define ADDR_FLASH_PAGE_509      0X08000000 + 128*508   //
    ) O! @, k& u  H8 E3 m- p
  6. #define ADDR_FLASH_PAGE_510      0X08000000 + 128*509   //
    ' C& }4 q6 p" F2 _
  7. #define ADDR_FLASH_PAGE_511      0X08000000 + 128*510   //
    : c0 u9 I( a$ D$ ^
  8. #define ADDR_FLASH_PAGE_512      0X08000000 + 128*511   //最后一页
复制代码

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

  1. void MY_DATAFLASH_Program(uint32_t Address, uint32_t Data)   ) d  y3 I) L& L$ n: V; q
  2. {. P% t7 J  M* J' g' M" E
  3. HAL_FLASH_Unlock();        
    . p5 n. ?! T" }, A4 i. T* H- g0 g9 R
  4.   if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){
    2 S# f1 L- U4 h9 N
  5.     printf("write data 0x%x OK\n", Data);
    ( D, {4 y; k" B  m# s. q
  6.   }. o; e; W4 f1 e; F4 p9 Z/ \
  7.   else{  y* L- X( I/ ?6 D6 y
  8.     printf("failed!!!\n");
    ( ]. R+ `8 T6 y5 P
  9.   }
    6 M' C7 D% `( @, Y9 |0 M6 j
  10. HAL_FLASH_Lock();, X) f, W  F' t9 r9 T! @# ^$ R- Q' z# P
  11. }
复制代码
& O/ v5 h: L8 F7 h

测试一下;

  1.   MY_DATAFLASH_Program(ADDR_FLASH_PAGE_512,write_data2);
    8 n. h/ M8 }! x' P$ c, U7 k
  2.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_512);
    4 u* h2 B4 B% t8 x( _" {
  3.   printf("the ADDR_FLASH_PAGE_512 is: 0x %x \r\n",read_data1);
复制代码

' S: F5 \3 f8 Q

在没有写入flash之前,该地址读出来的数据为0,写入后读出来是正常写入的数据这里有个疑问,按理来说,flash存储器有个特点,就是只能写0,不能写1,所以flash的写入,比需先擦除,或者至少检查一下该数据区是否可以写入,但是L051 怎么初始的时候读出来是0? 难道L051的有区别,需要测试一下

先在一个地址写0XFFFFFFFF ,然后写完了再写一次别的数据看看能否直接写入,结果是写入了0XFFFFFFFF ,不能继续直接写数据,说明,估计L051是相反的,这里具体是不是我只看测试结果,结论的话我自己知道就可以应用,希望有权威大神指正。

这里我们得用到一个关键的函数 ,这个函数是在stm32l0xx_hal_flash_ex.c 这个文件中的,是flash的擦除函数:HAL_StatusTypeDef HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *PageError)

所以这里我们知道了以后,可以优化一下写入函数,我们项目中用到的是可以直接对某个地址的写入,然后也不需要保存,此页其他的数据,所以我们把函数改成如下:

  1. void MY_DATAFLASH_Program(uint32_t Address, uint32_t Data)   6 C) Z; j0 {. ^
  2. {
    # u+ _& L( U: P& d6 D
  3.   FLASH_EraseInitTypeDef EraseInitStruct;$ A# R9 L9 ~2 j4 {4 o. }/ O
  4.   uint32_t checkdata;! a8 ]' F1 k/ U$ _
  5.   uint32_t PAGEError = 0;3 y8 l0 p1 A/ [( z( G2 Z+ Y& z3 I9 C  B
  6.   checkdata = FLASH_ReadWord(Address);
    . B1 O! D  d1 Q: |7 [
  7. HAL_FLASH_Unlock();
    4 L0 g5 X: R7 e. N
  8.   /*如果是0,直接写*/
    1 m0 a3 Q. \# m3 T/ Y# J
  9.   if(checkdata == 0){      
    / s  x4 x! b# L4 r0 X$ B, ]% J% N6 A
  10.     if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){
    - I" w8 i: |/ L4 Z
  11.       printf("write data 0x%x OK\n", Data);# j( N7 y' F. S
  12.     }
    & u+ Y: Z; v9 s2 X+ |
  13.     else{, j7 f4 o! x3 C$ L+ ~
  14.       printf("failed!!!\n");$ v7 H. A! P# S& ~5 M6 T
  15.     }
    * E7 B1 ~9 L. R+ ~: w+ L/ R: p
  16.   }5 z& L! G; L0 E3 V3 @
  17.   /*否则擦除再写*/. U1 r' I) m5 e, y6 ], ~
  18.   else{
    ' w8 ?* p3 {# X7 L
  19.      __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);
    3 k$ e1 i/ Y: B. k

  20. + g- ?2 d& {9 {0 \4 I
  21.       EraseInitStruct.TypeErase   = FLASH_TYPEERASE_PAGES; // 刷除方式# }$ W0 N& F8 M3 L" c$ c
  22.    EraseInitStruct.PageAddress = Address; // 起始地址9 W8 e0 |2 f0 V; e% Y& J+ y9 _0 s
  23.       EraseInitStruct.NbPages = 1;
    3 H0 l8 E- h/ z& y
  24. ' ?/ M' H/ ]9 H. F- ^
  25.       if (HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError) != HAL_OK)0 Z4 B/ Q1 Y8 V
  26.       {  q- H( x, q- T- N2 ]
  27.         // 如果刷除错误
    % h8 f4 }+ E# @) r8 f# f0 e( q
  28.         printf("\r\n FLASH Erase Fail\r\n");
    3 k' X* M6 z- n
  29.         printf("Fail Code:%d\r\n",HAL_FLASH_GetError());
    # O. x$ N: T" @/ r9 a3 r
  30.         printf("Fail Page:%d\r\n",PAGEError);
    7 z0 D9 q0 v+ V5 H: m! T
  31.       }
    6 M0 ]- W. r9 a3 V

  32. : M' o/ e( J8 z8 {" j
  33.       if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){& |& T* A5 Q" D( ]
  34.       printf("write data 0x%x OK\n", Data);
    * P8 P8 R# ^. h( R
  35.       }: q' P; x- G2 U. ^
  36.       else{
    3 ?- Y; [  N2 M  O( E/ E3 R* f
  37.         printf("failed!!!\n");
    & t' V* D% }3 f* P
  38.       }( s6 _$ u, n9 M- @9 Y' X- j

  39. , U: R+ k) d8 ?) Q- d
  40.   }; N" A. G  o/ W7 O
  41. HAL_FLASH_Lock();" p) h" v! i0 Q. g
  42. }
复制代码
" d; o1 q# Z0 n

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

  1. u32 write_data1 = 0X12345678;
    : Z0 m9 f$ H6 x* R
  2. u32 write_data2 = 0X87654321;3 o: I+ W: ?3 P$ `
  3. u32 write_data3 = 0XFFFFFFFF;  e( _: q1 u- N+ u' A$ L" u5 }) l
  4. : h+ [) `% ]& N% B" B- a7 L
  5. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_508 + 124,0X508508FF);2 d. ^8 A8 o8 E& m' m% \
  6. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509,write_data3);: u- P. J( K) {; D7 x8 B
  7. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509+4,write_data2);
    4 P$ I6 T& h7 K1 G
  8. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509+8,write_data1);2 `/ E5 }* s* A! I% E. }
  9. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_510,0X510510FF); " f; v, A" X" N/ f
  10. + i6 E- I2 j' b7 I" q7 M# s
  11. read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_508 + 124);
    " r3 I7 s" N% j: j
  12. printf("the ADDR_FLASH_PAGE_508 last is: 0x %x \r\n",read_data1);
    5 [+ d* H  r- R
  13.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509);
    9 K* J7 J' v% k9 i# H' X, n; Q% z
  14.   printf("the ADDR_FLASH_PAGE_509 1 is: 0x %x \r\n",read_data1);/ |8 b2 q: j2 x
  15.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 4);+ }$ ^0 l6 L& H+ a
  16.   printf("the ADDR_FLASH_PAGE_509 2 is: 0x %x \r\n",read_data1);
    3 s# \8 k- c2 F- h( D
  17.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 8);
    ) a4 K. U; Y  z4 l, C: h
  18.   printf("the ADDR_FLASH_PAGE_509 3 is: 0x %x \r\n",read_data1);
    ' Z4 P: P. b9 U4 ^; k
  19.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_510);
    . p3 J( w" d  k0 o" g* |1 z2 ~
  20.   printf("the ADDR_FLASH_PAGE_510 first is: 0x %x \r\n",read_data1);" ?( G1 M6 R# ]6 W" ~+ H4 H
  21. # i9 V2 Z- \) C: l3 H/ G( {
  22. if(btn_getState(&K2_BUTTON_150mS) == BTN_EDGE2){
    8 S% M+ i6 ]/ V9 }9 Z- t( B
  23.         printf(" K2 150ms button!EEPROM_read test\r\n");
    7 t' {! Z7 E6 e' g6 T
  24.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_508 + 124);' d* c7 |3 R  ?( q
  25.         printf("the ADDR_FLASH_PAGE_508 last is: 0x %x \r\n",read_data1);
    $ J) U. ~% Z3 r3 h9 j1 q' p
  26.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509);9 A* l) l" ~; E& F3 o) R0 ^* v
  27.         printf("the ADDR_FLASH_PAGE_509 1 is: 0x %x \r\n",read_data1);
    ; m# z9 ]" I; U7 X; N) e4 n, e$ {3 Z
  28.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 4);
    5 L; j0 z% F0 u2 S8 v; k! f& ^( c
  29.         printf("the ADDR_FLASH_PAGE_509 2 is: 0x %x \r\n",read_data1);: r# j7 m( Q  L( L+ O& S5 n/ z
  30.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 8);
    8 f% g: M* p0 S5 W$ O1 U
  31.         printf("the ADDR_FLASH_PAGE_509 3 is: 0x %x \r\n",read_data1);( U- T8 F1 u) L1 t$ n' j
  32.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_510);" G# b+ S1 ~  k
  33.         printf("the ADDR_FLASH_PAGE_510 first is: 0x %x \r\n",read_data1);
    2 e/ w) a$ x: {# _2 n! a- y# q

  34. 3 K8 ~) T# }; L  C
  35.     }" _0 V) X3 N' g, _. W1 Y
  36. if(btn_getState(&K1_BUTTON_150mS) == BTN_EDGE2){
    / Z0 d% b7 A% D% P/ w
  37.         // printf(" K1 150ms button!,EEPROM_Erase test\r\n");5 L& }* W4 Z/ e) z' U3 Y2 ]
  38.         // MY_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, DATA_EEPROM_START_ADDR, write_data1);4 \- w8 W' o7 h7 h
  39.         printf(" K1 150ms button!,flash write test\r\n");8 D) j5 E; W& s+ U: x! O
  40.         MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509,0X33333333);
    4 m5 S  ~; F- v4 F6 j
  41.         HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
    6 U9 E  Z! j9 u" w7 T( P& p" r% h: K
  42.     }
复制代码

# D0 Z3 |+ G$ [- C

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

8 F- u  ~/ l+ H" I, _2 A

微信图片_20230612172448.png

1 Q$ T; c, m( r* k! P/ D0 {' h2 U# k

微信图片_20230612172355.png


* F6 i- L2 e  ^8 ?& w

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

' _( y6 c( T$ M: K: e, n- p
2.4 读写EEPROM的后续问题

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

6 t8 x( t7 F; w) w/ x8 R9 ^0 E7 o

微信图片_20230612172339.png

/ r2 r( F3 t% g6 j0 e

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

( I5 K- V* S- Y; S6 y  G/ I; S: r3 B
2.4.1 问题的出现和解决

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

2 T, M& c8 Z- Z; g: r* y

微信图片_20230612172215.png


$ `# ~6 k* L9 {3 H- q5 ]0 j

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

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

5 c6 i/ J3 ^' t4 K! ?5 m

微信图片_20230612172212.png


: D: v! v( M1 r0 O! k

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

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

  1. BlueID_STRUCT test;# D% p$ f% |; m. |4 x1 ^
  2. .... Z0 c0 D: S% W: f' Y: a$ _2 {+ ]
  3. /*
    2 s, I3 h6 R" u9 g( g: E
  4. CHBlueID_STRUCT Flash_PowerOn_BlueCheck()  u  ^( n; ^- }3 x1 a0 _
  5. {* s& Q/ A3 d  a' _( j

  6. " w# B7 l0 R/ m2 Y1 d' ]0 M0 _0 }
  7. CHBlueID_STRUCT PowerOn_ID;
    / @% ]* I2 q0 S' i# z
  8. PowerOn_ID.CH1ID = FLASH_blueIDRead(CH1_ID_ADDR);
    3 z4 i" _9 ^, U! P  ~
  9. PowerOn_ID.CH2ID = FLASH_blueIDRead(CH2_ID_ADDR);8 E! ^! c) x4 c( w. O$ i, W
  10. PowerOn_ID.CH3ID = FLASH_blueIDRead(CH3_ID_ADDR);9 \% w& {2 B4 @/ ]; `- j1 a
  11. PowerOn_ID.CH4ID = FLASH_blueIDRead(CH4_ID_ADDR);
    ' ^; _( H* Z, I+ u/ d! T+ L9 r
  12. PowerOn_ID.CH5ID = FLASH_blueIDRead(CH5_ID_ADDR);* b4 d/ ^  c/ c6 E* u5 N
  13. PowerOn_ID.CH6ID = FLASH_blueIDRead(CH6_ID_ADDR);/ s# O1 ]5 ]- R6 p: N
  14. PowerOn_ID.CH7ID = FLASH_blueIDRead(CH7_ID_ADDR);
    8 i0 Z. }$ }+ G
  15. PowerOn_ID.CH8ID = FLASH_blueIDRead(CH8_ID_ADDR);3 ?: R# J" `2 M0 W* _* b5 n+ U
  16. PowerOn_ID.CH9ID = FLASH_blueIDRead(CH9_ID_ADDR);
    / u  v  Y9 O; M+ U& g5 m
  17. PowerOn_ID.CH10ID = FLASH_blueIDRead(CH10_ID_ADDR);
    3 w! {) i7 w! x* _
  18. & Y" ~9 [, W! [# d8 z0 R
  19. return PowerOn_ID;
    & l: D% N; Y/ A0 h8 Q
  20. }
    ! V/ ^1 d% g, m
  21. */" `9 l3 D" a7 u" e- m& R% |
  22. BlueChipID = Flash_PowerOn_BlueCheck(); //上电先把ID读出来做比较, j$ J9 j9 Z0 d2 r, u$ E
  23. //打印一个出来测试,看结果: Z0 R. P: y  v) e; v' I
  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);4 I6 J  z2 W$ }- z
  25. ) F4 T/ l1 h: P
  26. test.ID1= 0XFF; //结构体每个元素是 uint8_t 类型6 ^$ j/ n! |6 ]! x0 I; m  Z5 [
  27. test.ID2= 0XEE;" B. N) F7 K0 g% S! y, M' M& N
  28. test.ID3= 0XDD;
    8 _* X5 `% K" ]( e. r) o
  29. test.ID4= 0XCC;
    " w, W4 G1 R# V2 d* v
  30. test.ID5= 0XBB;, B( {5 o2 F8 w5 u1 \1 j
  31. test.ID6= 0XAA;
    & d2 G/ i+ l3 ^! Z3 F
  32. / Q; x' D- t& y5 B# w+ m
  33. while (1){...}
复制代码
" ^$ J3 T5 C4 b' I/ d  o9 ?

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


, J/ i' _! C, I0 c7 D  A1 |9 e

微信图片_20230612172209.png

1 B+ b; m) ]% n+ q7 j$ h

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

) t. e7 E( K% N8 V) I4 ]* C

微信图片_20230612172207.png

/ p! D: [! |$ H& [) J# q' P- q5 B

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

. D9 u4 ]1 n6 k% N! ]0 s

微信图片_20230612172058.png

, m& O' |, a( V; [- n8 g# u7 P

微信图片_20230612172050.png

8 |$ ~* K; V" i& m

打印的结果:


/ O4 t7 Z$ [2 B

微信图片_20230612172039.png

3 b1 Y& s$ o4 B$ Q

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

  1. BlueID_STRUCT FLASH_blueIDRead(uint32_t address)' W0 `/ F5 \% p3 w
  2. {5 u8 B$ x# `* f# r. x- y/ u
  3. BlueID_STRUCT u48id;4 M3 F6 e7 q5 ^; Z
  4.   // uint16_t temp1,temp2,temp3;  /**(__IO uint16_t*)address; */
    , R3 P, m6 Y: u. y" [
  5.   // temp1=*(__IO uint16_t*)address; , q4 B7 L3 Q2 S, [$ {$ n; q
  6. // u48id.ID1 = (uint8_t)(temp1>>8);; D, L& \( W* q& A" U
  7. // u48id.ID2 = (uint8_t)(temp1&0X00FF);1 c3 z( p; e, z$ H$ u3 N* G. E
  8.   // temp2=*(__IO uint16_t*)(address+2);
    5 L& [6 B) ?. [1 J
  9. // u48id.ID3 = (uint8_t)(temp2>>8);
    2 `9 i+ p8 M2 U7 J5 S
  10. // u48id.ID4 = (uint8_t)(temp2&0X00FF);9 A" M7 B" H5 D7 L! B; v3 R
  11. // temp3=*(__IO uint16_t*)(address+4);( F& B5 u/ Q! K& E
  12. // u48id.ID5 = (uint8_t)(temp3>>8);
    * K7 x, s. ~( C' H+ y3 @2 Y
  13. // u48id.ID6 = (uint8_t)(temp3&0X00FF);
    * ?& L0 V- Q( A, B" e
  14. . u2 V. o1 J5 y+ _
  15.   u48id.ID1 =  FLASH_Readbyte(address);
    % i; o  f0 |: @
  16.   u48id.ID2 =  FLASH_Readbyte(address+1);* w4 k/ u& W- ]9 Y! s
  17.   u48id.ID3 =  FLASH_Readbyte(address+2);7 S  P" j. s. K2 H! Z1 J, y
  18.   u48id.ID4 =  FLASH_Readbyte(address+3);
    6 E! I2 g6 `' O9 o) Q
  19.   u48id.ID5 =  FLASH_Readbyte(address+4);* ^5 T8 x9 B3 v0 x- M
  20.   u48id.ID6 =  FLASH_Readbyte(address+5);
    . `$ w+ A9 g4 a) f" o, I, N5 K
  21. ) Y' l2 v+ T. l/ g
  22.   return u48id;
    , z3 o/ G* m0 k$ U8 A2 E
  23. }
复制代码

" n* b# _  @0 W4 S- A. f" v

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

+ H  b" L; W2 ], n+ W1 M3 L4 I: j

微信图片_20230612172035.png

6 W- Z" ~7 H* D( k) o
2.4.2 问题的分析(大小端模式数据格式)

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

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

2 I% K* C4 N5 _% M( |. [* x  \& V

微信图片_20230612172032.png


5 W7 x6 }% o- g% O- G, x( K' o

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


4 s  Z9 j# M8 h$ p8 _3 T0 |

微信图片_20230612172030.png


- m/ ?$ p" f4 @) ~

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

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

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

  M0 i1 u: Q( ~1 r( c
2.4.3 STM32L071RBT6 EEPROM读写全字问题

$ _% p# ^8 W: s- ]" B读问题的出现

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

  1. uint32_t  FLASH_ReadWord(uint32_t address); Q3 u! k: f8 j& n8 M
  2. {% p, H, T" P' A) u4 D. b- Y
  3.   return *(__IO uint32_t*)address;( g6 g1 d$ C6 d
  4. }
复制代码

# ~8 f# X5 R8 _6 q

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

; x$ {$ S" w0 g8 U

微信图片_20230612172026.png

% K7 U; {/ w! R% J

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

; K* h% A3 {' u% c) F

问题的解决:

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


: Z# G' W" ~4 M) n9 P3 O

微信图片_20230612172019.png

0 l5 N1 _# n/ E+ b5 e. l4 y

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

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

- S8 F$ W# \) \3 m, n

微信图片_20230612172012.png


$ L( g$ x2 r& p. t$ b) n9 v4 u. K! z

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

  1. u32 FLASH_ReadEnoceanID(uint32_t address); ]* @- {) P7 z* |; i% S
  2. {0 s( v. v: K3 Q6 _
  3.   u32 keepid;# V- i  M4 m# D) P3 a( J+ |
  4.   u8 i;
    . A9 x0 h# k9 @! h. ?3 r. o3 i& C" q
  5.   keepid = FLASH_Readbyte(address);  D: d) H9 w5 V1 V* h
  6.   i = FLASH_Readbyte(address + 1);( ?. F! @; d- B; D' W7 \* t6 m
  7.   keepid = (i<<8)|keepid;  c6 h5 i1 u- A( @, [
  8.   i = FLASH_Readbyte(address + 2);8 W. r' l6 v6 ~# x$ X/ g; T! F; J5 w# m
  9.   keepid = (i<<16)|keepid;5 w& A. z+ _: I+ n+ A
  10.   i = FLASH_Readbyte(address + 3);
    # j$ I( I* P2 ~% L  t
  11.   keepid = (i<<24)|keepid;  y) p, b$ U4 G( t$ U! R' K1 |
  12.   return keepid;
    8 Z7 k; ~4 y8 t4 F
  13. }( y& C, d/ ~* t- o
  14. : b( O- Q  p- z* H
  15. CHID_STRUCT Flash_PowerOn_Check()
    6 ^5 T/ ~- b% [
  16. {
    % C, P3 X  M0 @, F3 V" [3 I

  17. ! x5 l, x# Z( |2 D+ Y+ C
  18. CHID_STRUCT PowerOn_ID;
    ! v; r; I/ T$ m2 O
  19. PowerOn_ID.CH1ID = FLASH_ReadEnoceanID(CH1_ID_ADDR);
    + |% Q7 _7 x( ]" G7 ^( O. F0 x
  20. PowerOn_ID.CH2ID = FLASH_ReadEnoceanID(CH2_ID_ADDR);/ [# O  D9 d5 i4 |: O
  21. PowerOn_ID.CH3ID = FLASH_ReadEnoceanID(CH3_ID_ADDR);0 F1 \8 R6 ?& F8 Z% m* L: S
  22. PowerOn_ID.CH4ID = FLASH_ReadEnoceanID(CH4_ID_ADDR);& x5 A' Y& d  v
  23. PowerOn_ID.CH5ID = FLASH_ReadEnoceanID(CH5_ID_ADDR);) g* p! y2 T& y7 {
  24. PowerOn_ID.CH6ID = FLASH_ReadEnoceanID(CH6_ID_ADDR);8 }, C+ V. R1 u8 u3 A5 Q8 o3 m8 C1 ~
  25. PowerOn_ID.CH7ID = FLASH_ReadEnoceanID(CH7_ID_ADDR);
    - A% s1 i4 w1 ?6 `* N
  26. PowerOn_ID.CH8ID = FLASH_ReadEnoceanID(CH8_ID_ADDR);" x5 _( S' _% V! E% j3 s
  27. PowerOn_ID.CH9ID = FLASH_ReadEnoceanID(CH9_ID_ADDR);
    5 q, u& L- N) q( M) g9 c
  28. PowerOn_ID.CH10ID = FLASH_ReadEnoceanID(CH10_ID_ADDR);: ]( b2 z+ ~% g

  29. . l# f& N1 i( w2 P( H
  30. return PowerOn_ID;
    7 X3 B. p& B$ T4 G
  31. }
复制代码

( V9 X  ~$ y+ v

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

" N7 H' ^* R( c/ u
写问题的出现

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

一直用的写全字函数为:

  1. FLASH_Status  FLASH_WriteWord(uint32_t Address, uint32_t Data): Z3 t% T; F+ c% E8 f
  2. {
      O* S. C: f: ?4 j7 g
  3. : h8 e- D0 W+ q& d8 x8 q
  4. FLASH_Status i = FLASH_COMPLETE;
    ) ?) ]5 O" z: C2 P- G6 |% \

  5. 9 o! j) s0 K2 S/ Y) Q: B' _2 a( S  \
  6. HAL_FLASHEx_DATAEEPROM_Unlock();  
    ' l1 N2 s2 L. {" R* D4 M: G
  7.   while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, Address, Data) != HAL_OK);1 I, S6 Z4 b. j6 U" U4 w! k1 t9 F
  8. HAL_FLASHEx_DATAEEPROM_Lock();
    & `) J3 \7 a3 k3 R5 L

  9. 8 U3 @/ A3 {( Q4 _# N& ]
  10. return i;3 D: L! j7 f2 {, L. l" H. }
  11. }
复制代码

; w# X/ B0 w% @0 r

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

8 |- x  h7 }+ ?) Z  G- A7 Q5 }

微信图片_20230612172009.png

: i: {# f! Z6 T

但是使用时候发现:


+ B' K# b: k% u

微信图片_20230612172006.png


1 _, h0 S- E6 K; z8 @# o3 G

问题的解决:

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

5 u" }) W/ J" X% \0 Q5 ?5 x9 R

微信图片_20230612172003.png


+ R" a9 k5 A6 y

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

  1. FLASH_Status  FLASH_WriteWord(uint32_t Address, uint32_t Data)% G( t9 N( a* Q5 v( y4 s/ ~. R
  2. {
    , g2 t& e9 N6 n0 U
  3. . p+ A( e# L) G2 `9 `7 y
  4.   FLASH_Status state = FLASH_COMPLETE;: ^( D+ s, u% @& G: }# @
  5.   u8 i = 0;
    6 b( x6 S6 i" X- {+ t4 T
  6.   u8 writedata[6]={0};' c' ^4 n% x, O0 \$ l0 X

  7. 6 L% i# q# S# k8 ]8 R
  8.   writedata[0] = (u8)Data;
    2 G! L' Y! F2 b5 D- u
  9.   writedata[1] = (u8)(Data>>8);
    8 V  u# ^4 Q7 `2 U! D7 E
  10.   writedata[2] = (u8)(Data>>16);" }+ ?- D# W* h
  11.   writedata[3] = (u8)(Data>>24);- `, r& d2 d" P+ R/ O- s$ Z

  12. * D4 P/ C& J8 i3 G: s# p
  13. HAL_FLASHEx_DATAEEPROM_Unlock();  , \7 R9 b7 c8 l" A, \
  14.   for(i=0; i<4; i++){
    $ ~5 X+ o- o6 S: }
  15.     while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, Address + i, writedata[i]) != HAL_OK);
    " ?+ _; q, n4 A3 M3 X4 {
  16.   }
      \0 H  N) ^  m6 O. B
  17. HAL_FLASHEx_DATAEEPROM_Lock();% e" V# |$ {  ?: C
  18. & c; i* }8 `1 F  |: T
  19. return state;6 K4 C/ e4 B* \& w" _9 }
  20. }
复制代码

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

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

2.4.3小结使用的芯片为  STM32L071RBT6

) i5 t; `0 d9 e% c! r
最后问题的解决

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

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


6 x' Y6 C) S$ s; [

微信图片_20230612172000.png


& Y/ r! a1 {5 p5 W" Z* Y& z* x  A

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

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


4 o, ?1 W' ]. d

微信图片_20230612171954.png


9 ~  a" E9 g8 K9 M6 i

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


, [1 q0 y! Y' F# }" w% t, P
收藏 评论0 发布时间:2023-6-12 17:30

举报

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