本帖最后由 giveup 于 2016-5-31 08:03 编辑
+ K4 Y7 A' a5 A. [2 w! H& k
0 y! z0 r' i. f2 x$ M采用STM32CubeMX 配置一块STM32F407VET6的核心板,通过I2C1读写AT24C08 EEPROM。可以选择BSP方式,或者到HAL库中挑选BSP驱动,效果一样。因为我的工程已经初始化过了,所以采用后种方式。
3 y9 t8 x# n, t$ {采用默认值配置I2C1,选中NVIC Settings中的 I2C1 event interrupt和 I2C1 error interrupt。生成工程模板。将AT24C98N EEPROM连接到PB6,PB7。器件A0,A1,A2引脚悬空,地址为0。
7 Y+ q0 b$ _: _5 j4 {& a在安装库中复制eeprom的BSP驱动,应该都一样。如复制下列4个文件到工程目录下:
- |% ~" w$ B; |+ `8 D2 [* @STM32Cube/Repository/STM32Cube_FW_F4_V1.12.0/Drivers/BSP/STM324xG_EVAL/stm324xg_eval_eeprom.c% A$ y3 N% q& C I" r: G
STM32Cube/Repository/STM32Cube_FW_F4_V1.12.0/Drivers/BSP/STM324xG_EVAL/stm324xg_eval_eeprom.h7 N2 \7 ?- q, {! P8 j- ?
STM32Cube/Repository/STM32Cube_FW_F4_V1.12.0/Drivers/BSP/STM324xG_EVAL/stm324xg_eval.c
! d. F6 g0 F+ @3 K* \STM32Cube/Repository/STM32Cube_FW_F4_V1.12.0/Drivers/BSP/STM324xG_EVAL/stm324xg_eval.h( a/ U9 V( u" ~4 G: @' ?* y
4 h. p+ P4 J- D& b8 [stm324xg_eval.c和stm324xg_eval.h中保留和EEPROM相关内容即可。. z( P+ y* k4 ?3 h/ S: S9 i; t8 [" |
" u: S- w6 E6 x% i; b; V) U2 s重点的问题是,这个驱动支持的是M24C64 EEPROM,和AT24C08 EEPROM有一定差异,需要修改部分内容才能使用。主要体现在读写页面尺寸、容量和器件地址表示不一致。( {. _9 F8 J* ]" q ~! }& f' ]
根据手册,AT24C04~AT24C32的内存地址是分为两部分,除低八位外,高3位占用了器件的地址。如AT24C08,内存大小为1024字节,地址为10位,高2位占用了器件地址A1,A0。M24C64 EEPROM的内存地址为16位,和AT24C32以上的EEPROM保持一致。9 ^: ^4 k0 }: Q& s" a8 k
因此,主要修改点为:
& Z- n3 Z1 n6 c1。修改页面尺寸与地址大小 + B2 y6 U7 o8 R7 p
stm324xg_eval_eeprom.h中,修改定义:- k% a, s8 @/ m# A
#define EEPROM_PAGESIZE 16
7 K5 ]; i& d$ H' {6 e4 Z# c$ ]' Q& K#define EEPROM_MAX_SIZE 0x400 /* 8Kbit */( j- m4 C2 U) m3 @
添加定义. w/ l; \# j- F O+ C) P
#define EEPROM_MEMADD_SIZE I2C_MEMADD_SIZE_8BIT9 ~9 S+ E/ M: l# D( q3 H
#define MEMADD_MSB_MASK 0x3
$ n1 S. U5 u8 b0 V- U- a2。修改读写函数9 N1 Q' L; g( v! ~
2.1 读操作部分修改逻辑:6 e+ U2 y$ c2 J& F
该部分的逻辑是 BSP_EEPROM_ReadBuffer 经过 EEPROM_IO_ReadData 调用I2Cx_ReadMultiple完成实际读写。
) \: ?# A3 y) k" N因为读函数采用连续读方式,只要设定了正确的内存起始地址,就能正常完成读操作。因此,修改只涉及到BSP_EEPROM_ReadBuffer和EEPROM_IO_ReadData两个函数。具体修改如下,红色部分是增加和修改的:
6 r" A, x. h; }$ ]# W- uint32_t BSP_EEPROM_ReadBuffer(uint8_t* pBuffer, uint16_t ReadAddr, uint16_t* NumByteToRead) {
% V% v7 Z! a7 e. r/ b6 R, l - uint32_t buffersize = *NumByteToRead;
/ X* Y8 J% J/ B
& ]% `" n3 ]% `- /* Set the pointer to the Number of data to be read. This pointer will be used) y. w# S/ |) q0 u; M3 S
- by the DMA Transfer Completer interrupt Handler in order to reset the
+ K& o* M; `# w; M - variable to 0. User should check on this variable in order to know if the
% r( d4 T7 ^1 [ - DMA transfer has been complete or not. */
& S. H: j% d- ]( F: ?" r* x5 }9 T; e% K - EEPROMDataRead = *NumByteToRead;
. v+ a0 y, ~9 s' M: w - <font color="Red">uint16_t A_EEPROMAddress = EEPROMAddress;
6 t. {( |" j- { - uint8_t A2A1A0 = (uint8_t) ((ReadAddr >> 8) & MEMADD_MSB_MASK);
+ k U+ X0 ]7 t- c' M9 g - A_EEPROMAddress = A_EEPROMAddress | (A2A1A0<<1);</font>
! r+ U8 [% R/ Z( F d. O& T - % l4 Z! U* M$ L; Z$ S2 i- |6 J
- if (EEPROM_IO_ReadData(<font color="Red">A_EEPROMAddress</font>, ReadAddr, pBuffer, buffersize) != HAL_OK) {) ~( G1 y/ i5 s' \. ^ K6 C
- BSP_EEPROM_TIMEOUT_UserCallback();. o# r6 A, o4 ?1 E
- return EEPROM_FAIL;1 S% ?+ H5 \. r; D* J4 i
- }- F* A, g- y5 U( A& ^) f9 w7 D) k) k) J
- /* If all operations OK, return EEPROM_OK (0) */ i# S' c$ P" N. C& l
- return EEPROM_OK;; w& `8 n7 Y- Y0 m! h
- }/ \% I/ x4 o8 I+ @& }: `8 F8 |$ p
复制代码 , |/ W" \9 G& E4 n# N$ r) o# w
将I2C_MEMADD_SIZE_16BIT替换为上面新增加的EEPROM_MEMADD_SIZE定义:2 x& `2 m% A7 K+ h
- HAL_StatusTypeDef EEPROM_IO_ReadData(uint16_t DevAddress, uint16_t MemAddress, uint8_t* pBuffer, uint32_t BufferSize)
( s/ y& N9 \) q2 _ - {
6 @% L" g: m# k4 S6 x5 \2 H, q7 y2 I9 u! Q - return4 q% `1 Y. s) a- e3 c7 p/ q s; E
- (I2Cx_ReadMultiple(DevAddress, MemAddress, <font color="Red">EEPROM_MEMADD_SIZE</font>, pBuffer, BufferSize));% V9 V4 T8 b9 }6 L' M9 i4 }
- }
复制代码 $ p' p, k: z/ u' r3 I. o
. V5 h1 Z( n8 ?4 I2.2写操作部分函数修改逻辑:/ q) j# p2 Y* v, t$ E. \
写操作调用关系是BSP_EEPROM_WriteBuffer负责将写地址分页,并完成页面对齐后,调用BSP_EEPROM_WritePage经过EEPROM_IO_WriteData完成实际写操作。修改部分只涉及到后边两个函数,具体见下面红色部分:
0 @, X! ~. ^7 p/ n5 h! t- uint32_t BSP_EEPROM_WritePage(uint8_t* pBuffer, uint16_t WriteAddr, uint8_t* NumByteToWrite) {
9 B. I3 A: r: N; Q% R" a4 A* D - uint32_t buffersize = *NumByteToWrite;
# s) S% k! |2 T. S: b4 o7 L, y - uint32_t status = EEPROM_OK;: l0 L0 W- O0 o/ a/ a* ]
- - |5 x! I W. L7 b3 f' m3 c
- /* Set the pointer to the Number of data to be written. This pointer will be used
8 L, W. G4 B& }' q4 | - by the DMA Transfer Completer interrupt Handler in order to reset the) m: A8 R2 k! J5 a% j9 W# G
- variable to 0. User should check on this variable in order to know if the" R6 R$ D. R$ n, j2 D4 ]
- DMA transfer has been complete or not. */
' _* G o# A4 B7 d - EEPROMDataWrite = *NumByteToWrite;
- h) z: _' f( H) m) j; g7 [# I% { - # @ w5 R7 Q; W( G! F5 {; h
- <font color="Red"> uint16_t A_EEPROMAddress = EEPROMAddress;/ j& H7 c4 K& C
- uint8_t A2A1A0 = (uint8_t) ((WriteAddr >> 8) & MEMADD_MSB_MASK);" { a6 v- d7 }' ?
- A_EEPROMAddress = A_EEPROMAddress | (A2A1A0<<1);</font> e$ M. P- A* K' k# f- V/ L
% V& r) H( j. m {- if (EEPROM_IO_WriteData(<font color="Red">A_EEPROMAddress</font>, WriteAddr, pBuffer, buffersize) != HAL_OK) {
( E* k- @4 Q2 t9 |6 X9 [) n6 q" k - BSP_EEPROM_TIMEOUT_UserCallback();
3 V; D, R" }7 K7 F* c - status = EEPROM_FAIL;3 K' m$ j# w3 @
- }) T) B8 L+ r! @+ W) D
- 1 m$ I0 A q' i8 ]# ~4 y M
- if (BSP_EEPROM_WaitEepromStandbyState() != EEPROM_OK) {
2 s3 x& q) c( F7 e - return EEPROM_FAIL;; [6 U' b2 Q' l9 a6 e
- }3 T [/ Q6 w; o( l9 H7 Z
- /* If all operations OK, return EEPROM_OK (0) */8 q, W" ~5 l2 m
- return status;8 }% s( p" l* j3 h b1 G2 d
- }" Y3 o; V1 j5 f7 Z' S& Y
复制代码
* M5 w4 y9 A) e+ |5 [0 Y4 C具体写操作修改和读操作差不多:
: T' k: C6 s$ B. x- HAL_StatusTypeDef EEPROM_IO_WriteData(uint16_t DevAddress, uint16_t MemAddress, uint8_t* pBuffer, uint32_t BufferSize)
3 h" e) N ^; P! P0 @) O - {
7 G3 n" g m& V0 E: | - return
* ?9 w" s6 R/ T; C% _$ a% x1 c - (I2Cx_WriteMultiple(DevAddress, MemAddress,<font color="Red"> EEPROM_MEMADD_SIZE</font>, pBuffer, BufferSize));9 e6 `! G5 K$ M5 p! c, R
- }
复制代码 - E3 h0 R) c' i9 m. d7 }! O) g
' p- r8 D6 ?" A9 h; D6 B9 y3 G) J2 g+ w
3. 初始化及其他操作修改。STM32CubeMX已经正确生成了初始化代码,因此除了下面的两个,大部分BSP的初始化代码都可以删除。
6 N$ u% z+ G' y3 \ K' G- void EEPROM_IO_Init(void)8 G# A9 p$ [: E6 P
- {
/ c2 Q X2 N! B9 W - I2Cx_Init();6 K0 G2 r7 K* Z( z/ N0 J
- }
复制代码 修改为MX的初始化代码- n4 K- [# J, f6 W* z
- void EEPROM_IO_Init(void)
) n7 q2 Y; n4 o1 B0 e& j - {) P* y- A/ c2 F( C5 y j
- MX_I2C1_Init();
- I" c. ~. r# `9 {4 g4 r& r2 c - }
; J2 R0 q( L( m
复制代码
7 [% u( B' g) |修改函数,取消一些不必要的调用:7 r# u# j+ q7 ]- c0 {- V
- uint32_t BSP_EEPROM_Init(void) {
! X: A. r( }5 A) N ? - /* I2C Initialization */) {1 d: W# Z% H4 m* d6 M5 j
- <font color="Red">// EEPROM_IO_Init();</font>
* r# f- Z# x+ p - /* Select the EEPROM address for A01 and check if OK */" R) Q& A' L- ^% I h3 g
- EEPROMAddress = EEPROM_I2C_ADDRESS_A01;9 t$ X: m. x( {" i- g' v6 C/ v |
- if (EEPROM_IO_IsDeviceReady(EEPROMAddress, EEPROM_MAX_TRIALS) != HAL_OK) {) `' \+ Q9 P- N$ `, W+ z& h
- // /* Select the EEPROM address for A02 and check if OK */1 j6 c4 V* ~1 |1 |* V* V- Z; e
- // EEPROMAddress = EEPROM_I2C_ADDRESS_A02;
) c. _) x$ `2 K" w9 C - // if(EEPROM_IO_IsDeviceReady(EEPROMAddress, EEPROM_MAX_TRIALS) != HAL_OK)0 D; l, z2 b% n. S) |6 g3 p9 E
- // {
( J6 e8 s" O0 r - return EEPROM_FAIL;
2 t: N* x8 n+ {) U+ E* d - // }7 Q# e/ N0 }$ I7 \, _) y
- } \0 S+ M2 V5 C. q) N) H
- return EEPROM_OK;4 n9 j! ~. }1 f. i! m
- }# `' ]2 R4 P/ p4 j' C
复制代码 # C* N8 O' u1 e" K; P" }
另外,需要将stm324xg_eval.c中的 heval_I2c 变量实例替换为hi2c1。可以用查找替换或者宏定义。
" X- J' ]5 Q( Z4. 使用例子
& \5 O) h# w1 U/ s9 G' u4.1 初始化( g5 U. h k' |# ]7 w; l4 \
在main.c的
, |4 F! k# l8 a, I; [2 R! g0 v r0 W- /* USER CODE BEGIN 2 */1 V# g# M0 s* k/ y; r3 Y: [
- BSP_EEPROM_Init();
5 E; e* r$ h M8 B6 X: ?+ Y4 D1 Y - /* USER CODE END 2 */
复制代码 或者其他适当地方添加初始化设置,完成EEPROM地址的设定。
Z( ]5 w- z1 h1 ]- @4.2使用可以参考相应代码。如:# t& H1 X q! A! n) I9 M0 J5 S6 c( p
- #define EEPROM_CFG_ADDRESS 0x120 //和页面对齐, w( V( A# ?( W- J$ y5 i
- __IO uint8_t EEPROMConnected = 1;. l8 x$ w9 h) y; s+ L
- typedef struct {' ^: ~, J' R _
- uint16_t valide;9 x+ I% W6 e; ?) `1 w# K$ |" X8 a
- uint8_t databuf[16];
' J3 v# _8 e7 O7 x0 Q0 p) z$ }# ~" e - }CfgData;
) Y1 B: B( Z* Z+ s% V - CfgData cfg; //全局的一个结构变量0 L& [, M0 X* N
- /**
5 e9 o8 m: v" o6 h - * @brief 从EEPROM 中读出参数, Z5 h: R$ e8 B! ?7 n3 ~: v+ I
- * @param 无) y; Q- @! A {
- * @retval 0成功 其他失败: 1 通信错误 2 无效设置& M' ^2 X( g' \+ \1 i& K# a: e9 k
- */
1 ?5 k6 w. n W; H& j1 U" A7 N - uint8_t read_cfg_eeprom(void) {
& x6 \: X, B2 X$ f - CfgData cfg_read;! o" a- F6 [7 _2 O2 H2 j- G
- uint16_t readSize= (uint16_t) sizeof(CfgData);
$ b/ L5 Y9 ~, [' v. m! N# L
+ {- U0 s1 M) x- EEPROMConnected = 1; //超时回调函数设定. j7 _- T3 X2 l. ^! P4 Y0 Z$ V
- 6 r6 h! p3 G5 `# Z! w$ i
- /* Wait for EEPROM standby state */
~7 E$ l a' v% ?6 e - BSP_EEPROM_WaitEepromStandbyState();
/ i, o# d4 ^9 R l
. Y/ }! v3 t+ Y4 U8 {% K- /* Read old parameter in EEPROM */8 z7 Y! O4 f3 A- {( s( L
- if (EEPROMConnected == 1) {( [$ h4 v: k9 @0 I" E/ C
- /* Set the Number of data to be read */2 m" S6 s/ `' C$ f" n( m6 Z
- memset((uint8_t*)&cfg_read,0,readSize);6 b# o4 U/ `% P6 l
- /* Read from I2C EEPROM from EEPROM_CFG_ADDRESS */0 |+ a4 a$ n$ J& q: h) C: r
- if (BSP_EEPROM_ReadBuffer((uint8_t*)&cfg_read, EEPROM_CFG_ADDRESS, &readSize) != EEPROM_OK) {
3 u2 N3 h% l/ | - return 1; //通信错误$ @. h% X- w8 r! q
- }' k9 Y" ]. I- F$ k2 q
- if (cfg_read.valide == 0x55aa) { //有效的1 j1 w8 E+ y% _7 W+ j8 Q- ^. R2 X
- memcpy((void*)&cfg, (void *)&cfg_read, sizeof(CfgData));- p8 W, T' ^% @- L2 K9 V7 h
- return 0; //正常; j5 d1 y j/ N' B) e& k' M
- }; M& B5 l" h4 J) B4 \" Q7 f$ o
- }
$ K y; h2 M# q! X - return 2; //无效设置) B Z5 Z' D7 c9 M! Y1 N7 q$ ~! K
- }
; R# H" [$ b3 s0 _- o - /**
0 U8 P, O, R* Q' z i. [ - * @brief 保存参数到 EEPROM 中
6 Y A& {' r. a5 m - * @param 无
( [* D* H2 j# a5 H - * @retval 0成功 其他失败
1 ?1 F7 O) J* z5 T* @8 T - */
9 G2 O2 o: n5 ?$ @0 c: L# } - uint8_t save_cfg2eeprom(void) {" L. U% @1 A3 }+ i5 G. a' X
% {7 q0 {6 u$ d4 v- EEPROMConnected = 1;
! y) T; N @5 n7 ~
3 c) t; s1 C' G8 R- R- /* Wait for EEPROM standby state */
- {$ q' P3 v) B! H2 A - BSP_EEPROM_WaitEepromStandbyState();
5 X1 [4 @/ i9 U: w. F6 {" e - 6 E" ^" q+ ^$ b( n8 s% F
- /* Write new parameter in EEPROM */
% \( [1 i+ ~: T* ]7 A' [0 ^8 T! G - if (EEPROMConnected == 1) {
' G2 h2 P# P a/ I# v8 `- i - /* Write on I2C EEPROM to EEPROM_CFG_ADDRESS */+ i: x6 F: \) b9 w D8 t: N; Y$ A
- if (BSP_EEPROM_WriteBuffer((uint8_t*)&cfg, EEPROM_CFG_ADDRESS, (uint16_t) sizeof(CfgData)) == EEPROM_OK) {9 C$ c- u( A V# F
- return 0;
" M: i% H4 `5 F" _$ h4 M; | - } p- a/ f# v6 o9 H
- }0 P* q0 V5 |5 x' s5 {8 @, b- t
- return 1;
6 J4 l7 |1 o; Q2 }) t& W - }1 U5 S- o l5 c& s
- /**8 w) l' c2 m) f* j1 d
- * @brief Basic management of the timeout situation.
" M8 A) u, m E0 R. L( b/ N - * @param None.
* p: {0 l$ |6 @! l9 h/ W - * @retval None, m r$ m1 D' E5 D9 D) z+ ^
- */
. P- W4 q0 c9 A- t& k/ n( f/ \ - void BSP_EEPROM_TIMEOUT_UserCallback(void) {
2 P0 x8 S/ S6 k$ _! o - EEPROMConnected = 0;
- V5 u# ~$ {7 T% k - }/ a- c3 Q( F/ K9 L( `0 o! V2 D
; K$ M" E4 Z+ I+ I4 p- h; y
复制代码 经过调试并下载到核心板运行,测试结果正常。所用软件环境:3 q: F# M$ I% t) q; O/ V* s4 k
STM32CubeMX 4.15.0
4 E2 ~& i3 U, @0 ZSTM32Cube_FW_F4_V1.12.0
2 m& f% U! M: q8 k: dGNU ARM C/C++ for eclipse 2.5.1.201604190915
- n. N( w0 n& I8 {" ~, leclipse 4.4.2 luna9 [' X8 E9 ~% B Y
Debian sid: Linux Debian 4.5.0-2-686-pae #1 SMP Debian 4.5.4-1 (2016-05-16) i686 GNU/Linux
% q- f( b. W: R6 S5 }$ S% {1 R( a* ~. ]; I
& G3 G5 g8 E; O# f9 g |