IIC(Inter-Integrated Circuit)是一种常见的串行通信协议,广泛应用于各种嵌入式系统中,包括STM32单片机。在STM32中,IIC通信可以通过硬件IIC和软件IIC两种方式实现。本文将介绍IIC的基本原理,然后重点探讨STM32中硬件IIC和软件IIC的区别。8 Q6 n; k/ l2 m7 `
% x1 i7 ~* a4 \$ X% [; H0 z
$ W: G( `1 N0 s7 d# u
: D4 e) Y1 I3 w' `- kIIC基本原理:IIC是由飞利浦(Philips)公司提出的一种串行通信协议,适用于在同一电缆上连接多个设备。它采用两根线进行通信,即SDA(数据线)和SCL(时钟线)。设备之间通过这两根线实现数据传输,其中SDA用于传输数据,SCL用于同步时钟。
% u" F* g* F$ N- T, J5 a" s# s2 k( `* g2 _: G8 Q6 J
# k- t/ G% B2 {8 t2 ^+ C2 E" l
& N- X2 P$ r0 K6 R. {9 ~1 R" g
一对IIC总线上面可以挂载多个设备并且多个设备之间有不同的地址,所以我们可以根据不同设备的地址来实现不同设备之间的通信。& \* ~) z$ n/ o" [' q' G
5 G# `+ t9 ?/ _ a) A软件IIC
6 J# b4 ?# x: D C在STM32中,软件IIC是一种通过程序控制GPIO口模拟实现IIC通信的方法。这种实现方式常用于一些资源有限的应用场景,或者在需要更灵活控制IIC通信时使用。
( i+ m2 D7 }% c6 ~4 l
$ K% K9 ~3 @" {2 G3 `# o% ]; |
/ f' f( f9 t4 r$ E
& ^1 S* \9 |) |$ E. N9 f8 Y T总而言之,软件IIC是利用GPIO的翻转,一个IO模拟SCL线,一个IO模拟SDA线实现IIC通信协议的实现。5 M6 X# S" D5 s/ m- x
2 H$ E0 h1 K( L/ u3 ^软件IIC不需要对IO有特殊的要求,只需要两个普通的GPIO即可实现,因此较为方便也方便移植,不同设备只需要重写IIC的基本通讯即可。! V* C* m4 f/ I: k6 ]
- // 定义IIC的GPIO口和引脚1 j6 s" x* w$ Y7 v+ E
- #define IIC_SCL_PIN GPIO_PIN_6) i1 {4 _+ _& B8 G$ g. |2 \" m0 U
- #define IIC_SCL_PORT GPIOB( m+ s; p3 e* c, k. F# Q
0 l; n# M. D8 A; o& h" v
& F7 G2 R3 p4 c) L/ B- #define IIC_SDA_PIN GPIO_PIN_9
, u: x6 s; i/ r! n" v - #define IIC_SDA_PORT GPIOB6 z0 @. a$ A5 A- C/ T3 |) f/ y
0 V& A7 @) ~5 }- a+ C) b! A# j1 e- 2 j: D# J+ F4 p( W
- // 定义读写控制位
) b7 V/ |% R0 b p, y - #define IIC_READ 11 }6 m4 a, u& z& N9 T
- #define IIC_WRITE 0
) r; Q e' O: c$ _! [7 O/ ~) P - - ~/ @3 ^7 t( ]& V
+ S4 z% c& Q7 F& }- // 定义函数! j8 H) z& `5 n4 P& L8 s. m! S
- void IIC_Start(void);' k; t3 ^2 z6 p# L
- void IIC_Stop(void);* ~% f9 B5 l6 x. x2 w0 E/ ^% z
- void IIC_SendByte(uint8_t byte);* M4 @% |" G: o: u2 f# V/ x5 ^
- uint8_t IIC_ReadByte(uint8_t ack);
' v' f$ b: Y2 L, U) T- i
) a1 w8 O. J- G. @3 T- z% [
- [; Q3 H$ D( L$ d; g/ A& \- ( \! I3 O- J7 b0 g8 a& c
) j" Z* C% l; ^) {2 w: T- // 启动IIC总线
% A. V1 e" B6 } - void IIC_Start(void)
( z, s* Q9 B+ L; p* V1 | - {$ F0 i3 x. k- j- ?
- HAL_GPIO_WritePin(IIC_SDA_PORT, IIC_SDA_PIN, GPIO_PIN_SET);; O+ Y- @/ I. M3 J! ~
- HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_SET);: h! S4 c! l/ f2 G
- HAL_Delay(2); // 稍微延时,确保时序正确
) \/ h0 x6 i' J; l# a h - HAL_GPIO_WritePin(IIC_SDA_PORT, IIC_SDA_PIN, GPIO_PIN_RESET);+ R7 m z: v t+ T
- HAL_Delay(2);; z, q5 z" ~; i- f- K: L. C9 `9 I
- HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_RESET);' E1 w0 T0 X/ k- m7 r
- }) |! c: |5 l4 y5 c3 T4 N$ T* w
l8 W3 l- m' d9 C8 }- 3 [/ y$ N4 F6 d, D: e `
- // 结束IIC总线3 G+ g N% \) |3 t
- void IIC_Stop(void)( q% q# a4 ?( |; w* L- C; k) M7 z5 n
- {$ R3 w. N( I3 Y9 a! w- g7 K
- HAL_GPIO_WritePin(IIC_SDA_PORT, IIC_SDA_PIN, GPIO_PIN_RESET);
- r8 L; I* c$ W+ @5 _ - HAL_Delay(2);/ f; k6 U+ R; D8 C* d( e; S6 P
- HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_SET);
* b2 d6 k: y1 T( w% ~7 y/ x1 n - HAL_Delay(2);
0 y2 B" w( s5 v; k+ f" d - HAL_GPIO_WritePin(IIC_SDA_PORT, IIC_SDA_PIN, GPIO_PIN_SET); Q5 X. V3 W) m. p. _* W
- HAL_Delay(2);
7 U. e1 Q2 D$ ^# `. p9 n& _ - }* E* g/ V/ C" y7 B
- ! ^5 `( O& [+ r0 f' F
- - }/ `, U7 g4 ?! l
- // 发送一个字节
5 G! O: j. ^* ~( l - void IIC_SendByte(uint8_t byte)
, q9 d! D1 D2 a( ?- p - {! q+ R) z3 X) ~3 i
- for (int8_t i = 7; i >= 0; i--)0 V7 G8 R2 R, \9 @( d* u
- {7 O& O+ Q5 d D1 p
- HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_RESET);
/ N& u2 T5 t0 r+ x; X9 M - HAL_GPIO_WritePin(IIC_SDA_PORT, IIC_SDA_PIN, (byte & (1 << i)) ? GPIO_PIN_SET : GPIO_PIN_RESET); I! p# I; ^) f/ S4 y
- HAL_Delay(1); // 稍微延时,确保时序正确+ U3 b2 s6 ]6 ^+ X4 d) z9 |
- HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_SET);
; B+ d* M& Q8 E7 J7 z3 D, H9 w - HAL_Delay(1);+ k5 h! ~# A6 m$ M ~
- }
- S: I4 @' S3 d+ a+ w$ ?4 \3 V - HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_RESET);
; C" _" ^' X1 ^- o, K3 d - }
1 F5 `2 Q! y; ]& B$ p
" F9 e6 Y: G0 f. y3 s% G- H$ F2 E- : E! R% B% |& K i& C
- // 读取一个字节
+ y, }+ Q' U" X. _" b( N, N+ O - uint8_t IIC_ReadByte(uint8_t ack)
" n' j8 F3 L* @& l, B - {
7 x* s9 [5 a% j: c# r( Q - uint8_t byte = 0;: f" d/ T4 c% \1 {6 C; u( |% g
- 7 o9 }5 e. Y- N7 k2 @; ^
- HAL_GPIO_WritePin(IIC_SDA_PORT, IIC_SDA_PIN, GPIO_PIN_SET);
; |8 @9 C: o+ D4 C, j: L - for (int8_t i = 7; i >= 0; i--)" s/ ]$ m& d. p5 X- p7 ]
- {6 A6 c1 I7 _) Z' S
- HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_RESET);
6 z1 C1 N6 E' d5 [/ s; O4 O" C - HAL_Delay(1); // 稍微延时,确保时序正确5 ]: ]: R* @; t# {9 q ]6 L7 B
- HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_SET);9 k4 N1 w" ]4 p
- HAL_Delay(1);
& g# Y' P3 U1 @0 G# @ - byte |= (HAL_GPIO_ReadPin(IIC_SDA_PORT, IIC_SDA_PIN) << i);
- D' \0 S$ T- k" J& c; e& } - }
- |0 r" O. |1 @7 B( Q1 @3 V -
& b/ H+ h n4 b. |/ U: g" U - if (ack)( @% ^! @5 x. \( d
- HAL_GPIO_WritePin(IIC_SDA_PORT, IIC_SDA_PIN, GPIO_PIN_RESET); // 发送应答
5 o5 g0 ]% G1 L. m$ P+ v - else3 M3 `7 N2 n4 c
- HAL_GPIO_WritePin(IIC_SDA_PORT, IIC_SDA_PIN, GPIO_PIN_SET); // 不发送应答1 \' j( ?0 M& v) k' r
-
) r6 f) ]3 A8 m - HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_RESET);
* L6 L4 S3 e3 p - HAL_Delay(1); // 稍微延时,确保时序正确
* g7 [* ]/ G2 j& u8 h" o o - HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_SET);8 k; c/ k2 ]2 M9 r) `; F
- HAL_Delay(1);
) k9 |$ M; _9 {% F - $ ~0 C& x' Y0 ]/ J) k
- return byte;
+ j% x" S; b9 i/ X( ]4 V8 X( B - }
复制代码 + G& ? b) U3 d7 l2 L
例如IIC的启动信号为:SCL为高电平的时候,SDA从高电平转为低电平。% U- I* y0 _- ~! e9 O4 d* Y# G
. b: R: x( C; j' i7 `' S/ ?4 h
IIC的结束信号为:在SCL线是高电平时,SDA线从低到高的跳变。6 y; H, M& y5 j) P5 L; R
0 b8 h" ~: Y% T5 h因此使用这种方法可以模拟IIC时序完成通讯。 R. C+ a+ k: u0 }
/ H5 R5 i" \6 V5 H4 l! @! l! p2 M但是这种方法的弊端也是非常明显的,需要占用CPU,其次这样子并不能确保I2C时序的正确性。所以无论是稳定性还是可移植性还是IIC的效率都欠妥。
2 }' h8 F" {) ^4 c N! V2 A4 U
, e; t0 r( ~! \4 n0 aSTM32中的硬件IIC
# K" E* t* {8 T0 {6 OSTM32系列单片机提供了硬件IIC(Inter-Integrated Circuit)模块,用于支持IIC通信协议。硬件IIC通过专门的硬件模块实现了IIC协议的基本功能,包括起始信号、停止信号、时钟同步等,无需用户通过软件模拟实现。
& a$ l0 q# [; U* F9 W! N9 D3 K( _: y
STM32硬件IIC优势:
3 A- U1 T s. q; |6 x5 ^1.高效性: 硬件IIC使用专用硬件模块处理通信,不需要CPU直接参与,提高了通信效率,降低了对CPU的负担。0 Q0 R- g1 m! W' t4 M' }- S
2.时序精确控制: 硬件IIC模块能够精确控制时序,确保通信的稳定性,避免了由于软件执行时间不确定性而引起的时序问题。" a1 r* `. V3 c8 Q/ R
3.多主机模式支持: 硬件IIC模块通常支持多主机模式,能够轻松处理多个设备同时访问总线的情况。4 q6 h( H9 ]- X; X3 F \8 V" x
4.易用性: 由于硬件IIC是芯片内置模块,使用起来相对简单,只需配置相应的寄存器和引脚即可。3 s0 m' `( t" Y& h' u
5.占用资源少: 硬件IIC模块通常占用较少的资源,对于资源有限的嵌入式系统来说,是一种较为理想的选择。6 D6 v; G, C* d, O5 k1 O- w9 N! t# G
) Y' \0 Q9 F b) R2 z* M1 }
& a7 D( f- \; q; j' Z
' k# O( Q0 ?. U& a
在CubeMX中配置好硬件IIC即可利用HAL库函数进行IIC通讯。- static void MX_I2C1_Init(void)) o( T$ f& y) p+ Q) K
- {+ D8 E% c% e/ X
- hi2c1.Instance = I2C1;
. e* Z% a' Y' {. L$ V- J' ? - hi2c1.Init.ClockSpeed = 100000;6 O+ Q) ] u& N, s0 R) x
- hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;) P( W1 _* u* ]/ }
- hi2c1.Init.OwnAddress1 = 0;7 x0 s0 S' T a2 V/ \( P9 F
- hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
: R% j0 K& E8 h0 r5 g - hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;4 k4 |- o* C' N+ \) F6 K. m
- hi2c1.Init.OwnAddress2 = 0;5 B" T3 ?) ?4 d4 ?5 U' c
- hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
( s. Y& {: [# [) X - hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;8 e; L# {* E. z- R
- HAL_I2C_Init(&hi2c1);
% a' ~' H7 E2 Z! P4 G6 W' Y - }" F5 b, f& f3 A0 L: I5 ^$ Q
7 J l5 C: v2 Q+ L3 b
" b7 _' r& I8 U: P# G- uint8_t deviceAddress = 0x50;//设备地址( a. M( f1 J! |; H
- uint8_t registerAddress = 0x00;//寄存器地址
* R1 Y% e! {2 H3 |8 C - HAL_I2C_Master_Transmit(&hi2c1, deviceAddress, ®isterAddress, 1, HAL_MAX_DELAY);
复制代码 3 Y$ w+ n9 e! r& L! A u& ]
调用CubeMX的初始化函数,之后就可以使用例如HAL_I2C_Master_Transmit,HAL_I2C_Master_Receive等函数来实现IIC通讯。; q4 r6 i$ k7 R, w" l( v
; D# m- t5 e& x# b
% Q$ v( X; H% Y5 p
STM32硬件IIC缺点: C" u/ f! e9 P0 ?/ F& ]3 U: T& l
1.资源占用: 相比软件IIC,硬件IIC模块可能会占用一定的硬件资源,因此在资源受限的系统中需要谨慎选择。
+ @! n$ X0 K( `5 u+ t: E2.灵活性: 硬件IIC通常由芯片内部硬件模块实现,因此在一些特殊场景下可能缺乏一些灵活性,例如无法更改时序。8 J5 {# Z, r' x9 z& G# r. W+ L
3.部分限制: 不同型号的STM32芯片的硬件IIC模块可能存在一些差异,不同芯片的IO不一样,所以设计时需要谨慎检查。! w! y* L& x2 M& G2 f7 m1 e
2 s2 n' ?* t, j0 Q1 o r" R3 R6 X
总体而言,STM32的硬件IIC是一种高效、稳定的IIC通信方式,特别适用于对性能要求较高的应用场景,同时在易用性和可靠性上有较大优势。在选择IIC通信方式时,可以根据具体需求权衡硬件IIC和软件IIC的优缺点。
5 ?3 t5 k: x l. B& o
4 o2 B! ]( J! _( b2 d% R3 a! h) }" M" D- ^$ Q
, b4 k. _5 h i* x2 i转载自:电路小白( W/ B9 w# A" v# K
如有侵权请联系删除
: Q3 A4 c6 e$ d4 f; ~8 b
: {4 P: a. ]( A* I9 d |