IIC(Inter-Integrated Circuit)总线是一种由NXP(原PHILIPS)公司开发的两线式串行总线,用于连接微控制器及其外围设备。多用于主控制器和从器件间的主从通信,在小数据量场合使用,传输距离短,任意时刻只能有一个主机等特性。
在 CPU 与被控 IC 之间、IC 与 IC 之间进行双向传送,高速 IIC 总线一般可达 400kbps 以上。
PS:1. 这里要注意IIC是为了与低速设备通信而发明的,所以IIC的传输速率比不上SPI
2.IIC是一种半双工的通信方式
I2C 总线在传送数据过程中共有三种类型信号, 它们分别是:开始信号、结束信号和应答信号。
开始信号: SCL 为高电平时, SDA 由高电平向低电平跳变,开始传送数据。
结束信号: SCL 为高电平时, SDA 由低电平向高电平跳变,结束传送数据。
应答信号:接收数据的 IC 在接收到 8bit 数据后,向发送数据的 IC 发出特定的低电平脉冲,表示已收到数据。
IIC串行总线一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL,其时钟信号是由主控器件产生。所有接到IIC总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。对于并联在一条总线上的每个IC都有唯一的地址。
一般情况下,数据线SDA和时钟线SCL都是处于上拉电阻状态。因为:在总线空闲状态时,这两根线一般被上面所接的上拉电阻拉高,保持着高电平。
1.空闲状态
如图所示:IIC总线的SDA和SCL都处于高电平的时候就是总线的空闲状态,此时各个器件的输出级场效应管均处于截止状态,即释放总线,两条总线的上拉电阻将电平拉高。
2.起始信号和停止信号
如上图所示:
起始信号:SCL 为高电平时, SDA 由高电平向低电平跳变,开始传送数据。启动信号是一种电平跳变时序信号,而不是一个电平信号。
停止信号:SCL 为高电平时, SDA 由低电平向高电平跳变,结束传送数据。停止信号也是一种电平跳变时序信号,而不是一个电平信号。
3.应答信号
发送器每发送一个字节(8个bit),就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。
0表示应答,1表示非应答。
4.数据有效性
IIC总线进行数据传送时,时钟信号SCL为高电平期间,数据线SDA上的数据必须保持稳定,只有时钟信号SCL为低电平时,数据线上的数据才允许发生改变。
即:SDA在SCL上升沿来之前就必须准备好,而且在下降沿来之前都保持稳定。
5.数据的传达
在IIC总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在SCL串行时钟的配合下,在SDA上逐位地串行传送每一位数据。数据位的传输是边沿触发。
在原理图中找到我们的IIC的两根线
1.初始化
//初始化IIC
void IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//RCC->APB2ENR|=1<<4;//先使能外设IO PORTC时钟
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOC, ENABLE );
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
IIC_SCL=1;
IIC_SDA=1;
}
2.起始信号
//产生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总线,准备发送或接收数据
}
3.结束信号
//产生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.等待应答
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;
}
5.产生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;
}
6.不产生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;
}
7.发送一个数据
void IIC_Send_Byte(u8 txd)
{
u8 t;
SDA_OUT();
IIC_SCL=0;//拉低时钟开始数据传输
for(t=0;t<8;t++)//一个字节8位
{
IIC_SDA=(txd&0x80)>>7;//将txd逐个位发送 SDA先准备好
txd<<=1; //取一位后左移一位
delay_us(2); //对TEA5767这三个延时都是必须的
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
delay_us(2);
}
}
8.读取一个数据
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;
}
1. 首先由主芯片发送一个start信号。
2.然后发出一个设备地址(用来确定往哪一个芯片写数据),后面接以为表示R/W的读写位。
3.从机将自己的地址与主机发出的地址进行对比,如果相同则认为自己正在被主机寻址,根据R/T位将自己确定为发送器和接收器
4.这个时候主机在等待从机的应答信号,接收到应答信号之后,继续发生访问从机的地址(比如说某个寄存器的地址,可能高八位、低八位这样发)
5.从机在接收到主机的数据后,给一个应答,然后主机继续发送数据,从机继续n次应答
6.主机产生停止信号,结束传送过程。
了解了IIC的发生数据,读取数据就变得简单多了
1.开始的时候主芯片要发送一个start信号(这个时候方向位为0,表示写),然后发送一个设备地址(用来判断是哪一个芯片读取数据)和R/W-------写
2.从机回应设备地址是否存在,然后主机再发送7位要读地址(某个寄存器)-------写
3.主机再次发送起始信号(方向位为1,表示读)这个时候才算正式读操作
3.从机会有一个应答信号,然后从机继续发送数据,进行n次数据的发送
5.当数据发送完成时,主机会发生一个nack表示不再接收数据或者然后发送一个停止信号