本帖最后由 giveup 于 2016-5-31 08:03 编辑 # y8 D l! Y! F, w& n" `1 S8 G" A
4 [4 t7 J3 N U \ |采用STM32CubeMX 配置一块STM32F407VET6的核心板,通过I2C1读写AT24C08 EEPROM。可以选择BSP方式,或者到HAL库中挑选BSP驱动,效果一样。因为我的工程已经初始化过了,所以采用后种方式。/ }0 W" w8 o, Q9 u
采用默认值配置I2C1,选中NVIC Settings中的 I2C1 event interrupt和 I2C1 error interrupt。生成工程模板。将AT24C98N EEPROM连接到PB6,PB7。器件A0,A1,A2引脚悬空,地址为0。. E% r% m. t1 y
在安装库中复制eeprom的BSP驱动,应该都一样。如复制下列4个文件到工程目录下:
/ ^# u) b) K& O) X5 H1 t9 ZSTM32Cube/Repository/STM32Cube_FW_F4_V1.12.0/Drivers/BSP/STM324xG_EVAL/stm324xg_eval_eeprom.c
, a. M! j; H' v! M7 ]7 ~# j* a, qSTM32Cube/Repository/STM32Cube_FW_F4_V1.12.0/Drivers/BSP/STM324xG_EVAL/stm324xg_eval_eeprom.h
# x( J% _9 Z0 e# U9 P' W$ o, M" ]STM32Cube/Repository/STM32Cube_FW_F4_V1.12.0/Drivers/BSP/STM324xG_EVAL/stm324xg_eval.c' |9 s$ C+ k, }% k- I0 p
STM32Cube/Repository/STM32Cube_FW_F4_V1.12.0/Drivers/BSP/STM324xG_EVAL/stm324xg_eval.h a( Q' j9 o* g( |, G
8 A# G+ f. D4 ~
stm324xg_eval.c和stm324xg_eval.h中保留和EEPROM相关内容即可。2 b) }. L1 {: N0 z
% c" {% R; A" w
重点的问题是,这个驱动支持的是M24C64 EEPROM,和AT24C08 EEPROM有一定差异,需要修改部分内容才能使用。主要体现在读写页面尺寸、容量和器件地址表示不一致。
* z, j u& t) J) o- L3 a, p6 e4 E6 `根据手册,AT24C04~AT24C32的内存地址是分为两部分,除低八位外,高3位占用了器件的地址。如AT24C08,内存大小为1024字节,地址为10位,高2位占用了器件地址A1,A0。M24C64 EEPROM的内存地址为16位,和AT24C32以上的EEPROM保持一致。
- x& C4 @; e0 T, J& f& H6 j5 U因此,主要修改点为:" N' m+ }1 ~1 M5 H$ v1 o
1。修改页面尺寸与地址大小 & J& x1 d- k! j/ D1 o6 B+ w
stm324xg_eval_eeprom.h中,修改定义:+ H' Y+ Z. I1 D0 h- S+ T% A. N, R2 w
#define EEPROM_PAGESIZE 16' Y6 T5 Q* r+ A7 }* B0 f7 H9 Z, r3 O
#define EEPROM_MAX_SIZE 0x400 /* 8Kbit */
4 j$ ?3 Y* y" O. l% S添加定义
8 G* U$ F/ H1 J }, u( M#define EEPROM_MEMADD_SIZE I2C_MEMADD_SIZE_8BIT
: y; C! |7 y$ l; s7 i4 H% B+ [#define MEMADD_MSB_MASK 0x3- m* \1 R7 r1 U. f" }- ^" M3 j
2。修改读写函数* ~; ~) o" D% `5 T
2.1 读操作部分修改逻辑:
: J- A2 d' e$ c5 T7 b该部分的逻辑是 BSP_EEPROM_ReadBuffer 经过 EEPROM_IO_ReadData 调用I2Cx_ReadMultiple完成实际读写。
& D! Q% T: u) N/ B0 h因为读函数采用连续读方式,只要设定了正确的内存起始地址,就能正常完成读操作。因此,修改只涉及到BSP_EEPROM_ReadBuffer和EEPROM_IO_ReadData两个函数。具体修改如下,红色部分是增加和修改的:
/ h" T6 i3 D( M8 {- uint32_t BSP_EEPROM_ReadBuffer(uint8_t* pBuffer, uint16_t ReadAddr, uint16_t* NumByteToRead) {
! ~2 d0 g8 E4 Q- P5 m' G! i/ Q - uint32_t buffersize = *NumByteToRead;
/ k- u& {( }$ D4 P9 K' e - 4 S4 [6 G" M2 p
- /* Set the pointer to the Number of data to be read. This pointer will be used
% O+ S+ x9 s' m1 H: b' w) T - by the DMA Transfer Completer interrupt Handler in order to reset the $ ?& ~( U5 t" k5 H
- variable to 0. User should check on this variable in order to know if the
/ h5 A9 v; O2 |: G - DMA transfer has been complete or not. */
1 C, C& P1 A: c - EEPROMDataRead = *NumByteToRead;( Y9 t. N8 g- v3 U
- <font color="Red">uint16_t A_EEPROMAddress = EEPROMAddress;# g: w, }1 y$ E0 A# [4 l! Y
- uint8_t A2A1A0 = (uint8_t) ((ReadAddr >> 8) & MEMADD_MSB_MASK);8 [/ k$ v& O# F% G o
- A_EEPROMAddress = A_EEPROMAddress | (A2A1A0<<1);</font>$ y! E, M3 I* y# V1 N' e
- . j* {9 r) W; o( U* V
- if (EEPROM_IO_ReadData(<font color="Red">A_EEPROMAddress</font>, ReadAddr, pBuffer, buffersize) != HAL_OK) {
k w+ }9 P) } A6 W6 y+ |" d - BSP_EEPROM_TIMEOUT_UserCallback();
$ E' A4 K1 m/ i" P; I) @ - return EEPROM_FAIL;
1 Z) d$ G/ P7 O) G- r - }
/ T* E2 ]- u: W3 `' r - /* If all operations OK, return EEPROM_OK (0) */
! ^4 g# {6 M1 j7 z+ C1 v - return EEPROM_OK;7 d* m! E, l$ F8 m. h8 j% F0 }( q8 g
- }- F6 V$ f# G' M
复制代码
: ?, a" f9 L! J$ H, s. M将I2C_MEMADD_SIZE_16BIT替换为上面新增加的EEPROM_MEMADD_SIZE定义:, h" }/ s+ M- F& b0 {
- HAL_StatusTypeDef EEPROM_IO_ReadData(uint16_t DevAddress, uint16_t MemAddress, uint8_t* pBuffer, uint32_t BufferSize)* K9 e% p8 C- [) V0 }$ D7 Q3 ~, r
- {
/ Q7 u+ S! U. o$ J% Z% m - return# D5 N& `5 c$ j
- (I2Cx_ReadMultiple(DevAddress, MemAddress, <font color="Red">EEPROM_MEMADD_SIZE</font>, pBuffer, BufferSize));
$ [! R, C& r; o - }
复制代码
5 _! M) M1 z9 T( c$ Z+ o9 N% E& r, N0 ~' }! x$ |2 g
2.2写操作部分函数修改逻辑:
; @0 [4 X8 s6 ~# M# S写操作调用关系是BSP_EEPROM_WriteBuffer负责将写地址分页,并完成页面对齐后,调用BSP_EEPROM_WritePage经过EEPROM_IO_WriteData完成实际写操作。修改部分只涉及到后边两个函数,具体见下面红色部分:! _+ s& e5 X, O$ e, {; l( i
- uint32_t BSP_EEPROM_WritePage(uint8_t* pBuffer, uint16_t WriteAddr, uint8_t* NumByteToWrite) {$ B x2 Q! n' U
- uint32_t buffersize = *NumByteToWrite;
$ _% x, H- \" N. H - uint32_t status = EEPROM_OK;
; P5 j9 h2 \' A' r+ y9 b: T
, x. X+ ~5 I' l% S7 g. k- /* Set the pointer to the Number of data to be written. This pointer will be used* ^$ f8 Y. Y* Y: K7 }6 O
- by the DMA Transfer Completer interrupt Handler in order to reset the
, P/ O$ Q0 y: n& ?+ ^8 M$ b: ` - variable to 0. User should check on this variable in order to know if the' `, h+ C3 @. j3 O6 @
- DMA transfer has been complete or not. */6 E0 k7 p! C7 C1 x; b8 ~
- EEPROMDataWrite = *NumByteToWrite;
, p9 S7 [ ?4 H- e% D' F# M - 4 ]1 W# C$ P' j* p) H! s9 a
- <font color="Red"> uint16_t A_EEPROMAddress = EEPROMAddress;
- G0 j! {6 Z4 o. X2 V8 z" ~/ ] - uint8_t A2A1A0 = (uint8_t) ((WriteAddr >> 8) & MEMADD_MSB_MASK);& g; [, v4 B& K' f7 W, s
- A_EEPROMAddress = A_EEPROMAddress | (A2A1A0<<1);</font>
% {+ r& A( m5 S/ e9 |; q2 P+ n - 8 ]; {6 I ]& Z
- if (EEPROM_IO_WriteData(<font color="Red">A_EEPROMAddress</font>, WriteAddr, pBuffer, buffersize) != HAL_OK) {; M/ h6 `$ e& g/ Y* e
- BSP_EEPROM_TIMEOUT_UserCallback();
2 X' h: @5 m0 z/ R' `5 F2 w' D - status = EEPROM_FAIL;
7 D% \& J% H! a8 W - }4 y7 w) x1 ~4 a2 P$ n
3 s. q# P- d+ g- if (BSP_EEPROM_WaitEepromStandbyState() != EEPROM_OK) {
! x$ A5 a. s+ m3 k) T - return EEPROM_FAIL;
: Q j# F1 V5 X* P - }/ r: b7 ^; c; Z% l0 _& I
- /* If all operations OK, return EEPROM_OK (0) */6 E* U. X( g% Q5 E# e; L
- return status;6 v k: V6 ]: g8 m V+ d: J% [6 k
- }7 k: {+ |$ J* ]$ o) R
复制代码 . \7 C8 t0 X" B* r
具体写操作修改和读操作差不多:6 l! v; w( a) [. Q9 Z! \
- HAL_StatusTypeDef EEPROM_IO_WriteData(uint16_t DevAddress, uint16_t MemAddress, uint8_t* pBuffer, uint32_t BufferSize)
# K% e A# t2 x$ k7 U& n - {# C0 l4 S9 G8 {* C2 H. Y
- return% o0 m( n# @$ V3 u7 v
- (I2Cx_WriteMultiple(DevAddress, MemAddress,<font color="Red"> EEPROM_MEMADD_SIZE</font>, pBuffer, BufferSize));
0 u' n9 h+ ~" b" n9 J- { - }
复制代码 0 n6 g* n' B" w6 c% F4 U: @/ t" J
5 y6 e* s$ y9 Y3 n3. 初始化及其他操作修改。STM32CubeMX已经正确生成了初始化代码,因此除了下面的两个,大部分BSP的初始化代码都可以删除。
6 F4 C" {2 s4 U, z* [8 T: U5 J- void EEPROM_IO_Init(void)
- r: J3 [1 T% n - {8 l$ w O% w+ Q! n, t
- I2Cx_Init();
g. F5 Q3 U4 H5 m6 ^ - }
复制代码 修改为MX的初始化代码
% [) a$ b P! K' B {1 _: x- void EEPROM_IO_Init(void)
. `" j3 _4 g, |1 w: T - {& R; l" Q0 W6 q2 u
- MX_I2C1_Init();7 s" u0 G0 U) V2 r2 L3 N
- }9 ^4 c& j" P7 d0 [# _4 B$ q. n* V
复制代码 ; [% [) ^) b0 k+ U- W# m+ e- b/ N
修改函数,取消一些不必要的调用:
! g- m! \; q. v( D! j- uint32_t BSP_EEPROM_Init(void) {
/ \6 i+ _$ K9 J4 k: m$ ~% G6 Q& `# Y - /* I2C Initialization */
9 {+ d7 O9 n" F% c( @/ e% E4 P, D - <font color="Red">// EEPROM_IO_Init();</font>
0 {2 i! ^' M# }# | - /* Select the EEPROM address for A01 and check if OK */
3 B$ Q2 V: p% p - EEPROMAddress = EEPROM_I2C_ADDRESS_A01;
0 L4 b/ x' K, q/ d3 U) q' S5 F - if (EEPROM_IO_IsDeviceReady(EEPROMAddress, EEPROM_MAX_TRIALS) != HAL_OK) {& N4 S) [, \ W2 y q$ C
- // /* Select the EEPROM address for A02 and check if OK */; ^4 d# }0 B% ?- T: f
- // EEPROMAddress = EEPROM_I2C_ADDRESS_A02;4 n. _* O9 O+ t' d. S2 p7 D, X M p, Q
- // if(EEPROM_IO_IsDeviceReady(EEPROMAddress, EEPROM_MAX_TRIALS) != HAL_OK)& T: S' Y) {" z: s x
- // {4 ^- |* I$ n( w: F' G; \" H7 h. }8 q- ]7 t3 s
- return EEPROM_FAIL;
/ _! b- L! e4 F! @% D! E' c - // }9 U. N. a6 Y2 p+ M" U6 J
- }
! G" j' o& ?, V! m - return EEPROM_OK;7 w. Y, w& s8 Z. I) U1 D7 F& K
- }
% Z6 y* `- o- o t! c* _
复制代码 3 `3 @& u* d, S. y) z
另外,需要将stm324xg_eval.c中的 heval_I2c 变量实例替换为hi2c1。可以用查找替换或者宏定义。+ i" u$ ~+ t6 {/ u* D% K
4. 使用例子, F4 |: [: G6 N3 l3 q, `
4.1 初始化
+ N+ m$ R6 u" N1 I. R: j4 @在main.c的 : g( R; {8 x( z) y# d, V. ^9 S
- /* USER CODE BEGIN 2 */% T& g# I# B4 S! b8 D$ e0 |$ o5 B
- BSP_EEPROM_Init();
+ V3 A3 Q) f( M - /* USER CODE END 2 */
复制代码 或者其他适当地方添加初始化设置,完成EEPROM地址的设定。 g( }; _; c( L3 v# v( J3 p# g
4.2使用可以参考相应代码。如:
1 [5 g' T4 d5 a- #define EEPROM_CFG_ADDRESS 0x120 //和页面对齐
6 b) D* Q6 R1 I8 i - __IO uint8_t EEPROMConnected = 1;
! _6 [; m$ A- F0 R - typedef struct {
6 y! g. `7 S5 f$ k/ s! @ - uint16_t valide;, [1 f$ u, x4 O+ ^0 d; a
- uint8_t databuf[16];4 x/ |; |$ d* H- i: E, K) D }. P( x
- }CfgData;' t5 v1 ]4 W9 T7 q f
- CfgData cfg; //全局的一个结构变量
v% N# l* [5 I1 _, ]# f$ J' Q - /**& a8 x$ A# j/ i' G# {$ |0 p
- * @brief 从EEPROM 中读出参数5 b# A" D( s6 I, O
- * @param 无1 W$ }' u: i( ^, I
- * @retval 0成功 其他失败: 1 通信错误 2 无效设置
5 o& S9 |' `! B% b; c+ @ - */
$ M/ F/ z0 d3 W6 A3 M! g - uint8_t read_cfg_eeprom(void) {
" I# W* p5 q! g* k$ Y0 b9 r$ q - CfgData cfg_read;
+ c! X0 e7 q: E) u! \$ i& G - uint16_t readSize= (uint16_t) sizeof(CfgData);
; P2 \9 F2 C! T; X' L1 o
! h2 n% V& [5 l4 F6 Z' e: |- EEPROMConnected = 1; //超时回调函数设定( @" `+ @0 o0 D$ k% h+ W1 d
9 W2 p1 O: _8 ^! n% k" v- /* Wait for EEPROM standby state */
3 R/ R/ N1 j% b0 D) y0 V - BSP_EEPROM_WaitEepromStandbyState();3 j; B- z+ r* z4 H) S" U$ Y, B( j: c
e- S3 Z& v; `5 d* X0 u- /* Read old parameter in EEPROM */
- B3 L+ s4 {! W- { - if (EEPROMConnected == 1) {
7 @; P% v- M0 e) b# h& O! l - /* Set the Number of data to be read */
4 R" y" `5 q# Z" x8 H. l# w5 l - memset((uint8_t*)&cfg_read,0,readSize);
" v2 w( q1 ~8 I( X% { - /* Read from I2C EEPROM from EEPROM_CFG_ADDRESS */4 h( T0 {6 A% G+ l9 ^) h
- if (BSP_EEPROM_ReadBuffer((uint8_t*)&cfg_read, EEPROM_CFG_ADDRESS, &readSize) != EEPROM_OK) {9 ?* ^- ^" N: Z
- return 1; //通信错误
- Q: m1 a0 e4 ?; o) b: U7 e6 L+ n& S - }5 j' d! S: q: ?
- if (cfg_read.valide == 0x55aa) { //有效的, Z: Y9 U" k. @% K! j4 P, b- v
- memcpy((void*)&cfg, (void *)&cfg_read, sizeof(CfgData));/ a1 F& J) C( y# ~7 p* k3 s
- return 0; //正常5 Z% y8 F- S, r- l4 E k
- }
6 H5 d6 q( Q7 @* y4 J - }
3 B. X% g! e+ | - return 2; //无效设置5 O, n+ o- T) O" A! G2 c
- }
$ X( }1 x t8 W# R1 k - /**' ]% s4 _1 Z/ U' b+ P" Z% Q
- * @brief 保存参数到 EEPROM 中
2 R* R( y9 j5 O( F$ } - * @param 无& t( g W( Z8 U4 X5 a) M4 b
- * @retval 0成功 其他失败5 z( J9 R- [6 ~, {5 O
- */& w9 o# B% Y9 {# ?' e
- uint8_t save_cfg2eeprom(void) {2 ]+ s5 ^. f/ n2 T
- 0 r) }4 ]. t5 J. u
- EEPROMConnected = 1;7 w( ^ } U# I& j% _
" g Y i: F j! M8 v5 x- /* Wait for EEPROM standby state */
! A; X( V' C3 G g - BSP_EEPROM_WaitEepromStandbyState();
, \# u B+ J. c1 {# n - / [0 H# b+ @+ i d2 Z
- /* Write new parameter in EEPROM */ G! z) f$ p! L
- if (EEPROMConnected == 1) {% W; `8 @* I& O) f; _
- /* Write on I2C EEPROM to EEPROM_CFG_ADDRESS */+ K" F" J: h- s) M
- if (BSP_EEPROM_WriteBuffer((uint8_t*)&cfg, EEPROM_CFG_ADDRESS, (uint16_t) sizeof(CfgData)) == EEPROM_OK) {
0 l& C" }# T7 g2 z: z3 Y - return 0;
8 v( J- Z' R1 x* m - }, X. C0 f3 |* d, w" F1 o
- }
5 p# R8 A$ |& Y. D) t2 w8 L2 N, Q - return 1;
7 i4 e' _6 K% _6 A( J - }
3 R' O/ j( q$ T8 k" V - /**
6 _8 x1 S. P) {( Z$ Z* i# | - * @brief Basic management of the timeout situation.' @/ C7 H5 c) Q" z' t
- * @param None.6 z! }" U0 L) x' @
- * @retval None
- B# a W7 z0 ] h - */
4 J3 O& [" n8 }; y; ? - void BSP_EEPROM_TIMEOUT_UserCallback(void) {! f" N7 x3 {% }3 Z
- EEPROMConnected = 0;4 G# F$ S6 [7 M/ \' ~9 Z
- }
% o' Z% T2 o; l6 a& K - + u+ M# h, Z% A8 L# a# q& B
复制代码 经过调试并下载到核心板运行,测试结果正常。所用软件环境:
' _9 P$ W7 N: B V8 KSTM32CubeMX 4.15.0+ F) L' l- W% L9 U- q. ?
STM32Cube_FW_F4_V1.12.0" O. O' \+ @4 M' E" j
GNU ARM C/C++ for eclipse 2.5.1.201604190915
2 y5 ^4 b4 Q5 z$ Z+ L) M: Aeclipse 4.4.2 luna7 `. R7 l4 F/ e, [" u
Debian sid: Linux Debian 4.5.0-2-686-pae #1 SMP Debian 4.5.4-1 (2016-05-16) i686 GNU/Linux
8 H! R K8 @3 r$ V7 n! n* u6 r' r* ^1 E X4 H6 M: A. V" Y. T
/ |6 r% K: p" ~& l9 i" t L- j a: e |