IIC(Inter
-
Integrated Circuit)
总线是一种由
PHILIPS
公司开发的两线式串行总线,用于连接
微控制器及其外围设备。它是由数据线
SDA
和时钟
SCL
构成的串行总线,可发送和接收数据。
在
CPU
与被控
IC
之间、
IC
与
IC
之间进行双向传送,高速
IIC
总线一般可达
400kbps
以上。
IIC
总线有如下特点:
①总线由数据线
SDA
和时钟线
SCL
构成的串行总线,数据线用来传输数据,时钟线用来
同步数据收发。
②总线上每一个器件都有一个唯一的地址识别,所以我们只需要知道器件的地址,根据时
序就可以实现微控制器与器件之间的通信。
③数据线
SDA
和时钟线
SCL
都是双向线路,都通过一个电流源或上拉电阻连接到正的电
压,所以当总线空闲的时候,这两条线路都是高电平。
④总线上数据的传输速率在标准模式下可达
100kbit/s
在快速模式下可达
400kbit/s
在高速
模式下可达
3.4Mbit/s
。
⑤总线支持设备连接。在使用
IIC
通信总线时,可以有多个具备
IIC
通信能力的设备挂载
在上面,同时支持多个主机和多个从机,连接到总线的接口数量只由总线电容
400pF
的限制决
定。
I
时序图:
![](https://img-blog.csdnimg.cn/9acd7a9af90b4d389619fd05834de267.png)
① 起始信号 当 SCL 为高电平期间,SDA 由高到低的跳变,起始信号是一种电平跳变时序信号,而不是 一个电平信号。该信号由主机发出,在起始信号产生后,总线就处于被占用状态,准备数据传 输。
② 停止信号 当 SCL 为高电平期间,SDA 由低到高的跳变;停止信号也是一种电平跳变时序信号,而不 是一个电平信号。该信号由主机发出,在停止信号发出后,总线就处于空闲状态
③ 应答信号 发送器每发送一个字节,就在时钟脉冲 9 期间释放数据线,由接收器反馈一个应答信号。 应答信号为低电平时,规定为有效应答位(ACK 简称应答位),表示接收器已经成功地接收了 该字节;应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成 功。 观察上图标号③就可以发现,有效应答的要求是从机在第 9 个时钟脉冲之前的低电平期间 将 SDA 线拉低,并且确保在该时钟的高电平期间为稳定的低电平。如果接收器是主机,则在它 收到最后一个字节后,发送一个 NACK 信号,以通知被控发送器结束数据发送,并释放 SDA 线,以便主机接收器发送一个停止信号。
④ 数据有效性 IIC 总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在 时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。数据在 SCL 的上 升沿到来之前就需准备好。并在下降沿到来之前必须稳定。
⑤ 数据传输 在 IIC 总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在 SCL 串行 时钟的配合下,在 SDA 上逐位地串行传送每一位数据。数据位的传输是边沿触发。
⑥ 空闲状态 IIC 总线的 SDA 和 SCL 两条信号线同时处于高电平时,规定为总线的空闲状态。此时各个 器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉 高。
//初始化 IIC
void IIC_Init(void)
{
RCC->APB2ENR|=1<<3; //先使能外设 IO PORTB 时钟
GPIOB->CRL&=0X00FFFFFF; //PB6/7 推挽输出
GPIOB->CRL|=0X33000000;
GPIOB->ODR|=3<<6; //PB6,7 输出高
}
//产生 IIC 起始信号
void IIC_Start(void)
{
SDA_OUT(); //sda 线输出
IIC_SDA=1;
IIC_SCL=1;
delay_us(4);
IIC_SDA=0; //START:when CLK is high,DATA change form high to low
delay_us(4);
IIC_SCL=0; //钳住 I2C 总线,准备发送或接收数据
}
//产生 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);
}
//等待应答信号到来
//返回值: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)
{
ucErrTime++;
if(ucErrTime>250)
{
IIC_Stop();
return 1;
}
}
IIC_SCL=0;//时钟输出 0
return 0;
}
//产生 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;
}
//不产生 ACK 应答
void IIC_NAck(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=1;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
//IIC 发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
void IIC_Send_Byte(u8 txd)
{
u8 t;
SDA_OUT();
IIC_SCL=0;//拉低时钟开始数据传输
for(t=0;t<8;t++)
{
IIC_SDA=(txd&0x80)>>7;
txd<<=1;
delay_us(2); //对 TEA5767 这三个延时都是必须的
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
delay_us(2);
}
}
//读 1 个字节,ack=1 时,发送 ACK,ack=0,发送 nACK
u8 IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
SDA_IN();//SDA 设置为输入
for(i=0;i<8;i++ )
{
IIC_SCL=0;
delay_us(2);
IIC_SCL=1;
receive<<=1;
if(READ_SDA)receive++;
delay_us(1);
}
if (!ack)
IIC_NAck();//发送 nACK
else
IIC_Ack(); //发送 ACK
return receive;
}