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

STM32的硬件I2C有BUG吗?

[复制链接]
STMCU-管管 发布时间:2021-5-20 16:47
坊间一直流传着一个传说~STM32的硬件I2C设计有BUG,最好不要用,用软件I2C比较靠谱。长久以来,为了不必要的麻烦,我也一直没有用过硬件I2C,主要是软件I2C也比较方便,基本上任意端口都可以用。, l) K2 _+ _4 ^' p- f
8 ?+ V" f* M1 O
最近画了块板子,正好用到了I2C,就顺便来测试一下硬件I2C是不是真的像有些人说的不好用。
1 p& Y9 f5 o6 f# u! e2 Y* N! N* Z4 N1 |
测试硬件:STM32F407VET6+AT24C64% d* @: J" K6 |# U( f: ], `( q$ @
测试软件:STM32CubeMX v6.1.1; _1 w; G' {. ?0 U- B4 x
HAL库:STM32CubeF4 Firmware Package V1.25.2 1 Y4 A! @+ O! l! \) V0 \; y

. \7 V& D3 Q& _
/ x' @+ D5 {& `4 F' b+ r! Z
STM32CubeMX配置
# ]. r7 b6 L  M* k$ S
, b  [6 z6 Q" `$ @" A
" _" f$ ^* p! |
使用STM32CubeMX配置很方便,时钟等基础配置不再详细介绍,直接看I2C配置如下:
  q5 B5 g) y* j9 c" g  G4 C  `
1.png

- ~& P! x: W) ~  A这里的速度模式选择为标准模式,时钟为100K。要求高的可以选择Fast模式,400K时钟。! `% ^, Y& P3 T7 G( ]1 t

& c; t7 ?: W# b

3 ]) N5 ?  j! d5 }配置完成后生成代码。
/ Q  m: ^" Y. p. B7 J1 C; L! W' L5 ^, o) \+ v' \
2 Y6 k/ W7 T. q4 L8 V
编写代码" B( |7 V( y  w2 O

* n% g  w; a' V3 ]3 ]- m
$ Y/ |& }5 [2 t8 t" j4 {$ ?
代码生成后,直接调用读写数据的函数即可:- i; V5 r' v% H/ d; t
* _: I. I2 q3 Z% h
! P2 n. }. R! y  t3 h
HAL_I2C_Mem_Read4 q0 x! `6 p7 K- O0 P
HAL_I2C_Mem_Write2 ?0 c1 \9 M# N; ]! B5 W
8 p7 v; ^0 R' {

! c# |. ~' ?; n* i! @# ^函数参数可参考代码注释。/ C$ S8 W1 d  l- m3 G; `- ]

+ n" n' k3 x) R( D

& {" r% S1 T) X1 G% r* G2 @24CXX系列的EEPROM进行写操作时需要注意,跨页写入时,要有一定的延时,否则会写入不成功。不同容量的页大小也不一样。
0 z5 u$ A/ ]0 r; |" C6 s, e: f  A: N7 K4 A: K

1 ?1 e9 a. j. J4 V7 ^( U另外,24C16以下容量的地址为8位,24C32以上容量的地址为16位,在调用读写函数时需要注意,选择I2C_MEMADD_SIZE_8BIT或者I2C_MEMADD_SIZE_16BIT。测试使用的是24C64,所以选择I2C_MEMADD_SIZE_16BIT。
+ t' v2 v$ n% Y: F5 d6 d# a9 C
5 l' z! X6 T: h4 O( O( I
" M  F# q% S0 d9 i& {8 F0 W
为了方便操作,将读写函数再封装一层,将跨页写入的各种情况都考虑到,实现任意地址连续写入。程序如下:: F: ]: I2 d6 Z

  1. $ v7 L, f- ?( n0 V/ R, U5 a) b. v
  2. #include "at24c64.h"" _+ c- D6 c* N) C9 O0 m" Z
  3. #include "i2c.h"$ H3 j4 h0 z' z% K0 F

  4. & M6 f* X1 r6 {5 ]) u+ g# _1 B
  5. #define AT24CXX_ADDR_READ   0xA1
    : y+ L" Z7 b7 X' j8 F3 y- c
  6. #define AT24CXX_ADDR_WRITE   0xA0
    # @% G8 K8 U  E- P; y; y
  7. #define PAGE_SIZE   32
    0 S3 U4 D7 C# h- ^. q
  8. /**
    . L) W9 I' ^- N6 g' [  s6 T
  9. * @brief        AT24C64任意地址连续读多个字节数据0 O% v: K  R$ Y& i
  10. * @param        addr —— 读数据的地址(0-65535)
    " C( _' I  h* n6 _
  11. * @param        dat  —— 存放读出数据的地址6 m0 J5 a7 [( C+ m1 Y
  12. * @retval        成功 —— HAL_OK
    3 W* {- F3 S+ K$ ~' e( o
  13. */
    4 P( }& ?$ p/ b
  14. uint8_t At24cxx_Read_Amount_Byte(uint16_t addr, uint8_t* recv_buf, uint16_t size); W# ?, U& ~, c( g. M. x8 O
  15. {
    - h: o8 N, ]. ^4 J" G" l  l
  16.     return HAL_I2C_Mem_Read(&hi2c2, AT24CXX_ADDR_READ, addr, I2C_MEMADD_SIZE_16BIT, recv_buf, size, 0xFFFFFFFF);
    # W& E" K1 F( m! {; {6 c' F9 o
  17. }
    9 Q' q. s2 w  ?

  18. 3 I% X# L  O" F3 h
  19. /**! `! j- N& _1 B4 U. F
  20. * @brief        AT24C64任意地址连续写多个字节数据
    % Z! F/ n0 g/ F) k) r9 b, ~* H
  21. * @param        addr —— 写数据的地址(0-65535)
    * X" c# e% \6 [6 C  Y- [, c8 _1 D
  22. * @param        dat  —— 存放写入数据的地址
    # f, r. {5 J- u( x  v9 Z& W
  23. * @retval        成功 —— HAL_OK
      C/ R4 x$ d' y2 r: c
  24. */
    % V# ?9 g1 t+ P
  25. uint8_t At24cxx_Write_Amount_Byte(uint16_t addr, uint8_t* dat, uint16_t size)
    * F3 g* }+ M0 q0 m7 y! N$ q
  26. {- f+ |# L  D* }* b' O
  27.     uint8_t i = 0;( _/ r- v& m5 G6 [* o+ L
  28.     uint16_t cnt = 0;        //写入字节计数
      t3 ]% F0 C% a
  29.    
    # E, v+ G3 [5 m" I
  30.     /* 对于起始地址,有两种情况,分别判断 */. i8 u3 o7 \& S3 u9 e  K/ g; i
  31.     if(0 == addr % PAGE_SIZE )- @; X( l$ I" s
  32.     {
    * H) [# G* D4 t
  33.         /* 起始地址刚好是页开始地址 */3 n  E7 ?( M* r6 U! P: C/ x
  34.         " E7 R- u5 j3 Q5 R' d
  35.         /* 对于写入的字节数,有两种情况,分别判断 */
    & a1 J$ u1 I5 ^# v3 k
  36.         if(size <= PAGE_SIZE)
    % v& _/ [! |7 r' W
  37.         {' y' {4 n" o. `( @
  38.             //写入的字节数不大于一页,直接写入
    9 f4 I, B+ @, L% ^! h) t7 e
  39.             return HAL_I2C_Mem_Write(&hi2c2, AT24CXX_ADDR_WRITE, addr, I2C_MEMADD_SIZE_16BIT, dat, size, 0xFFFFFFFF);8 V, U- y# ]7 y6 X( |/ ?  q6 D
  40.         }- a1 d- r. b1 w1 l1 T* p3 `
  41.         else8 i1 l: q1 i3 V4 S
  42.         {# s/ L: \4 A' g% i- m/ z
  43.             //写入的字节数大于一页,先将整页循环写入
    ) q$ X) B5 g. D4 @
  44.             for(i = 0;i < size/PAGE_SIZE; i++)9 Q0 q: N" ?1 X8 h# D+ P; s! H- y
  45.             {
    - r# ^  V, d* ^' J: F+ K
  46.                 HAL_I2C_Mem_Write(&hi2c2, AT24CXX_ADDR_WRITE, addr, I2C_MEMADD_SIZE_16BIT, &dat[cnt], PAGE_SIZE, 0xFFFFFFFF);* w3 c7 h% f" Z" n, @
  47.                 HAL_Delay(3);
    1 K3 y2 r, s( T, o/ {( S/ \
  48.                 addr += PAGE_SIZE;
    * u- V. `3 ^* [  t3 o" E# n
  49.                 cnt += PAGE_SIZE;. M* f/ n- r: O1 u9 G7 Q* n
  50.             }
    2 ?! A, R; `9 |) S& L+ I1 b
  51.             //将剩余的字节写入5 Z- J3 q4 P/ h) B6 A! h- E2 o0 a
  52.             return HAL_I2C_Mem_Write(&hi2c2, AT24CXX_ADDR_WRITE, addr, I2C_MEMADD_SIZE_16BIT, &dat[cnt], size - cnt, 0xFFFFFFFF);- `0 L% n/ [2 E9 P9 E& z
  53.         }- G) \0 J: \, K4 V/ n
  54.     }
    6 l# _, O' i# E1 M0 a  @
  55.     else" I/ }: l9 E' X" v3 H3 w
  56.     {
    # ~7 {% }" a, k4 X6 J
  57.         /* 起始地址偏离页开始地址 */7 m+ q& l* s7 Q- L9 q
  58.         /* 对于写入的字节数,有两种情况,分别判断 */
    " l9 `' N) k0 ~# ]
  59.         if(size <= (PAGE_SIZE - addr%PAGE_SIZE))0 ^; g' c0 @' r/ m' _
  60.         {( z7 o5 u' u& w! E- a
  61.             /* 在该页可以写完 */
    3 z. E0 K- k/ E1 K5 I+ {
  62.             return HAL_I2C_Mem_Write(&hi2c2, AT24CXX_ADDR_WRITE, addr, I2C_MEMADD_SIZE_16BIT, dat, size, 0xFFFFFFFF);
    6 q* S' f8 i* P& R7 v4 U5 q
  63.         }
    ' k  D  P6 i0 c; F8 y8 q. t$ }
  64.         else
    1 J9 W: M- G# e
  65.         {
    3 a* o# n9 {/ R) x! b- C4 {
  66.             /* 该页写不完 */
    + h1 n- Y" Q* S" j  L: N
  67.             //先将该页写完
    ; C) g( L3 ~. v2 M) ]1 @5 Q0 t
  68.             cnt += PAGE_SIZE - addr%PAGE_SIZE;
    . B# Q9 I( {# j% V
  69.             HAL_I2C_Mem_Write(&hi2c2, AT24CXX_ADDR_WRITE, addr, I2C_MEMADD_SIZE_16BIT, dat, cnt, 0xFFFFFFFF);% |1 I9 d8 S/ X  d) U
  70.             addr += cnt;; W) f2 c+ x4 G0 S- P
  71.             HAL_Delay(3);0 M6 ~. M2 _- A8 A" `8 z
  72.             //循环写整页数据$ f& @" F# c. L* V. Q5 g8 x
  73.             for(i = 0;i < (size - cnt)/PAGE_SIZE; i++)
    + N) h  \% T; R, M1 P; ?
  74.             {
    ' {0 `' w$ P$ R* O7 r
  75.                 HAL_I2C_Mem_Write(&hi2c2, AT24CXX_ADDR_WRITE, addr, I2C_MEMADD_SIZE_16BIT, &dat[cnt], PAGE_SIZE, 0xFFFFFFFF);: m1 p) b  ~( r' d/ U) I
  76.                 HAL_Delay(3);0 G( Y3 F  u$ H- k9 j
  77.                 addr += PAGE_SIZE;) M7 d! m* h; x. O2 _" ?# Z
  78.                 cnt += PAGE_SIZE;
    ( A: p; b5 e2 E. F6 G* X; @) c
  79.             }
    ; H$ J9 q* G9 E$ c% L
  80.             7 S/ Y  X8 R/ Y( \2 \
  81.             //将剩下的字节写入
    ' q) j  J, _7 t( b
  82.             return HAL_I2C_Mem_Write(&hi2c2, AT24CXX_ADDR_WRITE, addr, I2C_MEMADD_SIZE_16BIT, &dat[cnt], size - cnt, 0xFFFFFFFF);" j8 ~) m3 X: N- U" |6 ~* ~) y; k
  83.         }            & D+ ?2 Y6 Q4 X. v: I
  84.     }; Z# e* H; b$ R1 I+ }8 V
  85. }
复制代码
测试结果
) ?0 g  L1 a/ b: V- C
" s, f1 \- G0 g$ |8 U" R+ l0 A

% U) M, f/ T* b7 ]$ ~经过测试硬件I2C读写EEPROM正常。没有发现所谓的BUG,当然这只是M4内核的针对EEPROM一种器件的测试,对于其它内核(M3等)和其它I2C器件,还有待验证。  E& H( g7 |+ T0 E& i: a% h
7 \* f$ y6 R- d( Z! D2 S

! [7 Y7 _" V6 X- F* G总结
$ k7 Q9 T6 y3 i- u* e
/ R# |4 Z4 q8 ^3 J, ^! e
4 z5 `# p; H2 n2 Z( y9 _
硬件I2C使用起来比较简单,不需要自己去调节时序,但是只能使用固定的几个引脚。" x3 M9 U- C/ ~  Q
2 T, v" |$ U1 Z- r( p# J1 P7 E# K4 V
+ O6 g% `& i% `- r  `6 L
软件模拟I2C可以使用任意引脚,针对不同的MCU,移植起来比较方便,但对于不同频率的MCU,时序调节比较麻烦。% y3 r4 T& y7 O" h3 N# {

' p( u8 H8 c2 r1 t$ p6 {, P8 H

! {; G6 {9 P& q; d8 {% Z, E两者各有其优缺点,需要根据实际需求去选择。/ g; E) v9 _& A  i6 Z$ K
/ w0 N& J, P0 h* k: Q1 f
赞 收藏 评论0 发布时间:2021-5-20 16:47

举报

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