1、什么是I2C?
I2C(Inter-Integrated Circuit)是一种通用的总线协议。它是由Philips(飞利浦)公司,现NXP(恩智浦)半导体开发的一种简单的双向两线制总线协议标准。是一种半双工的同步通信协议。
2、I2C协议标准
I2C协议使用两根总线线路,即时钟线(SCL)和数据线(SDA),用于在主设备和从设备之间传输数据。主设备负责生成时钟信号,并控制通信的起始和停止,从设备则负责响应主设备的指令和传输数据。
对于I2C总线来说,支持总线挂载多设备(一主多从、多主多从),类似上面的就是一主多从。
I2C协议支持多主设备和多从设备的连接,每个设备都有一个唯一的地址。主设备通过发送地址和数据来选择要与之通信的从设备,并可以进行读取和写入操作。I2C总线保持向下兼容性。
I2C串行总线一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL。所有I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。各设备连接到总线的输出端时必须是漏极开路(OD)输出或者集电极开路(OC)输出。
在I2C的实际应用中,我们实际一般将软件模拟的I2C引脚定义为开漏输出(GPIO_Mode_Out_OD),将硬件模拟的I2C引脚定义为复用开漏输出( GPIO_Mode_AF_OD)
3、I2C的通信速率
- 标准模式:100Kbps,采用7位寻址。
- 快速模式:400Kbps,采用10位寻址。
- 高速模式:3.4Mbps。
4、I2C总线的工作原理
MCU与I2C芯片之间的通信,可以形象的比喻成两个人对话,因此必须遵循基本的规范;一是你说的话别人能够听懂,即双方要有约定的信号协议(通信协议);二是你的语速要能够被别人所接受,即双方满足时序要求。
5、I2C总线的信号要求
I2C总线上可以挂多个设备,每个I2C设备都有固定的地址,只有当I2C两条线上传输的地址值等于某个I2C设备的地址时,该I2C设备才做出响应。数据传输后,必须要有应答信号(响应信号)。数据传输前必须有开始信号,传输结束后必须有结束信号。
- 开始信号:I2C协议规定,SCL处于高电平时,SDA由高到低变化,这种信号是起始信号。当I2C总线上的设别检测到这个开始信号后,就知道该发送数据了。
- 停止信号:I2C协议规定,SCL处于高电平,SDA由低到高变化,这种信号是停止信号。
- 数据传输:I2C协议对数据的采样发生在SCL高电平期间,除了起始和停止信号,在数据传输期间,SCL为高电平时,SDA必须保持稳定,不允许改变,在SCL低电平时才可以进行变化。
- 应答信号:处理器把数据发给从设备,如何知道从设备已经接收到数据呢?这就需要从设备回应一个信号给主机,因为当主机发送玩数据会后就不再驱动总线(SDA引脚由输出变为输入),而SDA和SCL硬件在设计时都有上拉电阻,所以这时SDA变为高电平。当一个字节的数据传输完成后,即在第九个SCL时钟周期内,此时主机需要释放SDA总线,把总线控制权交给从机,由于上拉电阻的作用,此时总线为高电平,如果从机正确的收到了主机发来的数据,会把SDA拉低,表示应答响应。当在第九个SCL时钟周期内,若SDA是高电平,则为无效应答,表示从机位接收到主机发送来的数据。
6、软件模拟I2C通信
首先我们设置软件I2C的引脚,将其定义为开漏输出
void Soft_I2C_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
/*这里为什么两个引脚都设置为高电平,是由于I2C总线的要求,
在使用I2C协议时,当SCL和SDA处于高电平时,此时I2C总线为空闲状态。*/
GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11);
}
在这里我们定义SCL和SDA
void Soft_I2C_W_SCL(uint8_t BitValue)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue);
Delay_us(10);
}
void Soft_I2C_W_SDA(uint8_t BitValue)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_12, (BitAction)BitValue);
Delay_us(10);
}
I2C协议规定,SCL处于高电平时,SDA由高到低变化,这种信号是起始信号。
当I2C总线上的设别检测到这个开始信号后,就知道该发送数据了。
void Soft_I2C_Start(void)
{
Soft_I2C_W_SDA(1);
Soft_I2C_W_SCL(1);
Soft_I2C_W_SDA(0);
Soft_I2C_W_SCL(0);
}
当I2C准备发送一个字节时,SCL在低电平期间,主机会依次将数据位放到SDA线上(高位先行)
所以在这里我们将SCL拉到低电平,更便于我们理解。
void Soft_I2C_Stop(void)
{
Soft_I2C_W_SDA(0);
Soft_I2C_W_SDA(1);
Soft_I2C_W_SCL(1);
}
当SCL低电平,变换数据,高电平,保持数据稳定
void Soft_I2C_SendByte(uint8_t Byte)
{
uint8_t i;
for(i = 0 ;i<8;i++)
{
当SCL低电平时,把Byte的最高位放在SDA线上(高位先行)
Soft_I2C_W_SDA(Byte & (0x80 >> i));
Soft_I2C_W_SCL(1);从机将在SCL高电平期间读取数据位
Soft_I2C_W_SCL(0);当SCL低电平时,SDA会继续取下一位的数据
}
}