1、IIC定义, u! e, ]3 G& A% K& N$ b
IIC 即Inter-Integrated Circuit(集成电路总线),这种总线类型是由飞利浦半导体公司(后被NXP收购)在八十年代初设计出来的一种简单、双向、二线制、同步串行总线,主要是用来连接整体电路(ICS) ,IIC是一种多向控制总线,也就是说多个芯片可以连接到同一总线结构下,同时每个芯片都可以作为实时数据传输的控制源。多主多从的通讯协议。2 H. B' r T/ C
1 T: t0 x' z, V$ ?7 [下文将结合NXP官方的IIC手册讲解IIC协议。下载链接见文末。
. l: U3 D I" U) \: p( z
" |, m& u/ b5 o# r2 y8 M# e4 g7 ^' r* k
& x @, R- @! y7 a* S! ]& _) B# V4 c, c% W) w" F( N% w
I2C串行总线一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL。所有接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。速率最高400Kbit/s。
; t! n. R& S- u* l9 F3 C/ Y9 o: }. T, g G6 J. r
1 a p* x% j% E8 Q
; R# n+ \6 c+ J' Q1 z在1998年的修订中增加了高速模式,速率高达3.4Mbit/s。(这里不讲,只说快速模式)。3 w0 [2 e2 J% l# P
8 }6 u- P3 W' w, I/ `
2 v4 J+ f# C2 S C) p( k9 ^# S1 {# P u+ Q5 I) }9 D
2、IIC协议规范
% e0 h" a' y2 q2 o2.1 SDA和SCL信号
" A# s9 `6 ?9 ~$ {% M% z0 `4 Z2 Z1 o4 e/ S, ]% [2 w$ Q
m' b& Y* s5 z& r1 {8 w
( T7 v k5 y; j$ S( H
连接到总线的器件输出级必须是漏极开路或集电极开路才能执行线与的功能,当总线空
4 Q& _: b* B6 q2 B: ]5 C3 I" s: W* f7 a% y1 N$ H
闲时这两条线路都是高电平。SDA 和SCL 都是双向线路都通过一个电流源或上拉电阻连接到正的电源电压。一般情况下我们都采用上拉电阻的方式/ f# K e% Z% K0 K2 f+ K
4 V6 { I5 u+ C8 ~
2.2 数据有效性, c( f9 x3 F1 I0 P+ l4 u1 G, Y
$ O: U u# x# G+ u9 S2 @# U
6 ]. y, I( X) O
/ S5 _" L( P2 p7 u p
在SCL高电平的时候采样,也就是有效。低电平的时候切换数据* n! G) U' H/ U l
& J. A, j( {$ A
2.3 开始和结束信号
- Y' |; x4 C$ N2 N起始条件:SCL线是高电平时,SDA线从高电平向低电平切换。
6 y5 Y/ V1 Q# s7 O- m' u- E
: d5 U& u5 l$ n+ N C; [0 m停止条件:SCL线是高电平时,SDA线从低电平向高电平切换。 Q' m* y3 K$ b! {8 @& E: ^$ [
) U# D( p' I. Z9 p9 B
) f4 E0 j! }1 m. ^- ?9 _
2 H! _" I! e; C& ^# v( ]+ ?动画展示启动信号5 G% x$ s r) y8 z5 p6 q! L
; G5 m' Y( L, c" q1 _4 S0 Q
) N& z/ r5 n$ M H- z. a; W; F' `! u" N& k n' t/ f7 X
代码实现
( M& c8 T8 G# B4 M; X5 U
- G3 |9 z- L* \' g/ w7 A- void I2C_Start(void)
9 y4 b. `5 t4 ~8 @% N - {
) m6 p2 \1 {; R1 C, U' A - //IO输出
. s3 j: Z6 ]' D( E! l - SDA_OUT(); 1 y( A4 a9 `' A1 Y' Q' P
- SCL_OUT(); 6 i) M+ d. H, w& [3 ?
- I2C_DELAY();
; b! Q. r) l) T+ M! A' i7 } - //IO置高
' k% N+ d4 K+ d/ f9 }- U) B - SDA_SET();
- `. h0 l* A, l1 H - SCL_SET();
! u3 U5 \( y, U3 z! p8 | R - //延时8 S% t' Q( l% Y% K
- I2C_DELAY();
5 J1 A& X2 k7 N6 x) G& c+ L - //为低
7 _$ w+ B8 k# K# Y3 f# [3 z - SDA_CLR();
5 l2 ?7 p e" I - I2C_DELAY();- a: s( |6 p0 i5 p
- I2C_DELAY();. ^ q- p5 ]+ t- {: B5 b" }
- SCL_CLR();
# z. E+ k& f4 A - }
复制代码 - s4 U& N4 x# l3 t0 h
6 T* p- q4 Y) S3 ~ S结束信号时类似的方式(不是动图)2 N% @1 X' n) f8 l9 _9 t
5 l# ~3 t* Y4 E1 r" N2 Z3 N
0 ]/ o1 F+ ]) H0 ~
3 D4 c& K7 V/ K+ S, w代码实现
! j+ v) R+ y. f- R
6 T# Y4 @ @3 u! F, `% q- void I2C_Stop(void)
, @9 _8 B9 r- E - {8 @ A1 \3 T. g% j" T t* W
- //IO输出- h+ ?2 p3 V1 h. P1 |( ~; m
- SDA_OUT();
: d8 j" B2 W( [' O - SCL_OUT();" S7 ~2 ]9 m' H+ b% f
- //IO置0* r5 D5 ^# T9 h/ N U0 T
- SDA_CLR();
8 Z% A4 w1 D, O& f - SCL_CLR();
% v F3 z) D+ U7 G* B0 P - I2C_DELAY();; u$ f8 l5 n9 T7 H
- SCL_SET();
1 X3 U4 @. G+ h! L R Y* T2 z - //延时8 Q0 u8 j# E" @' _% X% W# N2 M
- I2C_DELAY(); + a% T. X* l6 o1 r% ]7 ~: X$ C" n. n$ u' ~
- I2C_DELAY();
4 M+ H9 [) ~1 ?- K - I2C_DELAY();
" j9 O$ R4 U# \& K8 D* N8 l, p4 K - //SDA置17 D i6 I+ x# H; i+ b4 v5 z6 g
- SDA_SET();# {5 C+ k. n0 {7 a
- I2C_DELAY();
1 [& d! W2 ?& M8 E4 e) K3 ^' C - I2C_DELAY();8 F9 p E* G$ W# @ ~
- }
复制代码 6 h* W" o. k/ I$ C! {3 h- C1 f
* r! _- \% ^1 z$ A. J' M+ h0 r; n- [2.4 字节格式
) ?2 u! B/ ? X8 v @& VSDA数据线上的每个字节必须是8位,每次传输的字节数量没有限制。每个字节后必须跟一个响应位(ACK)。首先传输的数据是最高位(MSB),SDA上的数据必须在SCL高电平周期时保持稳定,数据的高低电平翻转变化发生在SCL低电平时期。
( Y# l* |1 K$ x
7 ~- P$ V* X8 G0 ^3 I N) {
3 b$ Q6 _' Q, E! ^8 j! V
2 L# \8 a8 A' Q" ^/ \每一个字节后面跟着一个ACK,有ACK就可以继续写或读。NACK,就停止
8 R! ]" l: s6 X: l/ G& t3 ]: b5 L2 c7 n. j, O
ACK:主机释放总线,传输完字节最后1位后的SCL的高电处,从机拉低电平。; o7 k1 h/ R$ T% K' T, S
9 l4 N. f9 r' |
NACK:主机释放总线,传输完字节最后1位后的SCL的高电处,从机无响应,总线为高电平。
6 Q ~0 Y- Z) E" h! d2 A" `! v
. p8 y' @2 ]1 L8 m0 @0 Y7 c动画描述写入的过程8 S8 h6 [( A) o, j: o& C* t
G+ h+ I9 _4 m A5 c2 W/ H/ q$ V
2 u; S: A% O4 {( g+ r
# ^. x" L+ I# h* L$ F; R, o
代码实现
* F9 e5 U5 p$ U- b- d6 s9 V5 |4 |: K% D" t$ s( b
- uint8_t I2C_Send_byte(uint8_t data). ]5 a/ o0 L. W. u
- {
, K3 F i7 v" ^4 g& z( g9 m6 k - uint8_t k;7 R9 X/ l/ l( H+ H Z7 t
- //发送8bit数据
% V, r2 F( U! e3 |. z! Z - for(k=0;k<8;k++){& |% k& q9 @* B- F* R" h* {- B
3 b t$ O- B) a/ ]; U, Z# `- I2C_DELAY();5 U! P/ Q2 V' P c$ J! ]2 r
- if(data&0x80){
' C+ s& S" V' ]/ |- g4 W, W - SDA_SET();) O8 i' K x0 w- m" E$ g
- }
1 ~1 D+ @1 P6 s% ]% r4 K - else{
4 g# U* j/ m0 s3 U% A, F5 {4 P - SDA_CLR();0 J* s3 J& t" Z c& t
- }
- f7 p6 o: S% H- F. r& ` - data=data<<1;& O5 W' d! Z S/ D0 P0 V+ u
- I2C_DELAY();
6 U" L% I$ `+ F* M9 b - SCL_SET();" r9 Q1 h5 u& R) E" C( j! q
- I2C_DELAY();* O7 r8 |1 l3 p- U
- I2C_DELAY();
$ \9 J" K3 Q! S4 E: @8 j - SCL_CLR();0 P) Q+ ^ ~; C: h( U4 [# J
- }
5 H |8 t, U: x' K& e- w) S - //延时读取ACK响应( B! E, p9 q3 z z) j5 J3 y
- I2C_DELAY();* y, E1 J! M$ P$ d( h: k9 k0 V
- SDA_SET();
5 i" e# ~3 g6 B& c1 }& L b# s; M - //置为输入线8 F* L5 ~9 J q2 t# L" b" a) U
- SDA_IN();
/ B! d$ j6 Q1 @5 H- p, e' F - I2C_DELAY();
0 M' M) U9 B' l9 L, A4 V7 P - SCL_SET();
- `* S2 X4 q5 e - I2C_DELAY(); //这里出现了问题,延时变的无限大, @% l+ M) A: w0 ?- k
- //读数据$ @; s3 |: J) J
- k=SDA_READ();
( @) T" v/ L2 ?3 Z* a9 ? S - if(k){ NACK响应) t1 G U, Z! A9 j: @5 }
- return 0;0 t& G; K3 `* t; D
- }! }( ?1 Z, q/ `9 Q! R2 f) s
- I2C_DELAY();
% \/ j; O5 F( U2 W" H% S - SCL_CLR();
5 o( Q; F3 A+ n5 l1 F; z - I2C_DELAY();
/ r- V9 L: o1 ^; z t7 K. \0 _+ T: b - SDA_OUT();1 Q# \8 } L8 v
- if(k){ NACK响应( E- H3 s$ I% H/ }; k! L7 w; e
- return 0;9 A8 k; d1 [6 ^6 c( v
- }; S* m% N6 A( l! e5 G
- return 1;# M) h" S" Y0 |' S$ p* A4 r
- }
3 g9 Z* T9 B+ e* `/ L S) ^8 {7 }
" T1 _- W( L4 H" c: {" o7 k% d. H- uint8_t I2C_Receive_byte(uint8_t flg)8 f) [. m6 O& R! n" Y
- {
2 w0 }& }5 e& L6 b7 _ - uint8_t k,data;) @2 A/ m7 k- O) M4 f9 {
- //接收8bit数据
, x- D! |: H* o - //置为输入线
5 d3 y! j( c# t/ s, M: A8 z" O' X
& ^/ X; q& g; [# M; O# l- SDA_IN();
; n9 ?, o5 E& V - data=0;
* l$ _. @2 n3 E- f- F! X- h - for(k=0;k<8;k++){( k7 X7 R9 n; A# S: \+ U
- I2C_DELAY();; p( u# m" l& m$ r. z% c- }4 J
- SCL_SET();6 ~) N) }$ ]/ q Z
- I2C_DELAY();! X2 j5 Y! v6 w$ H2 i$ G. l
- //读数据3 E# L" R1 w8 h; o
- data=data |SDA_READ();
4 J8 \) o1 c! v& J2 g4 R( W) A5 X9 n - data=data<<1;% ^# i/ `" F2 J1 X1 W1 j5 m6 j9 b
- I2C_DELAY();
" w7 C! G! m- c& F3 p% w% U - SCL_CLR();( s6 u i" M) s$ L
- I2C_DELAY();
6 `9 {+ {0 Z- c& i: f - }9 x! [5 [2 L4 A. {
- data=data>>1; //往回移动1次9 E: h1 U& `% _1 X" x e* Q& w
- //返回ACK响应
0 W1 p' ~2 s/ s9 F - //置为输出线" e7 H. w' w# p( m7 y
- SDA_OUT();
$ x0 ?9 ~/ q! k( Y/ A) E. y! j: [5 \ - if(flg){! u' p8 O2 F6 w. v
- SDA_SET(); //输出1-NACK& v: Y. }* q; r$ C$ g0 x3 L
- }else{- |" y4 m9 K2 ]4 `. _+ q! d% u
- SDA_CLR();//输出0-ACK# h$ b! P1 O( Y8 g5 b: V
- }
- e4 x! I o+ K9 R& A; B - I2C_DELAY();
- @9 F5 A; G/ U" O - SCL_SET();
, D: i& i7 L# X# J - I2C_DELAY();
; l* O( R5 w- B3 y8 l- `2 r0 @ - I2C_DELAY();
- w" R3 s2 h0 F* ^7 P - SCL_CLR();
" H( c2 q& m' T8 ]0 y) {9 W - I2C_DELAY();
. P( }7 N' s3 Y - SDA_OUT();
3 `! Z8 K3 [# ~. R9 L/ L1 L - //返回读取的数据
! p' M) |% H: D; i6 ]3 `+ w! m - return (uint8_t)data;
9 Z! D2 t$ q$ v - }
复制代码
# X2 r/ w M$ W3 t% J2.5 从机地址和读写位
# o' i5 E0 J8 l% c开始信号—>地址—>读写位—>ACK—>数据—>ACK.............—>停止位) v% Y3 |4 ]6 H6 {2 J
$ c8 l5 u9 K- z: r
$ }" }$ E* P# V$ }
( C9 Z7 B: x+ a; F& ]! \这里只说7位地址,前7位为地址,最后一位为读写位,1表示读操作,0表示写操作' n! x4 y. i6 S* M
& O1 n" J8 D0 l u j; Y( m
% e+ W u h) b: m) q0 |' m) @1 D" v! B
主机发给从机数据,也就是写,没有数据转向时- a6 ~( ^/ }$ K Y
4 i" |. g( Z) u; k3 b! b6 d
# l) X: u! M$ R4 ^2 i/ Q4 r# z0 j8 j0 D0 f3 x
主机立即读从机数据,从第一个字节
1 U, |5 C7 F6 x- Q# |: R! L9 i+ a: C
7 s* f6 V" W" U4 o8 L
0 ]) t1 b3 c# b5 R(Combined)综合数据格式
+ @2 y' n4 c* z! O; d( F4 g3 t3 O. W1 E+ Q' h- L" t
+ c3 S3 ?( A" H' \$ G; R2 Q8 C6 x1 o& Z9 F0 N- S5 v) C( ~) |
3、计算IIC的频率
. |4 D' O; @/ V. U% d通信频率由主机掌控,也就是代码中的延时函数决定的
1 E+ M' [: X9 J! I( i2 Q1 G
" N2 X5 h! S& ^9 H6 n从上面,我们得知最高速为400Kbit/s。我们设计300Kbit/s。/ F ?7 A( V% [& s3 [( g0 O
+ j5 u+ o7 O# s+ C3 h
) X7 g+ T6 L5 T8 K8 d0 w" F, }: I- ]' n
速率300Kbit/s,对应周期1/300ms=10/3us≈3us,4分频就是3/4us。
7 e1 O3 J+ A/ b3 A& B! z6 a; K' y# o- i6 U
我们使用的延时是,1/120MHZ*3*30 =3/4us
9 q% A: [1 W4 Y# A* z5 ]6 j4 ]9 Z
- l: l/ Q b+ v5 H7 A# ~也就是频率是300Kbit/s
2 f! J! q3 z2 J* M y/ I% Q: c* L6 f# Z0 ~: o
和SPI类似,时钟下降沿时,数据转换,时钟上升沿时,采样数据。也就是时钟高电平数据有效。. u, s! G8 [9 L" y$ z/ _0 e" J
7 Z8 [4 a# m& c2 d6 C X% U- /*120Mhz时钟时,当ulCount为1时,函数耗时3个时钟,延时=3*1/120us=1/40us*/
: Z0 A: B/ ]* x( f( N# z2 S4 ?5 y7 T' _& I - /*6 ~0 j9 u1 F# ~# q9 j
- SystemCoreClock=120000000
& y2 A3 ?5 p7 t8 W3 V# K* y) b+ u - us级延时,延时n微秒
; I2 W+ k. ~7 u: p0 F: h: B - SysCtlDelay(n*(SystemCoreClock/3000000));
" p9 ] E: a/ {8 w. N* K - ms级延时,延时n毫秒
- x7 w2 q+ o% H& p, K5 `# e! Y6 F - SysCtlDelay(n*(SystemCoreClock/3000));
/ T1 {) Z) S/ E - m级延时,延时n秒6 e, \$ E( X/ n" O0 T
- SysCtlDelay(n*(SystemCoreClock/3));3 R j: J g! O
- */
8 ~" @! g b4 U: ^+ g4 J6 B% x$ a
0 T$ A, x% R/ H- #if defined (__CC_ARM) /*!< ARM Compiler *// @9 \$ c5 D7 E
- __asm void
}6 Z8 j h# C4 u# l* i - SysCtlDelay(unsigned long ulCount)
5 Z( ]2 D9 N9 w/ c" e - {* Y8 C9 V% A) D" [; q& q: }5 \
- subs r0, #1;
/ x' V. N/ A0 ~* I5 h; d4 V7 E/ u - bne SysCtlDelay;
+ i6 ^. T1 J7 k* i/ Q w - bx lr;1 k8 E5 Q' C$ o c9 c2 R: |9 R
- }, Y/ I* U' Q8 I2 u3 ]+ `5 l
- #elif defined ( __ICCARM__ ) /*!< IAR Compiler */6 ?% T. u8 g: t+ }
- void
8 l5 g0 j2 A9 f - SysCtlDelay(unsigned long ulCount)
; r6 ]5 I+ R, A7 u - {( y8 L. O: x& f I' i$ A
- __asm(" subs r0, #1\n"
- F. E3 p B- w" N1 o4 B d+ o* | - " bne.n SysCtlDelay\n"9 w# q0 ?# e3 K$ k. b! r
- " bx lr");
/ y! i+ }5 D/ G$ {4 f* R - }# E, A1 _6 O H0 _
- ' z& N Y4 x/ n6 T7 m
- #elif defined (__GNUC__) /*!< GNU Compiler */
0 j F Z, i6 a1 W3 m. F - void __attribute__((naked))' ?; I2 n2 @2 n
- SysCtlDelay(unsigned long ulCount)
% h/ m# U8 p- B3 Q2 P - {- W7 t' h5 R$ `0 u& C& D. V, h
- __asm(" subs r0, #1\n"' A* w( r! Z( f
- " bne SysCtlDelay\n"% Y1 U& T# u; W8 g5 |% s
- " bx lr");
6 ^4 }* s5 L5 d, V% X8 u3 d - }
' E( J, {. H% v7 W
+ \# X7 E' n& x# X- #elif defined (__TASKING__) /*!< TASKING Compiler */ # Q v, G7 P( K; Y; X2 U
- /*无*/- O. j( \/ W3 T, Z1 }
- #endif /* __CC_ARM */
- e+ Y' h$ W, ?& E* Z
+ J. d8 p e1 ~& D9 D9 O% e! }
5 B0 {3 W% I) V" a0 |- /*
: ]( u3 Q+ B5 n; M - * @brief SysCtlDelay
) R2 x1 v( j* N( M - * @param ulCount 延时值,必须大于0" c" A$ M$ L9 A! w
- * @retval None2 w5 E( D9 h2 e9 P: h& m, z
- */
" T0 y6 l0 ?- P4 e# ?7 E - void SysCtlDelay_IIC(unsigned long ulCount)
, D! o1 [$ \, o3 ? - {
( N: s+ u( h2 z1 g9 l. J: p - SysCtlDelay(ulCount);
/ S3 Q1 ]; H& W - }: m( g8 [+ M/ M- R* Y) b, E6 Q) u
- ' f6 @( v: ?+ N" t
- , h* o& d1 l' b% r. ~( Y: B+ y
- /定义时钟频率,300KHz8 F' n" J: T6 [5 N! f& \
- #define I2C_DELAY() SysCtlDelay_IIC(30)
复制代码 * U) N1 @2 B% B5 K0 {3 I2 [9 D
4、PCF8536
! B/ C ?' q/ {. Z+ e4.1 Acknowledge& s8 h) l' Y c# {2 x
这个地方能看到关于2.4节关于ACK和NACk的说明。
! a$ L- v1 S9 U8 w3 ]) O8 _+ v4 W7 x$ u* D$ i3 v7 f3 D$ S
7 h1 B$ Y- C. M, T) b) o: V1 X( N: O6 u, l% w) e
4.2 Addressing
4 o8 Z7 y5 |$ r- I2 v; J5 v1 c这里直接给出读地址和写地址,也就是最后一位的区别
$ Z! l0 C9 @$ }! j+ W+ [5 G9 y/ c" `
' P0 u, g, a7 q0 W6 p2 z% Y$ K
R% E7 p: t, G, f% [+ f! s( Y/ R' g% s$ D/ v# U0 L& ^
4.3 读写时序) t+ n9 s V& R; \/ v4 N
其实就是按照IIC协议的! M7 Q3 w" z6 m; `8 S2 S
" f7 i- c+ \4 @+ F( s: B$ Z
读指定器件的指定寄存器
! ^3 g- y8 p0 W7 V5 m* ~" W' h0 J2 V! o: r4 D+ }1 c
/ j+ Y: z: o$ t7 t: o
# K5 r4 ?7 H; k( t% o- ~: X主机设置完寄存器地址之后,再去读写
* e; s: N; o- Q. d2 ~, t* O* I$ }& X$ i, W; H5 ?8 Q( ~2 R
注意:读取多个字节,最后一个字节的回包应该是NACK6 l4 p- O( a: [' E, [3 _* L
8 J/ U: D8 ~: h
5 ~) j. O3 l- ^/ a: H
4 m) z& `' n9 o( Q( |主机立即从机第一个字节读取
0 ?& a7 v. i( x3 W ?
5 Y+ ~8 s$ X9 L; B注意:读取多个字节,最后一个字节的回包应该是NACK
2 Z; \) J3 g+ ]; w t/ w5 e! o- s3 P! R# H* w
5 `) l( b! ?' o' \" `1 V
& i! v- Q0 D/ E9 a" @
6 l; m8 F$ I" p( D5 U( ] |