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

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

# j& r9 V7 }/ G3 o

微信图片_20230612172451.png


* _, L9 O3 |6 N. D) c1 @

微信图片_20230612172454.png


8 f7 E- c3 N. p. X; k! x8 H

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


) Q; v/ |# L0 O+ q0 h

  1. #define FLASH_SIZE                (uint32_t)((*((uint32_t *)FLASHSIZE_BASE)&0xFFFF) * 1024U)( k' r% ^3 Q3 I4 h) \  v0 L6 c+ f" @
  2. #define FLASH_PAGE_SIZE           ((uint32_t)128U)  /*!< FLASH Page Size in bytes */
复制代码

4 F4 c2 s9 s! ~/ Z' q6 M

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


/ l9 o% ?) U7 X+ \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,因为怕有时候数据不够放……

! A! }. u4 a3 W$ {1 [6 Y; Q: B+ r
2.1 读取函数
  1. //读取指定地址的半字(16位数据)
    8 W  d+ d0 a: J8 j4 r% O5 h
  2. uint16_t FLASH_ReadHalfWord(uint32_t address)
    6 {7 c$ {% j, {- Z
  3. {  p: c: U7 w6 }; u- F. A4 e  p
  4.   return *(__IO uint16_t*)address; 0 }5 a6 i/ Q  Y+ u: F
  5. }
    ' G. |" Z8 _- v# b5 Y* I+ P
  6. 7 R7 H5 w& U5 F+ X) i: O
  7. //读取指定地址的全字(32位数据)
    * V* }. q! x  O' n
  8. uint32_t FLASH_ReadWord(uint32_t address)4 j; S4 l* a* T1 z0 A( ~. K
  9. {
    8 A/ |& i0 j& A' _
  10.   return *(__IO uint32_t*)address;  b! i# h3 F' P# @4 O8 j
  11. }
复制代码

- S6 I) U4 S! [, j7 ?0 O, P7 b+ R" Y' x5 o( T: h; Z

简单测试一下:

  1. u32 read_data1=0XFFFFFFFF;
    8 Z! V( h. `( R1 Y7 T2 y+ o% I$ v0 o/ q
  2. u32 read_data2=0XFFFFFFFF;
    3 R) V0 q% K$ D5 n5 R
  3. ...
    & ?/ O# `+ t! w5 |2 A, ~
  4. read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);: j& b, D2 C& l# h
  5. printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);
    : P, a4 Y* t2 E
  6. read_data2 = FLASH_ReadWord(DATA_EEPROM_START_ADDR + EEPROM_PAGE_SIZE);
    $ w+ u- ^& w3 m) o- z
  7. printf("the EEPROM sceond page test data is: 0x %x \r\n",read_data2);
复制代码

) H, {1 T4 E6 e- ~' J

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


3 n# u0 t: m$ a# o4 {2.2   EEPROM写函数

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

  1. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Unlock(void);% \7 P" {" S3 F) b8 L5 v
  2. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Lock(void);
    ( J9 x3 O1 P) K- Q

  3. 3 m: k% L* |' i$ g. l+ j7 U
  4. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Erase(uint32_t Address);) Z3 u5 h! m1 V4 j* Q4 K8 u$ W
  5. HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data);
复制代码

9 Z( W! L6 ~1 b3 S7 s" e' D

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

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

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

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

  1. HAL_FLASHEx_DATAEEPROM_Unlock();& z* c4 V+ u" p# H6 n7 z0 Q' Z
  2. HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, DATA_EEPROM_START_ADDR, write_data1);
      t9 v) d7 P6 g4 P9 t6 O3 Q" \
  3. HAL_FLASHEx_DATAEEPROM_Lock();! X0 n: }2 y1 L" m9 w
  4. ...
    8 x2 ^- s- W& H' @
  5. if(btn_getState(&K1_BUTTON_150mS) == BTN_EDGE2){
    4 e6 y/ T6 A" Z1 A% T9 L
  6.         printf(" K1 150ms button!,EEPROM_Erase test\r\n");3 \* i! n# v) T- P2 ~! ?
  7.         HAL_FLASHEx_DATAEEPROM_Unlock();! b# d7 U& x2 ~% r4 f) b3 [
  8.         HAL_FLASHEx_DATAEEPROM_Erase(DATA_EEPROM_START_ADDR+4);
    2 w* v# P1 E) h8 s
  9.         HAL_FLASHEx_DATAEEPROM_Lock();
    . A* J6 ^6 O# B6 }+ A& b
  10.         HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
    ; P/ I$ O! h  i. V6 X
  11.     }" @  a( A4 y- I7 l
  12. ...' u6 G* j- d5 E, b) u9 L* V. H7 [
  13. if(btn_getState(&K2_BUTTON_150mS) == BTN_EDGE2){& a% \( m, o$ n' i5 b! d$ S' Y
  14.         printf(" K2 150ms button!EEPROM_read test\r\n");
    # L8 V* ~& B" ?" A
  15.         read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);; k4 l5 a3 b# I4 _3 [6 M% g
  16.         printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);
    ) ~+ A, M; B0 H8 }/ ]
  17.     }
复制代码
% Q! r3 M4 i* E9 ?. H

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

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

* w/ S, P) j; p6 z
写入问题说明修改

2021/11/23 修改说明 上面一段的解释有误,这里修改一下,不是因为地址类型为uint32_t,地址类型永远是这个,是因为定义的数据类型为uint32_t ,然后STM32又是小端模式,所以保存的方式是从地址的最后开始保存,4个字节的全字,第一个字节放在地址开始+4 的位置,第二个字节放在地址开始+3的位置,所以如果调用HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, DATA_EEPROM_START_ADDR, write_data1);关键在于FLASH_TYPEPROGRAMDATA_WORD以全字方式写入半字,那么内核会自动分配4个字节的宽度,半字的第一个字节放在写入地址+4 的位置,第二个字节放在写入地址+3的位置,所以导致了上面的结果

  1.   HAL_FLASHEx_DATAEEPROM_Unlock();! a. C) O, @6 \  }* M" j5 Y: ^
  2.   HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, DATA_EEPROM_START_ADDR, 0X88);$ g$ o, m# i) k
  3.   HAL_FLASHEx_DATAEEPROM_Lock();
    # ]+ }- H8 j  s/ z* e
  4.   read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);
    . P: ]: @6 G/ A  @4 `9 x/ o
  5.   printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);
复制代码
, g, |; \% u( k% X' p

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

  1. void MY_DATAEEPROM_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data)   
    : N5 x* \) i! R& _+ p$ z$ V, z
  2. {/ \. \) _9 U4 B$ s
  3. HAL_FLASHEx_DATAEEPROM_Unlock();        
      F- n0 z5 w3 E4 J8 V3 T
  4.     HAL_FLASHEx_DATAEEPROM_Program(TypeProgram, Address, Data);
    2 r" I. U6 G6 w) B3 x/ |
  5. HAL_FLASHEx_DATAEEPROM_Lock();
    3 `0 b. h  T7 G# H
  6. }
复制代码
/ O5 z7 S7 V) y" e" U* x

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


) N& D7 t  Z- N2.3   Flash写函数

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

  1. /* IO operation functions *****************************************************/
    + Q+ }$ y/ f$ Q9 f* \
  2. HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data);9 y% H3 _( w$ I
  3. HAL_StatusTypeDef HAL_FLASH_Program_IT(uint32_t TypeProgram, uint32_t Address, uint32_t Data);
    3 `4 a  H( y4 i. ]

  4. , n* y! i. {' i' U
  5. /* FLASH IRQ handler function */0 \3 m/ |# [. ^; W: ?! f4 f/ ^
  6. void       HAL_FLASH_IRQHandler(void);
    * }; _+ R8 e7 G* B
  7. /* Callbacks in non blocking modes */, u) T$ j$ K$ X4 {0 Z" \; {
  8. void       HAL_FLASH_EndOfOperationCallback(uint32_t ReturnValue);
    5 Y' j8 O1 k. z
  9. void       HAL_FLASH_OperationErrorCallback(uint32_t ReturnValue);3 o: z8 ^" R2 A2 r2 T. {2 j
  10. 1 k6 b' O+ Y0 B
  11. /**
    6 z; Q, ?' z* a' A; ~
  12.   * @}" c6 o% q% u/ o+ H
  13.   */
      D2 W% B; B8 X+ ~( c  T, ]

  14. , {8 j, F5 I; v2 Y5 O4 w
  15. /** @addtogroup FLASH_Exported_Functions_Group2
    , n: i$ j# N/ D
  16.   * @{
    & R  ~6 @4 y! G9 U; C  W
  17.   */6 r" ]! b% w/ Z) n
  18. /* Peripheral Control functions ***********************************************/2 N. k9 {" `# ~4 o8 F- j! L! I, u
  19. HAL_StatusTypeDef HAL_FLASH_Unlock(void);
    ; ~# P" S9 r' D+ u: B& x1 y$ `
  20. HAL_StatusTypeDef HAL_FLASH_Lock(void);
    * q( _4 P- r5 R
  21. HAL_StatusTypeDef HAL_FLASH_OB_Unlock(void);% s' B, ?% K0 H; Q
  22. HAL_StatusTypeDef HAL_FLASH_OB_Lock(void);
      F: z, t" B3 `
  23. HAL_StatusTypeDef HAL_FLASH_OB_Launch(void);
复制代码
2 s0 E0 q, M' s0 u

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


* v/ U8 }8 p! H, _) Z# A

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

. e( Y2 s9 C, s. K# K
  1. HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data)# v/ \) [! l; u8 R1 P
  2. {7 ]/ P+ \. ^& Q* J
  3.   HAL_StatusTypeDef status = HAL_ERROR;! q( s8 w' f2 ?$ ]$ B/ R3 d3 H
  4.   
    , C, @  }0 G  h. x5 s
  5.   /* Process Locked */7 U; i5 j- L5 y2 ]* \8 |1 z
  6.   __HAL_LOCK(&pFlash);
    9 H! r: g/ s& Z0 K4 H2 ]
  7. 8 D4 B7 J' E6 K
  8.   /* Check the parameters */
    9 n( o4 M* L$ G& `, k; W  I2 t
  9.   assert_param(IS_FLASH_TYPEPROGRAM(TypeProgram));4 g: e5 I3 y8 e: \
  10.   assert_param(IS_FLASH_PROGRAM_ADDRESS(Address));1 ?+ y% `* x% y; G3 w
  11. ( N  B- Q- i1 L8 E
  12.   /* Wait for last operation to be completed */# G3 b8 i6 C$ b1 D+ ]
  13.   status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
    ; e; H  X1 |4 `  I9 A' O. [/ ^
  14.   
    ) t. O; o) d8 o+ e
  15.   if(status == HAL_OK)' p9 @7 i5 w  }# H  m( o& v) ~
  16.   {0 {& G7 Z/ G+ T* |9 r/ d- n
  17.     /* Clean the error context */: q. _1 M- P" C) L8 ~
  18.     pFlash.ErrorCode = HAL_FLASH_ERROR_NONE;& |" a( M, G: `3 r, @- G

  19. " J3 v7 c& f4 p
  20.     /*Program word (32-bit) at a specified address.*/! i$ i) x% Z$ F* @8 G
  21.     *(__IO uint32_t *)Address = Data;* j" [9 ~4 V% e+ M
  22. + L" Q1 N" m* \) c! ?% I3 T; t% Q
  23.     /* Wait for last operation to be completed */7 g$ s0 e, a9 ^% A2 j7 d# n9 \% j
  24.     status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);! |2 J) k* v$ e" D) _. W$ l
  25.   }
    9 M' a  ?# X/ f, |+ }. W

  26. " j9 X/ [; I. [4 n+ K0 H. _
  27.   /* Process Unlocked */
    5 _5 m3 [8 c: ]3 a
  28.   __HAL_UNLOCK(&pFlash);. n- t8 |! R) Q
  29. % p( [; S; E  b7 [* }
  30.   return status;6 {. m# ^- V/ n6 \1 H+ ~+ j
  31. }
    1 d2 q: f* i- t4 R% {3 U( I
复制代码

0 @# A; N) h, Y

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

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

  1. #define ADDR_FLASH_PAGE_505      0X08000000 + 128*504   //
    " n4 q1 N4 u0 k
  2. #define ADDR_FLASH_PAGE_506      0X08000000 + 128*505   //7 }4 \% G0 @6 \4 r* I
  3. #define ADDR_FLASH_PAGE_507      0X08000000 + 128*506   //
      `! q7 X  l1 O8 ]4 J& N& z/ Q
  4. #define ADDR_FLASH_PAGE_508      0X08000000 + 128*507   //4 y- x3 E. T: L( \- f  ~
  5. #define ADDR_FLASH_PAGE_509      0X08000000 + 128*508   //* R, ?) C7 s+ x
  6. #define ADDR_FLASH_PAGE_510      0X08000000 + 128*509   //
    9 S* W% t8 D2 ]' f) s  B8 g% i
  7. #define ADDR_FLASH_PAGE_511      0X08000000 + 128*510   //
    $ y* n! x$ n4 R* {
  8. #define ADDR_FLASH_PAGE_512      0X08000000 + 128*511   //最后一页
复制代码

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

  1. void MY_DATAFLASH_Program(uint32_t Address, uint32_t Data)   / ^$ y! g( I+ h' {9 `0 J
  2. {
    ( ?) C- n5 \+ I/ r1 Y, A
  3. HAL_FLASH_Unlock();        
    4 T$ L; M% V" s7 q4 B# z
  4.   if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){
    ! R- M8 A% D) g+ Z$ v
  5.     printf("write data 0x%x OK\n", Data);0 T5 d9 m( |+ J) x& P
  6.   }
    " [3 I- K! ?# p" [: k; H7 j
  7.   else{4 a8 S3 I% z, i5 _
  8.     printf("failed!!!\n");
    , D% M3 i* ]! w" i: R4 [
  9.   }0 o, o) ~! \: M/ c- K& W- Q+ |( }2 w
  10. HAL_FLASH_Lock();9 ^1 J- P. b2 M; C* |' N& N
  11. }
复制代码

3 x# }* E/ ?" m) H' |- ?8 ^

测试一下;

  1.   MY_DATAFLASH_Program(ADDR_FLASH_PAGE_512,write_data2);
    ! f! W% R( |) a
  2.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_512);
    8 V+ G# F( F, N; U- E
  3.   printf("the ADDR_FLASH_PAGE_512 is: 0x %x \r\n",read_data1);
复制代码
8 z7 l- }+ S5 U2 R4 D

在没有写入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)   
    7 I6 A( ~* _6 j; W
  2. {
    ' e1 z* g! a3 a+ Y( W6 O
  3.   FLASH_EraseInitTypeDef EraseInitStruct;
    : A! g- V0 b0 J7 G4 P
  4.   uint32_t checkdata;# O/ {1 ^7 @) K
  5.   uint32_t PAGEError = 0;; a9 _3 Y, X& U
  6.   checkdata = FLASH_ReadWord(Address);
    " p" V3 w1 G/ y& M4 L% q
  7. HAL_FLASH_Unlock(); # [0 n& l+ J* Y- J$ S: ?! W
  8.   /*如果是0,直接写*/8 S7 {+ ?5 P' O1 h" J0 W
  9.   if(checkdata == 0){       ; b6 h! W0 C4 S
  10.     if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){+ o" q: |5 X. [) e2 Z2 \* q
  11.       printf("write data 0x%x OK\n", Data);* z! f- T2 e( K" x% r- u& u) `
  12.     }
    " j' O# R+ g' }; x" g$ }' A2 ?
  13.     else{
    6 S) e) W$ J7 C* r2 p. }4 K8 |
  14.       printf("failed!!!\n");
    & [7 p7 h. f& B9 c# O
  15.     }( ]; U8 ^* Y# `: F
  16.   }! ~( Q4 l  l' ~2 g, o
  17.   /*否则擦除再写*/
    0 i2 Z5 r" I( e# v- D
  18.   else{9 I4 U# E' G3 p5 a# i
  19.      __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);
    8 r  R3 h5 ^( C" h

  20. 6 |4 S# N2 }; z
  21.       EraseInitStruct.TypeErase   = FLASH_TYPEERASE_PAGES; // 刷除方式" \% o: J( m' o) x# Y
  22.    EraseInitStruct.PageAddress = Address; // 起始地址
    6 h/ A8 B# Q1 s6 y* ~
  23.       EraseInitStruct.NbPages = 1;( y0 j- [8 c, Q; ^. t

  24. & K' s  V9 ^- x- a% g
  25.       if (HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError) != HAL_OK)
    5 O1 k* ?& I, U9 v
  26.       {* U# t8 I; C7 p4 k3 c
  27.         // 如果刷除错误
    $ c5 K% X% @2 N
  28.         printf("\r\n FLASH Erase Fail\r\n");- Y$ H$ |" l9 F$ R$ l# Z
  29.         printf("Fail Code:%d\r\n",HAL_FLASH_GetError());* Q4 D* I- a- ~3 B) h" h- i
  30.         printf("Fail Page:%d\r\n",PAGEError);* N9 o! {: W8 J& |
  31.       }
    % U1 Y, T( u" V' I

  32. ( _5 S# Z5 h5 @  h# p5 @
  33.       if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){
    ) I: g$ N. q5 E3 Y
  34.       printf("write data 0x%x OK\n", Data);
    9 h/ y4 _- v0 c! B  Q( w- ?/ _
  35.       }1 @1 s2 y% }5 e
  36.       else{8 S4 L9 s, L" a5 }3 C/ i
  37.         printf("failed!!!\n");2 R' t* x* G: _* r5 h
  38.       }' U- o5 A$ V# C" Q$ E0 W

  39. 6 ]$ R( k& n' D: @* u% f
  40.   }
    + o- a9 l. [. n- `" l
  41. HAL_FLASH_Lock();) t) h" v+ X4 r3 w0 M
  42. }
复制代码
" n7 s( h- w9 J* k8 r3 p. j

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

  1. u32 write_data1 = 0X12345678;* u* \3 U; @2 X2 X. y5 m( O! V. J
  2. u32 write_data2 = 0X87654321;) E9 z4 c0 a4 L, w% N( A
  3. u32 write_data3 = 0XFFFFFFFF;
    4 y" o) Q& c& N0 y  L
  4. 5 N2 C9 x) D  t) @6 |5 T9 C) a
  5. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_508 + 124,0X508508FF);& x" q( r3 i& c* g7 W
  6. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509,write_data3);
    & C7 b. ?# z8 g+ u4 d: A8 u7 B
  7. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509+4,write_data2);8 t  S, V- A' m4 q
  8. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509+8,write_data1);
    ) D% h- {  x% C" |9 p4 @; z% s
  9. MY_DATAFLASH_Program(ADDR_FLASH_PAGE_510,0X510510FF);   s8 Y0 d" I2 a+ N+ }0 p# a
  10. 7 j& ]9 L2 H8 ~4 G) [+ H
  11. read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_508 + 124);
    . t# E% ?& F5 j0 t5 Y; {  |
  12. printf("the ADDR_FLASH_PAGE_508 last is: 0x %x \r\n",read_data1);4 x' p& i. L  [4 @) T$ x) |5 X) n1 _
  13.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509);& k3 `: V4 q0 b5 s
  14.   printf("the ADDR_FLASH_PAGE_509 1 is: 0x %x \r\n",read_data1);
    0 o' X2 j9 H5 ]- u# n* b! \
  15.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 4);0 k% v" [$ w& u! l* _; q3 `; R
  16.   printf("the ADDR_FLASH_PAGE_509 2 is: 0x %x \r\n",read_data1);
    # J5 X, O2 F) F9 r# ^" Z( @
  17.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 8);
    7 I  j, d* M, E/ M# C( z( ]
  18.   printf("the ADDR_FLASH_PAGE_509 3 is: 0x %x \r\n",read_data1);( n4 ?. C' Q2 h! M+ s9 w
  19.   read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_510);3 l& t  Z5 w6 W5 t& \5 v% u
  20.   printf("the ADDR_FLASH_PAGE_510 first is: 0x %x \r\n",read_data1);
    4 x: N+ ~0 ^, `4 h4 E
  21. , P: N  D0 T- a+ l' o' E
  22. if(btn_getState(&K2_BUTTON_150mS) == BTN_EDGE2){* w3 l6 x, [( E9 f5 w
  23.         printf(" K2 150ms button!EEPROM_read test\r\n");5 h" w3 }$ n' {3 O. T$ H3 B
  24.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_508 + 124);
    ) Z  k8 P+ K& ?% j. D! R
  25.         printf("the ADDR_FLASH_PAGE_508 last is: 0x %x \r\n",read_data1);: A$ D4 M7 [! C6 A, O: H" r
  26.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509);
    % T# m6 r0 h; v2 C
  27.         printf("the ADDR_FLASH_PAGE_509 1 is: 0x %x \r\n",read_data1);
    ( K$ f& ^/ O0 ?2 S# u* U: G
  28.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 4);6 {% ^# ]8 k7 b! ?2 S
  29.         printf("the ADDR_FLASH_PAGE_509 2 is: 0x %x \r\n",read_data1);
    % v3 V( a2 ], `  q. J' g0 h5 [2 n
  30.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 8);# q+ r% o  `3 ?1 X
  31.         printf("the ADDR_FLASH_PAGE_509 3 is: 0x %x \r\n",read_data1);: ?4 e9 }/ h! K
  32.         read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_510);8 }: H) a* c6 d
  33.         printf("the ADDR_FLASH_PAGE_510 first is: 0x %x \r\n",read_data1);7 j! E7 W  y- ?* w8 q  ^
  34. * h7 M  [3 u! s* ]1 b  A2 C
  35.     }# H4 _' j4 a; F# w! P. Z
  36. if(btn_getState(&K1_BUTTON_150mS) == BTN_EDGE2){. A9 K% N4 Z& P" U3 p) y- _( P: x# J1 m
  37.         // printf(" K1 150ms button!,EEPROM_Erase test\r\n");
    6 H4 M) b' |+ k
  38.         // MY_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, DATA_EEPROM_START_ADDR, write_data1);7 L8 P* }; T# m9 _+ G; u
  39.         printf(" K1 150ms button!,flash write test\r\n");
    " }: p$ R+ A+ R6 j
  40.         MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509,0X33333333);
    2 M; j& H8 l9 K, L% E% |) g
  41.         HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);5 g3 R1 T; o6 @& h
  42.     }
复制代码
6 l) n/ ~; s1 B, b0 d# }, i

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


, y/ a2 }- [4 ]. U4 I5 m2 t' d

微信图片_20230612172448.png

3 U* x  |( {6 U

微信图片_20230612172355.png

  S  {1 E- f$ l' b& E: @

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

  W; l7 c! b* u' K! w: t
2.4 读写EEPROM的后续问题

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


2 ~3 G: o/ [7 u: d5 p/ v: V% _6 w

微信图片_20230612172339.png


" t1 ]% B) d) g1 m- J$ U

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

% L* p4 \6 H; |; i- d2 s. ]& Q+ ^; E
2.4.1 问题的出现和解决

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

# r8 B7 m/ r# G& H! ~) f; b

微信图片_20230612172215.png


7 o. f7 ~/ n; r

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

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


" ]5 R& a1 }& b: O3 T# {

微信图片_20230612172212.png

1 b! y% I+ y0 Z0 C( O* b

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

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

  1. BlueID_STRUCT test;% X) n: J4 q9 l) B7 k: L5 j
  2. ...
    % G0 h4 Y( u! D. y" H  ~
  3. /*
    $ i* S8 |/ L- H. F; B
  4. CHBlueID_STRUCT Flash_PowerOn_BlueCheck()
    & m" ^/ E' }* _
  5. {1 q2 U6 }% Q8 E+ ^2 c
  6.   o! Y: e8 D/ H7 H7 k! D/ t' ~' P
  7. CHBlueID_STRUCT PowerOn_ID;
    : u( I; P5 g5 d7 J6 m/ O" c
  8. PowerOn_ID.CH1ID = FLASH_blueIDRead(CH1_ID_ADDR);: o' j4 `- F, J. ~! R) h; A1 U# [
  9. PowerOn_ID.CH2ID = FLASH_blueIDRead(CH2_ID_ADDR);9 h- @. p% X, f0 o6 r
  10. PowerOn_ID.CH3ID = FLASH_blueIDRead(CH3_ID_ADDR);( D5 e4 Q9 E) Z2 D9 {, W$ w6 `' c
  11. PowerOn_ID.CH4ID = FLASH_blueIDRead(CH4_ID_ADDR);
    ; R; N3 i# {: _4 Z! Z
  12. PowerOn_ID.CH5ID = FLASH_blueIDRead(CH5_ID_ADDR);
    ; U9 W/ o% A* N& G. s
  13. PowerOn_ID.CH6ID = FLASH_blueIDRead(CH6_ID_ADDR);
    7 @- s3 d5 M, Z3 C6 s$ R1 f8 b
  14. PowerOn_ID.CH7ID = FLASH_blueIDRead(CH7_ID_ADDR);8 Y% W0 Y; V. s$ r: m' X
  15. PowerOn_ID.CH8ID = FLASH_blueIDRead(CH8_ID_ADDR);/ ]7 S" o( T. h
  16. PowerOn_ID.CH9ID = FLASH_blueIDRead(CH9_ID_ADDR);
    / d1 i. x: e2 [( b3 f6 w% r, K
  17. PowerOn_ID.CH10ID = FLASH_blueIDRead(CH10_ID_ADDR);7 E2 Q* o! y3 J  h; N5 n
  18.   Y' `! F' \5 a8 o7 b2 K% V# X
  19. return PowerOn_ID;3 G% H9 E& E* y1 x" n# Y
  20. }
    8 h, t, L* {- [4 Q4 i( C6 M6 \! G( W
  21. */# v( P8 |$ O$ Y
  22. BlueChipID = Flash_PowerOn_BlueCheck(); //上电先把ID读出来做比较
      Y8 A: G% X: _* x/ p- Z5 R
  23. //打印一个出来测试,看结果! x' t9 J0 E  |( 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);+ l. o. ]" k  E% y, R' A1 H8 ]
  25. & Q" q+ C2 h8 S8 |5 T, n
  26. test.ID1= 0XFF; //结构体每个元素是 uint8_t 类型
    9 v* L6 Q9 Q) I( s
  27. test.ID2= 0XEE;
    4 ~/ a1 W5 Z3 B' E) }0 P: j, D
  28. test.ID3= 0XDD;
    8 [' ~9 l" R0 g
  29. test.ID4= 0XCC;
    ) u4 h- G" H% O( i6 n3 V
  30. test.ID5= 0XBB;/ l, j. v% I2 M: ^0 I
  31. test.ID6= 0XAA;
    8 y+ {/ V  Y" O1 L* S8 |* V

  32. ! a' Z& {- Q& ?) F
  33. while (1){...}
复制代码

* E3 y6 C* ]0 ]' _& p

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

, }+ `3 Y2 U2 W4 J

微信图片_20230612172209.png

' F2 M$ ]; O- I  e; x

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


% X4 n+ D  \  U; r( p# o

微信图片_20230612172207.png


/ d, z- B+ f9 _! R- B

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

2 |0 z: S3 H# {% v' D9 j- A5 U4 }0 Q3 g

微信图片_20230612172058.png

. ^- L7 v) D( h1 @4 g/ C8 A

微信图片_20230612172050.png

+ q& @9 S' e" p1 N* ~

打印的结果:

" V. ]) W! `! A: w; D: ?7 r+ Y

微信图片_20230612172039.png


5 C, V7 |; v3 K7 S3 F

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

  1. BlueID_STRUCT FLASH_blueIDRead(uint32_t address)# E- M/ H1 ^3 y! Y# g! d7 y
  2. {  U7 v7 P5 {. W7 Y4 P
  3. BlueID_STRUCT u48id;$ Z  p8 s; M+ m& V/ T. L/ x% |9 e
  4.   // uint16_t temp1,temp2,temp3;  /**(__IO uint16_t*)address; */+ ~( |, ^( H* }$ d6 U
  5.   // temp1=*(__IO uint16_t*)address; 9 v: `% H9 w1 \  E
  6. // u48id.ID1 = (uint8_t)(temp1>>8);. K$ o( }5 |/ N% d2 P- [
  7. // u48id.ID2 = (uint8_t)(temp1&0X00FF);
    " h0 c2 w# R3 h& |
  8.   // temp2=*(__IO uint16_t*)(address+2);
    * ~7 [( Z! f6 s/ o. ?0 e1 C
  9. // u48id.ID3 = (uint8_t)(temp2>>8);
    / t0 i$ u" o! n! @% z; M% }: o% L& ?
  10. // u48id.ID4 = (uint8_t)(temp2&0X00FF);9 `; x8 n- {, p
  11. // temp3=*(__IO uint16_t*)(address+4);) V2 n% R! A1 O+ ]  s, u
  12. // u48id.ID5 = (uint8_t)(temp3>>8);8 m8 K6 I/ p4 a2 R2 _; _
  13. // u48id.ID6 = (uint8_t)(temp3&0X00FF); $ _; W! e; G  j8 \+ p$ O9 w

  14. 3 `- K6 ~; v/ q8 y4 X
  15.   u48id.ID1 =  FLASH_Readbyte(address);2 P5 L/ `& C( l6 K- }  _3 d
  16.   u48id.ID2 =  FLASH_Readbyte(address+1);" V# S7 h# v* w% j/ r. k, V6 z5 v
  17.   u48id.ID3 =  FLASH_Readbyte(address+2);7 F1 P7 Y1 m( T4 \, t- j
  18.   u48id.ID4 =  FLASH_Readbyte(address+3);
    ; M; Z# r/ f* `: R! D
  19.   u48id.ID5 =  FLASH_Readbyte(address+4);+ t1 w/ B  m8 x1 r
  20.   u48id.ID6 =  FLASH_Readbyte(address+5);
    0 Z# |2 n$ G# O4 ~6 E6 [: j# @

  21. * v& e4 k# ]! P- m/ I  e# H% d7 p
  22.   return u48id;
    / f* U2 ?. F8 _0 P, b& f; [
  23. }
复制代码
! K" A4 [+ Q9 }' X5 d

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

! i* R/ ~/ R' q% \+ `2 U

微信图片_20230612172035.png


' w/ P& \: X$ h2.4.2 问题的分析(大小端模式数据格式)

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

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


9 P2 p5 y2 y8 ]- ?# O2 P

微信图片_20230612172032.png


0 ^" Z9 t+ G$ ]4 m& @- O5 f( }

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

. |$ X1 G1 F6 I2 K

微信图片_20230612172030.png

/ `5 s; i0 d0 x) L& h; A

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

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

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

2 m9 a# i3 n+ f! X
2.4.3 STM32L071RBT6 EEPROM读写全字问题

% F9 y  j& ]* A( H* ~: Y! q5 @/ y读问题的出现

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

  1. uint32_t  FLASH_ReadWord(uint32_t address)
    5 B  o+ a) r, _  C" t
  2. {
    6 z& n3 B8 F. U' w2 H% B( b
  3.   return *(__IO uint32_t*)address;
    ( c% f* J8 }# v: t6 Q- }
  4. }
复制代码

3 Z6 W- z! D7 R  }% o% S! n% N& V

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

. \& @# H4 `  g' \

微信图片_20230612172026.png


" W- A, l2 ?7 A/ g3 ?1 T8 \

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


1 f8 V- t- i3 y) ~4 X

问题的解决:

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


) B# \2 c& T. [' S4 [- O

微信图片_20230612172019.png

1 v8 W4 L' l/ x

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

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


# v$ l, y* S/ d

微信图片_20230612172012.png

1 A9 D7 O( f& f7 I

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

  1. u32 FLASH_ReadEnoceanID(uint32_t address)
    : ^1 d: b. P7 ~9 E7 p1 m8 K3 s
  2. {' {9 ~% x9 h4 N0 J9 ?
  3.   u32 keepid;& Q" r5 n* S( D" O9 W4 x: Z, P
  4.   u8 i;
    / v% _" o# \0 K+ D& K, J, A- a
  5.   keepid = FLASH_Readbyte(address);- b* X+ ~0 U1 v- D7 L( d; q& z
  6.   i = FLASH_Readbyte(address + 1);
    * `; l# {* P; p8 J/ F6 }
  7.   keepid = (i<<8)|keepid;6 O6 P6 m+ u3 x
  8.   i = FLASH_Readbyte(address + 2);' v: M; V! Y; T5 P
  9.   keepid = (i<<16)|keepid;8 |7 {  O2 |: Q
  10.   i = FLASH_Readbyte(address + 3);
    - ]) `5 U7 {- P/ `9 q
  11.   keepid = (i<<24)|keepid;
    ; \; N# A" K1 H$ X" A4 W8 ?3 ?
  12.   return keepid;
    0 T6 k' P: Q% a. g5 x: x1 K( E
  13. }
    : _1 j" R% ~/ [/ |( ^2 t
  14. / q: r1 |: Z+ @% `5 k5 @2 {  G. I+ y
  15. CHID_STRUCT Flash_PowerOn_Check()
    6 a: z# V+ y. t/ e- v. s( |
  16. {
    : g- B6 i8 a' {& @! n0 N# {+ q+ |# E# U

  17. 1 [9 K; `3 l, T7 X9 M* ~
  18. CHID_STRUCT PowerOn_ID;3 a4 Y# _% ?0 W/ A" m' [7 h
  19. PowerOn_ID.CH1ID = FLASH_ReadEnoceanID(CH1_ID_ADDR);( o/ G% E2 f1 L0 s: A5 g& e1 U8 D1 e
  20. PowerOn_ID.CH2ID = FLASH_ReadEnoceanID(CH2_ID_ADDR);
    ' h/ `% h$ R6 W
  21. PowerOn_ID.CH3ID = FLASH_ReadEnoceanID(CH3_ID_ADDR);
    1 w4 g6 ]& T4 ]$ V: @% e  o
  22. PowerOn_ID.CH4ID = FLASH_ReadEnoceanID(CH4_ID_ADDR);
    ( t! Z6 R5 f# x* M# O# G( A
  23. PowerOn_ID.CH5ID = FLASH_ReadEnoceanID(CH5_ID_ADDR);
    / Q0 t% L. F' m) [2 _
  24. PowerOn_ID.CH6ID = FLASH_ReadEnoceanID(CH6_ID_ADDR);( h- n( C$ G. \+ O7 u+ C4 a
  25. PowerOn_ID.CH7ID = FLASH_ReadEnoceanID(CH7_ID_ADDR);1 W) P& ]8 G. X! V' \6 l- k0 R
  26. PowerOn_ID.CH8ID = FLASH_ReadEnoceanID(CH8_ID_ADDR);
    $ _7 M/ V2 e9 I# h. J
  27. PowerOn_ID.CH9ID = FLASH_ReadEnoceanID(CH9_ID_ADDR);8 @: n2 w/ S0 \  f4 N
  28. PowerOn_ID.CH10ID = FLASH_ReadEnoceanID(CH10_ID_ADDR);( [. s* l4 M9 ]/ X& h- i4 u
  29. * S% [. U& W' }) Q, M; d
  30. return PowerOn_ID;( m  S6 r$ P  h4 L- [
  31. }
复制代码
$ [" e4 G8 _3 @9 j

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


  R6 a" J- `: q) a, _写问题的出现

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

一直用的写全字函数为:

  1. FLASH_Status  FLASH_WriteWord(uint32_t Address, uint32_t Data)$ [) `* T) @  Y1 P' I2 [
  2. {
    1 W7 c# D4 f( h/ D9 {3 @
  3. + @5 @2 D  u; Z4 o4 S$ I" p
  4. FLASH_Status i = FLASH_COMPLETE;. _' B5 u1 L/ L4 V0 ~
  5. 1 T' \) p9 f5 k7 M4 Q
  6. HAL_FLASHEx_DATAEEPROM_Unlock();  
      v6 f0 c: r) _* _$ i9 k0 L8 F
  7.   while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, Address, Data) != HAL_OK);+ r, F8 b8 }" J+ c6 {+ C: f
  8. HAL_FLASHEx_DATAEEPROM_Lock();3 p4 I" [9 m. `% p; ]

  9. + o7 B; g# s, J2 z% V$ P
  10. return i;
    ( Y0 w* a- L& q% \  z" |' n! `) ^
  11. }
复制代码

5 j' g( _( R8 R

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


6 {8 F8 W# L+ E8 p' K

微信图片_20230612172009.png

) p3 u* b; X9 t4 n  P9 m

但是使用时候发现:


+ b: u- L9 y4 t# m, a8 b

微信图片_20230612172006.png


( H' G$ }* l  h) A6 W

问题的解决:

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


$ D$ x' T1 o  ~

微信图片_20230612172003.png

  x  H" O* g0 O: y# }6 q4 ~

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

  1. FLASH_Status  FLASH_WriteWord(uint32_t Address, uint32_t Data)6 Z/ B# n% l) }$ m, F
  2. {
    7 o2 }# I; X3 H" q& \* x/ `6 V

  3. ) e" n( l+ C  P9 G8 U  Y8 b/ \  `
  4.   FLASH_Status state = FLASH_COMPLETE;
    ) k& |0 S% B( h, q- n4 d; [
  5.   u8 i = 0;
    ) _# z  N9 \7 `0 h
  6.   u8 writedata[6]={0};
    - K2 ?4 h/ o/ ^4 r
  7. $ u7 n- z* V# r8 z8 E
  8.   writedata[0] = (u8)Data;% u+ ?$ z+ O( e+ c
  9.   writedata[1] = (u8)(Data>>8);$ ~: I! D* d$ t
  10.   writedata[2] = (u8)(Data>>16);
    ) b- p$ [% z7 N; c8 n, D
  11.   writedata[3] = (u8)(Data>>24);
    4 R/ c( N3 _& V) i+ U" k* l

  12. 8 Q7 q* D1 |* l+ r
  13. HAL_FLASHEx_DATAEEPROM_Unlock();  
    ; e* ^9 P% M+ V7 G
  14.   for(i=0; i<4; i++){
    ' z) b$ _& i: @1 W4 w; K
  15.     while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, Address + i, writedata[i]) != HAL_OK);$ t/ h' x: Y2 l) B5 t* }2 n5 s$ {
  16.   }
    6 f: W6 z/ D% n5 v4 n
  17. HAL_FLASHEx_DATAEEPROM_Lock();
    0 `. C, I) Q8 |1 p; ^" g
  18. ! A% E6 ^, I" K3 d' n$ u' }1 i9 }
  19. return state;
    8 e8 \$ I3 ]8 C. v4 i
  20. }
复制代码

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

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

2.4.3小结使用的芯片为  STM32L071RBT6


6 n4 N" d  g( }& ~6 q最后问题的解决

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

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

, ~- G. V1 w, E. o$ t: _& ~2 M

微信图片_20230612172000.png

6 f9 O# Z" d* ], D, S0 o. B/ x

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

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


) G2 r6 B, Q) j5 w9 G

微信图片_20230612171954.png


. N; i/ x0 d# x! c

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


* _* _0 o6 o! @5 `" {2 U5 q
收藏 评论0 发布时间:2023-6-12 17:30

举报

0个回答

所属标签

相似分享

官网相关资源

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