IIC(Inter-Integrated Circuit, 内部集成电路)总线是飞利浦公司开发的两线式串行总线,用于短距离传输,常用于微控制器及其外围设备,只有二根信号线,一根是双向的数据线SDA,另一根是时钟线SCL,两条线可以挂多个设备。 IIC设备(绝大多数)里有个固化的地址,只有在两条线上传输的值等于IIC设备的固化地址时,其才会作出响应。通常我们为了方便把IIC设备分为主设备和从设备,基本上谁控制时钟线(即控制SCL的电平高低变换)谁就是主设备。
I2C总线通过上拉电阻接正电源。即当总线空闲时,两根线均为高电平。如此,连在总线上的任一器件输出的低电平,都可以使得总线的信号变低,也就是说各器件的SDA和SCL都是线"与"关系。
I2C是同步串行总线,由SCL为所有设备提供统一的时钟信号。 即使多个节点发送时钟信号时,由于SCL"线与"的原因,SCL上为统一的时钟信号。
数据位(1\0)有效性规定:I2C总线进行数据传送时,时钟信号为高电平期间,SDA线上的数据必须保持稳定;只有在SCL线的信号为低电平器件,SDA线的才可进行高低电平状态变化。
起始信号: SCL线为高电平期间,SDA线由高电平向低电平跳变(下降沿)----是一种电平跳变时序信号
终止信号:SCL线为高电平期间,SDA线由低电平向高电平跳变(上升沿)-----是一种电平跳变的时序信号
应答信号:在接收数据的IC(接收器)在接收到8bit数据后,向发送数据的IC(发送器)发出特定的低电平脉冲,表示已收到数据。即发送器在时钟脉冲9期间释放数据线,这样接收器就可以反馈一个应答信号。ACK(低电平)—规定为有效应答位,NACK(高电平),规定为非应答位,表示接收器接收该字节咩有成功。
为了时钟信号的统一,我们假定只有一个设备发出时钟信号,该设备称为主机。其他IIC设备作为从机存在。
底层驱动函数有:
IIC_Start
IIC_Stop
IIC_SendAck
IIC_SendNack
IIC_ReadAck:读取不到ACK信号时,要发送IIC_Stop
IIC_WriteByte
IIC_ReadByte
假设使用的 IIC_SCL IIC_SDA 输出 以及READ_SDA输入
#define SDA_IN() 宏定义设置管脚为输入模式
#define SDA_OUT() 宏定义设置管脚为输出模式
SCL线为高电平期间,SDA线由高电平向低电平跳变(下降沿)
void IIC_Starts(void)
{
SDA_OUT();//--IO口配置为输出
IIC_SCL = 1; //--拉高时钟线
IIC_SDA = 1; //--拉高数据线
dealy_us(5); //--延时》4.7us
IIC_SDA = 0; //当SCL线为高电平是, SDA线上由高到低电平跳变
delay_us(5); //--延时》4.7us
IIC_SCL = 0; //钳住,等待发送或接受
}
SCL线为高电平期间,SDA线由低电平向高电平跳变(上升沿)
void IIC_Stop(void)
{
SDA_OUT();//--IO口配置为输出
IIC_SCL = 1;//--拉低信号线
IIC_SDA = 0; //--拉低数据线
dealy_us(5); //--延时》4.7us
IIC_SDA = 1; //当SCL线为高电平是, SDA线上由低到高电平跳变
delay_us(5); //--延时》4.7us
}
先将数据线拉高,延时等待稳定,然后将时钟线拉高,延时等待稳定,最后采样数据线电平状态,如果是高电平,未应答,如果是低电平,应答。
uint8_t IIC_Wait_ACK(void)
{
uint8_t ucTimeCnt= 0;
SDA_IN() ; //SDA管脚设为输入模式
IIC_SDA = 1; //--将数据线拉高
delay_us(1); //--延时1us
IIC_CLK = 1; //--将时钟线拉高
delay_us(1);//-延时等待稳定
while(READ_SDA)//--采样数据线电平变化
{
ucTimeCnt ++;
if(ucTimeCnt>250)
{
IIC_Stop();
return 1; //--无回应
}
}
//等到应道地电平
IIC_CLK = 0; //钳住,等待发送或接受
return 0;//--有回应
}
先将数据线和时钟线都拉低,延时等待稳定,然后将时钟线拉高,延时等待4us,将时钟线拉低
void IIC_ACK(void)
{
SDA_OUT(); //--将IO口配置为输出
IIC_CLK = 0; //--时钟线拉低
IIC_SDA = 0; //--数据线拉低
delay_us(2);//--延时等待
IIC_CLK = 1;//--将时钟线拉高
delay_us(2); //--延时等待
IIC_CLK = 0; //--将时钟线拉低
}
先把时钟线拉低,数据线拉高,延时等待稳定,然后把时钟线拉高,等待采样结束,然后把时钟线拉低
void IIC_NACK(void)
{
SDA_OUT() ;
IIC_CLK = 0;
IIC_SDA = 1;
delay_us(2);
IIC_CLK = 1;
delay_us(2);
IIC_CLK = 0;
}
void IIC_Send_Byte(uint8 byte)
{
uint8_t i = 0;
SDA_OUT();//--将IO口配置为输出
IIC_SCL = 0; // 只有在SCL线为低电平时,SDA线才可以改变
for(i=0;i<8;i++)
{
IIC_SDA = (byte&0x80)>>7; //每次发最高位
delay_us(2);
IIC_CLK = 1;
delay_us(2);
IIC_CLK = 0;
delay_us(2);
byte<<1; //更新最高位
}
}
uint8IIC_Read_Byte(uint8_t ack)
{
uint8_t i, recvVal=0;
SDA_IN(); //--将IO口配置为输入
for(i=0; i<8; i++)
{
IIC_CLK = 0;//--时钟线拉低的时候,SDA才允许变化
delay_us(2); //等待输出
IIC_CLK = 1; //拉高时钟线,不允许SDA变化,可以读取SDA
recvVal<<1; //将最低位空出
if(IIC_SDA()) recvVal++; //高电平,则最低位为1
delay_us(1);
}
if(ack)
IIC_ACK();//--通知主机继续发送
else
IIC_NACK();//--通知从机发送结束
retrun recvVal; //--返回读取的数据
}