1. 硬件原理
1.1 IIC 基础
IIC协议简介—学习笔记_iic标准协议_越吃越胖的黄的博客-优快云博客
I2C(Inter-Integrated Circuit)是一种串行通信协议,用于连接微控制器、传感器、存储器和其他外设。
I2C使用两条线(SDA和SCL)进行通信,可以连接多个设备,每个设备都有一个唯一的地址。I2C总线上的设备可以充当主设备或从设备。主设备负责发起通信,从设备负责响应通信请求。
I2C协议具有以下特点:
- 两根线
- 同步、半双工
- 带应答
- 一主多从、多主多从
1.2 硬件连接
要能手撕出来

仲裁:SDA线的仲裁也是建立在总线具有线“与”逻辑功能(线与逻辑,即两个以上的输出端直接互连就可以实现“AND”的逻辑功能。两个一出一,一个一出零、没有一出零)的原理上的。节点在发送1位数据后,比较总线上所呈现的数据与自己发送的是否一致。是,继续发送;否则,进行比较,输出低电平进行发送,输出高电平退出。SDA线的仲裁可以保证I2C总线系统在多个主节点同时企图控制总线时通信正常进行并且数据不丢失。总线系统通过仲裁只允许一个主节点可以继续占据总线。
1.3 时序
要能手撕出来
时序总结:
总线空闲状态 | SCL和SDA均为高电平,接上拉电阻。 |
---|---|
起始信号(START) | 在SCL保持高电平期间,SDA由高电平被拉低。由主控器发出。 |
数据位传送(DATA) | 在SCL保持高电平期间,SDA上的电平保持稳定,低电平为数据0、高电平为数据1。用法:主控器和被控器都可发出。 |
应答信号(ACK) | 在SCL保持高电平期间,SDA保持低电平。IIC总线上所有数据都是以8位字节传送的,发送器每发送一个字节,就在第9个时钟脉冲期间释放SDA(高电平),由接收器反馈一个ACK。 |
非应答信号(NACK) | 在SCL保持高电平期间,SDA保持高电平。如果接收器是主控器,则它在收到最后一个字节后,发送一个NACK,通知被控器结束数据发送,并释放SDA(高电平),以便主控器发送一个STOP。 |
停止信号(STOP) | 在SCL保持高电平时间,SDA由低电平被释放(拉高)。由主控器发出。 |
各时序部分的代码(要能手撕出来):
起始信号
void IIC_Start(void)
{
SDA_H; //拉高SDA线
SCL_H; //拉高SCL线
DelayUs(iicInfo.speed); //延时,速度控制
SDA_L; //当SCL线为高时,SDA线一个下降沿代表开始信号
DelayUs(iicInfo.speed); //延时,速度控制
SCL_L; //钳住SCL线,以便发送数据
}
停止信号
void IIC_Stop(void)
{
SDA_L; //拉低SDA线
SCL_L; //拉低SCL先
DelayUs(iicInfo.speed); //延时,速度控制
SCL_H; //拉高SCL线
SDA_H; //拉高SDA线,当SCL线为高时,SDA线一个上升沿代表停止信号
DelayUs(iicInfo.speed);
}
应答信号
//产生ACK应答,读取从机一字节数据后还要接着读的时候使用
//SCL在SDA一直为低电平期间完成低高电平转换
void IIC_Ack(void)
{
SCL_L; //拉低SCL线
SDA_L; //拉低SDA线<----
DelayUs(iicInfo.speed);
SCL_H; //拉高SCL线
DelayUs(iicInfo.speed);
SCL_L; //拉低SCL线
}
//不产生ACK应答,读取从机一字节数据后不读了的时候使用
//SCL在SDA一直为高电平期间完成低高电平转换
void IIC_NAck(void)
{
SCL_L; //拉低SCL线
SDA_H; //拉高SDA线<----
DelayUs(iicInfo.speed);
SCL_H; //拉高SCL线
DelayUs(iicInfo.speed);
SCL_L; //拉低SCL线
}
//等待ACK应答
//发送完一个字节后(释放SDA)的下一个时钟高电平时期,读取SDA电平,0为收到应答
_Bool IIC_WaitAck(unsigned int timeOut)
{
SDA_H;DelayUs(iicInfo.speed); //拉高SDA线
SCL_H;DelayUs(iicInfo.speed); //拉高SCL线
while(SDA_R) //如果读到SDA线为1,则等待。应答信号应是0
{
if(--timeOut)
{
printf("WaitAck TimeOut\r\n");
IIC_Stop(); //超时未收到应答,则停止总线
return IIC_Err; //返回失败
}
DelayUs(iicInfo.speed);
}
SCL_L; //拉低SCL线,以便继续收发数据
return IIC_OK; //返回成功
}
发送数据
void IIC_Send(unsigned