文章目录
I2C基本概念+硬件线路+通信结构:
概念
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设备地址:
主机如何能找到对应的从机与其进行通信?——设备地址
每个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数据传输:
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时钟速度和通信速度:
I2C通信的时钟速度由主设备控制。
标准模式下,时钟频率为100 kHz。
快速模式下,时钟频率为400 kHz。
高速模式和超高速模式下,时钟频率可以更高,但具体取决于设备和总线的支持能力。
IIC 总线通信速度:
低速:100Kbit/s 快速:400Kbit/s 高速:3.4Mbit/s
I2C应答机制:
在每个数据字节传输后,接收设备会发送应答位(ACK)以确认接收到数据。
如果接收设备成功接收数据,它会拉低SDA线发送ACK信号;如果无法接收或不希望接收,它会保持SDA线为高电平发送NACK信号。
应答的发送和接收:
I2C设备之间的冲突检测:
I2C总线上的多个设备共享同一对SDA和SCL线。
为了避免冲突,一般使用以下两个方法:
- 尽量保持地址唯一性。
- 总线仲裁,I2C使用仲裁机制来检测总线上的冲突情况,并由冲突检测的设备放弃发送数据。I2C总线使用开漏架构,这意味着设备可以拉低SDA线,但不能将其拉高。当两个或多个设备同时尝试将SDA线拉低时,发生冲突。在总线仲裁过程中,设备根据自身的地址优先级决定是否继续发送数据或放弃发送,以解决冲突。
总线仲裁的过程是通过设备对SDA和SCL线的观测来实现的。如果一个设备发送的数据与总线上的数据不匹配,它会立即检测到冲突并停止发送。然后,它会等待总线空闲,并重新尝试发送数据。 - 错误重传和硬件检测两个方式一般不用,大致就是错误重传是在检测到应答机制非正常或者数据传输非正常时候,使用IIC控制器和软件来重复传输数据(设置最大重复传输次数。)硬件检测就是检测时钟线和数据线的高低电平变化,来确定传输错误原因。
I2C设备规格:
熟悉要使用的具体I2C设备的规格说明、数据手册或参考资料。
学习设备的功能和特性,包括寄存器、命令、数据格式等。
主机读取数据
IIC通信过程
- 主机发送起始条件(占用总线)——在 SCL 为高电平时,SDA 产生一个下降沿
- 主机发送器件地址(寻找从机)
- 在当前总线上的从机会将此器件地址跟自己对比,匹配成功就会回一个应答给主机。其他从机继续休眠。
- 主机和从机就可以建立通信,在这个过程中,接收方每成功接收到一个字节数据必须要给发送方一个应 答,如果发送方等不到应答则标志通信失败。
- 主机发送停止条件(释放总线)——在 SCL 高电平期间,SDA 产生一个上升沿
MSB:高位
LSB:低位
数据位传输是高位在前
实际编程和应用:
初始化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;
}
IO 口模拟 IIC 总线
由于 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信号线的传输。
故障排除方法:
尽量减少I2C信号线与高电流、高频率或噪声源的接近。
使用屏蔽电缆或屏蔽导线来提供更好的信号干扰抑制能力。
考虑使用信号线滤波器或电磁兼容性(EMC)组件来抑制噪声。
时钟频率设置错误:不正确的时钟频率设置可能导致通信错误或设备无法同步。
故障排除方法:
检查主机设备的I2C控制器配置,确保时钟频率设置正确。
确保主机设备和从机设备之间的时钟频率匹配。
考虑降低时钟频率以提高稳定性,尤其是在长电缆或高噪声环境中。
使用IIC通信常见外设
- 温度传感器:I2C通信常用于连接温度传感器,例如LM75、TMP102和BMP180。这些传感器可以测量环境温度并向主机设备提供温度数据。
- 湿度传感器:一些湿度传感器,如SHT21和HTU21D,也可以通过I2C进行通信。它们能够测量环境的湿度并提供湿度数据。
- 光照传感器:TSL2561和BH1750等光照传感器通常使用I2C进行通信。它们可以测量环境的光照强度,并向主机设备提供相关数据。
- 三轴加速度计:常见的三轴加速度计模块(如ADXL345和MPU6050)通常通过I2C与主机设备连接。它们可以提供物体在三个轴上的加速度数据。
- 电子罗盘:一些电子罗盘模块(如HMC5883L和QMC5883L)也使用I2C进行通信。它们可以测量磁场,并提供与方向和导航相关的数
- OLED显示屏:OLED显示屏通常使用I2C总线连接到主机设备,以显示文本、图形和图像。常见的OLED控制器芯片有SSD1306和SH1106。
- EEPROM存储器:I2C EEPROM(Electrically Erasable Programmable Read-Only Memory)是一种非易失性存储器,可以通过I2C进行读写操作。常见的芯片有24C系列和AT24C系列。
深入学习高级特性:
学习更高级的I2C特性,如主从通信、多主通信、时钟拉伸等。
探索更复杂的应用场景,如I2C总线扩展、I2C多路复用等。