IIC协议

发布于:2022-12-02 ⋅ 阅读:(908) ⋅ 点赞:(0)

IIC协议



STM32—IIC协议

IIC 简介

IIC(Inter-Integrated Circuit)总线是一种由 PHILIPS 公司开发的两线式串行总线,用于连接微控制器及其外围设备。
它是由数据线 SDA 和时钟 SCL 构成的串行总线,可发送和接收数据。
在 CPU 与被控 IC 之间、 IC 与 IC 之间进行双向传送, 高速 IIC 总线一般可达 400kbps 以上。

IIC物理层

IIC总线的硬件,是由一根数据线SDA,一根时钟线SCL构成。不同的器件,都是并联接在这两条线上,I2C总线上的每个设备都自己一个唯一的地址,来确保不同设备之间访问的准确性。
SDA(Serial data)是数据线,D代表Data也就是数据,Send Data 也就是用来传输数据的
SCL(Serial clock line)是时钟线,C代表Clock 也就是时钟 也就是控制数据发送的时序的

在这里插入图片描述

IIC总线通过上拉电阻接到电源VCC,电阻大小要根据设备实际测量。
总线空闲的时候,SDA和SCL都是高电平。
当其中一个设备拉低总线,整条线就全是低电平,器件与器件之间“与”关系。

IIC特点

通常我们为了方便把IIC设备分为主设备和从设备,基本上谁控制时钟线(控制SCL的电平高低变换)谁就是主设备。

  • IIC主设备功能:主要产生时钟,产生起始信号和停止信号
  • IIC从设备功能:可编程的IIC地址检测,停止位检测
  • IIC的一个优点是它支持多主控(multimastering),其中任何一个能够进行发送和接收的设备都可以成为主总线。一个主控能够控制信号的传输和时钟频率。当然,在任何时间点上只能有一个主控。
  • 支持不同速率的通讯速度,标准速度(最高速度100kHZ),快速(最高400kHZ)
  • SCL和SDA都需要接上拉电阻 (大小由速度和容性负载决定一般在3.3K-10K之间) 保证数据的稳定性,减少干扰。
  • IIC是半双工,而不是全双工 ,同一时间只可以单向通信,IIC协议首先是发送从机硬件地址,然后发送命令,再发送数据/寄存器编号或者读取数据。IIC协议可以多字节连续读写数据。
  • 各设备连接到总线的输出端时必须是漏极开路(OD)输出或集电极开路(OC)输出。

IIC协议层

I2C 总线在传送数据过程中共有三种类型信号, 它们分别是:开始信号、结束信号和应答

信号。

  • 开始信号: SCL 为高电平时, SDA 由高电平向低电平跳变,开始传送数据。

  • 结束信号: SCL 为高电平时, SDA 由低电平向高电平跳变,结束传送数据。

  • 应答信号:接收数据的 IC 在接收到 8bit 数据后,向发送数据的 IC 发出特定的低电平脉冲,

表示已收到数据。 CPU 向受控单元发出一个信号后,等待受控单元发出一个应答信号, CPU 接

收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为

受控单元出现故障。

这些信号中,起始信号是必需的,结束信号和应答信号,都可以不要。

IIC 总线时序图

在这里插入图片描述

IIC基本读写过程

在这里插入图片描述

在这里插入图片描述

起始信号S有主机的IIC接口产生,这时连接到主机的所有设备都会接收到这个信号。

起始信号产生后,所有从机开始等待主机广播下来的从机的地址信号,在总线上,所有从机的地址都是唯一的,当主机广播的地址与设备的地址匹配后,这个设备就被选中,地址不匹配的设备会忽略之后的数据信号。从机的地址信号可以时7位和10位的。

主机发送完地址信号后接着发送传输方向的选择位,0表示数据的传输方向由主机传至从机,即主机写数据,1表示数据的传输方向由从机传至从机,及主机读数据。

从机匹配地址后,会返回一个应答(ACK)或非应答信号(NACK),主机只有接收到从机的应答信号后,主机才能继续向从机发送数据。(写数据过程) 读数据过程即主机要向从机发送应答或非应答信号,从机只有收到主机发送的应答信号,才能继续向主机发送数据
写数据
当配置的方向传输位为“读数据”方向时,主机广播完地址,并接收到从机的应答信号时,主机开始正式向从机传输数据,一次传输的 数据大小为8位,主机每发送完一个字节后,都要等待从机的应答信号,可以重复这个过程。当像停止发送数据时,主机向从机发送一个停止信号(P),表示不在传输数据。
读数据
当配置的方向传输位为“读数据”方向时,主机广播完地址,并接收到从机的应答信号 后,从机开始向主机发送数据,数据的大小为8位,从机每发送完一个数据,都会等待主机的应答信号,可重复此过程。当主机向停止接收数据时,就向从机发送一个非应答信号(NACK),则主机自动停止发送数据。
读和写数据
IIC更常用的是复合格式,该传输过程有两次起始信号(S)。一般在第一次传输中,主机通过地址找到设备后,发送一段数据,这段数据通常用于表示该设备内部寄存器或存储器的地址;第二次传输中,则对该地址进行读或写。 (第一次告诉从机要读写的地址,第二次才真正进行数据的传输)

信号的起始(S)和停止信号(P)

当SCL为高电平SDA来一个下降沿意味着通讯的开始。当SCL为高电平SDA来一个上升沿代表通讯的结束。

在这里插入图片描述

初始(空闲)状态

因为IIC的 SCL 和SDA 都需要接上拉电阻,保证空闲状态的稳定性

所以IIC总线在空闲状态下SCL 和SDA都保持高电平

开始信号

SCL保持高电平,SDA由高电平变为低电平后,延时(>4.7us),SCL变为低电平。

//产生IIC起始信号
//1.先拉高SDA,再拉高SCL,空闲状态
//2.拉低SDA
void IIC_Start(void)
{
	SDA_OUT();     //sda线输出
	IIC_SDA=1;	//首先把时钟线拉高
    delay_us(5);
	IIC_SCL=1;  //首先把时钟线拉高
	delay_us(5);
    
 	IIC_SDA=0;//START:在SCL为高时拉低SDA线,即为起始信号
	delay_us(4);
	IIC_SCL=0;//钳住I2C总线,准备发送或接收数据 
}	  

停止信号

停止信号:SCL保持高电平。SDA由低电平变为高电平。

//产生IIC停止信号
//1.先拉低SDA,再拉低SCL
//2.拉高SCL
//3.拉高SDA
//4.停止接收数据
void IIC_Stop(void)
{
	SDA_OUT();//sda线输出
	IIC_SCL=0;
	IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
 	delay_us(4); //STOP:当SCL高时,数据由低变高
	IIC_SCL=1; 
	IIC_SDA=1;//发送I2C总线结束信号
	delay_us(4);							   	
}

数据有效性

IIC信号在数据传输过程中,当SCL=1高电平时,数据线SDA必须保持稳定状态,不允许有电平跳变,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。

SCL=1时 数据线SDA的任何电平变换会看做是总线的起始信号或者停止信号。

也就是在IIC传输数据的过程中,SCL时钟线会频繁的转换电平,以保证数据的传输

在这里插入图片描述

地址及数据方向

设备地址可以是7位或11位,数据传输方向位和7位地址位一起传送。读数据方向时(相当于从机向主机写数据)由从机控制SDA线,写数据方向时,SDA由主机控制。

响应

当设备接收数据后,若希望对方继续发送,则向对方发送应答信号,发送方会继续发送下一个数据;若接收方不想接收数据时,则向对方发送非应答信号,发送方接收到非应答信号后会产生一个停止信号,结束信号传输。

在这里插入图片描述

传输时主机产生时钟,通讯开始后,发送端发送完地址及传输方向位信号时(前8位),第九位时数据发送端会释放SDA的控制权,由数据接收端控制SDA ,若SDA为高电平,表示产生非应答信号,低电平表示产生应答信号。

应答信号:主机SCL拉高,读取从机SDA的电平,为低电平表示产生应答

  • 应答信号为低电平时,规定为有效应答位(ACK,简称应答位),表示接收器已经成功地接收了该字节;

    //主机产生应答信号ACK
    //1.先拉低SCL,再拉低SDA
    //2.拉高SCL
    //3.拉低SCL
    void IIC_Ack(void)
    {
    	IIC_SCL=0; //先拉低SCL,使得SDA数据可以发生改变
    	SDA_OUT();
    	IIC_SDA=0;
    	delay_us(2);
    	IIC_SCL=1;
    	delay_us(5);
    	IIC_SCL=0;
    }
    
  • 应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。

    //主机不产生应答信号NACK
    //1.先拉低SCL,再拉高SDA
    //2.拉高SCL
    //3.拉低SCL
    void IIC_NAck(void)
    {
    	IIC_SCL=0;//先拉低SCL,使得SDA数据可以发生改变
    	SDA_OUT();
    	IIC_SDA=1;//拉高SDA,不产生应答信号
    	delay_us(2);
    	IIC_SCL=1;
    	delay_us(5);
    	IIC_SCL=0;
    }	
    
  • 等待应答信号

    //等待应答信号到来
    //返回值:1,接收应答失败
    //       0,接收应答成功
    u8 IIC_Wait_Ack(void)
    {
    	u8 ucErrTime=0;
    	SDA_IN();      //SDA设置为输入  
    	IIC_SDA=1;delay_us(1);	   
    	IIC_SCL=1;delay_us(1);	 
    	while(READ_SDA)// #define READ_SDA   PBin(7)  //输入SDA 
    	{
    		ucErrTime++;
    		if(ucErrTime>250)
    		{
    			IIC_Stop();
    			return 1;
    		}
    	}
    	IIC_SCL=0;//时钟输出0 	 
    	return 0;  
    } 
    

IIC数据传输

数据传送格式

SDA线上的数据在SCL时钟“高”期间必须是稳定的,只有当SCL线上的时钟信号为低时,数据线上的“高”或“低”状态才可以改变。输出到SDA线上的每个字节必须是8位,数据传送时,先传送最高位(MSB),每一个被传送的字节后面都必须跟随一位应答位(即一帧共有9位)。

当一个字节按数据位从高位到低位的顺序传输完后,紧接着从设备将拉低SDA线,回传给主设备一个应答位ACK, 此时才认为一个字节真正的被传输完成 ,如果一段时间内没有收到从机的应答信号,则自动认为从机已正确接收到数据。

IIC发送一个字节

//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答			  
void IIC_Send_Byte(u8 txd)
{                        
    u8 t;   
	SDA_OUT(); 	    
    IIC_SCL=0;//拉低时钟开始数据传输
    for(t=0;t<8;t++)
    {              
        //IIC_SDA=txd&0x80;   //获取最高位
        //获取数据的最高位,然后数据左移一位
        //如果某位为1,则SDA为1,否则相反
		if((txd&0x80)>>7)
			IIC_SDA=1;
		else
			IIC_SDA=0;
		txd<<=1; 	  
		delay_us(2);   //对TEA5767这三个延时都是必须的
		IIC_SCL=1;
		delay_us(2); 
		IIC_SCL=0;	
		delay_us(2);
    }	 
} 	    

IIC读取一个字节数据

//读1个字节,ack=1时,发送ACK,ack=0,发送nACK   
u8 IIC_Read_Byte(unsigned char ack)
{
	unsigned char i,receive=0;
	SDA_IN();//SDA设置为输入
    for(i=0;i<8;i++ )
	{
        IIC_SCL=0; 
        delay_us(2);
		IIC_SCL=1;
        receive<<=1;
        if(READ_SDA)receive++;   
		delay_us(1); 
    }					 
    if (!ack)
        IIC_NAck();//发送nACK
    else
        IIC_Ack(); //发送ACK   
    return receive;
}

IIC代码

IIC.h

#ifndef __IIC_H
#define __IIC_H
#include "sys.h"

//IO方向设置
 
#define SDA_IN()  {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;}
#define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;}

//IO操作函数	 
#define IIC_SCL    PBout(6) //SCL
#define IIC_SDA    PBout(7) //SDA	 
#define READ_SDA   PBin(7)  //输入SDA 

//IIC所有操作函数
void IIC_Init(void);                //初始化IIC的IO口				 
void IIC_Start(void);				//发送IIC开始信号
void IIC_Stop(void);	  			//发送IIC停止信号
void IIC_Send_Byte(u8 txd);			//IIC发送一个字节
u8 IIC_Read_Byte(unsigned char ack);//IIC读取一个字节
u8 IIC_Wait_Ack(void); 				//IIC等待ACK信号
void IIC_Ack(void);					//IIC发送ACK信号
void IIC_NAck(void);				//IIC不发送ACK信号

void IIC_Write_One_Byte(u8 daddr,u8 addr,u8 data);
u8 IIC_Read_One_Byte(u8 daddr,u8 addr);	  
#endif

IIC.c

#include "IIC.h"
#include "delay.h"

//初始化IIC
void IIC_Init(void)
{					     
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOB, ENABLE );	//使能GPIOB时钟
	   
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7); 	//PB6,PB7 输出高
}
//产生IIC起始信号
void IIC_Start(void)
{
	SDA_OUT();     //sda线输出
	IIC_SDA=1;	  	  
	IIC_SCL=1;
	delay_us(4);
 	IIC_SDA=0;//START:when CLK is high,DATA change form high to low 
	delay_us(4);
	IIC_SCL=0;//钳住I2C总线,准备发送或接收数据 
}	  
//产生IIC停止信号
void IIC_Stop(void)
{
	SDA_OUT();//sda线输出
	IIC_SCL=0;
	IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
 	delay_us(4);
	IIC_SCL=1; 
	IIC_SDA=1;//发送I2C总线结束信号
	delay_us(4);							   	
}
//等待应答信号到来
//返回值:1,接收应答失败
//        0,接收应答成功
u8 IIC_Wait_Ack(void)
{
	u8 ucErrTime=0;
	SDA_IN();      //SDA设置为输入  
	IIC_SDA=1;delay_us(1);	   
	IIC_SCL=1;delay_us(1);	 
	while(READ_SDA)
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			IIC_Stop();
			return 1;
		}
	}
	IIC_SCL=0;//时钟输出0 	   
	return 0;  
} 
//产生ACK应答
void IIC_Ack(void)
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=0;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}
//不产生ACK应答		    
void IIC_NAck(void)
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=1;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}					 				     
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答			  
void IIC_Send_Byte(u8 txd)
{                        
    u8 t;   
	  SDA_OUT(); 	    
    IIC_SCL=0;//拉低时钟开始数据传输
    for(t=0;t<8;t++)
    {              
        //IIC_SDA=(txd&0x80)>>7;
		if((txd&0x80)>>7)
			IIC_SDA=1;
		else
			IIC_SDA=0;
		txd<<=1; 	  
		delay_us(2);   //对TEA5767这三个延时都是必须的
		IIC_SCL=1;
		delay_us(2); 
		IIC_SCL=0;	
		delay_us(2);
    }	 
} 	    
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK   
u8 IIC_Read_Byte(unsigned char ack)
{
	unsigned char i,receive=0;
	SDA_IN();//SDA设置为输入
    for(i=0;i<8;i++ )
	{
        IIC_SCL=0; 
        delay_us(2);
		IIC_SCL=1;
        receive<<=1;
        if(READ_SDA)receive++;   
		delay_us(1); 
    }					 
    if (!ack)
        IIC_NAck();//发送nACK
    else
        IIC_Ack(); //发送ACK   
    return receive;
}

IIC协议的数据读写三种方式

IIC协议单向发送数据

比如显示设备,一般情况下只需要写入数据,传输过程如下:
在这里插入图片描述

  1. 首先是起始信号S
  2. 然后是发送7位从机地址+传输方向位,0写
  3. SCL拉高,判断ACK,再拉低SCL
  4. 发送第1个字节,一般情况下都是指令,或者寄存器地址
  5. SCL拉高,判断ACK,再拉低SCL
  6. 发送第2个字节,一般情况下都是数据
  7. SCL拉高,判断ACK,再拉低SCL
  8. 可以继续传输数据,或者停止

IIC协议发送地址后立即读取数据

比如传感器设备,一般情况下只需要读出数据,传输过程如下:
在这里插入图片描述

  1. 首先是起始信号S
  2. 然后是发送7位从机地址+传输方向位,1读
  3. SCL 拉高,判断ACK,再拉低SCL
  4. 读取第1个字节,一般情况下都是指令,或者寄存器地址
  5. 发送应答ACK
  6. 读取第2个字节,一般情况下都是数据
  7. 发送应答ACK,或者不应答NACK
  8. 可以继续传输数据,或者停止

IIC协议先制定寄存器地址,再读取该寄存器的数据

比如存储芯片,一般情况下要先制定存储地址,再读取数据。传输过程如下:

在这里插入图片描述

  1. 首先是起始信号S
  2. 然后是发送7位从机地址+传输方向位,0写
  3. SCL 拉高,判断ACK,再拉低SCL
  4. 写数据,一般是写某个寄存器地址或者指令
  5. SCL拉高,判断ACK,再拉低SCL
  6. 再次起始信号S
  7. 然后是发送7位从机地址+传输方向位,1读
  8. SCL拉高,判断ACK,再拉低SCL
  9. 读取第四步写的寄存器地址的数据
  10. 发送应答ACK,或者不应答NACK
  11. 可以继续传输数据,或者停止。

IIC实现的方式

IIC实现的方式主要有两种,硬件IIC和软件模拟IIC

软件IIC是使用程序控制SCL,SDA线输出高低电平,模拟i2c协议的时序。一般较硬件IIC稳定,但是程序较为繁琐,但不难。

硬件IIC程序员只要调用IIC的控制函数即可,不用直接的去控制SCL,SDA高低电平的输出。但是有些单片机的硬件IIC不太稳定,调试问题较多。

硬件IIC、软件模拟IIC的区别

模拟IIC 硬件IIC
用法 流程更清楚一些 用法比较复杂
速度 速度快、效率高、可以使用DMA
适用管脚 任何管脚 固定管脚
稳定性 稳定 不太稳定

给那些看完的朋友,奖励一个 赤赤博客-后端+前端,觉得不错的话可以推荐给身边的朋友哟!
在这里插入图片描述

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到