IIC工作原理——MCU单片机

一、常用的串行总线协议
目前常用的微机与外设之间进行数据传输的串行总线主要有UART、1-write、I2C和SPI总线。
1.UART:异步方式进行通信(一条数据输入线、一条数据输出线)。
2.1-write:单线总线,又称为单总线(只有一条数据输出线)。
3.I2C:同步串行2线方式(一条时总线,一条数据线)。
4.SPI:同步串行3线方式通信(一条时钟线,一条数据输入线,一条数据输出线)。

【注】I2C总线是phlips公司推出的一种串行总线,它只有两根双向信号线,一根数据线
	 SDA(serial data I/O),另一根SCL(serial clock)。
     每一条I2C总线可以挂载多个器件,而每个器件都有**唯一的地址**,这样可以标识通信目标。
     数据的通信采用主从机的方式,主机负责主动联系从机,
     从机负责被动回应数据。(如下图)<从机无法主动发送、读取数据> <多主机系统中,为了避免混乱,
     采用总线仲裁,决定哪一台主机控制总线 >

在这里插入图片描述

1.I2C总线构成
I2C总线通过上拉电阻接正电源,当总线空闲时,两根线均为高电平。连接到总线上的任一器件输出的低电平,都将使总线的信号变低,即各器件的SDA及SCL都是线“与”的关系。
在这里插入图片描述

二、I2C总线传输协议
1.数据位的有效性规定
SCL为高电平期间,数据线上的数据必须保持稳定,只有SCL信号为低电平期间,SDA的状态才允许变化
在这里插入图片描述

2.I2C的起始和终止信号
起始信号:SCL线为高电平期间,SDA由高电平向低电平的变化表示起始信号;
终止信号:SCL线为高电平期间,SDA线由低电平向高电平的变化表示终止信号。
在这里插入图片描述

3.I2C字节的传送与应答
每一个字节必须保证是8位长度。数据传送时,先传最高位,每一个被传送的字节后面都必须跟随一位应答位(即一帧共有9位)
在这里插入图片描述

3.数据帧格式
I2C总线上传送的数据信号是广义的,既包括地址信号,又包括真正的数据信号。
【注】在起始信号后必须传送一个从机的地址(7位),第8位时数据的传送方向(R/T),采用“0”
表示主机发送数据(T),“1”表示主机接收数据(R)。每次数据传送总是由主机产生的终止信号结束。

4.I2C总线的寻址
I2C总线协议有明确规定:采用7位的寻址字节(寻址字节是起始信号的第一个字节)
1>寻址字节定义

【注】D7~D1位组成从机地址,D0是数据传输方向位。

2>主机发送地址时,总线上的每个主机都将这7位地址码与自己的地址比较,如果相同,则认为自己正在被主机寻址,根据R/T位将自己确定为发送器或接收器。

3>从机的地址由固定部分和可编程部分组成,在一个系统中如果希望接入多个相同的从机,从机地址的可编程部分决定接入器件的最大数目。例如,一个从机的7位地址,有4位固定地址,3位可编程地址,那么最大的接入数目就是8个。

电子元器件手册查询网址:**www.alldatasheetcn.com**

4.软件模拟I2C通信时序
为保证数据的可靠性,I2C总线的数据传送有严格的时序要求,I2C总线的起始、终止,发送0、1的模拟时序如下图
在这里插入图片描述

//1. I2C起始信号程序
void I2Cstar()
{
	SCL = 1;
	SDA = 1;
	delay5us();
	SDA = 0;      //高到低的变化
	delay5us();
}

//2. I2C终止信号的变化
void I2Cstop()
{
	SCL = 0;
	SDA = 0;
	SCL = 1;
	delay5us();
	SDA = 1;   //低到高的变化
	delay5us();
}


//3. I2C主机读从机应答
bit ReadACK()
{
	SCL = 1;
	delay5us();
	if(SDA)
	{
		SCL = 0;
		return (1); //无应答
	}
	else
	{
		SCL = 0;
		return (0); //有应答
	}
}

//4. I2C主机发送应答
void SendACK(bit i)
{
	SCL = 0;
	if(i)
		SDA = 1;
	else 
		SDA = 0;
	delay5us();
	SCL = 0;
	SDA = 1; //释放总线
}

//5.I2C发送数据
void I2cSendByte(uchar DAT)
{
	uchar i; 
	for(i=0; i<8; i++) //分别写8次,每次写1位
	{
		SCL = 0;//拉低时钟总线,允许SDA变化
		if(DAT & 0x80)//先写数据最高位,DAT & 1000 0000
			SDA = 1;  //写1
		else
			SDA = 0;  //写0
		SCL = 1;	  //拉高时钟,让从机读SDA
		DAT <<= 1;	  //为发送下一位左移1位
	}
	SCL = 0; //拉低时钟总线,允许SDA释放
	SDA = 1;//释放数据总线
}


//6.I2C发送数据
#define	I2cRead 1		   //I2C读方向位
#define	I2cWrite 0		   //I2C写方向位
bit AckFlag;//应答标志位
void I2CWrite(uchar ADDR, uchar DAT)
{
	I2cStart();//I2C起始信号
	I2cSendByte(ADDR + I2cWrite);//发送器件地址加读写方向位
	if(ReadACK()) //读从机应答
		AckFlag = 1;	//NOACK
	else
		AckFlag = 0;	//ACK
	I2cSendByte(ADDR);//发送储存单元地址字节
	if(ReadACK())//读从机应答
		AckFlag = 1;	//NOACK
	else
		AckFlag = 0;	//ACK
	I2cSendByte(DAT);//发送一字节数据
	if(ReadACK())//读从机应答
		AckFlag = 1;	//NOACK
	else
		AckFlag = 0;	//ACK
	I2cStop();	//I2C停止信号
}

//7.I2C读取数据
uchar I2cReadByte()
{
	uchar i, DAT;
	for(i=0; i<8; i++)//分别读8次,每次读一位
	{
		DAT <<= 1; //数据左移1位,准备接收一位
		SCL = 0;   //拉低时钟总线,允许从机控制SDA变化
		SCL = 1;   //拉高时钟总线,读取SDA上的数据
		if(SDA)
			DAT |= 0X01;//为1则写1,否则不行执行写1,通过左移补0
	}
	return(DAT); //返回读出的数据
}

//8.I2C接收数据
uchar I2CRead(uchar ADDR)
{
	uchar DAT;
	I2cStart();//I2C起始信号
	I2cSendByte(ADDR + I2cWrite);//发送器件地址加读写方向位
	if(ReadACK())//读从机应答
		AckFlag = 1;	//NOACK
	else
		AckFlag = 0;	//ACK
	I2cSendByte(ADDR);//I2C发送一个字节
	ReadACK();//读从机应答
	I2cStart();//再次产生I2C起始信号
	I2cSendByte(ADDR + I2cRead);//发送器件地址加读写方向位 读
	if(ReadACK())//读从机应答
		AckFlag = 1;	//NOACK
	else
		AckFlag = 0;	//ACK
	DAT = I2cReadByte();//读一字节
	SendACK(1);//主机发送非应答
	I2cStop(); //I2C停止信号
	return(DAT);//返回读出数据			
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值