一、什么是I2C

IIC也称I2C,是一个多主从的串行总线,由飞利浦公司发明的通讯总线,属于半双工同步传输类总线,仅由两条线就能完成多机通讯,一条SCL时钟线,另外一条双向数据线SDA,IIC总线要求每个设备SCL/SDA线都是漏极开路模式,因此必须带上拉电阻才能正常工作。I2C协议占用引脚少,硬件实现简单,可扩展性强,I2C数据传输速率有标准模式(100kbps)、快速模式(400kbps)和高速模式(3.4Mbps)。

:本文目前只对常用方式(一主多从)进行讲解,多主机模式后续更新。

二、IIC协议简介

IIC总线的SDA和SCL两根总线需要上拉,使总线处于空闲状态。IIC总线一共有两种状态、四种信号。除此之外还需要了解IIC总线的数据有效性。

2.1 IIC总线物理接线

SDA 和SCL 都是双向线路,都通过一个电流源或上拉电阻连接到正的电源电压。当总线空闲时,这两条线路都是高电平。连接到总线的器件输出级必须是漏极开路或集电极开路才能执行线与的功能。

总线器件数目:由于每一个IIC器件在IIC总线上都有一个确切的7位地址码,这也意味着一条IIC总线上最多可链接127(0X00位地址不使用)个地址互不相同的IIC器件。但在单条IIC总线上链接不多与127个器件的同时,必须要满足总线电容不能超过400pF(协议规定),总线之所以规定电容大小是因为,IIC的OD要求外部有电阻上拉,电阻和总线电容产生了一个RC延时效应,电容越大信号的边沿就越缓,有可能带来信号质量风险。传输速度越快,信号的窗口就越小,上升沿下降沿时间要求更短更陡峭,所以RC乘积必须更小。实际设计中经验值大概是8个器件左右。

在这里插入图片描述

2.1 IIC协议时序

IIC时序图:
在这里插入图片描述

2.2.1 数据有效性

IIC总线进行数据传送时,在SCL的每个时钟脉冲期间传输一个数据位,时钟信号SCL为高电平期间,数据线SDA上的数据必须保持稳定,只有在时钟线SCL上的信号为低电平期间,数据线SDA上的高电平或低电平状态才允许变化,因为当SCL是高电平时,数据线SDA的变化被规定为控制命令(START或STOP,也就是起始信号和停止信号)。
在这里插入图片描述

2.2.2 空闲状态(IDLE)

IDLE表示总线空闲状态。此状态下时钟信号SCL和数据信号SDL均为高电 平,此时无I2C设备工作。时钟线(SCL)和数据线(SDA)接上拉电阻,默认高电平,就是为了表示总线是空闲状态。

2.2.3起始信号(START)

表示起始状态。在空闲状态下,时钟信号SCL继续保持高电平,数据信号SDL出现由高电平转换为低电平的下降沿,此时产生一个起始信号,与总线相连的I2C设备检测到起始信号之后,进入起始状态等待控制字节的输入。
在这里插入图片描述

2.2.4 停止信号(STOP)

I2C通信的停止信号由主设备发出,SCL保持高电平,SDA由低电平跳变到高电平。
在这里插入图片描述

2.2.5 应答(ACK)与非应答(NACK)

应答信号接收端收到有效数据后需要向对方响应的信号,发送端每发送一个字节(8位)数据,在第9个时钟周期释放数据线去接收对方的应答。
在第9个时钟周期:
当SDA是低电平为有效应答(ACK),表示对方接收成功;
当SDA是高电平为无效应答(NACK),表示对方没有接收成功。
注意:数据发射端需要在第9个时钟周期等待接收端的应答信号。
在这里插入图片描述

2.2.6 数据读写操作

IIC协议的读写操作都是一字节大小,从高到低收发数据。

三、IIC主设备与从设备的通信过程

1、 主设备给从设备发送/写入数据:

  1. 主设备发送起始(START)信号
  2. 主设备发送设备地址到从设备
  3. 等待从设备响应(ACK)
  4. 主设备发送数据到从设备,一般发送的每个字节数据后会跟着等待接收来自从设备的响应(ACK)
  5. 数据发送完毕,主设备发送停止(STOP)信号终止传输
    在这里插入图片描述

2、主设备从从设备接收/读取数据

  1. 设备发送起始(START)信号
  2. 主设备发送设备地址到从设备
  3. 等待从设备响应(ACK)
  4. 主设备接收来自从设备的数据,一般接收的每个字节数据后会跟着向从设备发送一个响应(ACK)
  5. 一般接收到最后一个数据后会发送一个无效响应(NACK),然后主设备发送停止(STOP)信号终止传输
    在这里插入图片描述注意:在使用IIC通讯的时候注意按照芯片通讯规则编写。

四、基于STM32G070RBT6的例程

该例程是基于STM32芯片编写的,是软件模拟IIC协议,在使用时可按照所使用的相关芯片将SDA与SCL线进行替换和相对应的设置。
在基于STM32编程时本例程是将SCL时钟线与SDA数据线所对应的GPIO口设置成开漏输出模式并且外接上拉电阻以使IIC总线处于空闲状态,如果使用的是推挽输出模式,这需要自己配置相对应的输入输出函数。

#ifndef  _IIC_H_
#define  _IIC_H_

#include "main.h"
#include "gpio.h"
 
#define SCL  GPIOB
#define SDA  GPIOB

#define SCL_PIN  GPIO_PIN_6  
#define SDA_PIN  GPIO_PIN_7


#define SCL_Read HAL_GPIO_ReadPin(SCL,SCL_PIN)
#define SDA_Read HAL_GPIO_ReadPin(SDA,SDA_PIN)


#define SCL_High HAL_GPIO_WritePin(SCL ,SCL_PIN ,GPIO_PIN_SET)
#define SCL_Low  HAL_GPIO_WritePin(SCL ,SCL_PIN ,GPIO_PIN_RESET)

#define SDA_High HAL_GPIO_WritePin(SDA ,SDA_PIN ,GPIO_PIN_SET)
#define SDA_Low  HAL_GPIO_WritePin(SDA ,SDA_PIN ,GPIO_PIN_RESET)  

void iic_delay_us(void );
void iic_start( void );
void iic_stop( void );
uint8_t iic_wait_ack(void );
void iic_ack(void );
void iic_no_ack( void );
void iic_write_byte(uint8_t data);
uint8_t iic_read_byte(void);

#endif
#include "iic.h"

void iic_delay_us(void )
{
   uint32_t time;
	
   for(time =0;time <100;time ++);
	 
}

/****起始条件结束后 SDA低,SCL低*****/

void iic_start( void )
{
 SDA_High ; 
 SCL_High ;
 iic_delay_us ();
 SDA_Low ;
 iic_delay_us ();
 SCL_Low ;
 iic_delay_us ();
}

/****停止条件结束后 SDA高,SCL高*****/

void iic_stop( void )
{
 SDA_Low ;
 SCL_High ;
 iic_delay_us ();
 SDA_High ;
 iic_delay_us ();
 	
}

uint8_t iic_wait_ack(void )
{
	uint32_t i=3000;
  SDA_High ;
	iic_delay_us ();
	SCL_High ;
	iic_delay_us ();
	while(SDA_Read)
	{
		i--;
		if(i==0)       
			return 0 ;          //等待超时 器件未应答
	}
 SCL_Low ;
 iic_delay_us ();
 return 1;	

}

void iic_ack(void )
{

	SDA_Low ;
	iic_delay_us();
	SCL_High ;
	iic_delay_us ();
	SCL_Low ;
	iic_delay_us ();
	SDA_High ;
	iic_delay_us ();

}

void iic_no_ack( void )
{
	
	SDA_High ;
	iic_delay_us ();
	SCL_High ;
	iic_delay_us ();
	SCL_Low ;
	iic_delay_us ();
}

void iic_write_byte(uint8_t data)
{
   uint8_t i=0;
	 
	 for(i=0;i<8;i++)
	{
	  if(data & 0x80)      //读取最高位数据
		{
		  SDA_High ;
		}
	  else SDA_Low ;
		iic_delay_us ();
		SCL_High ;
		iic_delay_us ();
		SCL_Low ;
		data <<=1;  //数据左移1位
		if(i==7)
		{
			SDA_High ;   //第8位结束后释放SDA线
		}
	}
  
}

uint8_t iic_read_byte(void)
{
   uint8_t i=0,data=0;
	 
	for(i=0;i<8;i++)
	{
	   data <<=1;  
     SCL_High ;
     iic_delay_us ();
		if(SDA_Read )
		{
		   data ++;
		}
	  SCL_Low ;
		iic_delay_us ();	
	}

  return data;
}
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐