本帖最后由 giveup 于 2016-5-31 08:03 编辑
0 \ {" o u. M w9 U: D% z0 f5 s# V% q+ u/ c
采用STM32CubeMX 配置一块STM32F407VET6的核心板,通过I2C1读写AT24C08 EEPROM。可以选择BSP方式,或者到HAL库中挑选BSP驱动,效果一样。因为我的工程已经初始化过了,所以采用后种方式。
* n l; @- T+ H3 P采用默认值配置I2C1,选中NVIC Settings中的 I2C1 event interrupt和 I2C1 error interrupt。生成工程模板。将AT24C98N EEPROM连接到PB6,PB7。器件A0,A1,A2引脚悬空,地址为0。
# _1 \8 ]8 T$ D7 a2 ~在安装库中复制eeprom的BSP驱动,应该都一样。如复制下列4个文件到工程目录下:
+ d( [% N8 S( b" I% L. qSTM32Cube/Repository/STM32Cube_FW_F4_V1.12.0/Drivers/BSP/STM324xG_EVAL/stm324xg_eval_eeprom.c' ~. y' q, b; ~9 S4 D
STM32Cube/Repository/STM32Cube_FW_F4_V1.12.0/Drivers/BSP/STM324xG_EVAL/stm324xg_eval_eeprom.h
# P: ?1 e: A, Y4 ^STM32Cube/Repository/STM32Cube_FW_F4_V1.12.0/Drivers/BSP/STM324xG_EVAL/stm324xg_eval.c! B# A5 H3 H% w: v% ~: ~' b
STM32Cube/Repository/STM32Cube_FW_F4_V1.12.0/Drivers/BSP/STM324xG_EVAL/stm324xg_eval.h* `- E1 H3 r& R/ o
/ e7 G5 g; v+ |: C0 @+ `. Tstm324xg_eval.c和stm324xg_eval.h中保留和EEPROM相关内容即可。: `8 `* R7 ^- \0 |1 |; ^
2 \+ `4 O9 l( d" q1 t( c1 D/ x重点的问题是,这个驱动支持的是M24C64 EEPROM,和AT24C08 EEPROM有一定差异,需要修改部分内容才能使用。主要体现在读写页面尺寸、容量和器件地址表示不一致。, A' f* N! z( |) Y) [: i- R4 b
根据手册,AT24C04~AT24C32的内存地址是分为两部分,除低八位外,高3位占用了器件的地址。如AT24C08,内存大小为1024字节,地址为10位,高2位占用了器件地址A1,A0。M24C64 EEPROM的内存地址为16位,和AT24C32以上的EEPROM保持一致。( ^+ a W! |7 y, q* g8 ~( }
因此,主要修改点为:1 E, p/ E, Y) o1 h3 @
1。修改页面尺寸与地址大小 ) _ y) H2 W% i% V4 C$ }: K
stm324xg_eval_eeprom.h中,修改定义:
9 ]3 t) |* H# [- r0 x4 J0 {0 q" k#define EEPROM_PAGESIZE 16
1 e% G' }( X3 n) u#define EEPROM_MAX_SIZE 0x400 /* 8Kbit */
( b) M: B, o, _" l2 Q7 i6 |/ H5 p' J O0 x添加定义
$ K3 ^; `. c0 z4 f#define EEPROM_MEMADD_SIZE I2C_MEMADD_SIZE_8BIT; d! U/ H* U" Q6 g: W# a: Q
#define MEMADD_MSB_MASK 0x3/ m, Y, [" o2 v& L8 g/ X, P
2。修改读写函数: [( _- a' B+ h
2.1 读操作部分修改逻辑:
( y; `) H/ s" i该部分的逻辑是 BSP_EEPROM_ReadBuffer 经过 EEPROM_IO_ReadData 调用I2Cx_ReadMultiple完成实际读写。, X: d* X3 m; M7 h6 P
因为读函数采用连续读方式,只要设定了正确的内存起始地址,就能正常完成读操作。因此,修改只涉及到BSP_EEPROM_ReadBuffer和EEPROM_IO_ReadData两个函数。具体修改如下,红色部分是增加和修改的:
" a) D- V3 ^+ C/ z5 `! Y9 I/ ?- uint32_t BSP_EEPROM_ReadBuffer(uint8_t* pBuffer, uint16_t ReadAddr, uint16_t* NumByteToRead) {8 }8 s: |% F" p
- uint32_t buffersize = *NumByteToRead;, e+ i( i& G: X, r3 j" j' ~+ l/ `# j
- : e* a1 T4 T5 w$ t
- /* Set the pointer to the Number of data to be read. This pointer will be used3 A; W! n4 ]" O" @/ d4 M
- by the DMA Transfer Completer interrupt Handler in order to reset the 7 w- s# I$ \5 P9 i
- variable to 0. User should check on this variable in order to know if the 2 d9 D+ ]) R G2 c
- DMA transfer has been complete or not. */. W0 O: e2 i' Y# O, Q
- EEPROMDataRead = *NumByteToRead;
7 j( q7 ?, {' d! P3 |& _ - <font color="Red">uint16_t A_EEPROMAddress = EEPROMAddress;
/ P8 n: G5 W" v! ^3 t - uint8_t A2A1A0 = (uint8_t) ((ReadAddr >> 8) & MEMADD_MSB_MASK);
( O# z' i5 Q+ W2 G. }7 T4 f* q6 S - A_EEPROMAddress = A_EEPROMAddress | (A2A1A0<<1);</font>1 C+ M# ]8 y; ]1 ~; v! B
- * J" Z) m4 B N- X# V2 O* g
- if (EEPROM_IO_ReadData(<font color="Red">A_EEPROMAddress</font>, ReadAddr, pBuffer, buffersize) != HAL_OK) {
# u5 ?1 S+ c; t4 h* } - BSP_EEPROM_TIMEOUT_UserCallback();
0 @$ L6 D0 l+ u) V- K$ a - return EEPROM_FAIL;6 b; q. b4 J ]. C/ J/ j& s
- }3 X9 J6 i6 P* S/ c, M6 f
- /* If all operations OK, return EEPROM_OK (0) */( ?) B7 ]$ `* D9 [) z1 {: t0 F
- return EEPROM_OK;
. N/ j# [) h# ]3 h - }
2 l( B* A$ k! e1 ^5 u: N
复制代码 $ Q8 f. H, X; Y& A: R4 w
将I2C_MEMADD_SIZE_16BIT替换为上面新增加的EEPROM_MEMADD_SIZE定义:$ p% E7 A" x1 j2 h. p- f- J8 i
- HAL_StatusTypeDef EEPROM_IO_ReadData(uint16_t DevAddress, uint16_t MemAddress, uint8_t* pBuffer, uint32_t BufferSize)
- N* v+ r2 }- _ - {
' z! n" Q2 j' j0 d& N1 C- ]( V - return
; W/ _$ u2 ]) I8 Y* u. d5 r0 g0 y - (I2Cx_ReadMultiple(DevAddress, MemAddress, <font color="Red">EEPROM_MEMADD_SIZE</font>, pBuffer, BufferSize));' K& ?3 P* C1 C9 O- f, v
- }
复制代码
. D+ v" ^0 p8 p, F. b/ r
" s$ q J- B6 t+ _ [" O f4 |2.2写操作部分函数修改逻辑:' O$ `) c m( }
写操作调用关系是BSP_EEPROM_WriteBuffer负责将写地址分页,并完成页面对齐后,调用BSP_EEPROM_WritePage经过EEPROM_IO_WriteData完成实际写操作。修改部分只涉及到后边两个函数,具体见下面红色部分:& X; ?! L/ u- P6 j2 S
- uint32_t BSP_EEPROM_WritePage(uint8_t* pBuffer, uint16_t WriteAddr, uint8_t* NumByteToWrite) {& Y+ i0 H! F% X
- uint32_t buffersize = *NumByteToWrite;
/ s: {! ^5 Q0 U% \5 F - uint32_t status = EEPROM_OK;
+ h0 |" O% P. `9 ~7 o: X; `9 E' ` - 0 A* k$ K8 o4 _ Y/ m- R! ?
- /* Set the pointer to the Number of data to be written. This pointer will be used
6 h+ D7 Q' ?# W" x1 C: n/ C* l - by the DMA Transfer Completer interrupt Handler in order to reset the
& ^, I% a( t2 Z! d2 c - variable to 0. User should check on this variable in order to know if the
! F+ q; D2 r0 O! G2 h1 O0 Q- } - DMA transfer has been complete or not. */' O8 P% f: N/ _
- EEPROMDataWrite = *NumByteToWrite;
4 J7 b: S$ v2 j: a - ! A/ e6 S4 g6 p2 S6 O
- <font color="Red"> uint16_t A_EEPROMAddress = EEPROMAddress;" l7 ?, x; M( G+ N4 {3 q# q" U$ O3 _* J
- uint8_t A2A1A0 = (uint8_t) ((WriteAddr >> 8) & MEMADD_MSB_MASK);+ ^5 B( g8 @( H; C: _) T
- A_EEPROMAddress = A_EEPROMAddress | (A2A1A0<<1);</font>+ \: a5 U% H; k5 Y* F
- - Q. Y9 P k0 t9 F5 P, i
- if (EEPROM_IO_WriteData(<font color="Red">A_EEPROMAddress</font>, WriteAddr, pBuffer, buffersize) != HAL_OK) {
/ B4 s4 g6 A0 K, I1 E, E$ W( I - BSP_EEPROM_TIMEOUT_UserCallback();, [7 e+ H( e3 s) S
- status = EEPROM_FAIL;) P8 U( R; D) \) V' q2 O3 Z5 C" K/ X
- }0 N# U' ]9 U8 m7 [
- - d& B& [. W" t% v4 A
- if (BSP_EEPROM_WaitEepromStandbyState() != EEPROM_OK) {2 I" Z) Y# X3 O3 v
- return EEPROM_FAIL;: B$ M) I$ b- ^! Q
- }
1 O% K& b0 b" Z: B - /* If all operations OK, return EEPROM_OK (0) */. O: Q* F5 C9 m$ q' o
- return status;3 v( x$ s6 a; B6 v
- }
! u# `1 Z* o5 e! e3 v( X
复制代码
8 u8 [* u7 o4 {; ^. m( w具体写操作修改和读操作差不多:
+ N" E$ ~6 ?8 s' z$ G( Z1 B+ j3 i- HAL_StatusTypeDef EEPROM_IO_WriteData(uint16_t DevAddress, uint16_t MemAddress, uint8_t* pBuffer, uint32_t BufferSize)" b6 ]0 f, B& H5 X
- {0 ^9 ^4 G j5 A$ ]$ u1 l" [1 ~
- return- f \1 c+ m7 L/ q
- (I2Cx_WriteMultiple(DevAddress, MemAddress,<font color="Red"> EEPROM_MEMADD_SIZE</font>, pBuffer, BufferSize));
; Q" q) Q0 S* c9 ~0 r/ j' H - }
复制代码
* @) I/ w! G# v9 G! R6 s ~6 ^; D5 m9 O& B% B `0 w
3. 初始化及其他操作修改。STM32CubeMX已经正确生成了初始化代码,因此除了下面的两个,大部分BSP的初始化代码都可以删除。 o8 B) ^1 [: \, x: m0 C
- void EEPROM_IO_Init(void)
! R0 ~% O9 f; c - {
, k q; R ^8 @( ~ - I2Cx_Init();$ F8 Y& _2 D. a$ r2 s) k% Q
- }
复制代码 修改为MX的初始化代码$ ]4 q* g+ R4 \2 W
- void EEPROM_IO_Init(void)
1 z; \* k9 q1 H' O$ L9 ]! \ - {
! E' _# B8 V( p - MX_I2C1_Init();* ^) ~ ]* }% r1 g* ^2 K5 s
- }
$ N& a! R, x# n3 `) X2 T J1 f# s
复制代码 ; d! s8 S3 F- P. n
修改函数,取消一些不必要的调用:6 a% {/ O1 O+ w. H
- uint32_t BSP_EEPROM_Init(void) {- ]4 _8 p2 I3 V+ Y/ c3 {
- /* I2C Initialization *// e0 h5 P$ C+ F) p; x
- <font color="Red">// EEPROM_IO_Init();</font>1 f" t9 {5 o% i- X, G- K
- /* Select the EEPROM address for A01 and check if OK */+ j" @$ l% y9 f2 T1 S+ f
- EEPROMAddress = EEPROM_I2C_ADDRESS_A01;
1 `- t F5 \1 a# [ - if (EEPROM_IO_IsDeviceReady(EEPROMAddress, EEPROM_MAX_TRIALS) != HAL_OK) {3 [$ g/ p+ M% [" L/ K
- // /* Select the EEPROM address for A02 and check if OK */
9 p( b6 b1 E8 G! E! e - // EEPROMAddress = EEPROM_I2C_ADDRESS_A02;
2 y& j' z1 K; h& l* a - // if(EEPROM_IO_IsDeviceReady(EEPROMAddress, EEPROM_MAX_TRIALS) != HAL_OK)
* O: g( h6 g2 t" P - // {
6 N) y9 A( b* _+ @ - return EEPROM_FAIL;/ R) w5 x! K6 `# h- u% y
- // }
[9 `* S$ W5 d. l! O7 f- R2 w7 S - }5 B: }0 s+ u+ L) ~
- return EEPROM_OK;
0 _$ _. U% w/ s* N/ r N9 P - }4 B6 v, Y$ E, B# b4 L0 {" I
复制代码
' S7 Z9 U7 e5 T另外,需要将stm324xg_eval.c中的 heval_I2c 变量实例替换为hi2c1。可以用查找替换或者宏定义。0 e1 B' E t8 n
4. 使用例子
/ T5 O) D6 O( r- N' m. W4.1 初始化
- V+ P: D7 S4 X8 j! [$ f4 w) s在main.c的 9 _" I+ k( c) i0 p+ y( V5 P+ ~
- /* USER CODE BEGIN 2 *// ^5 n# G3 N: o( \$ h
- BSP_EEPROM_Init();
9 k' O! w& A1 y8 P6 u" G6 I' A - /* USER CODE END 2 */
复制代码 或者其他适当地方添加初始化设置,完成EEPROM地址的设定。# p1 R& Z( t) O' U
4.2使用可以参考相应代码。如:6 S4 f% r( w# G, |8 ^6 z
- #define EEPROM_CFG_ADDRESS 0x120 //和页面对齐
; E* S" @5 e' o2 u/ r - __IO uint8_t EEPROMConnected = 1;
6 }3 @" t+ X7 p( n' D; J - typedef struct {
1 Y2 ~. g2 d0 T - uint16_t valide;
) P8 a) w1 }$ d8 j0 h9 h - uint8_t databuf[16];
' O3 G# H3 O9 w* h: U) B - }CfgData;
5 j/ B+ M1 s* J" |9 H9 y2 d2 z. Q - CfgData cfg; //全局的一个结构变量8 M. L' o5 H0 f7 N/ B4 R" C$ s( o
- /**
5 m2 E2 `0 B8 U* @' l" F7 l - * @brief 从EEPROM 中读出参数
& o+ B* a( ]4 _$ ]" r2 k - * @param 无6 j8 R7 }1 b; V. Y
- * @retval 0成功 其他失败: 1 通信错误 2 无效设置
5 T8 b5 Y$ o1 t - */
; G6 x6 ]3 O( q9 _ - uint8_t read_cfg_eeprom(void) {
/ N7 A G% V# w' S0 M/ v - CfgData cfg_read;
: ~ O$ o# \% m% I# @ - uint16_t readSize= (uint16_t) sizeof(CfgData);* y+ V L$ w3 ~4 P+ G) }
- 7 G5 u# \" X& F, g0 L
- EEPROMConnected = 1; //超时回调函数设定
H8 { T+ Q: J4 P" @0 }$ ]- A - 9 C1 f8 M9 x/ R+ o5 b7 Y1 G/ w
- /* Wait for EEPROM standby state */
- O, }: Z- B6 M! \% O - BSP_EEPROM_WaitEepromStandbyState();
0 d6 ]# f- C" u. R! |! }; U - 7 J8 Y( @" F* o
- /* Read old parameter in EEPROM */- Z0 l5 l0 c) \3 ?2 F6 z, U, C$ V
- if (EEPROMConnected == 1) {
( T- g* I" W2 l9 m - /* Set the Number of data to be read */+ n m' h- f& V( W
- memset((uint8_t*)&cfg_read,0,readSize);- v6 Z2 ^7 Y' _( {# ^/ c! d
- /* Read from I2C EEPROM from EEPROM_CFG_ADDRESS */( x, n: O) x3 V: m- j- M: o2 x+ r
- if (BSP_EEPROM_ReadBuffer((uint8_t*)&cfg_read, EEPROM_CFG_ADDRESS, &readSize) != EEPROM_OK) {
T9 M/ m" H. g. Q" s& k4 @$ I# u - return 1; //通信错误( v4 A, w6 q- L; x: \& ?) L
- }5 s8 v/ T4 e# l: V. R- V
- if (cfg_read.valide == 0x55aa) { //有效的
- ~$ { b$ I8 D6 n2 Q" i1 a( R7 \8 ] - memcpy((void*)&cfg, (void *)&cfg_read, sizeof(CfgData)); W* ?" E8 u4 H2 S
- return 0; //正常
3 d5 |0 ~6 p+ |. A, b/ Q - } X$ S7 m" K: y0 N0 S# G
- }1 G' d& k( I( p, l! M
- return 2; //无效设置
; h5 T5 B8 e) w3 ` - }
/ C1 a- i' }) e P - /**
8 f5 m2 l; f' s/ y, _8 p! L - * @brief 保存参数到 EEPROM 中
2 c2 n- t3 d# E# y9 G5 E0 I - * @param 无3 h' A+ N2 @, @4 W. o |; U
- * @retval 0成功 其他失败
/ z/ Q6 W @3 p$ c+ K; v5 \" ? - */
* @$ T' b7 ?7 U D; d - uint8_t save_cfg2eeprom(void) {
' [' x# [* @5 e7 n - 2 D/ e) p( v% q1 N, B* n
- EEPROMConnected = 1;
. u5 E4 |' m6 T/ C- @! F
I9 G B( y0 L- e W" w4 I$ s- /* Wait for EEPROM standby state */
5 x$ N8 I3 M% h7 D+ V - BSP_EEPROM_WaitEepromStandbyState();
h7 g' U0 B0 s9 f/ c4 O- t
9 }- M2 q9 Z( z8 u4 I* v- /* Write new parameter in EEPROM */
+ @/ X2 I$ v% ^6 \! ?! y. @ - if (EEPROMConnected == 1) {: J: j! }% {# M0 I8 D" ` v0 v
- /* Write on I2C EEPROM to EEPROM_CFG_ADDRESS */. P& _( B1 |7 W! d+ ?3 j: {
- if (BSP_EEPROM_WriteBuffer((uint8_t*)&cfg, EEPROM_CFG_ADDRESS, (uint16_t) sizeof(CfgData)) == EEPROM_OK) {
2 G) d/ r* M/ }7 ` - return 0;3 ]$ W. B* [7 Q8 G
- }- y. J! K1 Y" U6 M% y: F! c
- }
$ K; Y$ ?+ O `% k% e/ x - return 1;
! e: ]* W7 z8 v1 t5 v - }5 { k( w2 r; ^0 X* Q- ?. n
- /**2 h3 t- h/ l* j* ]' E& V( |" K
- * @brief Basic management of the timeout situation.
" G1 z+ o9 D3 n. q - * @param None.# X6 M+ y6 g s. D+ I! D
- * @retval None7 s+ X4 U$ ]. M8 R# C9 a2 U
- */7 d- f2 V; x4 q/ { L0 H4 h% K5 ^
- void BSP_EEPROM_TIMEOUT_UserCallback(void) {- Y9 T# v4 {5 j. s
- EEPROMConnected = 0;- R, Z O5 @9 \( p
- }9 x7 T% t7 j. E" _8 p2 X! ?
- + Z9 X- u$ j7 S4 d% ^; f- i9 E
复制代码 经过调试并下载到核心板运行,测试结果正常。所用软件环境:7 _! M* A6 B- ?, G- M* ]" D: L
STM32CubeMX 4.15.0 n3 p- H8 K) E
STM32Cube_FW_F4_V1.12.0! @0 o5 {. v9 C9 k9 _
GNU ARM C/C++ for eclipse 2.5.1.201604190915
& y+ @$ x9 L7 J5 u8 Heclipse 4.4.2 luna
1 A0 p4 ^$ c6 E; ~& X4 WDebian sid: Linux Debian 4.5.0-2-686-pae #1 SMP Debian 4.5.4-1 (2016-05-16) i686 GNU/Linux" q& ^$ p+ {8 h5 X g
, Y: |1 n; X3 z6 S1 N. ~
" Q, t! Q5 A6 l* C9 C6 W+ V |