IIC(Inter-Integrated Circuit)是一种常见的串行通信协议,广泛应用于各种嵌入式系统中,包括STM32单片机。在STM32中,IIC通信可以通过硬件IIC和软件IIC两种方式实现。本文将介绍IIC的基本原理,然后重点探讨STM32中硬件IIC和软件IIC的区别。
% q0 R! B" H" w S" N. D. j" H3 N; O' e& h0 P8 k! T* K
' v+ l) z( G5 t" }3 {7 X5 r
+ [1 |# y0 r0 B
IIC基本原理:IIC是由飞利浦(Philips)公司提出的一种串行通信协议,适用于在同一电缆上连接多个设备。它采用两根线进行通信,即SDA(数据线)和SCL(时钟线)。设备之间通过这两根线实现数据传输,其中SDA用于传输数据,SCL用于同步时钟。 p* _5 x/ s2 X+ Y' l
6 B: a- L8 Q' X9 @1 M9 x
8 l ?8 |1 H) s0 x, U8 ~& S* e3 r9 _ `& J h$ h
一对IIC总线上面可以挂载多个设备并且多个设备之间有不同的地址,所以我们可以根据不同设备的地址来实现不同设备之间的通信。) g& D5 `: b% Y3 S4 p
$ F0 [' H5 a$ l0 P' W9 l
软件IIC
7 O0 T. t7 Q' ?6 A在STM32中,软件IIC是一种通过程序控制GPIO口模拟实现IIC通信的方法。这种实现方式常用于一些资源有限的应用场景,或者在需要更灵活控制IIC通信时使用。2 x2 I$ y+ V- h4 \. c
4 z" G! }6 ~, U, O7 J
+ g( g" c* O: R# }7 Y& V) c
4 D% q, L$ X0 P: v/ ^7 i) k总而言之,软件IIC是利用GPIO的翻转,一个IO模拟SCL线,一个IO模拟SDA线实现IIC通信协议的实现。9 t. ?9 S$ f9 S2 \
/ g, P6 L. p& l G9 q软件IIC不需要对IO有特殊的要求,只需要两个普通的GPIO即可实现,因此较为方便也方便移植,不同设备只需要重写IIC的基本通讯即可。+ A1 h4 ?, a' N: N, p( q
- // 定义IIC的GPIO口和引脚
# x9 D. m0 X2 N2 L - #define IIC_SCL_PIN GPIO_PIN_6
9 {! x: t: B0 q7 o( f# S' k' ~& N - #define IIC_SCL_PORT GPIOB
/ l6 y; E% E1 M' Y' I5 }
" ^; e( p# b/ F6 m7 m/ `; W, s. H- 9 `% O! Q' x$ e2 O, O% Z! k
- #define IIC_SDA_PIN GPIO_PIN_9
$ T% K: n1 i {. t# g - #define IIC_SDA_PORT GPIOB
% i# z k6 K" J; }" z) P% _ - ! Q. F6 `) E! C! x+ ~3 H" q
( ^7 R! B4 C' h$ ?9 b- // 定义读写控制位0 V' ^, a/ } I: r
- #define IIC_READ 1
* F: Z' k1 a+ C, N, [ - #define IIC_WRITE 08 h: @2 Q0 c9 I- `
- 8 @3 }. p+ p+ N* A2 J" O5 _+ ^0 X9 R
- + ?9 G3 D Q0 f/ M9 w' k4 c
- // 定义函数
; H6 [! h9 `' e; x3 ^ - void IIC_Start(void);8 s! L$ b; G: Q* a
- void IIC_Stop(void);
% t' J7 e: v* k$ L# J) y% c - void IIC_SendByte(uint8_t byte);
# G- p! Y% ~$ Z* Y2 S - uint8_t IIC_ReadByte(uint8_t ack);" I6 [6 c! N8 h& ] q; G- b
3 z; G# H6 }. H- ; H! w) K& G& [% O9 F2 g
b2 H/ k9 [2 r3 z) E7 u- 4 I+ d- E+ D/ U: X/ v& ?
- // 启动IIC总线
6 J/ t7 a6 u. J - void IIC_Start(void)
/ v. b: |% {+ k" _7 N, [ - {
: C' e& a$ i. A+ V5 t - HAL_GPIO_WritePin(IIC_SDA_PORT, IIC_SDA_PIN, GPIO_PIN_SET);
$ h7 o6 N+ d4 K+ n' A: E2 C" u; v - HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_SET);
* a' i' x! E' X/ y* c6 W - HAL_Delay(2); // 稍微延时,确保时序正确
3 \, b1 a! ]2 |0 O5 `" t - HAL_GPIO_WritePin(IIC_SDA_PORT, IIC_SDA_PIN, GPIO_PIN_RESET);& d) U$ s* q" g2 q
- HAL_Delay(2);
- S: [6 f& d) |9 g8 R' I - HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_RESET);
& X$ t* j* [9 ]4 p4 X - }- a/ }8 J7 Y1 }
- 6 v$ R* `9 Y' u% x
- * u7 t+ Z% V* Z, |, M& L7 i
- // 结束IIC总线 D7 V2 L% A& }* B% W
- void IIC_Stop(void)
0 R& ^! l1 u: |2 J3 {* y: @% }! ]9 t/ d+ t$ x - {9 M3 p' Y$ ^9 s/ ]+ ^& c
- HAL_GPIO_WritePin(IIC_SDA_PORT, IIC_SDA_PIN, GPIO_PIN_RESET);: z0 [" I+ F; \" ^( k/ R
- HAL_Delay(2);
& g& D5 T+ M- s& t( j/ O# { - HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_SET);+ }( S) c6 F& l
- HAL_Delay(2);
! Q; g+ z5 T# b - HAL_GPIO_WritePin(IIC_SDA_PORT, IIC_SDA_PIN, GPIO_PIN_SET);# H& l0 v2 W# e5 }
- HAL_Delay(2);* T$ V: _+ h6 q& w6 H
- }
+ J- U; ~ q/ _8 s T5 o8 @
% u4 ^! a5 t( `6 I" h2 g2 W2 F
5 ^# ~# r: L& U$ d- // 发送一个字节
8 v5 _5 [* `! D: D - void IIC_SendByte(uint8_t byte)5 b7 H" h, B0 s# v9 G U S1 q) O
- {
+ T8 \1 Z/ q' K5 t& y2 Q" Y0 B - for (int8_t i = 7; i >= 0; i--)2 O& h0 C; q3 h) D7 y
- {
- _% u0 @- W3 j - HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_RESET);9 H& k3 o; j; c& e) ], P
- HAL_GPIO_WritePin(IIC_SDA_PORT, IIC_SDA_PIN, (byte & (1 << i)) ? GPIO_PIN_SET : GPIO_PIN_RESET);: ]; B9 ^$ K, ~4 m- T2 S
- HAL_Delay(1); // 稍微延时,确保时序正确
3 A. Q: U0 b: o/ N - HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_SET);1 T2 o# @! h/ G6 x" S
- HAL_Delay(1);
! P4 ^6 w" x9 z3 U - } |! D" \1 {) W
- HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_RESET);
+ ^: R! `. j- U3 N8 f - }
; N/ N# Y" h! [$ F - : c, p1 a% y5 \$ I
H" A6 F- h4 D& c) u- // 读取一个字节
* u6 h8 f% p# l+ r: F2 ]# o; t - uint8_t IIC_ReadByte(uint8_t ack)8 k8 G# A7 G4 L9 D* e. F/ Q& z; i W
- {0 P9 s; P8 O8 G: L! l, N
- uint8_t byte = 0;8 a% u( k* A3 L0 p$ j* n' G! ~
-
: j3 D( }8 I/ R- |' J7 k6 {- v5 G - HAL_GPIO_WritePin(IIC_SDA_PORT, IIC_SDA_PIN, GPIO_PIN_SET);7 @# g2 z' @. d& h
- for (int8_t i = 7; i >= 0; i--)6 V5 I0 Q+ S& T& J5 E; s
- {
1 ~+ q+ f! }7 P0 T - HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_RESET);% h" b; ?1 e- e6 \
- HAL_Delay(1); // 稍微延时,确保时序正确
* x' i# e7 W- B0 n& X3 E) e - HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_SET);
! s$ n* q1 F; `8 n, q - HAL_Delay(1);6 S: A+ l T( x/ v0 f5 g+ v8 R' G) ^
- byte |= (HAL_GPIO_ReadPin(IIC_SDA_PORT, IIC_SDA_PIN) << i);7 x/ G, ~$ H/ S
- }
, \) n; @/ l, B9 Z' P8 a - % U; ^2 b9 q' |5 l- j) A! j
- if (ack)' D5 l( P' U2 Y: s7 q
- HAL_GPIO_WritePin(IIC_SDA_PORT, IIC_SDA_PIN, GPIO_PIN_RESET); // 发送应答) X2 g& Z& [& E7 N0 l
- else/ _# L# H P* T* r$ ?) J! X8 D
- HAL_GPIO_WritePin(IIC_SDA_PORT, IIC_SDA_PIN, GPIO_PIN_SET); // 不发送应答
/ w$ {6 ]1 P, E) Y" b" e - & u) y }- i& |- @- a3 \7 M
- HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_RESET);
. X) G8 t1 E4 i4 V - HAL_Delay(1); // 稍微延时,确保时序正确5 a6 f7 _2 d' Q; ~9 }
- HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_SET);
4 I% }; E* s8 Q2 ~ - HAL_Delay(1);
# z/ n+ H0 ?. s9 i - , e) y) h1 A+ e$ o* `8 D
- return byte;. H: J. D) h( }! K& Y
- }
复制代码 3 b/ A4 n7 k" ]2 s3 O; Q' R
例如IIC的启动信号为:SCL为高电平的时候,SDA从高电平转为低电平。
! [* D+ \0 o3 m6 Y# B; B0 G8 P
; e0 z# U8 B; n& D1 |. l1 YIIC的结束信号为:在SCL线是高电平时,SDA线从低到高的跳变。5 u) r7 d. `/ c6 X" h
. @" t/ t$ c7 W( }1 x S
因此使用这种方法可以模拟IIC时序完成通讯。
! U) g- |! g) q1 ~3 B
( O' d$ d; Y8 ~ L& J- F但是这种方法的弊端也是非常明显的,需要占用CPU,其次这样子并不能确保I2C时序的正确性。所以无论是稳定性还是可移植性还是IIC的效率都欠妥。
, K- q! P, \$ z J. j
0 \, U8 ? S) F9 Q, `STM32中的硬件IIC + k1 P4 v& Z& k: B
STM32系列单片机提供了硬件IIC(Inter-Integrated Circuit)模块,用于支持IIC通信协议。硬件IIC通过专门的硬件模块实现了IIC协议的基本功能,包括起始信号、停止信号、时钟同步等,无需用户通过软件模拟实现。$ K- ^- h: ^% l" r
2 M) o* u ^; s: I8 m6 y: m& a
STM32硬件IIC优势:) [4 O7 o$ i; U5 U0 |6 b9 r
1.高效性: 硬件IIC使用专用硬件模块处理通信,不需要CPU直接参与,提高了通信效率,降低了对CPU的负担。
. M0 p+ X! m7 l6 y' Y2.时序精确控制: 硬件IIC模块能够精确控制时序,确保通信的稳定性,避免了由于软件执行时间不确定性而引起的时序问题。
+ ~ h4 z0 j5 a* o z$ s3.多主机模式支持: 硬件IIC模块通常支持多主机模式,能够轻松处理多个设备同时访问总线的情况。7 ~/ Y1 ]7 ~0 |
4.易用性: 由于硬件IIC是芯片内置模块,使用起来相对简单,只需配置相应的寄存器和引脚即可。- p4 y3 y0 P. j' H
5.占用资源少: 硬件IIC模块通常占用较少的资源,对于资源有限的嵌入式系统来说,是一种较为理想的选择。
5 X* P8 V9 ~2 E S" _) w: t0 J0 }" T0 ~" p9 \* }2 k/ |$ k
! a) `1 p# M v J
5 E$ T2 N) I$ y# R; h5 p在CubeMX中配置好硬件IIC即可利用HAL库函数进行IIC通讯。- static void MX_I2C1_Init(void) |/ O: [7 u- I. x3 |
- {
# K- t5 h8 h& u% o6 F: l - hi2c1.Instance = I2C1;0 w k. j& o- R8 ?* K2 x, P
- hi2c1.Init.ClockSpeed = 100000;8 w/ j, R' Y. V7 n ]1 J8 Y
- hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;+ M. X! r2 L v C3 I
- hi2c1.Init.OwnAddress1 = 0;
. O- u7 ^$ c0 }% R& g$ [' M3 N; Z$ } - hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;8 o; J0 w$ G5 u% ~; d- H
- hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;% S3 x, U4 W" U l9 |
- hi2c1.Init.OwnAddress2 = 0;+ n0 T# j! \5 S6 I$ I
- hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
0 u+ h3 ~& G- e% C' {) D - hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
/ R! z6 \1 _; k1 p: q& E - HAL_I2C_Init(&hi2c1);
' S1 V1 c/ K" _ - }: l# z% q# G/ P9 S! p, Z Q
- " y% W8 L3 ~2 L4 k9 x% V; q; g
/ o h0 E, Y7 L- uint8_t deviceAddress = 0x50;//设备地址
( B7 E3 w/ `; H, I8 B" n - uint8_t registerAddress = 0x00;//寄存器地址0 O' z) B, h6 X- {
- HAL_I2C_Master_Transmit(&hi2c1, deviceAddress, ®isterAddress, 1, HAL_MAX_DELAY);
复制代码
7 W7 |' v z. a0 a调用CubeMX的初始化函数,之后就可以使用例如HAL_I2C_Master_Transmit,HAL_I2C_Master_Receive等函数来实现IIC通讯。
8 U# k: Z: E9 `1 e4 x: v& N: p% h7 p. c; t" Q
" r" x! N& e3 ] y5 h/ J
STM32硬件IIC缺点:
# Z; H }2 j/ [( ?1.资源占用: 相比软件IIC,硬件IIC模块可能会占用一定的硬件资源,因此在资源受限的系统中需要谨慎选择。
" v: i/ M O$ N T5 q7 c2.灵活性: 硬件IIC通常由芯片内部硬件模块实现,因此在一些特殊场景下可能缺乏一些灵活性,例如无法更改时序。
9 b& i# H: M4 R& T( u; A3.部分限制: 不同型号的STM32芯片的硬件IIC模块可能存在一些差异,不同芯片的IO不一样,所以设计时需要谨慎检查。8 {( T8 z4 u& K) W- ^3 y1 L
! j6 r8 i6 i- n5 j6 f
总体而言,STM32的硬件IIC是一种高效、稳定的IIC通信方式,特别适用于对性能要求较高的应用场景,同时在易用性和可靠性上有较大优势。在选择IIC通信方式时,可以根据具体需求权衡硬件IIC和软件IIC的优缺点。
; c, W# X% Q& s- B) [! s j3 T5 T; G9 A9 n. u& K
. y+ X7 s% p* Z8 `& I, v; o
]& t" Y$ J! ]! \& G; T
转载自:电路小白
" z+ G( C# T( i如有侵权请联系删除2 O1 |7 U4 d) t6 Q/ e' f" d! l
! X' A- g. H5 j( @
|