IIC总线

本文详细介绍了IIC(Inter-IntegratedCircuit)同步传输协议的工作原理,包括其特点、数据传输流程、起始和结束信号以及与SPI的比较,还提供了AT24C02接口的具体实现代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、IIC特点

IIC是一种半双工的同步传输协议,它在工作的时候有两根线,一根是数据线,一根是时钟线。主机在发送开始信号之后,会先发送7个bit的从机地址位和一个读写控制位。每一个从机都有自己的地址,当发现该条指令是发送给自己的时候它会拉低数据线,即我们所说的回复ACK信号,然后主机开始发送或接收数据,直到数据发送或者接收完毕。传输要结束时,主机发送停止位给从机完成此处传输。

 IIC总线传输速率最大为400KHZ,且一次脉冲中高低电平的比例为1:2或者3:4

IIC总线的每一个设备都有一个独立的地址,该地址为7bit或者11bit。然后每个设备有读写两个操作,1表示读,0表示写,占1个bit。当需要操作IIC的设备时,需要把地址和读写操作合在一起。通常为8bit或者12bit。

IIC每次传输以都会有反馈。

因为IIC的 SCL 和SDA 都需要接上拉电阻,保证空闲状态的稳定性,所以IIC总线在空闲状态下SCL 和SDA都保持高电平

相比SPI的优点:

  • 不管总线上挂载多少个设备,都只用到两根导线
  • 发送端每次发送8bit数据后,接收端都必须返回1bit的响应信号,用来表示是否接收到数据

缺点:传输速度最大为400KHZ

二、IIC通信协议

1.起始信号:当SCL(时钟线)为高电平时,SDA(数据线)由高电平向低电平跳变,这标志着数据传输的开始。

2.结束信号:当所有数据都传输完毕后,会发送一个结束信号。这通常是在SCL为高电平时,SDA由低电平向高电平跳变

3.发送数据:在时钟线的控制下,数据线上的数据保持稳定进行传输。发送方将数据按照位(bit)的方式逐位发送到总线上。

4.接收数据:接收方在时钟线的控制下,从数据线上读取数据。接收方必须同步于发送方的时钟信号来读取数据。

5.发送反馈:

6.接收反馈:

当主机向从机发送完一个字节的数据后,从机会给出一个应答信号来确认是否接收到了数据。这个应答信号是一个低电平(表示接收成功)或高电平(表示接收失败),它出现在8位数据之后的那一个时钟周期。

数据传输阶段,都是在SCL为低电平时发生修改,SCL为高电平时传输。

 

 2.1 起始和终止信号

//起始信号
void at24c02_begin()
{
	//数据线为输出模式
	at24c02_sda_mode(GPIO_Mode_OUT);
	AT24C02_SCL = 1;
	AT24C02_SDA_OUT = 1;
	delay_us(5);
	//数据线下降沿
	AT24C02_SDA_OUT = 0;
	delay_us(5);
	//时钟线拉低----方便数据线修改电平
	AT24C02_SCL = 0;
	delay_us(5);
}
//结束信号
void at24c02_end()
{
	//数据线为输出模式
	at24c02_sda_mode(GPIO_Mode_OUT);
	//设置时钟线和数据线都是低电平
	AT24C02_SCL = 0;
	AT24C02_SDA_OUT = 0;
	delay_us(5);
	//先把时钟线拉高
	AT24C02_SCL = 1;
	delay_us(5);
	//把数据线拉高
	AT24C02_SDA_OUT = 1;
	delay_us(5);
}

2.2 收发数据

//发送数据-高位先出
void at24c02_sendbyte(uint8_t val)
{
	int i=0;
    //修改SDA数据线的模式为输出模式
	at24c02_sda_mode(GPIO_Mode_OUT);
	
	//拉低时钟线,方便数据线修改数值
	AT24C02_SCL = 0;
	delay_us(5);
	//高低电平比例是1:2
	for(i=0;i<8;i++)
	{
		//判断 7-i 位是1还是0,高位先出
		if( (val>>(7-i)) & 0x01)
		{
			AT24C02_SDA_OUT = 1;
		}
		else
			AT24C02_SDA_OUT = 0;
		delay_us(5);//等待数据线电平稳定	
		//拉高时钟线发送数据
		AT24C02_SCL = 1;
		delay_us(5);	
		//拉低时钟线准备下一次数据线的修改
		AT24C02_SCL = 0;
		delay_us(5);
	}
}

//接收数据--高位先出
uint8_t at24c02_recvbyte()
{
	uint8_t data=0;
	int i;
	//把数据线换成输入模式
	at24c02_sda_mode(GPIO_Mode_IN);
	//把时钟线电平拉低,让对方设备修改数据线的值
	AT24C02_SCL = 0;
	delay_us(5);
	for(i=0; i<8; i++)
	{
		//切换成高电平,准备接收数据
		AT24C02_SCL = 1;
		delay_us(5);
		if(AT24C02_SDA_IN == 1)
			data |= 1<<(7-i);
		//如果读取到0,因为data初值为0,所以不写入	
		//切换成低电平,等待对方修改数据线的值
		AT24C02_SCL = 0;
		delay_us(5);
	}
	return data;
}

2.3 接发反馈 

//发送1bit的反馈
void at24c02_send_ack(uint8_t val)
{
	//设置sda为输出模式
	at24c02_sda_mode(GPIO_Mode_OUT);
	//拉低时钟线
	AT24C02_SCL = 0;
	delay_us(5);
	//根据要发送的数值修改sda电平
	if(val)
		AT24C02_SDA_OUT = 1;
	else
		AT24C02_SDA_OUT = 0;
	delay_us(5);//等待要发送的内容稳定下来
	//等待对方接收到电平
	AT24C02_SCL = 1;
	delay_us(5);
	//对方接收完以后,拉低时钟线,准备下一次收发
	AT24C02_SCL = 0;
	delay_us(5);
}

//接收1bit的反馈
uint8_t at24c02_recv_ack()
{
	uint8_t ack;
	//把数据线换成输入模式
	at24c02_sda_mode(GPIO_Mode_IN);
	//拉低时钟线,等待对方设备把数据线电平修改
	AT24C02_SCL = 0;
	delay_us(5);
	//拉高电平.准备读取内容
	AT24C02_SCL = 1;
	delay_us(5);
	if( AT24C02_SDA_IN == 1)
		ack = 1;
	else
		ack = 0;
	//读取后拉低电平,准备下一次的数据改变
	AT24C02_SCL = 0;
	delay_us(5);
	return ack;
}

 2.4 写操作

 

//在at24c02的addr地址上写入len长度的buf
void at24c02_write_buf(uint8_t addr,char *buf, int len)
{
	//起始信号
	at24c02_begin();
	//写操作地址
	at24c02_sendbyte(0xA0);
	if(at24c02_recv_ack() != 0)
	{
		printf("write at24c02_sendbyte 0xA0 failed\n");
		return ;
	}
		
	//写操作的内存地址
	at24c02_sendbyte(addr);
	if( at24c02_recv_ack() != 0)
	{
		printf("at24c02_sendbyte addr failed\n");
		return ;
	}
		
	while(len--)
	{
		at24c02_sendbyte(*buf);
		buf++;
			
		if( at24c02_recv_ack() != 0)
		{
			printf("at24c02_sendbyte *buf failed\n");
			return ;
		}
	}
		
	//停止信号
	at24c02_end();
		
	printf("write success\n");
}

2.5 读操作 

 

//在at24c02的addr地址上读取len长度的buf
void at24c02_read_buf(uint8_t addr,char *buf, int len)
{	
    //起始信号
    at24c02_begin();
    //设备操作做地址
    at24c02_sendbyte(0xA0);
    if(at24c02_recv_ack() != 0)
    {
        printf("read at24c02_sendbyte 0xA0 failed\n");
        return ;
    }
    //发送内存地址
    at24c02_sendbyte(addr);
    if(at24c02_recv_ack() != 0)
    {
        printf("at24c02_sendbyte addr failed\n");
        return ;
    }
    //起始信号
    at24c02_begin();
    //设备读操作地址
    at24c02_sendbyte(0xA1);
    if(at24c02_recv_ack() != 0)
    {
        printf("at24c02_sendbyte 0xA1 failed\n");
        return ;
    }
    //前len-1个字节,都反馈响应0,第len个字节反馈响应1
    while(len-1 != 0)
    {
        //接收1个字节
        *buf = at24c02_recvbyte();
        //反馈低电平
        at24c02_send_ack(0);
        len--;//读取1个字节以后偏移1个字节
        buf++;//保存数据的缓冲区偏移1个字节
    }
    //最后第len个字节,要反馈1
    *buf = at24c02_recvbyte();
    at24c02_send_ack(1);
    //停止信号
    at24c02_end();
    printf("read success\n");
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值