II2C通信考虑两大问题:怎样区分发送和接收,怎样区分位和字节 由数据线SDA和时钟SCL构成的串行总线,可发送和接收数据。在CPU与被控IC之间、IC与IC之间进行双向传送,高速IIC总线一般可达400kbps以上。IIC是半双工通信方式。 (1)空闲状态 I2C总线总线的SDA和SCL两条信号线同时处于高电平时,规定为总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。 (2)开始信号 起始信号:当SCL为高期间,SDA由高到低的跳变;启动信号是一种电平跳变时序信号,而不是一个电平信号。
void IIC_Start(void) { SDA_OUT();//设置SDA线为输出模式 IIC_SDA=1; IIC_SCL=1; delay_us(4); IIC_SDA=0; //在此处SDA数据线出现了下降沿 //START:when CLK is high,Data change from high to low delay_us(4); IIC_SCL = 0; //钳住I2C总线,准备发送或接收数据 }(3)停止信号 停止信号:当SCL为高期间,SDA由低到高的跳变;停止信号也是一种电平跳变时序信号,而不是一个电平信号。
//产生 IIC 停止信号 void IIC_Stop(void) { SDA_OUT(); //sda 线输出 IIC_SCL=0; IIC_SDA=0; //STOP:when CLK is high DATA change form low to high delay_us(4); IIC_SCL=1; IIC_SDA=1; //发送 I2C 总线结束信号 delay_us(4); }(4)应答信号ACK,非应答信号NACK,等待应答信号的到来(重点看图,理解图) 正点原子讲的有点抽象,看个人注释就好 发送器每发送一个字节,就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。 1)产生有效应答(用于发送):应答信号为低电平时,规定为有效应答位(ACK简称应答位),表示接收器已经成功地接收了该字节;【个人注释:在一个SCL完全跳变周期内,SDA保持为低电平】
//产生 ACK 应答 void IIC_Ack(void) { IIC_SCL=0; SDA_OUT(); IIC_SDA=0; delay_us(2); IIC_SCL=1; delay_us(2); IIC_SCL=0; }2)非有效应答(用于发送):应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。 【个人注释:在一个SCL完全跳变周期内,SDA保持为高电平】 //不产生 ACK 应答//NACK
void IIC_NAck(void) { IIC_SCL=0; SDA_OUT(); IIC_SDA=1; delay_us(2); IIC_SCL=1; delay_us(2); IIC_SCL=0; }3)等待应答信号的到来(这里是接收)接收告诉单片机两种状态,成功(0)or失败(1)
//等待应答信号到来 //返回值: 1,接收应答失败 // 0,接收应答成功 u8 IIC_Wait_Ack(void) { u8 ucErrTime=0; SDA_IN(); //SDA 设置为输入 IIC_SDA=1;delay_us(1); //默认拉高,因为有效相应为低电平,用高电平去捕捉低电平 IIC_SCL=1;delay_us(1); while(READ_SDA)//#define READ_SDA PBin(7) //输入SDA { ucErrTime++; if(ucErrTime>250) { IIC_Stop(); return 1; } } IIC_SCL=0; //时钟输出 0 return 0; }4)对于反馈有效应答位ACK的要求是,接收器在第9个时钟脉冲之前的低电平期间将SDA线拉低,并且确保在该时钟的高电平期间为稳定的低电平。 【这里我解释一下:有效应答的要求之一时,在第9个时钟整周期中,SDA需要保持完整的低电平】 5)如果接收器是主控器,则在它收到最后一个字节后,发送一个NACK(非应答高电平)信号,以通知被控发送器结束数据发送,并释放SDA线,以便主控接收器发送一个停止信号P。 (5)数据的有效性:【个人理解,SCL是衡量,也就是说SCL是不变的,SDA要配合SCL,如何想数据有效,在SCL完整的周期内,SDA必须稳定】 (6)数据传输(A:发送一个字节 B:接收一个字节)主要先看这个图,虽然个人认为没什么用 个人理解:这段代码是怎么写出来的,怎么考虑的,主要是以SCL为主,先写SCL的电平。 个人理解:SCL为低电平,才能改变SDA,因为SCL一旦是高电平就要传输数据,同时要求SDA不能再改变
A:发送一个字节 8位 txd是一个字节,需要一位一位读取
//IIC 发送一个字节 //返回从机有无应答 //1,有应答 //0,无应答 void IIC_Send_Byte(u8 txd) { u8 t; //用于计数 SDA_OUT(); //传输数据,用于输出 IIC_SCL=0;//拉低时钟开始数据传输,应该说在SCL低电平才能改变数据,在高电平时传输数据 for(t=0;t<8;t++) { IIC_SDA=(txd&0x80)>>7;//1000 0000最高位的读出来,然后将其移到最低位 txd<<=1;//将最高位挪走。这时候数据已经准备好 delay_us(2); //对 TEA5767 这三个延时都是必须的,等待 IIC_SCL=1; //给高电平,传输数据 delay_us(2);//让他传一会 IIC_SCL=0; //传完。拉低,为下一次传位做准备。 delay_us(2); } }**B是接收一个字节,也是8位,跟刚才原理不一样,读的时候SCL为高电平才能说明有SDA传输过来。 所以SDA需要在SCL高电平期间去读数据。** 但是读与写有一区别,读要将数据读出来,通过函数返回,所以为非空函数,需要接收的数据return回来。
u8 IIC_Read_Byte(unsigned char ack) //输入为应答 { unsigned char i,receive=0; //清楚零,数据不能乱 SDA_IN(); //SDA 设置为输入 for(i=0;i<8;i++ ) { IIC_SCL=0; //数据要改变SCL需要变为低电平 delay_us(2); IIC_SCL=1; //设置为高电平 receive<<=1;//receive左移动一位,如果是高电平就要加1,不是就是0,发送是最高位,接收是最低位 if(READ_SDA)receive++; delay_us(1); } if (!ack) IIC_NAck(); //发送 nACK else IIC_Ack(); //发送 ACK return receive; }