概念
I2C是一种串行同步通信协议,用于在集成电路之间进行通信。
硬件路线
I2C使用两根线进行通信:串行数据线(SDA)和串行时钟线(SCL)。
通信结构
I2C使用主从结构,其中主设备发起通信并控制通信的节奏,从设备响应主设备的请求。从机通常不会主动给主机发送数据,一般为主机给从机发送读取指令后,从机才给主机发送数据。器件发送数据到总线上,则定义为发送器,器件接收数据则定义为接收器。主器件和从器件都可以工作于接收和发送状态。
不能在IIC通信总线上接入无限多个设备的原因:
1.地址冲突:在IIC总线上,每个设备都需要有一个唯一的地址。地址通常由设备的硬件引脚配置。然而,IIC地址字段的宽度有限,这意味着地址空间是有限的。当连接的设备数量超过地址空间的限制时,设备之间可能会出现地址冲突,导致通信失败。
2.总线负载:每个设备在IIC总线上会产生一定的负载。总线的负载是指总线上的所有设备对电流和电压的需求。当连接的设备数量增多时,总线负载会增加,可能导致通信速度变慢或信号失真。
3.电气特性:IIC总线的电气特性是有限的。总线上的设备之间通过开漏或开漏/开漏极性输出进行通信。然而,当连接的设备数量过多时,总线上的电气负载可能超过IIC规范的限制,导致通信错误或电气稳定性问题。
一些基础概念
SDA:双向串行数据线,数据既可以从主机发送到从机,也可以从从机发送到主机。
SCL:串行时钟线,驱动数据线的信号由 SCL 产生。
主机:主机产生串行时钟(SCL)控制总线的传输方向,并产生起始条件(占用总线)和停止条件(释放总线)。
从机:从机不会控制 SCL 线,从机可以发送数据给主机,但是从机永远不可能主动发送数据给主机。
发送器:发送数据的一方 接收器:接受数据的一方 。
仲裁器:解决多主机模式下竞争总线的问题。(通常情况下我们遇到的都是单主机模式,一主多从)
总线的空闲状态:SDA 和 SCL 都是高电平
重复起始条件(一般不提,跟普通通信一样但是能够切换主从机):I2C(Inter-Integrated Circuit)协议中的重复起始条件(Repeated Start Condition)是一种特殊的通信方式,允许在不断开总线的情况下切换主机和从机,并进行连续的数据传输。
主机如何能找到对应的从机与其进行通信?——设备地址
每个I2C设备都有一个唯一的7位或10位地址,用于在总线上唯一标识设备。
7(4、3)位地址模式下,可以有最多128个不同的设备地址(0x00至0x7F)。
10位地址模式下,可以有最多1024个不同的设备地址(0x000至0x3FF)。
器件地址包含固定地址(由厂家决定)和可编程地址(由使用者决定)——参考手册。
7位地址编码方式:
在7位地址编码方式下,从机设备的地址由7个位组成,范围是0x00至0x7F(0至127)。其中,最高位(MSB)通常由I2C总线规范中保留,用于指示读/写操作。读操作对应的最高位为1,写操作对应的最高位为0。
例如,一个从机设备的7位地址为0x50(十进制为80),对应的二进制表示是0101000(其中最高位为读/写位,0表示写操作)。
7位地址编码方式是I2C通信中最常用的方式,适用于大多数应用。
10位地址编码方式:
在10位地址编码方式下,从机设备的地址由10个位组成,范围是0x000至0x3FF(0至1023)。其中,最高两位(MSB)用于指示读/写操作和扩展地址位。
扩展地址位提供了更多的地址空间,可以用于连接更多的从机设备。在10位地址编码方式下,前六位(位7至位2)用于指定扩展地址位,后四位(位1至位0)用于指示读/写操作。
例如,一个从机设备的10位地址为0x235(十进制为565),对应的二进制表示是1000110101(前两位为读/写位和扩展地址位)。
10位地址编码方式相对较少使用,主要用于连接大量的从机设备。
如何寻址?
在I2C通信中,主机设备可以选择与多个从机设备进行通信,每个从机设备都有一个唯一的地址。通过在地址字节中指定正确的从机设备地址,主机可以选择与特定的从机设备进行通信。
7位和10位就是一个从机多一些。
I2C使用起始条件(Start Condition)和停止条件(Stop Condition)来标识数据传输的开始和结束。
数据传输可以是字节(Byte)级别的,也可以是多字节(Multi-Byte)级别的。
主设备通过将数据位推送到SDA线上发送数据,从设备通过SDA线接收数据。
I2C传输模式:
I2C支持两种传输模式:发送模式(Master Transmit)和接收模式(Master Receive)。
在发送模式中,主设备向从设备发送数据。
在接收模式中,主设备从从设备接收数据。
数据帧格式:
起始条件(占用总线)+数据位(8 位,发送方发出,一般为7位地址位和1位读写位)+应答位(1 位,接收到 1 个字节数据的 一方要回一个应答,0 有应答,1 非应答)+数据传输(8位)+停止条件(释放总线)
I2C通信的时钟速度由主设备控制。
标准模式下,时钟频率为100 kHz。
快速模式下,时钟频率为400 kHz。
高速模式和超高速模式下,时钟频率可以更高,但具体取决于设备和总线的支持能力。
IIC 总线通信速度:
低速:100Kbit/s 快速:400Kbit/s 高速:3.4Mbit/s
在每个数据字节传输后,接收设备会发送应答位(ACK)以确认接收到数据。
如果接收设备成功接收数据,它会拉低SDA线发送ACK信号;如果无法接收或不希望接收,它会保持SDA线为高电平发送NACK信号。
应答的发送和接收:
I2C总线上的多个设备共享同一对SDA和SCL线。
为了避免冲突,一般使用以下两个方法:
熟悉要使用的具体I2C设备的规格说明、数据手册或参考资料。
学习设备的功能和特性,包括寄存器、命令、数据格式等。
初始化I2C接口、配置通信参数、发送和接收数据。参考代码:
void IIC1_Pin_Init(void)
{
//PB6----SCL---推挽输出
//PB7----SDA---开漏输出
RCC->AHB1ENR |=0x01<<1;//端口时钟使能
GPIOB->MODER &=~(0x0f<<12);
GPIOB->MODER |=(0x05<<12);
GPIOB->OTYPER |=(0x03<<6);//PB6PB7开漏输出
//总线在空闲状态
IIC1_SCL=1;
IIC1_SDA_OUT=1;
}
/*
****************************************************************************************
* Function: IIC1_Start
* Description: 起始条件
****************************************************************************************
*/
void IIC1_Start(void)
{
IIC1_SCL=0;
Delay_us(6);
IIC1_SDA_OUT=1;
IIC1_SCL=1;
Delay_us(6);//延时---起始条件的建立时间
IIC1_SDA_OUT=0;//---产生起始条件
Delay_us(6);//延时---起始条件的保持时间
IIC1_SCL=0; //---结束起始条件
}
/*
****************************************************************************************
* Function: IIC1_Stop
* Description: 停止条件
****************************************************************************************
*/
void IIC1_Stop(void)
{
IIC1_SDA_OUT=0;
IIC1_SCL=1;
Delay_us(6);//延时---停止条件的建立时间
IIC1_SDA_OUT=1; //---产生了停止条件
Delay_us(6);//延时---本次通信结束到下次通信开始的时间
}
/*
****************************************************************************************
* Function: IIC1_Send_Ack
* Description: 主机发送应答信号
* Input: ack--0表示有应答 1表示非应答
****************************************************************************************
*/
void IIC1_Send_Ack(uint8_t ack)
{
IIC1_SCL=0;
if(ack)//(主机准备数据)
IIC1_SDA_OUT=1;
else
IIC1_SDA_OUT=0;
Delay_us(6);//延时(数据稳定在数据线上)
IIC1_SCL=1;//(从机在时钟线上升沿从SDA上采集数据)
Delay_us(6);//延时(给时间从机读取数据)
// IIC1_SCL=0;//方便后续的操作;防止意外产生了停止条件
}
/*
****************************************************************************************
* Function: IIC1_Revice_Ack
* Description: 主机读取应答信号
* Return: 0--有应答 1--非应答
****************************************************************************************
*/
uint8_t IIC1_Revice_Ack(void)
{
uint8_t ack=0;
IIC1_SCL=0;//(从机准备数据)
IIC1_SDA_OUT=1;//读模式-----让输出电路与管脚断开!!!!!!!!!!
Delay_us(6);//延时(给时间从机准备数据并且数据稳定在数据线上)
IIC1_SCL=1;
Delay_us(6);//延时 (给时间主机读取数据)
if(IIC1_SDA_IN)//主机读取SDA线上的数据
ack=1;
//IIC1_SCL=0;//方便后续的操作;防止意外产生了停止条件
return ack;
}
/*
****************************************************************************************
* Function: IIC1_Send_Byte
* Description: 主机发送一个字节给从机并且读取一次应答信号
* Input: 待发送的一个字节数据
* Return: 应答信号 0--有应答 1--非应答
****************************************************************************************
*/
uint8_t IIC1_Send_Byte(uint8_t data)
{
uint8_t i=0;
for(i=0;i<8;i++)
{
IIC1_SCL=0;
if((data<<i)&0x80)//(主机准备数据)
IIC1_SDA_OUT=1;
else
IIC1_SDA_OUT=0;
Delay_us(6);//延时(数据稳定在数据线上)
IIC1_SCL=1;//(从机在时钟线上升沿从SDA上采集数据)
Delay_us(6);//延时(给时间从机读取数据)
}
//IIC1_SCL=0;//方便后续的操作;防止意外产生了停止条件
return IIC1_Revice_Ack( );
}
/*
****************************************************************************************
* Function: IIC1_Revice_Byte
* Description: 主机读取一个字节并且发送一次应答信号
* Input: 应答信号 0--有应答 1--非应答
* Return: 读取的一个字节数据
****************************************************************************************
*/
uint8_t IIC1_Revice_Byte(uint8_t ack)
{
uint8_t i=0;
uint8_t data=0;
for(i=0;i<8;i++)
{
IIC1_SCL=0;//(从机准备数据)
IIC1_SDA_OUT=1;//读模式-----让输出电路与管脚断开!!!!!!!!!!
Delay_us(6);//延时(给时间从机准备数据并且数据稳定在数据线上)
IIC1_SCL=1;
data = (data<<1) | (IIC1_SDA_IN);
Delay_us(6);//延时 (给时间主机读取数据)
}
//IIC1_SCL=0;//方便后续的操作;防止意外产生了停止条件
IIC1_Send_Ack(ack);
return data;
}
由于 IIC 控制器存在缺陷,一般都不会采用芯片内部的 IIC 控制器。所以要想采用 IIC 通信跟外设进行数据交流,就需要用 IO 口模拟 IIC 时序。IIC 总线是两线制通信协议,所以只需要采用两个 IO 口即可,一个 IO 口作为 SCL,另一个作为 SDA
IO 口初始化
作为 SCL 的 IO 口:SCL 只能由主机发出,把这个 IO 配置成输出模式,推挽输出和开漏输出均可作为 SDA 的 IO 口:SDA 是双向数据线,既能从主机发出数据,主机也能在 SDA 读取数据。刚好在 IO 口配置成输出模式时,输入电路并没有被关闭。但是,在采用输入的时候,不能让输出电路影响到输入电路,必须配置成开漏输出,在读取数据前,输出“1”把输出电路从 IO 口断开。
通信错误:包括从机设备无应答、从机设备应答错误、数据丢失或损坏等。
故障排除方法:
确保从机设备的地址正确,并确保从机设备已正确连接到I2C总线。
检查从机设备的电源供应和信号线连接是否正常。
检查主机设备的I2C控制器配置和软件实现是否正确。
考虑增加适当的延时或超时设置,以确保从机设备有足够的时间响应。
冲突和仲裁错误:当多个设备尝试在同一时间发送数据时,可能发生冲突和仲裁错误。
故障排除方法:
检查每个设备的唯一地址分配,确保每个设备具有不同的地址。
检查I2C总线上的电源供应和信号线连接是否正常。
检查设备的I2C控制器和软件实现是否正确配置。
考虑延长仲裁超时时间或增加重试机制来处理冲突。
电源供应问题:不足的电源供应可能导致通信错误或设备无法正常工作。
故障排除方法:
检查从机设备和主机设备的电源供应是否符合规格要求。
检查电源线路和电源连接是否可靠,避免电源波动或噪声干扰。
确保电源线路的容量能满足所有设备的需求,特别是在连接多个设备时。
信号线干扰和噪声:外部干扰或电磁噪声可能干扰I2C信号线的传输。
故障排除方法:
尽量减少I2C信号线与高电流、高频率或噪声源的接近。
使用屏蔽电缆或屏蔽导线来提供更好的信号干扰抑制能力。
考虑使用信号线滤波器或电磁兼容性(EMC)组件来抑制噪声。
时钟频率设置错误:不正确的时钟频率设置可能导致通信错误或设备无法同步。
故障排除方法:
检查主机设备的I2C控制器配置,确保时钟频率设置正确。
确保主机设备和从机设备之间的时钟频率匹配。
考虑降低时钟频率以提高稳定性,尤其是在长电缆或高噪声环境中。
学习更高级的I2C特性,如主从通信、多主通信、时钟拉伸等。
探索更复杂的应用场景,如I2C总线扩展、I2C多路复用等。