本帖最后由 giveup 于 2016-5-31 08:03 编辑 $ h% W$ J! \* N/ S# J' |' r
2 b( K1 A- Q C& Y' J' N' i; @采用STM32CubeMX 配置一块STM32F407VET6的核心板,通过I2C1读写AT24C08 EEPROM。可以选择BSP方式,或者到HAL库中挑选BSP驱动,效果一样。因为我的工程已经初始化过了,所以采用后种方式。0 D, C* o6 S9 b Y9 [
采用默认值配置I2C1,选中NVIC Settings中的 I2C1 event interrupt和 I2C1 error interrupt。生成工程模板。将AT24C98N EEPROM连接到PB6,PB7。器件A0,A1,A2引脚悬空,地址为0。
# a B7 b( ?$ D& i: S" ^; U在安装库中复制eeprom的BSP驱动,应该都一样。如复制下列4个文件到工程目录下:0 @) R5 h+ Q: O" z- O, D6 x
STM32Cube/Repository/STM32Cube_FW_F4_V1.12.0/Drivers/BSP/STM324xG_EVAL/stm324xg_eval_eeprom.c5 u5 Q8 Y8 E% K7 n( L; g
STM32Cube/Repository/STM32Cube_FW_F4_V1.12.0/Drivers/BSP/STM324xG_EVAL/stm324xg_eval_eeprom.h
# B0 G [7 d7 t5 N3 Y& LSTM32Cube/Repository/STM32Cube_FW_F4_V1.12.0/Drivers/BSP/STM324xG_EVAL/stm324xg_eval.c8 U; J# S' O% X) [" J# x% V
STM32Cube/Repository/STM32Cube_FW_F4_V1.12.0/Drivers/BSP/STM324xG_EVAL/stm324xg_eval.h8 R+ s/ z! L# i W
: h5 Z% n. }; v% A+ Y, C9 Xstm324xg_eval.c和stm324xg_eval.h中保留和EEPROM相关内容即可。" @; b( a; S1 h9 y; |; Z
6 U4 L% ~, }- `0 N1 K
重点的问题是,这个驱动支持的是M24C64 EEPROM,和AT24C08 EEPROM有一定差异,需要修改部分内容才能使用。主要体现在读写页面尺寸、容量和器件地址表示不一致。/ o( v) K3 Q/ N6 y
根据手册,AT24C04~AT24C32的内存地址是分为两部分,除低八位外,高3位占用了器件的地址。如AT24C08,内存大小为1024字节,地址为10位,高2位占用了器件地址A1,A0。M24C64 EEPROM的内存地址为16位,和AT24C32以上的EEPROM保持一致。( E( E4 [7 k6 D, _' ^7 Y
因此,主要修改点为:1 _# L- ~ S) c- A" G& k7 ]5 H, Z
1。修改页面尺寸与地址大小
& u7 R4 `& \( ^9 \ h1 Lstm324xg_eval_eeprom.h中,修改定义:8 F) H7 X) g5 D+ `# Z3 R: e
#define EEPROM_PAGESIZE 16
) M' G n' P- S, V4 x" [4 d#define EEPROM_MAX_SIZE 0x400 /* 8Kbit */, C( x, x) p+ b, P) u- Z- Y& E
添加定义3 r" d( y# i0 B
#define EEPROM_MEMADD_SIZE I2C_MEMADD_SIZE_8BIT
) T- S8 Q) H# D#define MEMADD_MSB_MASK 0x3
2 { P$ k" e$ O# R }6 b$ a& R2。修改读写函数
% H3 S$ X) O ]0 P2.1 读操作部分修改逻辑:0 a$ v, H# {0 t4 k/ ]1 l3 _
该部分的逻辑是 BSP_EEPROM_ReadBuffer 经过 EEPROM_IO_ReadData 调用I2Cx_ReadMultiple完成实际读写。% [/ | ]; L; q( X; [* b
因为读函数采用连续读方式,只要设定了正确的内存起始地址,就能正常完成读操作。因此,修改只涉及到BSP_EEPROM_ReadBuffer和EEPROM_IO_ReadData两个函数。具体修改如下,红色部分是增加和修改的:* n# E/ W# K0 u1 B/ B8 O& q
- uint32_t BSP_EEPROM_ReadBuffer(uint8_t* pBuffer, uint16_t ReadAddr, uint16_t* NumByteToRead) {
2 j+ Z C% s8 V7 X: O% E+ @ - uint32_t buffersize = *NumByteToRead;
7 k" k+ {% x3 p3 p! q0 H - 1 k8 T0 Q, a. ^0 C( B; g
- /* Set the pointer to the Number of data to be read. This pointer will be used. g! d6 E2 a. E; a- @+ ?$ ]
- by the DMA Transfer Completer interrupt Handler in order to reset the # k, O: C4 c3 b+ M3 G" k- o" I/ N) ~
- variable to 0. User should check on this variable in order to know if the ! U/ T, }% J: ?
- DMA transfer has been complete or not. */. A. ^ ~8 |3 t% [8 H
- EEPROMDataRead = *NumByteToRead;4 v/ N3 ?" v. @
- <font color="Red">uint16_t A_EEPROMAddress = EEPROMAddress;
. g6 x- D* p& W9 m - uint8_t A2A1A0 = (uint8_t) ((ReadAddr >> 8) & MEMADD_MSB_MASK);
2 u& I8 H) {, P - A_EEPROMAddress = A_EEPROMAddress | (A2A1A0<<1);</font>
7 ~( X q" [, Q. W, Y
+ v$ c# e9 C- D4 ~" X t* |- if (EEPROM_IO_ReadData(<font color="Red">A_EEPROMAddress</font>, ReadAddr, pBuffer, buffersize) != HAL_OK) {6 m0 c4 P7 {) R/ U8 {" |" U
- BSP_EEPROM_TIMEOUT_UserCallback();
2 o% {) m% c& G, Q( @4 W" p - return EEPROM_FAIL;, ?1 a. T: e0 K
- }
3 X! R" e3 t! q) [/ }" Q - /* If all operations OK, return EEPROM_OK (0) */7 L) t( M9 b0 s' l; O7 c" A
- return EEPROM_OK;
% p( H* N/ Z1 A( c: t - }3 b' y `$ z6 F8 i( \0 e7 S
复制代码
h6 Y4 w! ^- u2 p& U将I2C_MEMADD_SIZE_16BIT替换为上面新增加的EEPROM_MEMADD_SIZE定义:% u" V: C. |3 W- h" Z5 s) E
- HAL_StatusTypeDef EEPROM_IO_ReadData(uint16_t DevAddress, uint16_t MemAddress, uint8_t* pBuffer, uint32_t BufferSize)* m2 w" T3 m0 `2 O' d5 @
- {, p) t% h1 N% k+ z+ I7 C
- return
- N/ b$ V( \; s! I8 \) O% ^ - (I2Cx_ReadMultiple(DevAddress, MemAddress, <font color="Red">EEPROM_MEMADD_SIZE</font>, pBuffer, BufferSize));9 M5 ~2 v. U4 w a% n, ^
- }
复制代码 : \. B+ v4 f6 q
$ R$ k! t( v1 g6 M8 \8 l% H
2.2写操作部分函数修改逻辑:$ R+ x3 k4 |4 y6 o2 M) G1 z
写操作调用关系是BSP_EEPROM_WriteBuffer负责将写地址分页,并完成页面对齐后,调用BSP_EEPROM_WritePage经过EEPROM_IO_WriteData完成实际写操作。修改部分只涉及到后边两个函数,具体见下面红色部分:5 y# u$ q% @2 }: @& g1 Y0 n3 n; X
- uint32_t BSP_EEPROM_WritePage(uint8_t* pBuffer, uint16_t WriteAddr, uint8_t* NumByteToWrite) {. e1 r3 j/ ?' e8 [
- uint32_t buffersize = *NumByteToWrite;) i7 v- J- l1 a
- uint32_t status = EEPROM_OK;% d: }0 _2 X' E% s9 w
5 }( _3 m8 X! U% S- U9 o2 I' r T- /* Set the pointer to the Number of data to be written. This pointer will be used5 z9 b: ^' u* `! f0 d8 L
- by the DMA Transfer Completer interrupt Handler in order to reset the
7 h- x# K* L: E8 H# V7 x) ` - variable to 0. User should check on this variable in order to know if the
4 P( l6 }) U4 t4 k: A - DMA transfer has been complete or not. */
6 H" ]! g: F; X/ m& b# ] - EEPROMDataWrite = *NumByteToWrite;
* c. J% \) d7 m e j
7 p# @8 A2 P" \9 O, X- <font color="Red"> uint16_t A_EEPROMAddress = EEPROMAddress;% y% E: ]& l: `1 u6 z. l2 o
- uint8_t A2A1A0 = (uint8_t) ((WriteAddr >> 8) & MEMADD_MSB_MASK);2 m* I4 J& j' z1 Q' ?
- A_EEPROMAddress = A_EEPROMAddress | (A2A1A0<<1);</font>
\, J& L: u% z) A) ^( D2 H - + i: V3 g5 t2 F9 ~: G8 C# b
- if (EEPROM_IO_WriteData(<font color="Red">A_EEPROMAddress</font>, WriteAddr, pBuffer, buffersize) != HAL_OK) {
; W% I& J8 n, A1 | - BSP_EEPROM_TIMEOUT_UserCallback();9 d" N% a1 t' L0 i3 Y" _
- status = EEPROM_FAIL;9 o% @ K$ j) b& f u6 Y& N
- }
4 K" _) v! s4 z" u" D
, V. r' |! E6 L5 E- if (BSP_EEPROM_WaitEepromStandbyState() != EEPROM_OK) {* j! J) s: a) c' Y# x) \
- return EEPROM_FAIL;
2 R O$ n* ^: f' Y - }
- G% z! n9 x, f' {, _0 d - /* If all operations OK, return EEPROM_OK (0) */# o9 O5 I' k0 J( z4 x5 `: I1 B
- return status;1 \. p; Q# ^/ S+ g9 ~/ K* G" @
- }0 r5 T/ {9 R% [
复制代码 0 c/ \2 D9 v M
具体写操作修改和读操作差不多:5 B/ s( e! J+ A, V+ Z/ b$ ?
- HAL_StatusTypeDef EEPROM_IO_WriteData(uint16_t DevAddress, uint16_t MemAddress, uint8_t* pBuffer, uint32_t BufferSize)
9 P" \4 p% j/ S- v w - {' @ \; D/ z5 s0 z
- return2 U0 r/ L$ _9 e( m) q1 l3 P
- (I2Cx_WriteMultiple(DevAddress, MemAddress,<font color="Red"> EEPROM_MEMADD_SIZE</font>, pBuffer, BufferSize));+ V1 n# j& g! C/ O u# q0 y0 O) T y+ t- Z
- }
复制代码
0 j. e- w. r# J! } n& i0 {2 e2 f! ]
3. 初始化及其他操作修改。STM32CubeMX已经正确生成了初始化代码,因此除了下面的两个,大部分BSP的初始化代码都可以删除。
9 N0 C6 d$ d4 d- void EEPROM_IO_Init(void)
' [" a: p! |+ b/ z& S7 W$ | l - {, r: o- q% I5 D5 f: r0 h: K) [# O
- I2Cx_Init();8 B& [* S6 g" S# y& R
- }
复制代码 修改为MX的初始化代码; Q3 Q b# I% n% I" G7 @
- void EEPROM_IO_Init(void); J* k: m4 \! k, ]
- {( T! S( I9 `. ^: A3 i' w
- MX_I2C1_Init();
' T& q# g3 d& V" p - }
, N+ o9 n- ^5 }. a1 X- g( ^
复制代码
9 d o, e" Z# V0 P4 ?修改函数,取消一些不必要的调用:
6 f% P2 x& j( k/ A+ X& P$ I- uint32_t BSP_EEPROM_Init(void) {
/ ^2 J8 r) r! P - /* I2C Initialization */3 t3 S( ^, O# B) }+ ~: Q
- <font color="Red">// EEPROM_IO_Init();</font>, b9 Y( X, _% A3 X6 z
- /* Select the EEPROM address for A01 and check if OK */
) ^( m+ V7 z5 I# h2 ^3 Y - EEPROMAddress = EEPROM_I2C_ADDRESS_A01;1 x4 n; Z/ P+ l f
- if (EEPROM_IO_IsDeviceReady(EEPROMAddress, EEPROM_MAX_TRIALS) != HAL_OK) {
- ?2 ?9 c& t( \3 ]' e9 T - // /* Select the EEPROM address for A02 and check if OK */
5 a! X q1 f% h2 [ - // EEPROMAddress = EEPROM_I2C_ADDRESS_A02;
$ D5 z/ z. ^- k$ k# m - // if(EEPROM_IO_IsDeviceReady(EEPROMAddress, EEPROM_MAX_TRIALS) != HAL_OK); e5 c' t, R \' t7 O* k
- // {% ~* |- P- _$ e* ~- p! S: X" @! P
- return EEPROM_FAIL;
. r4 F/ X( P5 y# H* {+ N8 W - // }4 T0 m# `, |+ A @
- }
) [3 J) ]5 K# k! k4 S I) N" z - return EEPROM_OK;
' X4 b7 Q. k! K5 `0 j - }% s: S7 U |$ J8 F) I
复制代码 : B% \7 _, u8 }. Y8 K# w: H4 R
另外,需要将stm324xg_eval.c中的 heval_I2c 变量实例替换为hi2c1。可以用查找替换或者宏定义。" l' Y' h% g, ^$ \8 V ^( p
4. 使用例子# L# N! u) P& A7 g; z0 Q# q$ p
4.1 初始化
9 i: v' K* b% _1 ]8 R; s在main.c的 , G9 u( e- l S( s
- /* USER CODE BEGIN 2 */) n0 Q& V: L2 Y) _
- BSP_EEPROM_Init();, }4 I4 W- i/ m. n! V" V5 z. J
- /* USER CODE END 2 */
复制代码 或者其他适当地方添加初始化设置,完成EEPROM地址的设定。
3 K& A& \9 R# H6 [- a/ T4.2使用可以参考相应代码。如:
# {* m3 y D3 l8 M, |- #define EEPROM_CFG_ADDRESS 0x120 //和页面对齐
/ U$ |6 x7 i% f7 a* H/ ` - __IO uint8_t EEPROMConnected = 1;: n7 M0 j+ a g8 m, m
- typedef struct {/ F( ~6 K" _3 c: [
- uint16_t valide;1 W: m6 C5 |# b* ?
- uint8_t databuf[16];2 o0 {8 E3 _, c; I& |$ T3 I0 e
- }CfgData;
8 S$ b9 X; a( q7 t# y" ?4 V" h" z - CfgData cfg; //全局的一个结构变量
5 B; s; [! X+ M6 ^ - /**( y4 I3 [/ Y# H4 E
- * @brief 从EEPROM 中读出参数
6 a- `" y- ^ r+ q: T - * @param 无1 U: |6 u, e1 e, n1 a& j0 e
- * @retval 0成功 其他失败: 1 通信错误 2 无效设置9 c- f" y& [. p+ O! Q* V- Q. b
- */
3 C5 _2 L2 b V% A/ B - uint8_t read_cfg_eeprom(void) {
! P1 B% T6 s+ h2 `$ \ - CfgData cfg_read;6 ]1 Q. x- d7 ^( K% a: _# H0 o
- uint16_t readSize= (uint16_t) sizeof(CfgData);
% a5 u) ]$ p7 A2 o+ ~ - 6 w, M* d% S8 ^& Q4 }0 Y V7 g
- EEPROMConnected = 1; //超时回调函数设定
, A6 C+ i' K q0 \
" Y1 Y4 G# p Z2 _- /* Wait for EEPROM standby state */
4 ~; [7 ^. L* V4 c - BSP_EEPROM_WaitEepromStandbyState();/ W3 U( G& r4 f: a3 t7 { w8 q# Z
; |. f" Q: {/ J- /* Read old parameter in EEPROM */: o; @* v5 p" c# X6 K
- if (EEPROMConnected == 1) {( L% q+ q2 t* B3 G, M
- /* Set the Number of data to be read */" c0 N b2 M1 k, G1 [* O! u
- memset((uint8_t*)&cfg_read,0,readSize);
' {+ s3 E- `2 s$ N - /* Read from I2C EEPROM from EEPROM_CFG_ADDRESS */
* H, t! S- Y, R7 ?+ R - if (BSP_EEPROM_ReadBuffer((uint8_t*)&cfg_read, EEPROM_CFG_ADDRESS, &readSize) != EEPROM_OK) {
1 j J% m* s, t$ b - return 1; //通信错误* z) E- y. S9 u& B; K
- }) R D" Q( ]/ v& F8 O9 ~
- if (cfg_read.valide == 0x55aa) { //有效的
* [4 Y2 q1 b( @$ i7 V4 J/ E - memcpy((void*)&cfg, (void *)&cfg_read, sizeof(CfgData));
1 c! p9 T8 ?1 A( o! \2 { - return 0; //正常
& K+ ^" G+ }" x+ d# `6 \ - }4 y7 ?: A0 B1 z% |0 `( a
- }
" r& w& R1 @. E/ l8 H" V ^. Q+ u4 q* ? - return 2; //无效设置8 F; Y s6 R( _
- }
! m U: z% F4 a. L* Z% T1 R - /**3 `8 B# m" q" b$ l" a
- * @brief 保存参数到 EEPROM 中* D' T3 }* i0 N* l
- * @param 无5 {" T8 d/ b7 Z6 e8 {& c; s
- * @retval 0成功 其他失败
$ |9 Y& A) B7 ~( }( T - */
1 V, N9 S, ]; j) g ` - uint8_t save_cfg2eeprom(void) {( O5 T: a; w( P7 x* ]" |7 i2 f% f
) C6 ?) y" A: Y# u- EEPROMConnected = 1;+ m0 x, u9 v! m, \) k- l+ D7 B5 I
- $ T$ ~* u( k8 l7 U, c4 ^$ R. o) O" T
- /* Wait for EEPROM standby state */
! H9 ~( V: ] B- r - BSP_EEPROM_WaitEepromStandbyState();9 V: U2 ^, Y; _- g5 B$ r* X# u
- 3 d# K L) }7 _5 W% \$ E
- /* Write new parameter in EEPROM */. k! h8 C2 Q0 _+ N: L- B
- if (EEPROMConnected == 1) {
8 W! t! U& M* C" v1 f- ? ~ - /* Write on I2C EEPROM to EEPROM_CFG_ADDRESS */, O& k) O3 n$ g2 }
- if (BSP_EEPROM_WriteBuffer((uint8_t*)&cfg, EEPROM_CFG_ADDRESS, (uint16_t) sizeof(CfgData)) == EEPROM_OK) {( `0 k- h; X/ {8 y/ z/ U) Q
- return 0;
" e4 Z# J, q3 m9 i - }1 O( d% S% } d7 i
- }
( R0 w3 M7 |6 F& X$ l; V - return 1;
" J- b7 e. r" L - }" b. N7 q7 V, M0 C3 n' @5 h
- /**
& P* i# ~/ K2 n9 r! o - * @brief Basic management of the timeout situation., @, C' s7 q1 f h! x. _* R" q S& ^
- * @param None. k$ U* g) T3 D
- * @retval None
0 I( g# y( `" u - */ T5 @% Q8 n j2 \$ r
- void BSP_EEPROM_TIMEOUT_UserCallback(void) {
. s$ I* x9 L" v ~+ O7 T4 A - EEPROMConnected = 0;- p9 t$ m; t: F2 r A0 Z4 v# J" L* M
- }1 ^% j, X+ a5 E+ v5 ~
- 7 s* ]: ^- [5 s1 P! [' f. ?
复制代码 经过调试并下载到核心板运行,测试结果正常。所用软件环境:
" X# t) ]# B/ X+ `9 }4 k' o# m7 ASTM32CubeMX 4.15.0$ Y5 _8 V+ [. l5 b4 e8 h- k9 u6 B
STM32Cube_FW_F4_V1.12.0
9 a Z# [) b- [GNU ARM C/C++ for eclipse 2.5.1.201604190915 6 }' K* @- y+ U
eclipse 4.4.2 luna9 M8 g, g& a7 ~ i: J8 c
Debian sid: Linux Debian 4.5.0-2-686-pae #1 SMP Debian 4.5.4-1 (2016-05-16) i686 GNU/Linux
+ o- X- `" N6 ?# {0 ~1 u) y
8 Z G7 b% l* S8 {) F
- w' n6 \0 d/ M ?# a- W |