ReRain
认证:普通会员
所在专题目录 查看专题
【蓝桥杯单片机】一、蜂鸣器+继电器+led
【蓝桥杯单片机】二、DS1302模块学习
【蓝桥杯单片机】三、I2C+E2PROM
【蓝桥杯单片机】四、A/D&D/A
【蓝桥杯单片机】五、DS18B20温度传感器
【蓝桥杯单片机】六、关于定时器(+PWM波)
作者动态 更多
【蓝桥杯单片机】十二、底层练习
2021-04-20 17:46
【蓝桥杯单片机】十一、第十届蓝桥杯省赛失败总结加试题分析
2021-04-11 20:50
【蓝桥杯单片机】十、第九届蓝桥杯国赛之“多功能测量仪表”
2021-04-01 16:36
【蓝桥杯单片机】九、第九届省赛之彩灯控制器
2021-03-30 18:19
【蓝桥杯单片机】八、第八届省赛之电子钟
2021-03-29 18:41

【蓝桥杯单片机】三、I2C+E2PROM

一. I2C时序

首先明确I2C总线是由时钟总线SCL数据总线SDA构成的,另外需要注意I2C总线的“线与“关系,也就是说只要任何一个器件输出低电平,那么总线就会被占用(所以说为了防止总线被占用,我们一定要记得拉高电平)。

下面我们来看I2C的时序图:

注意!!!这里要特别注意最后的应答位 ACK ,它的作用是什么呢? 可以把它理解为I2C的一个应答信号,如果ack = 1,表示I2C并没有收到数据,即主机写入不成功,我们一般用NAK来表示,相反,如果ack = 0,则表示写入成功,用ACK表示。

我们现在来分析时序图: (要注意一点:在SCL为低电平期间才能入数据,在SCL为高电平期间才能取数据) 从时序图上我们可以清楚地看到I2C通信流程中有开始部分数据传输部分,还有结束部分,那么相对应的我们的底层就应该分成这三个部分来写。

1、开始部分

分析时序图,I2C通信的开始信号是在SCL以及SDA都是高电平期间,SDA产生一个由高到低的变化,代码如下:

void I2CStart()
{
	I2C_SCL = 1;
	I2C_SDA = 1;
	I2CDelay();
	I2C_SDA = 0;
	I2CDelay();
	I2C_SCL = 0;//拉低SCL,方便下次写入数据
	I2CDelay();
}

对了!I2C里还用到了延时函数(5us延时),因为我们规定I2C通信中SCL以及SDA的高低电平持续时间不得短于5us,这个5us的延时函数是可以用STC的软件生成的。

#define I2CDelay() Delay5us()

void Delay5us()		//@11.0592MHz
{
	unsigned char i;

	_nop_();
	i = 11;
	while (--i);
}

2、数据传输部分

 这里的数据写入的方法与我之前写的DS1302(从低位到高位传输)是不是有相似之处呢,需要注意的是I2C通信是 从高位到低位 传输的,代码的解释部分可以看一下我写的注释

bit I2CWrite(u8 dat)//注意这里函数有返回值,故不能定义为void形式
{
	bit ack;
	u8 mask;
	
	for(mask = 0x80; mask != 0x00; mask >>= 1)
	{
		if((mask & dat) == 0)//运算符优先级的问题!!!!!
			I2C_SDA = 0;
		else
			I2C_SDA = 1;
		I2CDelay();
		I2C_SCL = 1;//拉高SCL
		I2CDelay();
		I2C_SCL = 0;//再拉低SCL,完成一个位的操作
		I2CDelay();
	}
	I2C_SDA = 1;//拉高SDA,便于主机读取应答位ack的值,注意是主机读!!!
	I2CDelay();
	I2C_SCL = 1;//拉高SCL,主机读取ack
	ack = I2C_SDA;
	I2CDelay();
	I2C_SCL= 0;//拉低SCL,方便下次写入
	I2CDelay();
	
	return(~ack);//这里的返回值是~ack,是为了符合正常的逻辑,即ack=1,表示写入成功,ack=0,表示写入失败
}

注意一点!!! 主机读取应答位时,一定要把SDA拉高,即释放总线,如果总线被占用,主机就无法从从机中读取数据!!!!

有了写操作(这个写,是主机也就是单片机向从机也就是I2C写入数据),自然也有读操作(这个读,是主机读,主机从从机里读数据),主机读取完信息后也会给从机发一个应答信号,这个应答信号是为了告诉从机,“我”(主机)还要不要再读取信息了,与ack一样,代码如下:

u8 I2CReadACK()//连续读
{
	u8 mask;
	u8 dat;
	
	I2C_SDA = 1;//只有在sda为高电平时主机才能进行读操作
	I2CDelay();//这里不要忘记加延时函数
	
	for(mask = 0x80; mask != 0x00; mask >>= 1)
	{
		I2C_SCL = 1;//scl在高电平期间才能读
		I2CDelay();
		
		if(I2C_SDA == 0)
			dat &= ~mask;
		else
			dat |= mask;
	    I2CDelay();
		I2C_SCL = 0;//拉低scl,使从机发送出下一位
		I2CDelay();
	}
	I2C_SDA = 0; //ack=0,表示还要继续读数据                                                                              
	I2CDelay();
	I2C_SCL = 1;//拉高SCL
	I2CDelay();
	I2C_SCL = 0;//再拉低SCL,完成一个位的操作
	I2CDelay();
	
	return dat;
}

u8 I2CReadNAK()//只读一次
{
	u8 mask;
	u8 dat;
	
	I2C_SDA = 1;
	I2CDelay();//这里不要忘记加延时函数
	
	for(mask = 0x80; mask != 0x00; mask >>= 1)
	{
		I2C_SCL = 1;//scl在高电平期间才能读
		I2CDelay();
		
		if(I2C_SDA == 0)
			dat &= ~mask;
		else
			dat |= mask;
	    I2CDelay();
		I2C_SCL = 0;
		I2CDelay();
	}
	I2C_SDA = 1;//ack=1,表示不要读了
	I2CDelay();
	I2C_SCL = 1;
	I2CDelay();
	I2C_SCL = 0;
	I2CDelay();
	
	return dat;
}

3、结束部分:

分析时序图,I2C通信的结束信号是在SCL以及SDA都是低电平期间,先把SCL拉高,然后SDA产生一个由低到高的变化,代码如下:

void I2CStop()
{
	I2C_SCL = 0;
	I2C_SDA = 0;
	I2CDelay();
	I2C_SCL = 1;
	I2CDelay();
	I2C_SDA = 1;
	I2CDelay();
//	I2C_SCL = 0;//停止后scl不必再置0
}

 二. E2PROM

既然提到了I2C怎么能不提到E2PROM呢,这里我们直接从E2PROM的多字节读写开始说吧,这里的写我们也直接开始讲解页写入,多字节的理解了单字节的还不好理解嘛,哈哈~~

直接上代码吧:

/*多字节读,*buf用来存储读到的数据,addr为E2起始地址,len字节长度*/
void E2Read(u8 *buf, u8 addr, u8 len)//多字节读
{
	do{
		I2CStart();//开启I2C
		if(I2CWrite(0x50 << 1))//寻址器件,后续写,I2Cwrite返回值为~ack故若为1,则表示应答,跳出循环,执行read操作
		{
			break;
		}	
		I2CStop();
	}while(1);
	I2CWrite(addr);//写入起始地址
	I2CStart();//重复启动,因为接下来要执行读操作了
	I2CWrite((0x50 << 1) | 0x01);//写入寻址器件,后续读
	
	while(len > 1)//注意这里是len>1,也就是说读完倒数第二个字节的数据后就跳出这个循环了,开始执行下面的程序
	{
		*buf++ = I2CReadACK();//最后字节之前为连续读取
		len--;
	}
	*buf = I2CReadNAK();//最后一个字节读
	I2CStop();	
}

void E2Write(u8 *buf, u8 addr, u8 len)//页写入
{
	while(len > 0)//页循环,循环的是页数
	{
		do{
			I2CStart();
			if(I2CWrite(0x50 << 1))//判断E2prom是否处于响应状态,若ack = 0,表示处于响应状态
			{
				break;
			}
			I2CStop();
		}while(1);
		I2CWrite(addr);
		while(len > 0)//循环写入1页数据
		{
			I2CWrite(*buf++); //buf为源数据指针
			addr++;
			len--;
			if((addr & 0x70) == 0)//判断地址是否达到页边界,24C02每页8字节
			{
				break;//如果达到一页的边界,就跳出该页写数据的循环,然后写下一页的内容
			}
		}
		I2CStop();
	}
}

做几点解释:

  1. 关于do,while 这个循环:作用是什么呢? 判断E2PROM是否处于响应状态,因为我们这是多字节读写操作,而E2PROM在每次读取或者写入一个字节的数据后,都需要一定的时间把数据搬移到“非易失区域”,在这个过程中,E2PROM是不作出响应的,所以我们在写入下一个字节的数据之前,一定要判断一下E2PROM是否处于响应状态。
  2. 判断E2PROM是否响应的原理是什么呢? 寻址器件,也就是if条件中的代码,(注意if条件句中非0才为真,也就是说为1才能进入if循环)这里涉及到了I2C寻址模式,我们只要记住24C02(E2PROM的器件型号)的7位地址位为:0b1010000(也就是0x50),而第8位是读写标志位,0表示接下来是写操作1表示,那么如果要实现寻址24C02并且表明后续操作为写,我们应该向I2C内部写入什么呢?我们只需要让第8位为0就好了呀,那么就让0x50左移1位,不就可以实现低位补0了嘛,明白了这些我们再来看这段代码:
do{
		I2CStart();//开启I2C
		if(I2CWrite(0x50 << 1))//寻址器件,后续写,I2Cwrite返回值为~ack故若为1,则表示应答,跳出循环,执行read操作
		{
			break;
		}	
		I2CStop();
	}while(1);

明白是怎么判断了的吗?其实就是向I2C中写入E2PROM的地址,如果存在这个器件或者E2PROM处于响应状态,那么应答位(即ack)就是0,那么I2CWrite这个函数的返回值就是1,if条件为真,执行break语句,跳出do,while循环,执行读写操作。下面再附上24C02的原理图,方便大家理解:

解释一下为什么24C02的7位地址是0b1010000呢?24C02的高四位地址是固定的(0b1010),低三位的地址是由A0,A1,A2,这三个引脚决定的,从原理图上我们可以看出,这三个引脚都是接地的,所以低三位000,合在一起就是0b1010000啦~

好了~~~这些就是我对于I2C的理解了,明天更A/D,D/A啦~

声明:本内容为作者独立观点,不代表电子星球立场。未经允许不得转载。授权事宜与稿件投诉,请联系:editor@netbroad.com
觉得内容不错的朋友,别忘了一键三连哦!
赞 106
收藏 105
关注 302
成为作者 赚取收益
全部留言
0/200
  • dy-SgCRSAWd 2021-04-29 21:24
    比论文强一万倍
    回复
  • dy-lZg1ikQS 2021-04-29 21:03
    请教一下
    回复
  • dy-6jaMNnKj 2021-04-29 11:05
    对我很有帮助
    回复
  • dy-uP6R9bIG 2021-04-28 22:21
    感谢分享
    回复
  • dy-faUxdNVf 2021-04-28 22:16
    大开眼界,真是好文
    回复
  • dy-ARsdJtu2 2021-04-28 16:42
    思路清晰,受益匪浅
    回复
  • dy-PfBg9fHc 2021-04-28 15:17
    讲的真好!
    回复
  • dy-BntE74dS 2021-04-27 11:37
    期待继续
    回复
  • dy-AstN3YsZ 2021-04-27 11:24
    比论文强一万倍
    回复
  • dy-kWQSvfcY 2021-04-26 11:27
    大开眼界,真是好文
    回复
  • dy-IRhxrrTG 2021-04-26 11:06
    大开眼界,真是好文
    回复
  • dy-EWZRbIzj 2021-04-22 11:27
    感谢分享
    回复
  • dy-HlVFyepq 2021-04-21 20:50
    精彩,很多东西还没接触到
    回复
  • dy-fEyVNAbF 2021-04-21 15:42
    感谢博主!
    回复
  • dy-cfgdwamL 2021-04-21 12:59
    佩服楼主
    回复
  • dy-ayNTwT3L 2021-04-21 12:26
    比论文强一万倍
    回复
  • dy-LiDZV1Qr 2021-04-20 18:09
    不亚于看了一篇高质量论文
    回复
  • dy-rfrUF2fp 2021-04-20 17:02
    期待继续
    回复
  • dy-bHww98js 2021-04-19 23:02
    围观学习
    回复
  • dy-FPtpScGU 2021-04-19 09:06
    什么时候更新
    回复