本文测试 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的图: # t7 u: F* H5 d. z" K) \
, G+ }- I7 H7 U Q( ?+ `
& ^! M9 B% w8 h, l' Y
然后来看个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 : 1 o" g1 Q, W" I) K+ I
- #define FLASH_SIZE (uint32_t)((*((uint32_t *)FLASHSIZE_BASE)&0xFFFF) * 1024U)
$ y' _# n; ^9 c- I& D - #define FLASH_PAGE_SIZE ((uint32_t)128U) /*!< FLASH Page Size in bytes */
复制代码
* f. s( a* x, `: e. Z4 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意为“字节”,是计算机文件大小的基本计算单位;
- q! w8 s; H6 c2、读写函数的设计HAL库中肯定是有对flash和EEPROM进行操作的函数,我们这里新建一个stml0_flash.c 和stml0_flash.h 函数分别放在对应位置,进行自己的函数设计。库中Flash与EEPROM的函数看样子是分别放在 stm32l0xx_hal_flash.c 和 stm32l0xx_hal_flash_ex.c 中的,我们先使用EEPROM,因为提供EEPROM,就是让用户可以保存一些掉电后的数据的嘛,测试完EEPROM,再去测试下flash,因为怕有时候数据不够放…… 2 E" t+ n, n8 {& O4 s
2.1 读取函数- //读取指定地址的半字(16位数据)
, L9 c' j3 d# R! E9 B6 W - uint16_t FLASH_ReadHalfWord(uint32_t address)
$ p9 R/ |- E+ G: k; y - {$ j( @6 N3 ]- p3 s. u: p
- return *(__IO uint16_t*)address;
) p* c# m$ B# D$ t5 b - }
7 e, q( N) y7 H6 ] i# | - 4 I8 l0 @+ ]1 N2 Q4 r' v
- //读取指定地址的全字(32位数据) Y; |6 ? v* m5 j9 R3 K) h: ~8 ^
- uint32_t FLASH_ReadWord(uint32_t address): c5 U! r. ?3 \+ e! Q: E2 K
- {* ^* s, W: m" A% M- \- y
- return *(__IO uint32_t*)address;# z }. a/ r1 ~
- }
复制代码
; H! L2 p) s& h% R- b
4 _9 B/ q; J( R7 c简单测试一下: - u32 read_data1=0XFFFFFFFF;
7 Y( h6 g, Z { - u32 read_data2=0XFFFFFFFF;$ Y- Q7 d3 ^7 n' O9 W* B2 B( B/ P
- ...' A l0 q& T0 E4 o4 ]5 F0 s2 ]) r( D
- read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);
: E. p' a# z- P X# } - printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);
Y% b5 l( t- _7 N - read_data2 = FLASH_ReadWord(DATA_EEPROM_START_ADDR + EEPROM_PAGE_SIZE);
( Z/ q: y- ^/ o5 Z1 w. t, E1 @( L - printf("the EEPROM sceond page test data is: 0x %x \r\n",read_data2);
复制代码
; _: j' o1 f- |6 V. E4 X" y没有写入数据读取的值应该都是0。 # y8 p' e# [% n8 U5 L
2.2 EEPROM写函数对EEPROM的写函数:stm32l0xx_hal_flash_ex.h中函数如下: - HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Unlock(void);
/ g% s' u% @) a" r - HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Lock(void);! C) h$ m5 o* V1 M" C1 \
- & i& l5 K" T) L* ?. ]; L# m
- HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Erase(uint32_t Address);! N+ ]! F; O$ g+ f; k
- HAL_StatusTypeDef HAL_FLASHEx_DATAEEPROM_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data);
复制代码 8 H. h9 t G1 m; [9 e1 b
通过函数看来,可以直接用,但是这里有一个问题 需要测试一下,擦除是否会擦除整个扇区,有待验证!! 答:EEPROM的擦除可以直接擦除某个地址的字,不会擦除整个片区 EEPROM的操作相对Flash,比较简单,直接使用HAL库中的函数即可完成 - HAL_FLASHEx_DATAEEPROM_Unlock();
1 N) s0 U- k0 H5 A: h, ~0 U - HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, DATA_EEPROM_START_ADDR, write_data1);9 p% x5 e7 D& q+ E9 M# l+ S+ G+ y
- HAL_FLASHEx_DATAEEPROM_Lock();9 O& |; J1 |$ A L: Y7 b, `: l1 d% y
- ...
6 U* x+ j: G; v7 f - if(btn_getState(&K1_BUTTON_150mS) == BTN_EDGE2){
) }5 ]. f6 ?3 I4 t7 m3 r* Q - printf(" K1 150ms button!,EEPROM_Erase test\r\n");0 F& [* d+ Z' G, {2 X
- HAL_FLASHEx_DATAEEPROM_Unlock();
- a& v8 l: n# q - HAL_FLASHEx_DATAEEPROM_Erase(DATA_EEPROM_START_ADDR+4);) c# g% M5 _- X! j
- HAL_FLASHEx_DATAEEPROM_Lock();
6 M/ p% K( N- Z: s - HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
9 {2 j0 I9 L7 N# O, z* ^6 w& R8 b* k# u2 Q - }
+ M' r$ i8 v) f! H' y; ]1 O - ...
: j6 @ G" L# M$ n& L8 N6 G1 @ - if(btn_getState(&K2_BUTTON_150mS) == BTN_EDGE2){# C9 j7 A- d7 U) b( d; z$ {
- printf(" K2 150ms button!EEPROM_read test\r\n");
& Z( g0 r7 K% l1 S - read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);7 \5 |( T m& i
- printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);+ F. s- T% I& u
- }
复制代码 ) W& [7 X9 o- j2 l+ W" |" P& t
按照上面的例子,擦除DATA_EEPROM_START_ADDR+4的地址不会影响DATA_EEPROM_START_ADDR地址的开始写入的数据 写入一样,如果不在意以前的数据,直接写入就可以。 总结来说EEPROM的使用还是很好用而且简单的。而且EEPROM是可以按照字节,半字,全字写入的,测试了一下,是右对齐 右对齐什么意思呢,打个比方就是如果在地址 addr 中,本来写入全字 0x12345678 然后直接在EEPROM 的 addr 这个地址,写入半字 0xFFFF, 再读取全字的话,addr 地址的全字就变成了 0x1234FFFF ,这个具体的为什么在地址写入半字,不会直接从地址开始占用2个字节,是因为地址的类型为 uint32_t ,所以该地址就是 4个字节类型的数。 4 T" i) A+ A1 D- j) 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的位置,所以导致了上面的结果 - HAL_FLASHEx_DATAEEPROM_Unlock();
, _+ i4 i; U C1 b' u - HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, DATA_EEPROM_START_ADDR, 0X88);
* C4 U8 V7 Z Q+ N - HAL_FLASHEx_DATAEEPROM_Lock();
1 Q: _$ l% u! c8 I - read_data1 = FLASH_ReadWord(DATA_EEPROM_START_ADDR);
I' l0 a$ B9 J$ l% E( n3 y3 D - printf("the DATA_EEPROM_START_ADDR is: 0x %x \r\n",read_data1);
复制代码
* x" v1 n' Y5 s6 j最后在 stml0_flash.h 中把函数完善声明一下,使得主函数中的程序更加简洁。 - void MY_DATAEEPROM_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data) 3 t/ ?; n1 B# Q" S5 L3 l, y9 {) P
- {' M) a$ R2 k6 a) a. v1 [. N; J
- HAL_FLASHEx_DATAEEPROM_Unlock();
, U3 Q* p. D, t+ c/ T4 E - HAL_FLASHEx_DATAEEPROM_Program(TypeProgram, Address, Data);, c, b' `5 z% l% O
- HAL_FLASHEx_DATAEEPROM_Lock();
5 h3 a9 k$ D: } - }
复制代码
0 Z* `$ N) J( S. m& u那么L051 的EEPROM的测试就到这里,其实有EEPROM,在项目中的保存数据的功能就问题不大了,但是我们既然开始了,就把L051 Flash的读写也测试一下。 $ m: d# E6 M# ]$ }% ~
2.3 Flash写函数Flash的读取其实和EEPROM一样,主要是写函数,来看一下stm32l0xx_hal_flash.h 中有哪些函数 - /* IO operation functions *****************************************************/: e3 U* Y7 y1 x0 g
- HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data);" i* I! h1 `6 m6 [5 {' J; Q6 E5 B- J, u( G
- HAL_StatusTypeDef HAL_FLASH_Program_IT(uint32_t TypeProgram, uint32_t Address, uint32_t Data);8 s. t: Z0 O2 H3 Z$ e( H
- 4 r; e3 `7 a, J/ ^8 X
- /* FLASH IRQ handler function */6 W- Q/ B- u' D" O9 n* I
- void HAL_FLASH_IRQHandler(void);
5 V- V( D1 ?! c [+ J; S3 D8 b - /* Callbacks in non blocking modes */
$ Q% I. k; X* o( B; O5 T9 S# j4 B) t - void HAL_FLASH_EndOfOperationCallback(uint32_t ReturnValue);' L1 d( T# l+ d
- void HAL_FLASH_OperationErrorCallback(uint32_t ReturnValue);
7 [6 }- L% {$ D. k - 7 {0 m$ _3 p; Q) H9 W2 W
- /**- D, b8 c4 |2 J2 |
- * @}0 W& t0 m+ s$ }; q: F4 g" P
- */) G3 Y8 p* ~. c& ^( u1 t1 Q
) c5 f) k* t5 b# x& O$ E- /** @addtogroup FLASH_Exported_Functions_Group2
2 P: M$ R" C: B7 V D& X - * @{6 @! F% j5 ^) F" f6 o# E
- */
. |2 A; i% X) D - /* Peripheral Control functions ***********************************************/. B; L2 Y! K; l! h, x& \
- HAL_StatusTypeDef HAL_FLASH_Unlock(void);
; W5 S3 M. M( J/ f/ f - HAL_StatusTypeDef HAL_FLASH_Lock(void);7 Q9 b; q0 e& B9 x2 b/ m' R
- HAL_StatusTypeDef HAL_FLASH_OB_Unlock(void);
% u$ o3 j* q! W1 E8 A) q - HAL_StatusTypeDef HAL_FLASH_OB_Lock(void);7 N! s/ l3 q. T
- HAL_StatusTypeDef HAL_FLASH_OB_Launch(void);
复制代码
7 ~1 o+ \+ m: C! D有点忙,Flash的后面再也,看了几个demo,只需要做几个测试就可以; 3 G4 h: r$ i8 N( H5 k. `
2021.8.5 今天有空来接着测试一下L051 Flash的读写,看了下HAL_FLASH_Program函数:
; O+ g# X- E% r* ]7 U- HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint32_t Data)& v' d/ W1 v W& O5 H
- {
) A# {* b: Q; I& [1 g - HAL_StatusTypeDef status = HAL_ERROR;
- K% V5 F- `( n% k; m -
! ?4 ~2 c: H" }# g* y) S7 k - /* Process Locked */
2 q! r5 Q+ p+ c9 ~2 O l - __HAL_LOCK(&pFlash);
$ a) m. G; d% J- x
7 q* w% r0 U/ f. `- /* Check the parameters */
% g. O* P4 b& t9 ]: e - assert_param(IS_FLASH_TYPEPROGRAM(TypeProgram));
% w) o; d! K5 Q. Z - assert_param(IS_FLASH_PROGRAM_ADDRESS(Address));/ v* h* L; C! q/ U
- 8 }: Z2 a4 `4 O: l
- /* Wait for last operation to be completed */) l/ F, q2 ~; q9 G; x3 b
- status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
' H& Q2 \5 Z# | -
# C* P! ?. n& b - if(status == HAL_OK)
. @- }: x1 i9 n/ Z/ h2 U+ m" d - {6 t0 C* u4 f# j- j4 H2 i& Z
- /* Clean the error context */
' K, l/ J9 y6 I7 p - pFlash.ErrorCode = HAL_FLASH_ERROR_NONE;
6 W8 I3 v7 n8 r& J* Q$ g- k0 G1 J - , t- [/ v. S) W1 z3 s
- /*Program word (32-bit) at a specified address.*/
7 T2 ~% @7 i( U, D N k0 e - *(__IO uint32_t *)Address = Data;
7 \) e9 K' N4 [* P - $ \& ^: @! P8 F# [0 |
- /* Wait for last operation to be completed */8 o, L2 ]2 _2 g$ n; y, K, |) ]# K
- status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
7 b+ w# n8 x9 a! |6 y/ F - }1 ]0 d t) k% L; N7 j
- - G5 Y8 `5 b# d& X* c
- /* Process Unlocked */: G! T6 `+ k! k$ {
- __HAL_UNLOCK(&pFlash);
2 Z. Y7 H& g" K( i( }9 O1 \/ q( p
4 S, w; F& s' Q. B- return status;
' c" N: |; |) O# a4 u - }
+ T& |6 q. X! i7 R$ _
复制代码
- ~" s7 c! \" T& \( E$ ]这里也再次说明了,L051的写必须以字的方式写入。 不管了,先测试一下,不擦除直接写入,这里先定义一下写入的地址,前面我们已经知道了L051 flash一共 512页,每页128bytes,所以我们直接拿最后面的几页来测试 - #define ADDR_FLASH_PAGE_505 0X08000000 + 128*504 //
* P4 s q. V! t% g - #define ADDR_FLASH_PAGE_506 0X08000000 + 128*505 //
5 ?1 d+ v, ]5 Q7 z - #define ADDR_FLASH_PAGE_507 0X08000000 + 128*506 //: d2 @: t% Q$ e* i4 w# T
- #define ADDR_FLASH_PAGE_508 0X08000000 + 128*507 //" z$ a/ a, s; b$ V r% f
- #define ADDR_FLASH_PAGE_509 0X08000000 + 128*508 //1 b, [- S0 I- C& f$ o7 Z: x6 F
- #define ADDR_FLASH_PAGE_510 0X08000000 + 128*509 //) F" b+ g) ~0 |& I
- #define ADDR_FLASH_PAGE_511 0X08000000 + 128*510 //
5 y! k' B- o H, u6 L2 `2 | - #define ADDR_FLASH_PAGE_512 0X08000000 + 128*511 //最后一页
复制代码开始先不擦除,直接在最后一页写入一个字读取一下试试,整理一下写入函数: - void MY_DATAFLASH_Program(uint32_t Address, uint32_t Data) 9 a$ G9 A% w2 i( [
- {, |! J' r2 a* ~7 _# x s' S
- HAL_FLASH_Unlock();
8 i( \0 H! V7 {6 Z: B - if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){$ `1 D7 T6 x) n% }( f
- printf("write data 0x%x OK\n", Data); \, O; O1 V# e1 W. p
- }
. g$ d' }, z$ X$ a, c - else{/ r. L& K! _( H( O1 J
- printf("failed!!!\n");. d) g( }3 B3 N
- }1 v( s, s- V3 J' ?3 Q) Y7 b+ U( _
- HAL_FLASH_Lock();
9 {0 Q& e+ w8 R" T0 O4 r+ l L - }
复制代码 * J/ e- J+ I- v+ Y, @4 {# b/ @
测试一下; - MY_DATAFLASH_Program(ADDR_FLASH_PAGE_512,write_data2);
9 y x3 x% `5 f& E - read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_512);; Y! }1 M5 {, t' C9 A: ?
- printf("the ADDR_FLASH_PAGE_512 is: 0x %x \r\n",read_data1);
复制代码 5 e: O; y2 R4 z9 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) 所以这里我们知道了以后,可以优化一下写入函数,我们项目中用到的是可以直接对某个地址的写入,然后也不需要保存,此页其他的数据,所以我们把函数改成如下: - void MY_DATAFLASH_Program(uint32_t Address, uint32_t Data)
1 m- h( }( a6 G/ F( \ - {+ g0 U4 E' m- N! H8 E
- FLASH_EraseInitTypeDef EraseInitStruct;
8 R) \8 T. U4 A9 I7 U - uint32_t checkdata;
+ s0 Y( ]; R3 n9 E - uint32_t PAGEError = 0;
; r! }4 _3 h6 [ - checkdata = FLASH_ReadWord(Address);5 N/ [' M2 y, y* A& Y/ a6 g
- HAL_FLASH_Unlock();
& c, s8 p# |' i k; a9 h' ? - /*如果是0,直接写*/
5 J1 {5 {" R* D( H8 W D - if(checkdata == 0){ % o, J T. @- I# \9 v
- if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){
* B3 |+ W0 c, A, B+ X [ - printf("write data 0x%x OK\n", Data);* B; p7 J: K; v! @
- }8 G/ ^! J* T) C: p
- else{- K% M0 |5 w1 s8 N1 I
- printf("failed!!!\n");
! Y: B- y# g$ H) q! } - }
% y* @ ^' g& }' B$ w1 F - }
' ^$ x* N2 e$ e3 Y2 E0 o+ a - /*否则擦除再写*/+ X$ ]0 r& N5 X( i
- else{
3 @) l6 \2 d8 P, f* O! e# ` - __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);0 n, _% C' { h0 W
- / {6 y( w& q) H, j& Z
- EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; // 刷除方式
& H% {- k( j$ l - EraseInitStruct.PageAddress = Address; // 起始地址5 T2 G$ _& k! E! \
- EraseInitStruct.NbPages = 1;# i' `- B5 Q, B, i( ]9 _& Y" N
- # X, t7 i- y7 a" Q
- if (HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError) != HAL_OK)) H9 C$ [4 [3 M5 r) o5 }' o0 W3 ?
- {
5 \1 w. X) i) f" Q" F - // 如果刷除错误/ V' n9 `" k6 }' J( A
- printf("\r\n FLASH Erase Fail\r\n");" T+ R0 z. ^$ y, o5 N4 I
- printf("Fail Code:%d\r\n",HAL_FLASH_GetError());
4 z, R# z0 \% O& N* \6 { - printf("Fail Page:%d\r\n",PAGEError);
7 x% E$ ?2 n+ o8 C1 _5 C9 [ - }! }8 ^; ^5 Z) i: ]; A
- 2 G9 M/ g5 [2 X( w( f! Q: e
- if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK){
* B/ R" A$ V& a! N" y3 U; I9 v: N - printf("write data 0x%x OK\n", Data);
P H5 Z4 j4 T5 D# u. ]# d- c4 Q - }
5 H8 p' H0 l7 ], ^ - else{
4 P# v _, O' l. i; L H( E! z - printf("failed!!!\n");( X8 l0 G% R1 p+ ^9 [( m
- }/ D: [: f1 I! G: x& `
- % T" n# |% h+ W* j9 z& z. Q) R+ N- ^
- }0 |: O \3 ]. k/ K# _, C' P
- HAL_FLASH_Lock();6 M$ ^( m3 z9 Y* d5 t! |
- }
复制代码 % S1 t5 ~6 T4 P( }& r/ R. K
自己修改了一个函数, 改成这样以后,就能直接在想写入的地方写入数据了,到这里,flash足够我项目中的使用了。但是还有最后一个疑问,就是擦除的一页到底是不是128bytes我来验证一下。 - u32 write_data1 = 0X12345678;
* S# L5 r1 {8 P- H1 ?' S; p - u32 write_data2 = 0X87654321;
% }; j% R8 E, ]7 F, U1 R - u32 write_data3 = 0XFFFFFFFF;
( g. }; j( F6 p% H
, C( x e! V6 q4 m9 {6 c- MY_DATAFLASH_Program(ADDR_FLASH_PAGE_508 + 124,0X508508FF);
! |3 G y7 y* Y- X/ e. w - MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509,write_data3);! j3 T- E1 E: o( @% T
- MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509+4,write_data2);
% i# n: Z; ~! P5 |0 o - MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509+8,write_data1);1 Q: w1 D: Y2 `4 x# p3 Q9 r
- MY_DATAFLASH_Program(ADDR_FLASH_PAGE_510,0X510510FF); $ E; B: B0 W( G K0 \* y: } t/ N
( y' c; B9 v1 {- |7 K+ ^- read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_508 + 124);, `7 P9 }) l8 @6 `& @& {
- printf("the ADDR_FLASH_PAGE_508 last is: 0x %x \r\n",read_data1);
# c' {3 \; l/ N. c1 z - read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509);4 r$ O, ]* D' R' L; S
- printf("the ADDR_FLASH_PAGE_509 1 is: 0x %x \r\n",read_data1);
+ R! i" j1 h3 \* u" { - read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 4);
/ ]- r# ^3 h& M7 @$ p: t" y7 b - printf("the ADDR_FLASH_PAGE_509 2 is: 0x %x \r\n",read_data1);
& I8 `. O4 T$ E9 F* s - read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 8);9 Z" ^) w* ]1 \% _) b
- printf("the ADDR_FLASH_PAGE_509 3 is: 0x %x \r\n",read_data1);) Z3 E# W) @% h2 I# I
- read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_510);4 \9 e5 F' i, |; O* a" |/ p$ E
- printf("the ADDR_FLASH_PAGE_510 first is: 0x %x \r\n",read_data1);/ A' E* d. l/ V9 h) P/ T( Y
- : n( C' u# f: _0 V* ]! F' f* |4 S
- if(btn_getState(&K2_BUTTON_150mS) == BTN_EDGE2){; ?5 W* Q* ]/ }9 M9 ]9 j2 ~: {: s+ A% ^
- printf(" K2 150ms button!EEPROM_read test\r\n");* M- e, i: Z# ?8 G" b/ q
- read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_508 + 124);! O! y" q4 T: k$ j
- printf("the ADDR_FLASH_PAGE_508 last is: 0x %x \r\n",read_data1); d! r" N& e8 w7 U0 Z: ]
- read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509);
# B3 e) s2 n4 P" i& w- C - printf("the ADDR_FLASH_PAGE_509 1 is: 0x %x \r\n",read_data1);
% W" @/ G, Z" Z6 R$ b1 h0 P - read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 4);* X9 u; a3 s( V9 l
- printf("the ADDR_FLASH_PAGE_509 2 is: 0x %x \r\n",read_data1); A+ ?4 ?( {1 K1 O: m& U9 B' G1 u
- read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_509 + 8);) E# R l9 O7 V4 Y' ^
- printf("the ADDR_FLASH_PAGE_509 3 is: 0x %x \r\n",read_data1);# @ e- V+ ~: R
- read_data1 = FLASH_ReadWord(ADDR_FLASH_PAGE_510);
- \6 n9 x1 U% { - printf("the ADDR_FLASH_PAGE_510 first is: 0x %x \r\n",read_data1);
& U0 t) k3 U! x& X - ( u7 G- U* r% d* X
- }
7 d+ {7 O; [1 O% c; C# U% { - if(btn_getState(&K1_BUTTON_150mS) == BTN_EDGE2){
, o0 }% u% Y$ q+ h - // printf(" K1 150ms button!,EEPROM_Erase test\r\n");0 B1 j# x$ D4 j: y; S \
- // MY_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, DATA_EEPROM_START_ADDR, write_data1);8 P5 a; N/ }4 E7 k6 C
- printf(" K1 150ms button!,flash write test\r\n");* C x6 [% ~9 X% i8 l: s- L
- MY_DATAFLASH_Program(ADDR_FLASH_PAGE_509,0X33333333);
) s0 f& U5 P- [. ^" I - HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
. M, G! l: H4 x+ U7 I - }
复制代码 - B7 |# l# L A" g8 M) ^8 I
因为地址位置如果有数据的话会擦除一页再写入,所以我们看了一下508页最后一个地址,和510页的第一个地址数据,对509页的数据进行了操作,结果发现不会改变508 和510的数据,509页的数据会全部清除! ( M( }6 h# _% V$ ?
6 w1 `" X7 l1 S0 t% ]
" l2 R- Q. ]# [6 [所以得出的结论显而易见,flash擦除按照一页一页擦除, 在L051上面一页为128bytes,而且擦除后数据全部为 0; / }' H) T+ e; A# d( X
2.4 读写EEPROM的后续问题最近有一个项目,因为缺货 STM32L051R系列缺货,买了 STM32L071RBT6 替代: " T% n, M' ^+ Y6 Z; a3 R; ]% |
% n( W5 p/ P6 z6 X看了一下地址,其实我们上面所有的测试代码基本都是可以直接用的,代码直接用STM32L051的代码也是可以的,实际项目中,还是使用EEPROM比较方便,所以在使用EEPROM的时候,发现了一个问题(并不是换了芯片的问题),还是数据读取和写入的问题。
9 m5 o+ _5 [& j7 |* G4 i# T2.4.1 问题的出现和解决下面程序的硬件平台是 STM32L071RBT6,首先在程序中,有一个写ID的函数: + l& F. c6 _) B9 o1 r# B. C. G
3 {- N" i/ E& D. d+ C% r写入IO是通过字节的方式 byte 写入的,写了6个字节(蓝牙设备的ID)。 然后最初读取的函数用的是: 1 m8 m! r5 Y6 t' k" t( _
& y7 W. ?; d9 U `
使用这个读ID的函数,问题就出来了。上图代码中,我读取ID使用的是半字读取,处理方式是把读到的半字前面8位给ID1, 后面8位给ID2, 但是测试中发现 数据读出来与想要的相反,什么意思呢,看下面的测试说明: 上电打印出EEPROM中读取的一个地址的,每个ID(每个ID是uint8_t类型)的数据,在代码开始定义了测试数据: - BlueID_STRUCT test;
" }9 S A; _4 S# V - ...
8 b1 \$ ?/ h9 i - /*
! K+ d" ~3 L- P$ g2 v9 z; y' J5 Y! [ - CHBlueID_STRUCT Flash_PowerOn_BlueCheck()/ c d) z2 E O3 S. i8 P
- { h) R: m$ g, r! w4 e- {( q
$ s7 b; c: N7 L6 D+ S+ Z- CHBlueID_STRUCT PowerOn_ID;
3 R8 ?6 s& _! O, L - PowerOn_ID.CH1ID = FLASH_blueIDRead(CH1_ID_ADDR);+ X* B6 v& e5 l2 `0 c
- PowerOn_ID.CH2ID = FLASH_blueIDRead(CH2_ID_ADDR);
* Q) B7 z' J2 J5 O, U5 ^ - PowerOn_ID.CH3ID = FLASH_blueIDRead(CH3_ID_ADDR);
9 V; X9 T1 v6 @ \) G( t# t k - PowerOn_ID.CH4ID = FLASH_blueIDRead(CH4_ID_ADDR);3 }3 w( o$ D4 r C+ V0 ^2 I
- PowerOn_ID.CH5ID = FLASH_blueIDRead(CH5_ID_ADDR);9 r; r+ Q: t+ [! R) v4 Y
- PowerOn_ID.CH6ID = FLASH_blueIDRead(CH6_ID_ADDR);
( }) U# z% G$ b t* |/ E - PowerOn_ID.CH7ID = FLASH_blueIDRead(CH7_ID_ADDR);
$ V' T$ P- h2 h: a" x$ r - PowerOn_ID.CH8ID = FLASH_blueIDRead(CH8_ID_ADDR);
E6 C3 s/ u+ E- M7 ] - PowerOn_ID.CH9ID = FLASH_blueIDRead(CH9_ID_ADDR);1 x1 V6 D$ v$ V) z) R
- PowerOn_ID.CH10ID = FLASH_blueIDRead(CH10_ID_ADDR);1 ^& F- j+ n4 h. I* m" k6 o
) ]/ ?4 r: t8 |3 o: u, {- return PowerOn_ID;# r( {- y7 W- c7 J1 [/ D. T
- }* w0 z) h+ O! b) L
- */
# n( S. w9 o( h. `# W, P, _ - BlueChipID = Flash_PowerOn_BlueCheck(); //上电先把ID读出来做比较
3 V" G4 ]7 n/ }" i: M" G - //打印一个出来测试,看结果
" p( m% Z1 U7 f7 j - 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);
, |" F" u. z% P3 s, p
8 T' Z7 G. Y ~1 X' q) }: D% _; D- test.ID1= 0XFF; //结构体每个元素是 uint8_t 类型7 }" N8 I/ P/ B+ @7 d5 P
- test.ID2= 0XEE;4 J- V) P2 v5 i$ O4 V4 w
- test.ID3= 0XDD;) u: {" [! K1 x, J9 |- e
- test.ID4= 0XCC;
5 y3 }7 X' O) k _: w- u - test.ID5= 0XBB;
$ D" j+ H4 n( { m1 O - test.ID6= 0XAA;
3 ]: _" v5 i7 }) N) r- q- ^
2 r% |) E, i' ?6 ?- while (1){...}
复制代码
- h" ^0 z' s2 ?' S2 i在程序中通过操作,写入测试数据,调用上面提到过的写ID的函数,写ID的函数是每个字节每个字节写的: n# p5 ^9 x' Q+ E, M: i% K3 t1 X
+ {% w4 ~; T8 ?* y1 b4 u( _3 R
按我们希望的结果来说,读出来按照顺序打印,应该是:0xff,0xee,0xdd,0xcc,0xbb,0xaa 。测试实际上是: 2 p2 Q( T- ~7 I$ ]) D v
. @% A5 V% O, T4 d+ N" p
为了确实是读的问题还是写的问题,添加了读字节的函数: 
2 b8 ]8 G+ Z8 X9 w- w [! C" C
( W8 D& n* G3 p! U9 r/ n
8 j- V( P6 z, ^( y0 d3 r3 ?0 x打印的结果: 1 Y2 d. f! J2 {0 ^$ Q) K
: c( ?$ W% B( D- v5 D说明确实是ID的读取函数出的问题,是因为使用的半字读取,问题处理不麻烦,我们把读取函数修改一下: - BlueID_STRUCT FLASH_blueIDRead(uint32_t address)
8 m# T* D6 U8 B$ q q! ] - {
* @8 W; l+ `9 R/ \. v; h" Z - BlueID_STRUCT u48id;
5 X, _, N* h" [. x O - // uint16_t temp1,temp2,temp3; /**(__IO uint16_t*)address; */' h, M7 f4 R6 S0 u, H6 F5 P* A5 ~6 [
- // temp1=*(__IO uint16_t*)address; 6 v( J7 Y! I' p G x* w4 N& n7 k# z
- // u48id.ID1 = (uint8_t)(temp1>>8);
9 K' x. [2 ?: G0 h$ L - // u48id.ID2 = (uint8_t)(temp1&0X00FF); H5 Z' n, v1 h z% D# ~$ f
- // temp2=*(__IO uint16_t*)(address+2);
8 _( T! [. S, k8 C) k" K m8 |8 O: j8 P! K - // u48id.ID3 = (uint8_t)(temp2>>8);0 \+ L8 H" t% G, w
- // u48id.ID4 = (uint8_t)(temp2&0X00FF);
~0 I! O; f) N- q8 q3 n1 ] - // temp3=*(__IO uint16_t*)(address+4);/ x ?1 x8 C7 K7 I5 W; Y
- // u48id.ID5 = (uint8_t)(temp3>>8);+ q7 u$ Z0 S& f/ I; b' s
- // u48id.ID6 = (uint8_t)(temp3&0X00FF); * r4 N% }" p( B% C
T. T1 ^7 m: g, K6 x, {8 G0 z% H- u48id.ID1 = FLASH_Readbyte(address);6 |4 X8 W6 e- R' D2 Y0 T- i
- u48id.ID2 = FLASH_Readbyte(address+1);
) j' p f+ M, e9 R - u48id.ID3 = FLASH_Readbyte(address+2);
# z G; `, W( ?; J' P* A( j) [' C - u48id.ID4 = FLASH_Readbyte(address+3);
$ g) g; v$ K2 d8 d9 G& l - u48id.ID5 = FLASH_Readbyte(address+4);; l) z4 B& N8 N4 B
- u48id.ID6 = FLASH_Readbyte(address+5);
+ y& U( O/ z4 t - , Z, G& \. Z \& r& \& J" ^" o+ E
- return u48id;2 k- r/ M2 y" @) ~! ?) B( U
- }
复制代码
7 t. S6 A% f/ a9 q测试结果才正常了,如下图:
- f& z4 c% E2 p
, X* C! |* A" @3 z2 {& i& \
2.4.2 问题的分析(大小端模式数据格式)出现上面的问题,其实是和我们经常说的大端模式和小端模式有关的。 STM32使用的是小端模式,简单介绍一下大端模式小端模式数据存放的方式,如下图: : e) k3 s+ s! B
# G3 }7 Q* N& q! [4 ^% R知道上面的知识,我们在开始的读取函数中是直接读取的半字(__IO uint16_t*)address; ,但是我们写入的时候是一个字节一个字节写入,上面的例子所以我们内存中的数据实际上如下图所示:
- l- X, ~1 T# j& N$ X
7 c1 ~8 r( B* s5 l
使用(__IO uint16_t*)address; 去读取,读取出来的数值一个uint16_t类型的数值: 假设是 a,a=*(__IO uint16_t*)addr; 会有 a = 0xEEFF; 所以高8位变成了 0xEE。OK!解释清楚!问题解决! 至此,我们基本上把STM32L051 的EEPROM 和Flash 功能都测试过了,把工程中需要用到的功能做了测试,也学到了一些新的知识,还是实践出结论啊,当初没有自己测试之前看了网上的有关类似的介绍,还是很多误解,这下全部清晰了。 5 }1 I# U3 q* _9 y4 H
2.4.3 STM32L071RBT6 EEPROM读写全字问题3 e" \7 U* z/ ~1 O
读问题的出现前面其实测试过,读取全字是可以的,直接使用return *(__IO uint32_t*)address;:便可以读到该地址的全字: - uint32_t FLASH_ReadWord(uint32_t address)7 Y6 V( u, j+ K; N8 Q6 j6 R( Z% k% M# F
- {7 i+ z! Y6 X: h3 _
- return *(__IO uint32_t*)address;3 p: L& f% \ }% c( T& ]' V
- }
复制代码 1 ]) c, @4 D# j, }
所以在后面使用过程中,有这么一个函数:
: U. D( v8 Y! }$ `
0 n. A! Q0 Y2 |# X# f9 m
一开始还真的不知道哪里出了问题?折腾了好一阵才发现调用函数读取ID时就会卡死。 2 q. R' F8 R4 b3 t3 R1 [, r d
问题的解决: 因为还有另外一个蓝牙版本的产品,如果是蓝牙设备的ID,因为蓝牙设备的ID是6位的,所以当时读取蓝牙的ID的时候使用的是(蓝牙版本是没问题的):
! E" C N% ?! N' _
* A9 x+ t8 r) Z# v% Y% D6 A其实折腾了好一会儿,后来想着蓝牙是读一个字节,要不要试着把 全字 分为 4个字节,单独读取试一试? 于是学蓝牙把程序分为4个字节读取:
! C, H8 p6 l1 w
/ S" Y' C/ Z1 c2 r5 s
代码也放一下,方便复制: - u32 FLASH_ReadEnoceanID(uint32_t address): P0 T! Q2 k* `7 q" P
- {0 z. F* r! l; e
- u32 keepid;
; O; y& i- `! c( L: }" J - u8 i;# C8 T4 V/ s8 }, x" Q
- keepid = FLASH_Readbyte(address);
* g2 c6 V" C( p3 y - i = FLASH_Readbyte(address + 1);
z, f8 ]) h& Z. N7 B P0 ^+ P - keepid = (i<<8)|keepid;
/ j% W0 P; p7 d& [. z. S* a - i = FLASH_Readbyte(address + 2);
/ @& l; ]) j+ L# b2 F - keepid = (i<<16)|keepid;/ j/ X$ M0 p+ [: D: |
- i = FLASH_Readbyte(address + 3);
7 S+ i2 B6 v n! z6 l/ h - keepid = (i<<24)|keepid;
! U+ Y ^0 _% h: [ - return keepid;# ?; X! j. e7 y
- }
1 R; m- l$ R+ E, Z* C8 k6 R
9 R" e$ t: d' p0 H+ n- CHID_STRUCT Flash_PowerOn_Check()
, X2 z! N: C9 I O- S( ? - {
1 `4 N8 f! d9 M% I1 J+ s% u5 o - , \; W5 F3 P& D! Z3 M5 T. ]9 e+ P
- CHID_STRUCT PowerOn_ID;4 R; i! G* k% q+ x- {& A
- PowerOn_ID.CH1ID = FLASH_ReadEnoceanID(CH1_ID_ADDR);
1 c3 x- _8 Z( Z+ y - PowerOn_ID.CH2ID = FLASH_ReadEnoceanID(CH2_ID_ADDR);" h9 W' b; ^- q1 u) L. E m
- PowerOn_ID.CH3ID = FLASH_ReadEnoceanID(CH3_ID_ADDR);
5 K1 R4 v9 H: f% [) u - PowerOn_ID.CH4ID = FLASH_ReadEnoceanID(CH4_ID_ADDR);) Q$ F$ U5 _1 R* r6 ^! A2 g
- PowerOn_ID.CH5ID = FLASH_ReadEnoceanID(CH5_ID_ADDR);: V0 K7 Z! W6 z
- PowerOn_ID.CH6ID = FLASH_ReadEnoceanID(CH6_ID_ADDR);
: X- P* U9 j- }9 Y# F9 O* w - PowerOn_ID.CH7ID = FLASH_ReadEnoceanID(CH7_ID_ADDR);( O1 h7 Y9 C0 B- D- T9 _
- PowerOn_ID.CH8ID = FLASH_ReadEnoceanID(CH8_ID_ADDR);) v2 m! N$ A( R3 |2 T
- PowerOn_ID.CH9ID = FLASH_ReadEnoceanID(CH9_ID_ADDR);" j$ [) k! U7 _0 g: h
- PowerOn_ID.CH10ID = FLASH_ReadEnoceanID(CH10_ID_ADDR);
$ w. i& E+ W0 ~+ A: I' c - & S6 {0 G! |/ O x
- return PowerOn_ID;
0 Y, L6 ]; ~3 ?5 g) h& d( T' t! R! h - }
复制代码
5 f. E7 n4 j) s# A测试一下,发现就好了,至于原因,目前还不知道为什么……(最后问题解决有说明,内存字节对齐问题)
8 Y3 @5 E/ G7 v& a+ B L. Y6 B写问题的出现本来以为解决了上面问题OK了,可是后面测试的时候发现写的时候也有问题: 一直用的写全字函数为: - FLASH_Status FLASH_WriteWord(uint32_t Address, uint32_t Data)
; }$ o- h: i2 E! \2 D, ] - {" n% O; D) L0 c5 L; B
# c) Z# f' X3 V# H- FLASH_Status i = FLASH_COMPLETE;: b4 C* L5 b- K! ?3 q, I$ S! M
- 1 r; D& m- V! Q: n% X
- HAL_FLASHEx_DATAEEPROM_Unlock();
: ^' p8 ^% H* [# L! E5 e. _- q$ G - while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, Address, Data) != HAL_OK); p* |& m. O% V. H! J# c
- HAL_FLASHEx_DATAEEPROM_Lock();
3 `1 J y; D8 E- m - ; ]% p# w! Q* P# Q5 L
- return i;
& }1 b9 B* E5 C+ o5 Q3 _' a, w9 i - }
复制代码
; e( [0 W" S' I. R, ^在程序中会调用此函数进行 ID 保存:
/ f, D1 R7 |0 I, p. |
' n' i! N! R' M: ^5 |. a6 N但是使用时候发现:
* t7 l T7 v& F! g( v: g6 t
/ B& ? B! e6 I4 I0 x$ X8 x" }
问题的解决: 其实这个问题也莫名其妙,真是说不出来为什么,估计是得详细的查看数据手册,但是还是 因为 在蓝牙的版本上面没有此类问题:
; n- [8 @! l+ f$ `7 `8 z
* B/ f8 o3 J. |4 \; y+ ]
所以这里还是尝试改成 以 字节 方式写入: - FLASH_Status FLASH_WriteWord(uint32_t Address, uint32_t Data); f6 q [1 O3 P; b
- {
( M) b! O0 k% l* w - 2 ]8 i% q. S) S" U+ W5 C) {. Z. A
- FLASH_Status state = FLASH_COMPLETE;
) h" U) N4 ?4 a2 d( Q1 b I6 b - u8 i = 0;
. }+ |% g/ r& o7 }1 ? - u8 writedata[6]={0};7 Q( `% p6 w; T. c
1 [5 U" F2 d& X4 ]; h- writedata[0] = (u8)Data;
- ]9 `% g) p/ `. ^- i& D - writedata[1] = (u8)(Data>>8);
4 S5 C, d" K, }6 } - writedata[2] = (u8)(Data>>16);
3 J8 S# t7 H# M5 }3 g( i - writedata[3] = (u8)(Data>>24);) B/ J' T v4 O* R* g! A
. [, [1 o) m+ n$ Q6 M- HAL_FLASHEx_DATAEEPROM_Unlock(); % Y4 t- T( J/ U# \5 u
- for(i=0; i<4; i++){: i0 r2 ^# ~ e8 A& o
- while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, Address + i, writedata[i]) != HAL_OK);$ y9 T s* c, N8 n
- }
4 V) u8 d" P$ U4 r* O - HAL_FLASHEx_DATAEEPROM_Lock();
9 [! c, B& I. i w K6 A
) y* r/ o, m7 C# X- return state;, m% T4 t6 @& F5 u
- }
复制代码使用此种方式写入,就不会出现问题! 其实也可以尝试修改地址,使得成为 4 的倍数,可能也不会出问题,这里就不测试了(最后问题解决有还是测试了,内存字节对齐问题) 2.4.3小结使用的芯片为 STM32L071RBT6 / X7 R! e1 }* N+ a# N
最后问题的解决先直接说结论,就是EEPROM地址定义的问题,应该是4字节对齐(4的整数倍),读取全字的操作才能正常! 上面 STM32L071RBT6 EEPROM 读写全字问题的关键在于,存储地址的定义上,如上面一张图所示:(我在EEPROM区域定义了10个地址,用来存放无线设备的ID数据,如果是蓝牙芯片,那么ID为6个字节,如果是Enocean芯片,那么ID为4个字节,为了保持代码的统一,我在使用保存4个字节的ID数据的地址定义时候沿用的是蓝牙的 EEPROM区域定义)
/ k, V! y6 `4 J, P' j ^( ] k( W( @. o
t% w+ @" g) m+ H. N* A3 T- }0 T5 p
那么正如我图中猜想的一样,蓝牙的 ID 6个字节,我是都是通过一个字节一个字节操作,组合起来进行的,所以一切正常。但是对于 4个字节的 ID ,期初是用的 全字的方式,就出问题了,换成一个字节一个字节的操作,看上去是解决问题了。 但是实际上多了一些隐藏问题,暂时也说不清楚,在产品使用的时候,读写ID还是会有莫名其妙的问题,最终还是对当初的这个猜想,地址是不是也需要4字节对齐?进行了修改测试,于是乎,对于 4 个字节 ID的处理,地址改成:
% k5 y7 A k$ l/ I8 [+ y* q
) G) O+ Q& V' x4 }+ {% a把地址修改成 4 的倍数以后,上面的读取全字的两个函数便可以正常使用,而不会出上面莫名其妙的问题。 8 O# a# @6 Q3 W3 K
|