IIC(Inter-Integrated Circuit)是一种常见的串行通信协议,广泛应用于各种嵌入式系统中,包括STM32单片机。在STM32中,IIC通信可以通过硬件IIC和软件IIC两种方式实现。本文将介绍IIC的基本原理,然后重点探讨STM32中硬件IIC和软件IIC的区别。/ V9 _* O! m! M: z
* ]7 I. L7 S3 a, c
2 |% d/ S0 }6 R3 u r( x0 \
6 t- t: {7 M6 B: d& E4 h. x4 l
IIC基本原理:IIC是由飞利浦(Philips)公司提出的一种串行通信协议,适用于在同一电缆上连接多个设备。它采用两根线进行通信,即SDA(数据线)和SCL(时钟线)。设备之间通过这两根线实现数据传输,其中SDA用于传输数据,SCL用于同步时钟。
+ r, U! N: g1 z
0 {5 g ^6 c* X
) `1 q$ I, w7 {( l8 c
+ u1 O! S9 ?1 _2 H6 u- d
一对IIC总线上面可以挂载多个设备并且多个设备之间有不同的地址,所以我们可以根据不同设备的地址来实现不同设备之间的通信。6 S4 \& `9 I' h& d6 o
" M8 N: { @+ I5 e- S( e
软件IIC
0 t5 [. F" W! n' Q9 T( `在STM32中,软件IIC是一种通过程序控制GPIO口模拟实现IIC通信的方法。这种实现方式常用于一些资源有限的应用场景,或者在需要更灵活控制IIC通信时使用。) ^5 E: \8 j1 x2 P' V# b; @& i
0 Z, O# F& x3 b: |- W2 H
$ J l$ G9 i$ f- X# ~+ J
' E) p2 t, J1 n" ?9 l* d% X总而言之,软件IIC是利用GPIO的翻转,一个IO模拟SCL线,一个IO模拟SDA线实现IIC通信协议的实现。2 p+ f/ r# l; ?# X' I3 {
5 N/ Z8 T' L! Z; e- H L
软件IIC不需要对IO有特殊的要求,只需要两个普通的GPIO即可实现,因此较为方便也方便移植,不同设备只需要重写IIC的基本通讯即可。
a0 k+ Z ]1 {9 u0 b- // 定义IIC的GPIO口和引脚/ E H6 ]* A" x! J! v2 o
- #define IIC_SCL_PIN GPIO_PIN_63 F0 F6 }: R6 l. H
- #define IIC_SCL_PORT GPIOB
1 y5 }( N& `! R# \+ A
* f( |7 S/ o7 \5 U- # T6 \# G7 ?9 g7 \- M5 `
- #define IIC_SDA_PIN GPIO_PIN_9
9 n* u/ g7 L0 c# U - #define IIC_SDA_PORT GPIOB
: U- c( U# y) p+ x5 I: p - ' v4 I9 y. \ `) \
- 9 `2 ?8 L" t* a( R- A2 f4 g
- // 定义读写控制位/ }& W3 t4 n% v5 K$ a# f
- #define IIC_READ 1+ r& w3 v5 A, C* d. y
- #define IIC_WRITE 0* q/ }4 M' W8 ~* r1 H1 t0 T
- ( }0 f T% g1 c6 c5 H
- 1 w. @8 i) o9 K" D" ^
- // 定义函数4 P) r% R- Q0 I# C! J
- void IIC_Start(void);; b/ h9 M$ _9 i# e0 ?3 A( m# V, M
- void IIC_Stop(void);
$ p$ S% L }, F% Y: Q* p - void IIC_SendByte(uint8_t byte);" Z7 R4 y$ K# @* @3 F
- uint8_t IIC_ReadByte(uint8_t ack);7 w* l! q8 L( G" ?- s: n
% ?( h+ R6 H, m# Z+ {, y5 E* B0 [9 d- ) ?1 V5 C6 D3 s6 n0 u
- & `0 C. b A5 F4 V* T' i+ f
$ u: Q$ T h) p) p+ v- // 启动IIC总线9 e* u. p: ? N& l0 N
- void IIC_Start(void)
1 k" H+ `! |6 i( p r0 L. H - {
: t. l$ Z* O$ Y - HAL_GPIO_WritePin(IIC_SDA_PORT, IIC_SDA_PIN, GPIO_PIN_SET);
' k, G8 Y% S% x! x, ? - HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_SET);
: ]" x% M5 f3 q, i - HAL_Delay(2); // 稍微延时,确保时序正确8 ?: H+ h$ e: j9 z r* s
- HAL_GPIO_WritePin(IIC_SDA_PORT, IIC_SDA_PIN, GPIO_PIN_RESET);
' T( D2 M' I. T; r0 G4 O - HAL_Delay(2);
8 q, w6 n, N* C - HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_RESET);
4 m) p; B8 G1 F$ Q S - }! d. I' C9 d4 y3 ^
- 4 M; J8 c$ s/ I
; T! \) T& q3 Q& I1 z- // 结束IIC总线7 i) b0 Y+ ? C' m& P2 p9 z# V8 x
- void IIC_Stop(void)) P, T" f% S& e4 u4 f
- {
6 S: H G: J) f! A1 o" P - HAL_GPIO_WritePin(IIC_SDA_PORT, IIC_SDA_PIN, GPIO_PIN_RESET);
4 M2 e) T# B+ @( y - HAL_Delay(2);
; p7 \/ Z# U' O5 c' N2 [ - HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_SET);
9 c% q1 T. _* V0 Q$ w/ v - HAL_Delay(2);$ q0 \$ s1 ^$ k0 c
- HAL_GPIO_WritePin(IIC_SDA_PORT, IIC_SDA_PIN, GPIO_PIN_SET);+ E* |5 _9 c0 Q4 M& Z5 e) r' m* T
- HAL_Delay(2);: h. k7 R4 K; [1 |7 `% A3 y& l
- }! S% W3 _ a6 Q: {9 O# l8 N9 v! v
- 9 ]! j+ D# z* X$ U5 T$ @% `
4 w9 B! G/ p( Y# J- // 发送一个字节( M y( q) r2 ]6 b6 W
- void IIC_SendByte(uint8_t byte) C; O( S( Q( }% e2 q
- {
' x' \* |& F* S3 A d4 X - for (int8_t i = 7; i >= 0; i--)& |9 V. g) T, s/ P W, v4 D
- {5 }, a' X; z" @4 t; w
- HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_RESET);0 w1 L" R3 |+ T
- HAL_GPIO_WritePin(IIC_SDA_PORT, IIC_SDA_PIN, (byte & (1 << i)) ? GPIO_PIN_SET : GPIO_PIN_RESET);
, }2 h+ I% Z3 l - HAL_Delay(1); // 稍微延时,确保时序正确# o( y! A# u. C( Y/ `1 P- \' W- `
- HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_SET);
. X+ w9 r* h0 A) B - HAL_Delay(1);) l( r- b* v8 V) U3 X `/ W
- }( }: K: v0 `. u. T2 s
- HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_RESET);( x& a& D7 y0 x1 [4 }# r; ?* K1 m3 l
- }5 K6 M# s A2 G( N8 f/ G7 |# Q
- 5 W5 @+ _1 Z! S* X
# s4 _5 z8 E5 E- // 读取一个字节1 G5 ? T5 F* a# A& d" @
- uint8_t IIC_ReadByte(uint8_t ack)0 k0 [ j* v( W
- {# ?) a" k/ k v _5 ^, b. A* X3 o; \
- uint8_t byte = 0;
, g: k. O: x6 G W( { - ! ^7 T) I" `6 [) T
- HAL_GPIO_WritePin(IIC_SDA_PORT, IIC_SDA_PIN, GPIO_PIN_SET);+ b# b" V3 ^/ I6 ~4 u5 N0 j
- for (int8_t i = 7; i >= 0; i--)
- B, Y7 B" e4 R - {9 G2 G. K9 P: b
- HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_RESET);
( f/ T/ P) z. u0 g1 ~6 D. n - HAL_Delay(1); // 稍微延时,确保时序正确7 w, [( B2 I: W' H9 U* ~% \) q0 q# @( [
- HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_SET);9 W: J7 i6 Z9 Q7 z& [# _, |5 Q
- HAL_Delay(1);: x( L" d* r7 M% f, s
- byte |= (HAL_GPIO_ReadPin(IIC_SDA_PORT, IIC_SDA_PIN) << i);) ?, t# ^: O. W% ?- B" P
- }0 V2 H& e$ e) z
-
6 B4 e- u2 |2 x - if (ack)
, y3 M# k6 G2 }' P0 G' M - HAL_GPIO_WritePin(IIC_SDA_PORT, IIC_SDA_PIN, GPIO_PIN_RESET); // 发送应答
# }* u$ d* e+ [ - else
1 ^5 p+ m$ Y9 [9 t - HAL_GPIO_WritePin(IIC_SDA_PORT, IIC_SDA_PIN, GPIO_PIN_SET); // 不发送应答
4 |* E9 P% R$ z" u -
* x8 Q+ u. a9 d9 q$ L3 l/ ? - HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_RESET);
; A }0 a. i% c" R - HAL_Delay(1); // 稍微延时,确保时序正确
- r" P! t" r0 T3 T8 ?! P! ^ - HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_SET);
2 m8 z2 h5 _; L" c1 _) X - HAL_Delay(1);
4 @ m) j7 g* B$ d% n - 5 \+ N: Y! \9 j1 m& f( C; `
- return byte;& y7 b0 S' ~" X# H
- }
复制代码 ' s. K$ _/ _# H# c8 x5 i
例如IIC的启动信号为:SCL为高电平的时候,SDA从高电平转为低电平。$ {/ J5 ^$ @0 O
& U7 n; M' [5 z9 c& r* S; qIIC的结束信号为:在SCL线是高电平时,SDA线从低到高的跳变。) q/ K. u8 a* W4 `# J# C) ]
3 @. N2 l6 Q& V
因此使用这种方法可以模拟IIC时序完成通讯。. m. j% @; m2 H# z4 w4 V
0 r( M2 \4 X" s; U" W. w但是这种方法的弊端也是非常明显的,需要占用CPU,其次这样子并不能确保I2C时序的正确性。所以无论是稳定性还是可移植性还是IIC的效率都欠妥。% C# w) O" g+ h+ Y
* u* i/ [6 j" S* aSTM32中的硬件IIC - ?6 W( D7 f8 C, o1 u- r
STM32系列单片机提供了硬件IIC(Inter-Integrated Circuit)模块,用于支持IIC通信协议。硬件IIC通过专门的硬件模块实现了IIC协议的基本功能,包括起始信号、停止信号、时钟同步等,无需用户通过软件模拟实现。
/ b6 a/ } j* Z3 u) j/ U4 f4 V8 P' G' @. p
STM32硬件IIC优势:( C: R" O- q( }* m4 F
1.高效性: 硬件IIC使用专用硬件模块处理通信,不需要CPU直接参与,提高了通信效率,降低了对CPU的负担。1 ]3 d5 y' g8 D8 l+ S6 H) e7 Y
2.时序精确控制: 硬件IIC模块能够精确控制时序,确保通信的稳定性,避免了由于软件执行时间不确定性而引起的时序问题。
) D6 E' W$ \4 t" h/ D! ^& I3.多主机模式支持: 硬件IIC模块通常支持多主机模式,能够轻松处理多个设备同时访问总线的情况。! {8 f' O* _$ Q) R/ K% E
4.易用性: 由于硬件IIC是芯片内置模块,使用起来相对简单,只需配置相应的寄存器和引脚即可。% L/ F2 x: ?) a5 Y- H! p
5.占用资源少: 硬件IIC模块通常占用较少的资源,对于资源有限的嵌入式系统来说,是一种较为理想的选择。) |+ D& s% v. F( G0 k- I1 y
5 S; T6 h. T5 q1 _' j/ E2 q
( V( d- O2 l% n3 u# ~, y6 w7 R3 `) M, H6 A6 q! Z1 o- N7 G
在CubeMX中配置好硬件IIC即可利用HAL库函数进行IIC通讯。- static void MX_I2C1_Init(void)
/ B2 `7 Z4 @/ n; u2 @ - {
" [0 r& }9 L2 v1 h. ?) d - hi2c1.Instance = I2C1;$ Y5 U+ Y, O# ~# v$ {8 m
- hi2c1.Init.ClockSpeed = 100000;
2 c! O+ C: m4 @4 g, ~& \ x' | - hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;6 U1 V% q- G/ m: W. v& V4 n
- hi2c1.Init.OwnAddress1 = 0;* W7 d, V6 y! h. N7 V
- hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;" T- E* l8 S1 w: {# w- }8 i: B5 [
- hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;, i: \; S5 K1 ^6 r) L0 a
- hi2c1.Init.OwnAddress2 = 0;- r3 S# e: n* b# D6 S
- hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;9 C/ t: [. `% l5 e$ B
- hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
8 K4 j$ l4 t' S9 i2 Q G. \ - HAL_I2C_Init(&hi2c1);
1 O2 h! P* h/ o# U+ U' A: W! r7 A - }
/ t4 ]% k- B3 p8 G( Z( e0 r& k
# u1 h! M7 r& y; D' ?* y8 R4 v
) A* `$ \6 r) H- uint8_t deviceAddress = 0x50;//设备地址
* t) f4 r' S$ F; s - uint8_t registerAddress = 0x00;//寄存器地址
6 {% [& R, {7 ?' x3 ^4 h; u - HAL_I2C_Master_Transmit(&hi2c1, deviceAddress, ®isterAddress, 1, HAL_MAX_DELAY);
复制代码
0 v d, r! X \4 Z V9 \调用CubeMX的初始化函数,之后就可以使用例如HAL_I2C_Master_Transmit,HAL_I2C_Master_Receive等函数来实现IIC通讯。+ K) T, O" O5 ]4 D
& m2 I" l5 x! E0 L, H \8 }) A* @2 j+ \, G6 F$ m: `: J& t
STM32硬件IIC缺点:4 l$ i2 G" C6 v! ]) `/ | Y/ L3 R
1.资源占用: 相比软件IIC,硬件IIC模块可能会占用一定的硬件资源,因此在资源受限的系统中需要谨慎选择。
5 j) E9 }' ]2 g% n- ]! \# B' z3 r2.灵活性: 硬件IIC通常由芯片内部硬件模块实现,因此在一些特殊场景下可能缺乏一些灵活性,例如无法更改时序。
1 S$ |+ ^" H( h- f& F8 U# r$ |1 I3.部分限制: 不同型号的STM32芯片的硬件IIC模块可能存在一些差异,不同芯片的IO不一样,所以设计时需要谨慎检查。
) h) S h- e( N6 T$ D% e
/ f" M" k3 X; s; S8 _总体而言,STM32的硬件IIC是一种高效、稳定的IIC通信方式,特别适用于对性能要求较高的应用场景,同时在易用性和可靠性上有较大优势。在选择IIC通信方式时,可以根据具体需求权衡硬件IIC和软件IIC的优缺点。
4 b" |1 Z" W7 o& E7 S7 O* q p# B, S1 [7 z+ l
4 J% G4 M0 W5 L/ Z
* w- K2 k5 U& Y* r3 K3 O2 _转载自:电路小白
4 f: u2 E% }4 U如有侵权请联系删除$ ?' ?1 k' g# f, C% w
8 s2 i! a& q7 e' x. Y+ A$ t |