IIC通信快速梳理

IIC通信总结


由于通信协议一段时间不用后,会忘记一些细节,因此抽空总结出来,以便于自己和各位后续快速复习,刚好手上有个项目是使用IIC通信的,先从IIC通信开始总结吧

简介

IIC是一种需要两线时的半双工通信方式,用于连接微控制器和外围设备,它是由数据线 SDA 和时钟线 SCL 构成的串行总线,具有以下一些特点:

  • 半双工通信:设备直接可以发送和接收,但是不能同时进行。

  • 通信速率:常见的有100k和400k的速率,更高有3.4M和5M速率,但是很少用,一般不做讨论

    请添加图片描述

  • 可使用IO口模拟实现IIC:硬件IIC效率相较于软件IIC效率更高,传输速度更快,并且可以使用DMA,但是限制固定IO口,软件IIC则更加灵活,选取两个通用IO进行高低电平模拟即可。

  • 实现多对多功能:可以同时挂载多个主机和多个从机设备,主机设备数量不限制,从机数量理论上为127(2^8)。

IIC通信协议的详解
  • 大致原理:每一个器件都有一个对应的器件地址,主机通过发送器件地址找到对应的外围设备,然后发送需要进行读写的寄存器地址,然后如果是写,则主机继续发送需要写入的数据。如果是读则等待从机回应数据。

    • 写数据:MCU–>开始信号–>从机器件地址(7位)+R/W(读为1,写为0)–>需要操作的寄存器地址–>需要写入的数据–>结束–>slave
      请添加图片描述

    • 读数据:MCU–>开始信号–>从机器件地址(7位)+R/w(读为1,写为0)–>需要操作的寄存器地址–>从机器件地址–>转入接收模式–>slave–>发送主机需要读取数据–>MCU–>发送结束信号–>结束–>Slave。

      请添加图片描述

  • IIC通信的四种信号:

    • 1.开始信号:SCL为高电平的时候,SDA从高电平到低电平跳变,开始传输数据
    • 2.结束信号:SCL为高电平的时候,SDA从低电平到高电平跳变,结束传输数据
    • 3.应答信号:接收数据的IC在接到8bit数据后,想发送数据的IC回一个低电平脉冲作为应答信号
    • 4.数据信号:在一个时钟周期内,SCL为高电平时,SDA保持高,这表示该bit传输数据为1,SDA保持为低,则表示该bit传输数据为0
    • 5.注意事项
      • 1.只有起始信号是必须的,结束信号和应答信号都可以不存在。
  • 时序图:

    • 如下图,黄色为时钟线,粉色为数据线.传输两字节数据。主机发送特殊指令

      • 1.MCU发送左边红色框中start信号
      • 2.紧接着发送数据0001 1001(时钟线为高时,数据线为低则为0,数据线为高则为1),即发送数据0x19。
      • 3.从机给一个低电平脉冲应答信号
      • 4.从机给主机回1001 0001,即发送数据0x91。
      • 5.主机未做应答信号
      • 6.主机发送stop停止信号。

      请添加图片描述

  • 实例代码(以软件IIC为例)

    • 1.开始信号:

      // 开始总线(主设备 --> 从设备)
      static void I2C_Start(I2C_CONFIG_DEF *io_conf)
      {
      	i2c_dat_output_init(io_conf);//IO口设置为输出模式
      
      	I2Cdat_H(io_conf); // 在SCL为高时SDA由1变为0开始总线
      	I2Cclk_H(io_conf);
      	I2Cdat_L(io_conf);
      	i2c_delay;
      	I2Cclk_L(io_conf); // 钳住I2C总线,准备发送或接收数据
      	i2c_delay;
      }
      
    • 2.结束信号:

      // 结束总线(主设备 --> 从设备)
      static void I2C_Stop(I2C_CONFIG_DEF *io_conf)
      {
      	i2c_dat_output_init(io_conf);
      
      	I2Cdat_L(io_conf); // 在SCL为高时SDA由0变为1结束总线
      	I2Cclk_H(io_conf);
      	i2c_delay;
      	I2Cdat_H(io_conf);
      	i2c_delay;
      }
      
    • 3.发送应答信号:

      // 设置应答位(主设备 --> 从设备)
      // ack: 0 = 确认, 1 = 不确认
      static void I2C_Set_Ack(I2C_CONFIG_DEF *io_conf, unsigned char ack)
      {
      	i2c_dat_output_init(io_conf); // SDA输出
      	I2Cclk_L(io_conf);
      	if (ack)
      	{
      		I2Cdat_H(io_conf);
      	} // 不确认,主设备告诉从设备,接收数据结束。
      	else
      	{
      		I2Cdat_L(io_conf);
      	} //   确认,主设备每收到一个字节后都要发送确认
      	I2Cclk_H(io_conf);
      	i2c_delay;
      	I2Cclk_L(io_conf);
      	I2Cdat_L(io_conf);
      }
      
    • 4.接收应答信号:

      // 接收应答位(主设备 <-- 从设备)
      // ret: 0 = 确认, 1 = 确认错误
      static unsigned char I2C_Get_Ack(I2C_CONFIG_DEF *io_conf)
      {
      	unsigned char ack;
      
      	i2c_dat_input_init(io_conf); // SDA输入
      	I2Cdat_H(io_conf);			 // 8位发送完后释放数据线,准备接收应答位
      	I2Cclk_H(io_conf);
      	i2c_delay;
      	if (I2C_getdat(io_conf))
      	{
      		ack = 1;
      	} // 从设备无应答,说明器件损坏、忙状态或者不存在
      	else
      	{
      		ack = 0;
      	} // 从设备正确应答
      	I2Cclk_L(io_conf);
      	i2c_delay;
      	return (ack);
      }
      
    • 5.写入单个字节:

      static void I2C_Write_8bits(I2C_CONFIG_DEF *io_conf, unsigned char dat)
      {
      	unsigned char i;
      	i2c_dat_output_init(io_conf); // SDA输出
      	for (i = 8; i; i--)
      	{
      		if (dat & 0x80)
      		{
      			I2Cdat_H(io_conf);
      		} // 先发送最高位MSB
      		else
      		{
      			I2Cdat_L(io_conf);
      		}
      		i2c_delay;
      		I2Cclk_H(io_conf); // SCL为高时,保持数据稳定
      		i2c_delay;
      		dat <<= 1;
      		I2Cclk_L(io_conf); // SCL为低时,改变数据
      	}
      	i2c_delay;
      }
      
    • 6.读取单个字节:

      static unsigned char I2C_Read_8bits(I2C_CONFIG_DEF *io_conf)
      {
      	unsigned char i, dat;
      
      	i2c_dat_input_init(io_conf); // 输入
      	dat = 0;					 // 初始化变量
      	for (i = 8; i; i--)
      	{
      		i2c_delay;
      		I2Cclk_H(io_conf);
      		dat <<= 1; // 先收到最高位
      		i2c_delay;
      		if (I2C_getdat(io_conf))
      			dat++;
      		I2Cclk_L(io_conf);
      	}
      	i2c_delay;
      	return (dat);
      }
      
    • 7.读写集成接口函数:

      /***************************
      ***** brief  :对器件进行IIC的读写操作
      ***** input  :io_conf:软件I2C的IO结构体,包括了时钟和数据使用哪个IO口模拟
      *****         WR_flag:读写操作,写是1,读是0
      *****         ChipAddress:从机器件地址
      *****         RegAddress:从机寄存器地址
      *****         data:用于保存发送或者读取数据的指针
      *****         data_number:需要发送或者读取的数据长度
      ***** return :返回通信结果,SUCCESS表示通信成功,ERROR表示通信失败
      ****************************/
      u8 I2C_ReadWrite(I2C_CONFIG_DEF *io_conf, u8 WR_flag, u8 ChipAddress, u8 RegAddress, u8 *data, u8 data_number)
      {
      	u8 temp;
      	ChipAddress = ChipAddress << 1;
      	I2C_Start(io_conf);					   // 开始总线
      	I2C_Write_8bits(io_conf, ChipAddress); // 写入地址
      	if (I2C_Get_Ack(io_conf) != 0)		   // 接收应答位
      	{
      	I2C_Stop(io_conf);
          printf("I2C communication Error 0x%x\n", ChipAddress >> 1);
      	return ERROR;
      	}
      
      	I2C_Write_8bits(io_conf, RegAddress); // 写入数据
      	if (I2C_Get_Ack(io_conf) != 0)		  // 接收应答位
      	{
      	I2C_Stop(io_conf);
          printf("I2C communication Error 0x%x\n", ChipAddress >> 1);
      	return ERROR;
      	}
      	if (WR_flag)
      	{
      		//循环发送
      		while (data_number--)
      		{
      			I2C_Write_8bits(io_conf, *data++); // 写入数据
      			if (I2C_Get_Ack(io_conf) != 0)	   // 接收应答位
      			{
      				I2C_Stop(io_conf);
      				printf("I2C communication Error 0x%x\n", ChipAddress >> 1);
      				return ERROR;
      			}
      		}
      		I2C_Stop(io_conf); // 结束总线
      		return SUCCESS;
      	}
      	else
      	{
      		I2C_Stop(io_conf);						   // 结束总线
      		I2C_Start(io_conf);						   // 开始总线
      		I2C_Write_8bits(io_conf, ChipAddress + 1); // 写入地址
      		if (I2C_Get_Ack(io_conf) != 0)			   // 接收应答位
      		{
      			I2C_Stop(io_conf);
      			printf("I2C communication Error 0x%x\n", ChipAddress >> 1);
      			return ERROR;
      		}
      		while (data_number--)
      		{
      			//			printf("data_number=%d\n",data_number);
      			//最后一个char关闭ack
      			if (data_number == 0)
      			{
      				temp = I2C_Read_8bits(io_conf);
      				*data++ = temp;
      				I2C_Set_Ack(io_conf, 1);
      			}
      			//等待接收完成
      			else
      			{
      				temp = I2C_Read_8bits(io_conf);
      				*data++ = temp;
      				I2C_Set_Ack(io_conf, 0);
      			}
      		}
      		I2C_Stop(io_conf); // 结束总线
      		return SUCCESS;
      	}
      }
      
      

-引用:

  • 1.https://baijiahao.baidu.com/s?id=1732309316664193755&wfr=spider&for=pc

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值