IIC底层驱动程序
2 N" z- `0 a1 g% O* g这次I是普通I/O端口模拟IIC通信实验
& I' b. O5 g1 D) v6 M9 L具体引脚配置如下:
, H/ z: V2 g: s1 R
3 V3 C) {6 F- ]6 e8 ~! D: s' M- A8 K4 l$ k$ [3 ^- O6 f! ~
6 y0 q6 S( k) n; g2 e" G
IIC主控实验----底层驱动GPIO配置
9 w5 B' d! V. d6 L4 {: W/ |GPIO初始化配置" O' s. i" @, j9 i3 X9 E5 @
具体代码如下:
) T R/ v6 e1 W) G' g
1 L2 b# r- Z' B2 T: n- //初始化IIC0 f5 _8 Y+ f: Q2 A: y
- void IIC_Init(void)
4 J0 M! r6 j8 u; F9 s/ j - { 9 O, T4 R3 t( _% _/ O
- GPIO_InitTypeDef GPIO_InitStructure;% x) A" |9 o2 j1 o& p A- o
- RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE ); //使能GPIOB时钟
6 l4 A8 g$ T9 G1 c& J - # ~( L) P0 f/ ~5 t9 N# I
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
% i6 E& z- L. i+ d6 m4 [ - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; //推挽输出( c$ o& g; D$ n5 I- w* O; Y: x0 |
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;! f5 {% Z" V* a( L8 P
- GPIO_Init(GPIOB, &GPIO_InitStructure);
( ?5 a# x% y( i# x! s" A+ C - - H- @' ~ A, P3 V+ v
- GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7); //PB6,PB7 输出高电平---空闲状态5 r; [3 W5 \- X5 F2 t" f
- }
复制代码 1 ^1 k8 E) k6 W: ~! R6 b
时钟线(SCL)—GPIO功能配置
. A& o% X8 e2 F" C4 X7 X在IIC通信中,主控的时钟线(SCL) 一直是输出状态
0 c, j" p) }) E1 k) V. B具体程序如下:# O. r& U/ G s% H: h2 \& z% b
" i+ R; P0 ^( c, B2 |$ {- //时钟线(SCL):PB6输出---高电平
& @" p# \, ?+ f8 p - #define SCL_H GPIO_SetBits(GPIOB,GPIO_Pin_6)
* Z3 A, j2 _2 J - % ?1 c( t* P0 C
- //时钟线(SCL):PB6输出---低电平
0 A, |- P. \$ L9 W) r - #define SCL_L GPIO_ResetBits(GPIOB,GPIO_Pin_6)
复制代码 1 S3 G R, o. [ H2 P' e" E- g
数据线(SDA)—GPIO功能配置
0 g5 |: z8 f, ^0 R; D9 Y由于IIC通信是半双工通信方式
1 x$ Z, a( h* d3 n( Y% T因而数据线(SDA)可能会是数据输入,也可能是数据输出。/ L' Q5 L' C5 a0 m! S- R
% }/ e6 ^) K6 @/ [6 O: R. b
- 数据线(SDA)输入模式$ C9 E* D9 O- |5 a" y' l6 e
- //数据线(SDA):PB7输入' Z, k9 z5 t2 o4 h; ?4 I
- #define SDA_IN() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;}7 @ Z5 ?1 r* \4 l0 k$ I
- % `& c" ^: s1 I9 G, D3 R
- //读取PB7的电平
, ^$ o9 u6 E7 T% s/ E/ t* f - #define READ_SDA GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_7)
复制代码 : K1 C8 |& Y9 h
数据线(SDA)输出模式
# M9 H/ n/ a* B/ p( s8 W3 ^6 G- //数据线(SDA):PB7输出1 E# ?- T& B4 v7 b2 ` J' c' E
- #define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;}
5 E" b s- o7 G! l
9 Y5 ~2 s7 `& v* q0 g) p- //数据线(SDA):PB7输出---高电平
5 \+ s, K( S' }" h( r - #define SDA_H GPIO_SetBits(GPIOB,GPIO_Pin_7)
) P# n& z/ B- m& n/ o - //数据线(SDA):PB7输出---低电平3 `: g5 O/ r- k( A! k& \! W( e
- #define SDA_L GPIO_ResetBits(GPIOB,GPIO_Pin_7)
复制代码 7 ?0 K- _; v, y2 g; K
IIC主控实验----底层驱动程序* ]6 o! M G% p( x; p; w/ {4 [
开始信号0 z' S% H7 L3 X8 j$ E9 ?: V
当时钟线SCL 为高电平时,数据线SDA 由高电平向低电平跳变。
8 o, h3 Z* T4 @0 ^, Q& z
- V; e3 z7 g, m% ~1 j$ [具体程序如下:
. `3 r1 p l. _) U: f E$ v: P6 X/ H- d$ S
- void IIC_Start(void)//开始信号
# O5 z+ ~! W5 N$ x. N$ p - {7 |/ [7 {( [, n9 D6 i) J5 L5 g
- SDA_OUT(); //数据线(SDA)输出模式
* @+ \( ?1 }0 {0 ^3 p1 ^ - SDA_H; //数据线(SDA)输出高电平
3 f! ^1 ~0 Q+ L - SCL_H; //时钟线(SCL)输出高电平$ C6 _' L& d0 v+ I
- delay_us(5);
6 a" z/ W1 [( o1 m( S% u P - SDA_L; //数据线(SDA)输出低电平8 y8 ]( L- c2 Y$ V! ^! }
- delay_us(5);
8 O ?$ d8 D$ ?: x0 P - SCL_L; //时钟线(SCL)输出低电平( D- O1 ^- y7 d& d
- }
复制代码 + @& j; @7 [' H: k
终止信号- I( `# i' j: l! E1 k( @8 T+ b
当时钟线SCL 为高电平时,数据线SDA 由低电平向高电平跳变。0 D# P/ P0 K$ D
6 u6 C. `! k; [. t, q
" o( D' J7 Y# _/ N M
( V- q! h) l: Z具体程序如下:7 R0 C0 o% H8 L
. p/ ^0 v$ [8 s' K- void IIC_Stop(void)//终止信号
% u' |, t: e6 N4 K# | - {) d: i% Y; E& T2 i
- SDA_OUT(); //数据线(SDA)输出模式& ]) n( R3 T3 q4 h" V2 Z7 B
- SCL_L; //时钟线(SCL)输出低电平
" P. G+ ]5 D+ m - SDA_L; //数据线(SDA)输出低电平5 P2 x" D X/ e1 [
- delay_us(5);0 ~0 n `$ M/ {/ k5 G, k
- SCL_H; //时钟线(SCL)输出高电平
: h9 a4 X& S& k$ H - SDA_H; //数据线(SDA)输出高电平
7 @2 S [) T3 ^ - delay_us(5);+ z6 g v4 H2 F- \ O' l
- }
复制代码 8 p, I7 k5 v) _. @6 `
应答信号
x9 j7 U1 @- D- v% h+ h1 i# s0 r我们这里写的是IIC主控程序,在读取从控数据的时候,需要回复应答信号。
K+ w% g) n/ ~% S; n- v+ W: s4 P产生应答信号,表示接收数据成功。
6 g( Y3 R! m* y1 B% h( P9 [. g' ]# j6 K+ i$ X$ H0 z
6 j1 N" h8 _* o" j
& |; i" `& `% X- b$ K9 B具体程序如下:
6 p6 ^2 ^& K) ~ ]( X& V* r' G" e- o8 v/ R1 S% O
- void IIC_Ack(void)//产生应答信号
3 n% k Y4 V# ^8 M9 ]# t - {& Q. w6 `8 }+ r
- SCL_L; //时钟线(SCL)输出低电平
U0 D& f) u/ F: F7 ]$ h3 { - SDA_OUT(); //数据线(SDA)输出模式
* G$ p& Q% k- ^2 d* d; z - SDA_L; //数据线(SDA)输出低电平, c% R6 W( @! e4 _ a# ^
- delay_us(2);
3 ]6 x3 P0 w" S8 `; e$ K+ t- S - SCL_H; //时钟线(SCL)输出高电平
9 f$ j" R0 H9 a$ E1 {; z y - delay_us(5);
, {) W' @- j" [) [ - SCL_L; //时钟线(SCL)输出低电平
, q( l" u' k0 M: R5 g# S2 N - }
复制代码 9 p& J& Q% |. c- J2 T; ^
产生非应答信号,表示接收数据失败。
: z- _7 o( j1 M; {! Y
+ W4 d7 G, q8 u# `# Z
( d( B* U7 l+ A1 B5 v1 x
+ k! z! q# d% b: `具体程序如下:
* z! t5 G, @3 E$ u1 F8 ~6 H. V* u0 i y) \ \' O( P; [
- void IIC_NAck(void)//不产生应答信号; U! w% T% N$ c% z
- {' K2 \- a) h0 W4 q5 K" i6 r
- SCL_L; //时钟线(SCL)输出低电平# e( W9 k0 ~) Z* j2 F! k3 V! `
- SDA_OUT(); //数据线(SDA)输出模式) a3 a. A3 I5 t3 {" H% ?; z5 }
- SDA_H; //数据线(SDA)输出高电平1 }3 j8 h2 v" @+ Z+ C5 E4 m
- delay_us(2);/ n6 R. A' L" @( G5 M
- SCL_H; //时钟线(SCL)输出高电平
. r$ r, x( J+ E2 o! X9 A1 F - delay_us(5); a& O1 R* |2 o* B" B& S6 s6 P
- SCL_L; //时钟线(SCL)输出低电平
7 f7 R$ M6 j# M) r+ S, S- H - }
复制代码 - o5 c, }6 v2 l
# P9 ] N5 `+ _9 r" gIIC主控程序,在写入数据到从控的时候,需要等待应答信号。
) Q) H0 i4 n; W' E' ^* M
$ R- \, q% `, V( f- //返回值:1,接收应答失败0 L) M! Z* V% ^% K) T& c
- // 0,接收应答成功
% I8 ^1 C% ?* h& k, w' t; U - u8 IIC_Wait_Ack(void)//等待应答信号. _ x# o* [; ~" v$ f/ Z
- {
. ]7 N! x9 {# j# m& o" j - int uc=0;! i: t7 E3 o. P E# Q
- SDA_IN(); //数据线(SDA)输入模式8 p# v5 T$ |" O5 n& v" O* @
- SDA_H; delay_us(1);
9 T/ @+ q1 l- [& s - SCL_H; delay_us(1);% }1 G# \" _+ Y0 y* Y6 k
- while(READ_SDA)
2 V- v0 L$ g! s! M0 ~7 C, e P - {) k! F& Z6 M( w0 `* U9 m
- uc++;: t2 l: W5 @1 }9 C- r
- if(uc>250)//接收应答失败0 @% l9 g5 f1 Q$ f
- {! P) n; M9 u) V" E" K
- IIC_Stop();//发送终止信号 * X/ @7 I; |. k
- return 1;; r/ D; j3 ?4 x3 V
- }6 N1 i- ~ V8 G: F2 D+ K; _
- }- b' |; \( E& v( X
- SCL_L;
Q( W; C' D- y* c; ^3 E& O- Z - return 0;
6 O; m$ u9 @+ }7 }- d; y8 } - }
复制代码 / i7 H0 t; c, D
数据传输* I3 Y0 h9 f, t) u0 B
IIC总线进行数据传送时,时钟线(SCL)上的信号为高电平期间,数据线(SDA)上的数据必须保持稳定。% C/ a1 B; l) ^9 \$ a" d
只有在时钟线(SCL)上的信号为低电平期间,数据线(SCL)上的高电平或低电平状态才允许变化。
2 S$ Z' F. s; B
4 h, y C- O0 A% Q. q$ F& S, Z6 d3 H# u6 G
9 z3 f+ M8 k. ~
7 l( J" U( S8 l/ N
输出到数据线(SDA)上的每个字节必须是8位1 q. x- i- [. h3 C+ N G$ S
数据传送时,先传送最高位(MSB),后传送最低位(LSB), y2 @7 A' r9 X& p. h' h! v
每一个被传送的字节后面都必须跟随一位应答位(即一帧共有9位); F$ f* T% s0 L7 b& {, D
( I: t, ^% e7 t0 w
& k* W) {( K; Q# ^0 [# M- B
% q0 S1 A5 b2 H) P6 ^9 iIIC主控发送一个字节数据1 C3 p J8 ~4 x+ D. C+ f
具体程序如下:
- u8 }. {% O& q" |: E+ P1 k6 Y
' O2 c' O9 O' H, }$ l- void IIC_Send_Byte(u8 txdata)//发送一个字节数据---高位在前,低位在后
0 o W5 w8 h) }) Q: _' h* R0 p - {
' }& z' m" B' [% [2 { - SDA_OUT(); //数据线(SDA)输出模式
. N- y/ `. o# W' |; { - SCL_L;9 X1 Q+ ^7 I8 A; s* p
- for(int t=0;t<8;t++) //高位在前,低位在后。9 {. c, ~0 M, ^+ u% V
- {1 A$ V+ n6 c$ O" L# B- ?! f: h5 F
- if((txdata&0x80)>>7) SDA_H;( D5 H" y: S8 N, @" d
- else SDA_L;1 E0 n+ m- e: G5 x# H& Q
- txdata<<=1;/ b# r* S! Q6 e" y i2 l
- delay_us(2);
9 p' X3 q, X. Q9 _. | - SCL_H;/ ^1 x! k# u% |3 k$ L: B1 ]
- delay_us(2);
) w2 C: F. w$ m; X - SCL_L;* b, w* m* F$ U& s% B& [# }
- delay_us(2);
5 r( w* R8 o. L - }/ ^/ g$ }9 v8 P2 F% H
- }
复制代码
$ T# Z X( K" f0 W* l: K% D8 YIIC主控接收一个字节数据& _5 }6 v( X% |% p0 y
具体程序如下:6 Q0 X9 X A8 t" s& t; }
% ^ S+ j+ H0 Q% e- u8 IIC_Read_Byte(unsigned char ack)//读取一个字节数据---ack=1时,发送ACK,ack=0,发送nACK9 `; r3 `7 ]% A- `" O
- {4 u$ i# z. L' g, t$ e1 i
- unsigned char rxdata=0,i;2 |& S, T2 b1 w, M8 i% g
- SDA_IN(); //数据线(SDA)输入模式
( `* H- b% g. Y6 d' m! C - for(i=0;i<8;i++)9 `/ A; K& Q9 E! x5 u+ `
- {1 b2 Z$ R% W1 J# \: D; g1 y
- SCL_L;
4 A [' V: O! X+ Y3 _. V6 x9 [ - delay_us(2);. i. v4 p2 b3 m8 M0 u9 T4 J
- SCL_H;; g3 a' Q/ R2 h, V
- rxdata<<=1;
8 T8 ~) U M" ?/ X3 O9 U - if(READ_SDA) rxdata++;
; y. j6 T2 Y2 z - delay_us(1);8 i( V) ^1 M+ U3 I
- }7 l6 W7 d* w9 g' v8 W
- if(!ack) IIC_NAck();/ i" t- e% a2 L, T$ b
- else IIC_Ack();
5 S" l+ ]% {$ P4 n! o8 N - return rxdata;
% z7 J" p( Q* N1 Q4 {5 w& Y - }
复制代码 5 G1 L7 s8 V. K0 L+ o3 n
% F( d7 z1 {- N O; R5 I2 l: R————————————————" j! O8 N+ l6 }+ Y! m+ b
版权声明:根号五
% R) o) V. A* t& F+ ?
) n9 T5 |4 j8 L% i' S4 P/ A7 p0 a7 O* R; A$ N$ }9 \
|