|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
( A. _; ?0 C" B- p# c现在很多单片机都已经对常见的通信方式进行了集成,但是51单片机没有这些功能。如果想要使用其他通信方式,只能使用通用I/O引脚进行模拟。. ~8 D$ X) N, ?& Z* @5 f9 U
今天我们就以I2C通信作为例子,演示一下如何模拟这些通信方式。2 ?: E9 |9 ~& s6 h* ~) V
2 p- n& X; b& ~/ E
一、I2C通信介绍& j) G9 L* L8 \
I2C总线是一种简单的双向串行总线结构。只需要SDA(数据线)、SCL(时钟线)两根线就能在器件之间传送信息。
. Z1 i, V H1 P7 T8 |& RI2C通信中,器件有主有从,一般以控制产生时钟信号的器件作为主器件,其余作为从器件。每个从器件又有一个7位设备地址,用于寻址。这就意味着,一条I2C总线上可以并联多个I2C设备,主器件通过设备地址可以分别对这些器件进行操作。
) w8 x% N& E* ^, \0 n一次I2C通信可分为三个部分:I2C通信起始信号(SCL高电平下,SDA从高到低跳变);I2C有效通信;I2C通信结束信号(SCL高电平下,SDA由低到高跳变)。
- D" B6 x% D6 D0 \3 N" z9 `' |同时,在I2C有效通信阶段,数据接收方每次成功接收一字节数据,都会主动将SDA线拉低,表示传输成功。! i: r& V* ~. z- ]% U0 c% F
_, X' V& I3 F" S$ O7 u7 H5 X
由此,我们可以编写得到I2C设备的基本读写函数与等待应答函数:" t: L# e/ D! O8 \3 P5 F- |
//开始信号
0 \) P% R3 o" V2 Jvoid I2C_start()
5 C! i- o$ I! G! b+ J. Q{
M! k M5 z8 n/ JSDA = 1;//同时拉高数据线与时钟线
8 D Y! \7 t2 m1 }, P+ P' ^SCL = 1;
$ A, c: e% C5 {0 zdelay_1us(1);0 V4 C1 Y# E ]) F9 M/ y+ V* p
SDA = 0;//SCL高电平,SDA下降,启动I2C通信) T5 `% l2 l! y+ ^1 B
delay_1us(1);//等待1us
$ y$ N. r( Z/ i$ Q7 w( W; DSCL = 0;- H! r6 w$ _1 E. @0 b& C2 Q
}
/ E; s. _4 `) G
7 c, Q6 J x+ {( `* ~" v//结束信号
; x8 V+ A4 [1 d; @+ Cvoid I2C_Stop()
* t# ~; K6 r3 W! j{
2 h0 t h6 m: p" eSDA = 0;# d$ l, Z& I9 ~" G J
SCL = 1;
+ S' X" \8 Q0 V" w6 O* @6 cdelay_1us(1);
# E* Z0 w' L- K1 }3 ^SDA = 1;
' O9 L d$ p& J& r) t* r6 Ydelay_1us(1);
, t- j- @; _# z: Z2 t' T$ Q) f}
* Y+ Y! V9 ]5 y8 W" i5 y/ H
! E' ^5 H9 t+ p//主设备应答函数, q A3 I( H2 c% F' q `6 U
void I2C_SenDACK(bit ack)
! X" _- h+ ~5 |& O+ O, t6 E! X{
6 p4 ?3 L! z" O2 e) B# b! ?1 lSCL = 0;//SCL 拉低,数据改变+ E5 d- k* M; I4 i; O
SDA = ack;//数据位改变
1 a& _2 [* q# n. E+ A( pdelay_1us(1);//延时等 稳定信号3 ~' ^. P& |# D9 u
SCL = 1;//SCL拉高,传送数据
8 k: [, n3 a: O9 \+ f( ^5 @delay_1us(1);//等待
2 _6 m/ I2 T) ^9 R# CSCL = 0;//SLC拉低$ A+ C9 r3 @- q3 {) |" ~# J
}
5 `. {( t& L+ I3 l& p; u- s e) i
. x! P5 ]. j) f3 i//从设备应答函数# b. }1 g$ Y8 g
bit I2C_RecvACK() O. k/ {9 v. U; Y
{- s: B6 J' a" X4 w1 \
uchar wait=0xff;
. \8 f# }, G" o1 lSCL = 1;//SCL时钟拉高 数据有效
9 g( k" a+ j9 \4 W) f' p' S8 @while(SDA&&wait--);//SDA高电平,并且在512us没有走完
$ q3 {. ~1 Z7 k( A7 aif(wait<=0)
, w7 L2 k4 x0 u7 ]' g{: b I: Z" X1 y, ?
I2C_Stop();7 t( @/ f7 P7 H9 @( E$ C$ b2 R: P
return -1;
N) s1 ^" @2 n: N" I; m}
8 N8 ]3 x+ O2 ^/ ~+ q L AWSCL = 0;//否则SCL为低电平,结束该应答
+ D- Z0 Q+ p% ~+ Ddelay_1us(1);
1 [5 p/ Y2 Y/ g0 Ereturn 0;
" E! Z; \) ^/ O' S9 n1 H}3 M' |3 V) T$ ?& k
//数据发送函数) T" `$ m. z: Y1 A
void I2C_SendByte(uchar dat)
) l& g4 [6 N* x% i1 M{/ m# q+ P0 a g4 v+ J
uchar i;
1 M; D2 ^. ^/ efor(i=0;i<8;i++), y$ g& t! [" l9 M9 x; F* c
{
) F3 a x" p# dif(dat&0x80)//dat与0x80进行与运算3 R: B6 _& i6 m( C" b# M* m& S; p; b
SDA = 1;
( Q2 P, K! x4 y$ |3 J6 o$ Selse* M P- T: v+ I/ `# p7 @0 V
SDA = 0;' J. J! {7 A# ^) s+ ^; i ^
SCL = 1;* i) [/ K8 J q/ w
delay_1us(1);' r, X2 I( \6 p( y$ p
SCL = 0;
+ c" u* ^5 J6 Q+ i# idelay_1us(1);
# N4 A: I& {& I8 ], k" M4 `dat<<=1;3 k( }6 X) u+ p6 ?3 A- ]5 y
}, a/ w! k/ G$ U. j
}
) C% N# w+ j5 n3 K8 M/ h//数据接收函数) b+ h( F0 u+ r0 w: X
uchar I2C_RecvByte()//接受一个字节! z2 V# o5 R. ~* X: E# S
{/ f2 l+ W& L' ^
uchar i;
' e3 `: H# }% i9 G6 t9 s4 Duchar dat = 0;
* T9 i* S' G7 w/ h! D8 zWSDA = 1;) V& @6 {3 C/ G. Q/ D; h
for(i=0;i<8;i++)//一字节 八位3 b# B$ y r% ^* Y8 i
{; G- `# O% s0 H _
dat<<=1;//dat左移一位,从高到低接受数据4 k) F$ |9 ^# d+ w( m
WSCL = 1;//SCL拉高,数据有效
' v5 G1 K0 @. u% [+ f* s, Vdelay_1us(1);//延时保持
) D1 F2 I/ c! t, q6 d% l$ D6 Wdat|=WSDA;0 d9 z9 P% z; a2 S
WSCL = 0;//SCL拉低结束数据传输9 @9 N. x2 t" m
delay_1us(1);//延时保持" ]& ^7 s, F0 `# b p7 N/ d+ w
}
% g) y! u2 s' d Creturn dat;//接受八次之后返回dat值2 U$ _8 e0 c% W. v5 @
}
+ [( `4 m) Y% O" E- ]# C+ u* |$ D9 ~% v( ^5 E# L7 I
二、I2C通信流程7 x) E, n W( O6 Y% |& l6 `
主设备向从设备完整的写操作" g4 W& L6 o5 _
1.主设备发出I2C开始信号:$ z2 u/ t+ y3 s
2.主设备向I2C总线发送要操作的设备地址以及要进行的操作(读/写)7位设备地址+1位读写标识位(0读1写); p# d2 _6 W7 \2 a& y; |" `
3.主设备向从设备发送要操作的从设备寄存器地址9 H& ]4 | X$ l% ^6 a5 Y
4.主设备向从设备发送要写入的寄存器的数据
( Z1 a2 ?6 Z0 ]1 D7 H% A+ S: j4 Y- w5.发起I2C停止信号- r* V4 {2 _8 y% [
//对I2C设备的完整写操作
' y# J' R' F; }$ M) i8 O, e: M# o1 Kvoid Single_WriteI2C(uchar REG_Address,uchar REG_data)" {8 o: M0 s: s2 z) X- i
{
7 T5 k R" ?1 K/ p) ]% _I2C_Start();
/ P U: y! c3 n9 V v/ L8 _I2C_SendByte(SlaveAddress<<1|0x00);$ z+ x, F) k! l( d( P7 B9 i* l$ C
I2C_RecvACK();# `; z* b! S6 h2 C2 r
I2C_SendByte(REG_Address);. I6 B2 \0 N9 j
I2C_RecvACK();3 v/ }" j7 J# n
I2C_SendByte(REG_data);
" D3 z, u- F& GI2C_RecvACK();
; B! G C* x T, b- F: C5 G3 l6 P9 II2C_Stop();
- ^& U8 a h) t4 l1 ^0 T+ H}: M9 t5 [9 s& `% L0 D0 |
处SlaveAddress为设备地址,使用宏定义。
( V4 V) K$ d" @; I主设备接收数据流程如下:( z2 i+ @, ~% k" M
1.主设备发出I2C开始信号:" P2 z8 Y3 | |) y( g& h7 H7 [1 D% G
2.主设备向I2C总线发送要操作的设备地址以及要进行的操作(读/写)此时,最后一位要为 写操作
" U$ k& h+ s5 e, T8 N3.主设备向从设备写入要读取的从设备寄存器的地址; o+ B& Z, u {
4.主设备向总线发送7位I2C设备地址+1位读操作 标识7 i! ^( d* p! X$ m5 [
5.接收一个字节的数据
; y2 b% O% V1 A7 h6.发起I2C停止信号
7 u" O8 {& x2 Y, Y A' \" y$ r
1 x) A+ M# `- ?6 J' f# ~' evoid Single_ReADI2C(uchar REG_Address,uchar *REG_data,uchar len)7 x- y3 i& O$ J0 l% b# d8 ~
{+ o3 P0 J# j# g0 M* Z5 o1 g
int i;
5 q+ K/ z' e w. W: lI2C_Start();
% G" r" m- _* qI2C_SendByte((SlaveAddress<<1)|0x00);
, I L3 A) o: _/ ~3 h2 r2 q* {I2C_RecvACK();
, H, o% _7 c6 `$ [- gI2C_SendByte(REG_Address);% [% L7 Z* R% R! J, T- ?
I2C_RecvACK();! [4 F! Q, n0 O8 q
I2C_Start();
7 D! U# Y; K. M1 v6 M0 J. dDelay_Ms(50);1 U e( I, p4 w
I2C_SendByte((SlaveAddress<<1)|0x01);- [- `/ ?+ C( K0 H! z/ m8 |
I2C_RecvACK();
/ b+ c0 ]" u X& ^" F, h) z1 h+ W( f- _
for(i=0;i<len;i++)) D$ e1 `4 H5 {3 O* k* d# h% R9 a
{: @, z) }( p0 e1 J; r
REG_data=I2C_RecvByte();( i2 ^- C7 L3 N: J" e
if(i==len-1)$ g5 F( O- A+ T) Q1 \! P3 Q/ h
I2C_SendACK(1);
3 H5 |$ j( s/ }6 N- o: u4 r5 _else
7 {+ e) c2 L1 f0 r& ?7 JI2C_SendACK(0);
" `) j) S5 S+ }* T r}
1 w6 A" n% D! o/ k2 _( VI2C_Stop();$ n4 U' g( W: K/ l' I* Y0 A
* e5 U w7 b o1 s5 |! N8 v2 U6 k
}
% W! U6 }7 G/ ~以上就是常见的I2C设备通信的通用函数。7 w7 W# h. q) k- p& c
使用单片机的I/O接口,再配合时钟,多数的通信方式都可以被模拟出来,就比如51单片机只有一组串口,如果有需要,自然也可以使用两组通用I/O引脚进行串口通信的模拟。 |
|