你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32CubeMX + STM32F407VET6读写AT24C08的实验

[复制链接]
giveup 发布时间:2016-5-30 19:31
本帖最后由 giveup 于 2016-5-31 08:03 编辑 & _: p  c. l( a( y
* C, |  k: U& w/ u+ ?- {9 Z
采用STM32CubeMX 配置一块STM32F407VET6的核心板,通过I2C1读写AT24C08 EEPROM。可以选择BSP方式,或者到HAL库中挑选BSP驱动,效果一样。因为我的工程已经初始化过了,所以采用后种方式。
: M0 o5 `( \& A! ]采用默认值配置I2C1,选中NVIC Settings中的 I2C1 event interrupt和 I2C1 error interrupt。生成工程模板。将AT24C98N EEPROM连接到PB6,PB7。器件A0,A1,A2引脚悬空,地址为0。# M6 I! ]3 Y; N  l
在安装库中复制eeprom的BSP驱动,应该都一样。如复制下列4个文件到工程目录下:
& D7 G+ I8 I8 C$ Z; h. E- V4 eSTM32Cube/Repository/STM32Cube_FW_F4_V1.12.0/Drivers/BSP/STM324xG_EVAL/stm324xg_eval_eeprom.c
4 g& l( b% T; q, R% b8 ZSTM32Cube/Repository/STM32Cube_FW_F4_V1.12.0/Drivers/BSP/STM324xG_EVAL/stm324xg_eval_eeprom.h
# q2 y0 j7 H- T1 w* l" t) }0 o7 n* TSTM32Cube/Repository/STM32Cube_FW_F4_V1.12.0/Drivers/BSP/STM324xG_EVAL/stm324xg_eval.c
0 ^  R2 ~' I. tSTM32Cube/Repository/STM32Cube_FW_F4_V1.12.0/Drivers/BSP/STM324xG_EVAL/stm324xg_eval.h
% j, P) `& V; X9 a) {3 K
2 y& r. x4 |! e& ?- fstm324xg_eval.c和stm324xg_eval.h中保留和EEPROM相关内容即可。
3 i0 Q# i8 Z, K4 f& k* M7 w2 h: ^) K7 @" r/ e
重点的问题是,这个驱动支持的是M24C64 EEPROM,和AT24C08 EEPROM有一定差异,需要修改部分内容才能使用。主要体现在读写页面尺寸、容量和器件地址表示不一致。9 E5 j. j0 f3 u! U# H
根据手册,AT24C04~AT24C32的内存地址是分为两部分,除低八位外,高3位占用了器件的地址。如AT24C08,内存大小为1024字节,地址为10位,高2位占用了器件地址A1,A0。M24C64 EEPROM的内存地址为16位,和AT24C32以上的EEPROM保持一致。% U- M' b2 ?" l5 y* C
因此,主要修改点为:2 p' P7 P0 V% B5 j% s
1。修改页面尺寸与地址大小 3 L$ H4 X( }: M/ |. n  v1 w5 c- i5 M: e
stm324xg_eval_eeprom.h中,修改定义:' b) k7 m7 V; v$ P
#define EEPROM_PAGESIZE             16) I8 p* C9 r; y: F
#define EEPROM_MAX_SIZE             0x400  /* 8Kbit */) J; t" T7 T5 N  Z4 Y1 Q
添加定义( Y0 r' j0 X2 ?' k3 b, Z
#define EEPROM_MEMADD_SIZE         I2C_MEMADD_SIZE_8BIT
, [2 p& \% U5 t: |6 x; j/ d#define MEMADD_MSB_MASK     0x3  J2 _) v4 ^$ g, F/ @, O0 D) y
2。修改读写函数
/ g! _0 v  k, b' \; g! T3 `4 K; _# Q2.1 读操作部分修改逻辑:
9 }$ }; k& f) w; I2 u, @4 S该部分的逻辑是 BSP_EEPROM_ReadBuffer 经过 EEPROM_IO_ReadData 调用I2Cx_ReadMultiple完成实际读写。
! b3 N7 a- m% [1 v因为读函数采用连续读方式,只要设定了正确的内存起始地址,就能正常完成读操作。因此,修改只涉及到BSP_EEPROM_ReadBuffer和EEPROM_IO_ReadData两个函数。具体修改如下,红色部分是增加和修改的:8 b1 u7 t" `/ g
  1.     uint32_t BSP_EEPROM_ReadBuffer(uint8_t* pBuffer, uint16_t ReadAddr, uint16_t* NumByteToRead) {6 |9 c4 J/ z( w$ C; Z0 ?
  2.     uint32_t buffersize = *NumByteToRead;. l5 L' Z1 q1 n! |
  3. / ^6 @; T- K6 f
  4.     /* Set the pointer to the Number of data to be read. This pointer will be used7 z( W0 [% A3 A9 u
  5.      by the DMA Transfer Completer interrupt Handler in order to reset the
    . l. \$ p- I6 T( u! f
  6.      variable to 0. User should check on this variable in order to know if the + H, W5 }+ {, r
  7.      DMA transfer has been complete or not. */, z3 _) P/ ~3 o8 D
  8.     EEPROMDataRead = *NumByteToRead;- p5 Z2 j9 A8 y4 C/ b! T6 W
  9.     <font color="Red">uint16_t A_EEPROMAddress = EEPROMAddress;
    $ w* t2 V4 l+ x  x* ]' {6 G
  10.     uint8_t A2A1A0 = (uint8_t) ((ReadAddr >> 8) & MEMADD_MSB_MASK);
      G) J6 [0 Y/ A2 O
  11.     A_EEPROMAddress = A_EEPROMAddress | (A2A1A0<<1);</font>) j; i3 U3 j4 ^; d  n

  12. 7 k, m' |/ ?5 g1 E  k
  13.     if (EEPROM_IO_ReadData(<font color="Red">A_EEPROMAddress</font>, ReadAddr, pBuffer, buffersize) != HAL_OK) {
    # |* B) a- |# G- W% I7 c
  14.         BSP_EEPROM_TIMEOUT_UserCallback();# {$ X/ A4 ^2 l5 c, K) v' v3 d3 {
  15.         return EEPROM_FAIL;% I; @! ^/ D0 k2 \; N* z
  16.     }
    8 T4 J" `" ~$ T
  17.     /* If all operations OK, return EEPROM_OK (0) */3 R& S; s! e7 M0 r4 S/ ^: n
  18.     return EEPROM_OK;" R" b9 q* D; W' ^/ k/ ~* g
  19. }2 K9 Q% q3 h( S
复制代码

- [. W4 C! k3 r6 x# ?! p3 k将I2C_MEMADD_SIZE_16BIT替换为上面新增加的EEPROM_MEMADD_SIZE定义:/ n: f1 s; `( p1 C0 b
  1. HAL_StatusTypeDef EEPROM_IO_ReadData(uint16_t DevAddress, uint16_t MemAddress, uint8_t* pBuffer, uint32_t BufferSize)
    / x8 _9 |/ q4 z
  2. {' v/ B8 X4 _4 `
  3.   return
    ' a5 P9 y" n6 _: a, O: y) x, p
  4. (I2Cx_ReadMultiple(DevAddress, MemAddress, <font color="Red">EEPROM_MEMADD_SIZE</font>, pBuffer, BufferSize));% ?8 z! Z- A: Q: {1 Z+ G5 ?* I
  5. }
复制代码
! h5 k& k8 x# g. Z

, n  b) ?" v- E5 x) Y2.2写操作部分函数修改逻辑:
+ m5 t* C$ d& F* j1 ]; G0 j写操作调用关系是BSP_EEPROM_WriteBuffer负责将写地址分页,并完成页面对齐后,调用BSP_EEPROM_WritePage经过EEPROM_IO_WriteData完成实际写操作。修改部分只涉及到后边两个函数,具体见下面红色部分:
% ]2 p7 g# H2 K
  1.     uint32_t BSP_EEPROM_WritePage(uint8_t* pBuffer, uint16_t WriteAddr, uint8_t* NumByteToWrite) {6 t; t' s1 b3 T7 x9 [( L
  2.     uint32_t buffersize = *NumByteToWrite;
    $ _: [( s1 e7 v- G: ~2 p
  3.     uint32_t status = EEPROM_OK;
    7 @! k5 g$ Q' x$ c; n$ F/ i' e

  4. 8 I" n; G3 s( v* k
  5.     /* Set the pointer to the Number of data to be written. This pointer will be used* I/ P% |- p- b
  6.      by the DMA Transfer Completer interrupt Handler in order to reset the
    6 a  J1 _  J2 u: U6 v! Y' \, r0 f! C
  7.      variable to 0. User should check on this variable in order to know if the/ U0 i' z1 r* P5 Q. W3 U
  8.      DMA transfer has been complete or not. */6 g" S: B* Z; W+ ?" I
  9.     EEPROMDataWrite = *NumByteToWrite;0 D$ c1 h, x- @, v
  10. & s7 v* L( g. P/ s/ S8 E0 Q
  11. <font color="Red">  uint16_t A_EEPROMAddress = EEPROMAddress;  S& _) K6 Z* f. K6 S8 e" x  q
  12.     uint8_t A2A1A0 = (uint8_t) ((WriteAddr >> 8) & MEMADD_MSB_MASK);$ H& [4 U' D, X5 _! r$ ~
  13.     A_EEPROMAddress = A_EEPROMAddress | (A2A1A0<<1);</font>
    , r( l7 M- }, a) }9 L" r3 x. e
  14. ; W0 L: `8 b* J" Q
  15.     if (EEPROM_IO_WriteData(<font color="Red">A_EEPROMAddress</font>, WriteAddr, pBuffer, buffersize) != HAL_OK) {
    1 ]- w1 e3 C, g- X0 ?
  16.         BSP_EEPROM_TIMEOUT_UserCallback();
    % `. Q5 @' g0 ~' k, g( o
  17.         status = EEPROM_FAIL;
    9 q' k. G6 v/ s* l+ t+ D
  18.     }
    ; w! ~  H  r! i. n2 a  V  `

  19. % i0 [4 {, y2 ?& z
  20.     if (BSP_EEPROM_WaitEepromStandbyState() != EEPROM_OK) {9 c. t% ~4 I$ t9 t2 x3 ^% U! s* i8 I* ~
  21.         return EEPROM_FAIL;
    2 t5 E+ J5 J7 L4 c: }' u5 n- w4 [
  22.     }* U' j7 P' t+ A
  23.     /* If all operations OK, return EEPROM_OK (0) */
    7 x+ B2 I* ?3 I+ K+ h
  24.     return status;
    , l- p3 s* F- f% n- {7 N# s7 n4 g3 k
  25. }
    / _# |7 @/ t/ n% M
复制代码

2 p# c+ F/ G- S& {  `6 i2 }具体写操作修改和读操作差不多:: N, `7 L  s, G  B
  1. HAL_StatusTypeDef EEPROM_IO_WriteData(uint16_t DevAddress, uint16_t MemAddress, uint8_t* pBuffer, uint32_t BufferSize)3 a3 d7 r! _5 D( B+ B2 s$ ^1 s' }
  2. {
    , x/ S7 C' r# k
  3.   return7 J9 }3 g  ?& f) u) I; L
  4. (I2Cx_WriteMultiple(DevAddress, MemAddress,<font color="Red"> EEPROM_MEMADD_SIZE</font>, pBuffer, BufferSize));' K: z: X( r8 \! u4 c+ G
  5. }
复制代码

9 l# x  p; Y6 C5 w' d: I- ?0 j7 O. N- C& N  y+ H8 k! V3 G
3. 初始化及其他操作修改。STM32CubeMX已经正确生成了初始化代码,因此除了下面的两个,大部分BSP的初始化代码都可以删除。
+ u& |+ }+ G- \" v- d
  1. void EEPROM_IO_Init(void)
    + V1 R! W$ Q, X2 X, C
  2. {+ Y3 p  x6 j/ @. q2 u# ?
  3.   I2Cx_Init();) K, y$ G$ L0 ]" ?# p
  4. }
复制代码
修改为MX的初始化代码6 \6 t- z* K7 g0 l
  1. void EEPROM_IO_Init(void)0 ?& U- }0 R+ V4 C
  2. {
    " @2 S' E. u( p& h
  3.    MX_I2C1_Init();
    * b2 p  z/ x: H" R% ]  W# `! x
  4. }
    3 F, O0 I1 w7 t
复制代码

7 s7 @* y! ?7 N8 x! F4 K" _  Z修改函数,取消一些不必要的调用:
# x0 w* X+ W1 ?+ c/ m& b5 ^
  1. uint32_t BSP_EEPROM_Init(void) {
    ' O7 q$ @9 _& J& o# N
  2.     /* I2C Initialization */
    - U. ]1 A: k; q* z  v
  3. <font color="Red">//  EEPROM_IO_Init();</font>
    5 u1 j3 S& o& Q) U, }# x- M- y5 G, O
  4.     /* Select the EEPROM address for A01 and check if OK */
    6 d: U( k# N/ @' I7 s, d
  5.     EEPROMAddress = EEPROM_I2C_ADDRESS_A01;* l; O" S# n0 f. H" X7 J2 q) ^
  6.     if (EEPROM_IO_IsDeviceReady(EEPROMAddress, EEPROM_MAX_TRIALS) != HAL_OK) {
    2 x& ?5 m* B1 q' h7 [) I& W, X# R' u
  7. //    /* Select the EEPROM address for A02 and check if OK */
    5 \, y& j- H% p- }( |* K
  8. //    EEPROMAddress = EEPROM_I2C_ADDRESS_A02;& O4 Y! g6 D; ?. r- _+ k6 \
  9. //    if(EEPROM_IO_IsDeviceReady(EEPROMAddress, EEPROM_MAX_TRIALS) != HAL_OK)
    9 q9 I* Q& z/ u# a+ x4 i
  10. //    {
    4 l( \  }/ ~) K0 t
  11.         return EEPROM_FAIL;% A* c, n" X1 s$ D: Z! }
  12. //    }1 v9 Z. [) H; B& [- C
  13.     }' n8 k7 P, q4 o, \2 a
  14.     return EEPROM_OK;: p. N+ j' Y* s5 M* Z) Y
  15. }
    & T% \/ x* Y2 e4 L' [) X
复制代码

& F1 [' z3 M% T  D另外,需要将stm324xg_eval.c中的 heval_I2c 变量实例替换为hi2c1。可以用查找替换或者宏定义。$ D( H1 E$ w1 P' |1 _" Q" w
4. 使用例子- ~5 d5 |" n- w, p) I
4.1 初始化- D7 b8 \# D0 h  z) {
在main.c的  , B+ K/ w8 b  x& Z1 R/ ~
  1. /* USER CODE BEGIN 2 */& @: ~% a5 {; s% b
  2. BSP_EEPROM_Init();. p% B4 [0 o; v9 s4 x
  3.   /* USER CODE END 2 */
复制代码
或者其他适当地方添加初始化设置,完成EEPROM地址的设定。3 n1 P: m+ V: T0 B+ v( R3 v
4.2使用可以参考相应代码。如:5 K; L; F$ }+ P- B4 K3 o$ f
  1. #define EEPROM_CFG_ADDRESS       0x120       //和页面对齐+ Q1 ^+ ~* }& d: d+ B) r$ t' k0 J
  2. __IO uint8_t EEPROMConnected = 1;
    5 _3 ~; `( B: _
  3. typedef struct {% H% ^$ I3 K4 r3 [/ m5 h
  4.     uint16_t valide;( l, ?2 R) m0 s* |& N0 z* l, ?
  5.     uint8_t databuf[16];* R2 o& {2 {- L; ?4 A8 |  L, X
  6. }CfgData;
    % S5 @- D1 r0 ~/ l
  7. CfgData cfg; //全局的一个结构变量
    9 R: y4 j; A  f7 I5 \
  8. /**) D  o% [9 U, N4 g8 p6 z  g$ ~/ W( k
  9. * @brief  从EEPROM 中读出参数
    8 [" T- w4 s* y. i# Z3 m' T" D
  10. * @param  无1 n* [6 r  O+ r# V" O. r3 \
  11. * @retval 0成功 其他失败: 1 通信错误  2 无效设置0 o/ X, n' v, [
  12. */
    2 k$ P& u4 p- j, a$ A9 t
  13. uint8_t read_cfg_eeprom(void) {! w4 O& U! K3 t
  14.     CfgData cfg_read;; M! t5 Z7 `) N2 C$ w
  15.     uint16_t readSize= (uint16_t) sizeof(CfgData);
    # H9 ]9 {$ S# P9 f3 g6 _% m

  16. 5 q, }  T8 p8 m9 d, a- B
  17.     EEPROMConnected = 1; //超时回调函数设定, H- d. _$ z: ]8 K! R3 v1 O7 x, o. ~
  18. 1 C' H0 t# f$ u  d
  19.     /* Wait for EEPROM standby state */6 O$ H0 _: k! D7 v
  20.     BSP_EEPROM_WaitEepromStandbyState();
    9 _: C: a/ O8 E* n) d9 @: t6 ]
  21. 9 [( x  t* ]$ _+ }* d
  22.     /* Read old parameter in EEPROM */8 u. d9 S1 U$ ^; x( E
  23.     if (EEPROMConnected == 1) {
    1 @+ p1 w* {# n! h; B" y# U; \
  24.         /* Set the Number of data to be read */
    7 a( G% I% T3 J1 z. _
  25.         memset((uint8_t*)&cfg_read,0,readSize);
    9 }3 [0 T/ Z2 H+ K$ g4 l4 x. e
  26.         /* Read from I2C EEPROM from EEPROM_CFG_ADDRESS */
    % l# }& l) [5 h% M' q6 |/ d
  27.         if (BSP_EEPROM_ReadBuffer((uint8_t*)&cfg_read, EEPROM_CFG_ADDRESS, &readSize) != EEPROM_OK) {
    . u$ y( C" H5 d) M1 c
  28.             return 1;  //通信错误% k% c3 p1 ~% R
  29.         }
    ' l9 ^8 ?, Z: u6 @
  30.         if (cfg_read.valide == 0x55aa) {  //有效的/ o# r1 u8 h, F
  31.             memcpy((void*)&cfg, (void *)&cfg_read, sizeof(CfgData));
    * L1 g1 w5 P* r+ T6 K
  32.             return 0; //正常
    # C! [& H5 X6 g! t+ M/ P
  33.         }
      G8 S5 t6 }) x2 t2 h
  34.     }' }# I7 W  w: b# d  i, t9 S! |) P
  35.     return 2; //无效设置! _1 _8 s; ]1 Y5 L$ v4 F& g
  36. }
    / a) K" q0 q8 D
  37. /**
    ( `4 N) Y  G3 {3 r; ?: U
  38. * @brief  保存参数到 EEPROM 中8 A0 X! p6 P0 {6 l8 U  ]; a
  39. * @param  无
    2 s' t8 M: N$ ~. B$ h
  40. * @retval 0成功 其他失败
    . ?$ o1 j3 D  L4 |, e$ C
  41. */3 f* o1 p' y( j- N# z
  42. uint8_t save_cfg2eeprom(void) {
    + u7 I) R. `, L; `5 H
  43. : ~/ _* r+ n- V+ o9 R
  44.     EEPROMConnected = 1;
    4 _7 Z& T; t  N: V/ a" v

  45. & M6 b3 q7 M/ V
  46.     /* Wait for EEPROM standby state */
    3 @% U- P" L0 p: A# b
  47.     BSP_EEPROM_WaitEepromStandbyState();: D& b: J  e  ]9 |' m  K* Y

  48. 5 w5 [) w6 `8 }- ~7 b; ^
  49.     /* Write new parameter in EEPROM */
    / M9 y$ {9 T% v5 r; e: j
  50.     if (EEPROMConnected == 1) {
    / s2 r, ~/ T0 I$ c' P
  51.         /* Write on I2C EEPROM to EEPROM_CFG_ADDRESS */0 t( B# L8 _# C+ [7 J) ^2 o( z
  52.         if (BSP_EEPROM_WriteBuffer((uint8_t*)&cfg, EEPROM_CFG_ADDRESS, (uint16_t) sizeof(CfgData)) == EEPROM_OK) {
    : o# ^4 M7 X! {
  53.             return 0;/ t2 P2 p' ^* y1 _
  54.         }3 Q3 F4 P* }: _' ^  }2 |
  55.     }9 C) _  Y! B: p- Z  c- U
  56.     return 1;- `- J% f3 Y: D/ U! ^( x
  57. }
    9 E2 h% N6 i" D2 B" C1 |' Y) K
  58. /**
    1 Q1 X& D/ J2 w( ?  ~4 W
  59. * @brief  Basic management of the timeout situation.: f+ Z3 I9 b% O' z0 |
  60. * @param  None.
    $ X# Z5 d  M. H) c2 \
  61. * @retval None
    ' a  V- r$ l5 b7 d
  62. */! f) m; n) i+ W, v  B5 W
  63. void BSP_EEPROM_TIMEOUT_UserCallback(void) {3 O! g6 r( u6 s3 g$ p& L- t7 C; l  h
  64.     EEPROMConnected = 0;
    - v, b/ ?% b6 {! Q
  65. }
    & c) g7 p) h4 g* T1 C2 I, U

  66. ' I' P( f+ j& j' B+ x/ F* `' [
复制代码
经过调试并下载到核心板运行,测试结果正常。所用软件环境:: z$ A/ `/ b* d
STM32CubeMX 4.15.0
! S! n% \6 T+ I4 ?8 `: ?+ lSTM32Cube_FW_F4_V1.12.0
+ S# C2 Q! O9 {2 b. h3 q) O: F0 rGNU ARM C/C++ for eclipse 2.5.1.201604190915
0 Z& ?. Q0 f2 i% _- j% Ieclipse 4.4.2 luna
6 P! K$ G7 [- wDebian sid: Linux Debian 4.5.0-2-686-pae #1 SMP Debian 4.5.4-1 (2016-05-16) i686 GNU/Linux5 n( Q* _2 X, }
3 @* s  [! ~7 f( W7 s- ^

& k- J2 p. p- T5 o: v) c/ V
赞 收藏 评论3 发布时间:2016-5-30 19:31

举报

3个回答
giveup 回答时间:2016-5-31 08:04:02
红色字体显示出现问题。应该能看懂。
anny 回答时间:2016-5-31 08:26:55
谢谢分享
wbaojiang 回答时间:2020-3-23 11:56:38
感谢!

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版