一、简介

    I²C(Inter-Integrated Circuit,集成电路总线,也可以写成IIC)是一种由飞利浦公司开发的双向二线制串行通信总线,只通过一根串行数据线(SDA)和一根串行时钟线(SCL),就能让主板、外围设备(如传感器、存储器)等芯片之间实现数据传输,具有硬件结构简单、占用引脚少、支持多设备挂载和热插拔等特点,在嵌入式系统中应用广泛。

二、发展历史

起源与专利阶段(1982 年)
由荷兰飞利浦(Philips)公司于 1982 年开发,最初用于连接主板与外围芯片(如 EEPROM、传感器),解决多设备通信时引脚占用问题。
1980 年代至 2000 年,I²C 作为飞利浦专利技术,广泛应用于消费电子(如电视机、音响)和嵌入式系统。
标准化与开放(2000 年后)
2006 年,飞利浦将 I²C 技术贡献给 JEDEC(固态技术协会),成为开放式标准(JESD42-B),推动其在物联网、穿戴设备等领域普及。
技术迭代:从最初的标准模式(100kbps)发展出快速模式(400kbps)、高速模式(3.4Mbps)及超高速模式(5Mbps),满足不同场景需求。

三、内容详解

3.1 物理层特性

双线结构:
SDA(串行数据线):传输数据,双向通信。
SCL(串行时钟线):主机提供时钟信号,同步数据传输。
电气特性:
采用开漏输出,需外接上拉电阻(通常 4.7kΩ),默认高电平。
工作电压兼容 3.3V/5V 系统,传输距离通常 < 1 米(受电容负载限制)。

3.2 数据链路层协议

3.2.1 空闲状态

    SCL与SDA均为高电平。

3.2.2 起始与结束

起始与结束
起始:SCL为高电平时,SDA由高变低,表示开始传输;
结束:SCL为高电平时,SDA由低变高,表示结束传输。

3.2.3 收发数据

收发数据

    从上文也可得知,正常收发数据时,SDA要翻只能在SCL为低的时候进行。当需要发送一个字节的数据时,先拉低SCL,根据要发送的第一位数据(高位优先)拉高/拉低SDA,然后拉高SCL,保持一段时间,完成第一位的发送,以此循环8次。

3.2.4 应答

应答

    在主机发送完一个字节的数据后,需要等待从机回复一个应答信号,正常做法是在第8个SCL高电平拉低后,释放SDA信号线(由外部上拉自行拉成高电平),然后拉高SCL,等待从机控制SDA线,如果在第9个SCL高电平保持期间,读取到SDA线为低电平,则认为接收到从机的应答;否则为无应答。

四、应用及对比

    常见的串行通信有IIC、UART、SPI、CAN等,下面直接用一张表来展示各种串口的区别。

对比维度 I²C UART(串口) SPI CAN
引脚数量 2 根(SDA+SCL) 2 根(TX+RX) 4-5 根(MOSI+MISO+SCK+CS) 2 根(CAN_H+CAN_L)
传输模式 半双工(同一时刻单向传输) 全双工(收发独立) 全双工(同步收发) 半双工(差分信号)
最高速度 超高速模式 5Mbps 通常 115.2kbps-4Mbps 可达数十 Mbps(如 30Mbps) 最高 1Mbps(高速 CAN)
拓扑结构 多主多从(需仲裁机制) 点对点 一主多从(需 CS 片选) 总线型(多节点共享总线)
地址寻址 7/10 位地址,自动寻址 无地址(需软件协议定义) 靠 CS 片选指定从机 靠 ID 号仲裁(非地址寻址)
典型应用 传感器、EEPROM、低速外设互联 串口通信、调试接口、Modem 高速存储(SD 卡)、LCD 驱动 汽车总线、工业控制网络
优势 引脚少、多设备挂载简单 协议简单、传输距离较远(RS-485) 速度快、全双工、时序灵活 抗干扰性强、支持长距离和多节点
劣势 速度受限、仲裁机制复杂 无硬件寻址、多设备需软件管理 引脚多、多从机需多 CS 线 协议复杂、成本高

核心差异总结

I²C 的独特价值:以最少引脚实现多设备互联,适合 “轻量级” 数据交互(如传感器配置、少量参数读写),在低功耗、小数据量场景中不可替代。
与 SPI 的对比:SPI 速度更快但引脚多,适合高速批量数据传输(如 LCD 图像数据);I²C 适合低速、多设备的 “碎片化” 通信。
与 UART 的对比:UART 无硬件寻址,多设备通信需额外协议设计,而 I²C 原生支持多从机自动寻址,硬件层面更简洁。

五、代码实现

    用IO口模拟IIC协议,这里就不单独写,直接贴一份github上的代码,这里也不全贴,只贴出模拟IIC几个阶段的IO口翻转方式,想看完整的主从机模拟,可以去原路径观摩,原路径https://github.com/daimaCoder/IIC_MS

#define  IIC_SCL_H       HAL_GPIO_WritePin(GPIOB, IIC_SCL_Pin, GPIO_PIN_SET)
#define  IIC_SCL_L       HAL_GPIO_WritePin(GPIOB, IIC_SCL_Pin, GPIO_PIN_RESET)
#define  IIC_SDA_H       HAL_GPIO_WritePin(GPIOB, IIC_SDA_Pin, GPIO_PIN_SET)
#define  IIC_SDA_L       HAL_GPIO_WritePin(GPIOB, IIC_SDA_Pin, GPIO_PIN_RESET) 
#define  READ_SDA        HAL_GPIO_ReadPin(GPIOB, IIC_SDA_Pin)
#define  READ_SCL        HAL_GPIO_ReadPin(GPIOB, IIC_SCL_Pin)
#define  IIC_Delay       delay_us(4)    //IIC使用的延时,可通过改变延时,改变IIC传输速率
#define  Time_Count      250           //while等待超时计数,不可小于某个值,	最小值根据单片机时钟变化

void IIC_Start(void)				//发送IIC开始信号
{
	IIC_SCL_H;
	IIC_SDA_H;
	IIC_Delay;
	IIC_SDA_L;
	IIC_Delay;
	IIC_SCL_L;
}
	
void IIC_Stop(void)	  			//发送IIC停止信号	
{
	IIC_SCL_L;
	IIC_SDA_L;
	IIC_Delay;
	IIC_SCL_H;
	IIC_Delay;
	IIC_SDA_H;	
}
	
void IIC_Ack(void)					//IIC发送ACK信号
{
	IIC_SCL_L;
	IIC_SDA_L;
	IIC_Delay;
	IIC_SCL_H;
	IIC_Delay;
	IIC_SCL_L;
	IIC_SDA_H; //添加释放总线
	
}

void IIC_NAck(void)				//IIC不产生ACK信号
{
	IIC_SCL_L;
	IIC_SDA_H;
	IIC_Delay;
	IIC_SCL_H;
	IIC_Delay;
	IIC_SCL_L;
}

uint8_t IIC_Wait_Ack(void)				//IIC等待ACK信号	
{
	int time = 0;
	IIC_SDA_H;
	IIC_Delay;
	IIC_SCL_H;
	IIC_Delay;
	while(READ_SDA)     //等待应答信号
	{
		time++;
		if(time>Time_Count) //等待时间过长,产生停止信号,返回1,表示接收应答失败
		{
			//IIC_Stop();
			return 1;
		}
	}	
	IIC_SCL_L;   //应答成功,则SCL变低
	return 0;
}	

void IIC_Send_Byte(uint8_t data)	 //IIC发送一个字节
{
	IIC_SCL_L;
	IIC_Delay;
	for(int i=0;i<8;i++)         //从高位开始一位一位地传送
	{		                        //发数据放到数据线上
		if((data & 0x80)>0)      //当前最高位为1
		  IIC_SDA_H;            //拉高数据线
		else
			IIC_SDA_L;
		data<<=1;             //数据左移一位
		IIC_Delay;
		IIC_SCL_H;
		IIC_Delay;
		IIC_SCL_L;          //上一个数据发送完毕,为下一个数据发送准备
		IIC_Delay;
	}
}	

uint8_t IIC_Read_Byte(unsigned char ack)       //IIC读取一个字节	
{
	uint8_t read_Data = 0;
//	IIC_SDA_H;	                   //释放总线,此处不释放,另处释放
	for(int i=0;i<8;i++)
	{
		IIC_SCL_L;
		IIC_Delay;
		IIC_Delay;
		IIC_SCL_H;        //主机开始读数据,从机不能再改变数据了,即改变SDA的电平
		read_Data<<=1;
		if(READ_SDA)
			read_Data++;
		IIC_Delay;
	}
	if(ack==0) //说明主机不需要给从机应答
		IIC_NAck();
	else
		IIC_Ack();
	return read_Data;
}

六、注意事项

1、因为IIC协议本身是不具备任何校验的,所以如果想要保证通信数据的准确性,可以主从机协定好每传输一段数据后带一个校验,以此来验证数据有效性。
2、IIC协议本身很简单,没有太多的约束,所以在跟各种设备通信时,需要注意各自的协议及时序要求。另外因为IIC本身并没有让同一总线上多个设备同步的机制,如果出现主机与从机两个设备通信过程中,因某种异常导致主机复位重启,此时从机还处于接收或发送的状态中,此时可以通过发送 9 个时钟脉冲(SCL高 - 低循环),强制从机释放总线;另外检测SDA线状态,若为低电平则判定总线被占用,继续发送脉冲直至释放。

七、相关链接

【工具使用】STM32CubeMX-硬件IIC配置-实现EEPROM读写功能

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐