#循循渐进学51单片机#IIC总线与EEPROM#not.13

1、彻底理解I2C的通信时序,不仅仅是记住。

前几章我们学了一种通信协议叫做 UART 异步串行通信,这节课我们要来学习第二种常用的通信协议 I 2 C I 2 C 总线是由 PHILIPS 公司开发的两线式串行总线,多用于连接微处理器及其外围芯片。I 2 C 总线的主要特点是接口方式简单,两条线可以挂多个参与通信的器件,即多机模式,而且任何一个器件都可以作为主机,当然同一时刻只能有一个主机。从原理上来讲,UART 属于异步通信,比如电脑发送给单片机,电脑只负责把数据通过TXD 发送出来即可,接收数据是单片机自己的事情。而 I 2 C 属于同步通信, SCL 时钟线负责收发双方的时钟节拍,SDA 数据线负责传输数据。 I 2 C 的发送方和接收方都以 SCL 这个时钟 节拍为基准进行数据的发送和接收。从应用上来讲,UART 通信多用于板间通信,比如单片机和电脑,这个设备和另外一个设备之间的通信。而 I 2 C 多用于板内通信,比如单片机和我们本章要学的 EEPROM 之间的通信.
在硬件上,
I 2 C 总线是由时钟总线 SCL 和数据总线 SDA 两条线构成,连接到总线上的所有器件的 SCL 都连到一起,所有 SDA 都连到一起。 I 2 C 总线是开漏引脚并联的结构,因此我们外部要添加上拉电阻。对于开漏电路外部加上拉电阻,就组成了线“与”的关系。总线上线“与”的关系就是说,所有接入的器件保持高电平,这条线才是高电平,而任何一个器件输出一个低电平,那这条线就会保持低电平,因此可以做到任何一个器件都可以拉低电平,也就是任何一个器件都可以作为主机,如图 14-1 所示,我们添加了 R63 R64 两个上拉电阻。
#循循渐进学51单片机#IIC总线与EEPROM#not.13_第1张图片
虽然说任何一个设备都可以作为主机,但绝大多数情况下我们都是用单片机来做主机, 而总线上挂的多个器件,每一个都像电话机一样有自己唯一的地址,在信息传输的过程中, 通过这唯一的地址就可以正常识别到属于自己的信息,在 KST-51 开发板上,就挂接了 2 个 I 2 C 设备,一个是 24C02 ,一个是 PCF8591
我们在学习 UART 串行通信的时候,知道了通信流程分为起始位、数据位、停止位这三 部分,同理在 I 2 C 中也有起始信号、数据传输和停止信号,如图 14-2 所示,
#循循渐进学51单片机#IIC总线与EEPROM#not.13_第2张图片
从图上可以看出来, I 2 C UART 时序流程有相似性,也有一定的区别。 UART 每个字 节中,都有一个起始位、8 个数据位、 1 位停止位。而 I 2 C 分为起始信号、数据传输部分、停 止信号。其中数据传输部分,可以一次通信过程传输很多个字节,字节数是不受限制的,而 每个字节的数据最后也跟了一位,这一位叫做应答位,通常用 ACK 表示,有点类似于 UART 的停止位。
下面我们一部分一部分的把 I 2 C 通信时序进行剖析。之前我们已经学过了 UART ,所以学习 I 2 C 的过程我尽量拿 UART 来作为对比,这样有助于更好的理解。但是有一点大家要理解清楚,就是 UART 通信虽然用了 TXD RXD 两根线,但是实际一次通信中, 1 条线就可 以完成,2 条线是把发送和接收分开而已,而 I 2 C 每次通信,不管是发送还是接收,必须 2条线都参与工作才能完成,为了更方便的看出来每一位的传输流程,我们把图 14-2 改进成图14-3。
#循循渐进学51单片机#IIC总线与EEPROM#not.13_第3张图片
起始信号: UART 通信是从一直持续的高电平出现一个低电平标志起始位;而 I 2 C 通信的起始信号的定义是 SCL 为高电平期间, SDA 由高电平向低电平变化产生一个下降沿,表示起始信号,如图 14-3 中的 Start 部分所示。
数据传输:首先, UART 是低位在前,高位在后;而 I 2 C 通信是高位在前,低位在后。其次,UART 通信数据位是固定长度,波特率分之一,一位一位固定时间发送完毕就可以了。而 I 2 C 没有固定波特率,但是有时序的要求,要求当 SCL 在低电平的时候, SDA 允许变化,也就是说,发送方必须先保持 SCL 是低电平,才可以改变数据线 SDA ,输出要发送的当前数据的一位;而当 SCL 在高电平的时候, SDA 绝对不可以变化,因为这个时候,接收方要来读取当前 SDA 的电平信号是 0 还是 1 ,因此要保证 SDA 的稳定,如图 14-3 中的每一位数据的变化,都是在 SCL 的低电平位置。 8 位数据位后边跟着的是一位应答位,应答位我们后 边还要具体介绍。
停止信号: UART 通信的停止位是一位固定的高电平信号;而 I 2 C 通信停止信号的定义 是 SCL 为高电平期间, SDA 由低电平向高电平变化产生一个上升沿,表示结束信号,如图14-3 中的 Stop 部分所示。

2、能够独立完成EEPROM任意地址的单字节读写、多字节跨页连续写入读出。

存储器件,掉电后数据不丢失

单字节读写

#include 


extern void InitLcd1602();
extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str);
extern void I2CStart();
extern void I2CStop();
extern unsigned char I2CReadNAK();
extern bit I2CWrite(unsigned char dat);
unsigned char E2ReadByte(unsigned char addr);
void E2WriteByte(unsigned char addr, unsigned char dat);

void main()
{
    unsigned char dat;
    unsigned char str[10];
	
	InitLcd1602();
	dat = E2ReadByte(0x02); 
	str[0] = (dat/100) + '0';  
    str[1] = (dat/10%10) + '0';
    str[2] = (dat%10) + '0';
    str[3] = '\0';
    LcdShowStr(0, 0, str);     
    dat++;                    
    E2WriteByte(0x02, dat);   
    
    while (1);
}
unsigned char E2ReadByte(unsigned char addr)
{
	unsigned char dat;
	
	I2CStart();
	I2CWrite(0x50<<1);
	I2CWrite(addr);
	I2CWrite((0x50<<1)|0x01);
	dat = I2CReadNAK(); 
	I2CStop();
	
	return dat;
}
void E2WriteByte(unsigned char addr, unsigned char dat)
{
    I2CStart();
    I2CWrite(0x50<<1); 
    I2CWrite(addr);    
    I2CWrite(dat);    
    I2CStop();
}

 1602液晶模块

#include 

#define LCD1602_DB P0
sbit LCD1602_RS = P1^0;
sbit LCD1602_RW = P1^1;
sbit LCD1602_E  = P1^5;

void LcdWaitReady()
{
	unsigned char sta;
	
	LCD1602_DB = 0xff;
	LCD1602_RS = 0;
	LCD1602_RW = 1;
	do{
	LCD1602_E = 1;
	sta = LCD1602_DB;
 	LCD1602_E = 0;
	}while(sta & 0x80);
}

void LcdWriteCmd(unsigned char cmd)
{
   LcdWaitReady();
	 LCD1602_RS = 0;
   LCD1602_RW = 0;
	 cmd = LCD1602_DB;
	 LCD1602_E = 1;
	 LCD1602_E = 0;
}

void LcdWriteDat(unsigned char dat)
{
   LcdWaitReady();
	 LCD1602_RS = 1;
   LCD1602_RW = 0;
	 dat = LCD1602_DB;
	 LCD1602_E = 1;
	 LCD1602_E = 0;
}

void LcdSetCursor(unsigned char x, unsigned char y)
{
 unsigned char addr;
	if(y == 0)
		addr = 0x00 + x;
	else
		addr = 0x40 + x;
	LcdWriteCmd(addr | 0x80);
}

void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str)
{
		LcdSetCursor(x, y); 
	while(*str != '\0')
	{
	LcdWriteDat(*str++);
	}
}

void InitLcd1602()
{
    LcdWriteCmd(0x38); 
    LcdWriteCmd(0x0C); 
    LcdWriteCmd(0x06); 
    LcdWriteCmd(0x01);  
}

 I2C模块

#include 
#include 

#define I2CDelay()  {_nop_();_nop_();_nop_();_nop_();}
sbit I2C_SCL = P3^7;
sbit I2C_SDA = P3^6;

void I2CStart()
{
  I2C_SDA = 1;
	I2C_SCL = 1;
	I2CDelay();
  I2C_SDA = 0;
	I2CDelay();
  I2C_SCL = 0;

}

void I2CStop()
{
  I2C_SDA = 0;
	I2C_SCL = 0;
	I2CDelay();
	I2C_SCL = 1;
	I2CDelay();
	I2C_SDA = 1;
	I2CDelay();
}

bit I2CWrite(unsigned char dat)
{
	bit ack;
	unsigned char mask;
	
	for(mask = 0x80;mask!=0;mask>>=1)
	{
	  if((mask&dat)==0)
			I2C_SDA = 0;
		else
			I2C_SDA = 1;
		I2CDelay();
		I2C_SCL = 1;
		I2CDelay();
		I2C_SCL = 0;
	}
	I2C_SDA = 1;
	I2CDelay();
	ack = I2C_SDA;
	I2CDelay();
	I2C_SCL = 0; 
	
	return (~ack);

}

unsigned char I2CReadNAK()
{
	unsigned char mask;
  unsigned char dat;

	I2C_SDA = 1;
   for(mask=0x80; mask!=0; mask>>=1)
    {
		    I2CDelay();
			  I2C_SCL = 1;
			if(I2C_SDA == 0)
			dat &= ~mask;
			else
				dat |= mask;
			I2CDelay();
			I2C_SCL = 0;
		}
	I2C_SDA = 1;
	I2CDelay();	
	I2C_SCL = 1;
	I2CDelay();	
		I2C_SCL = 0;
		
		return dat;
	}

unsigned char I2CReadACK()
{
    unsigned char mask;
    unsigned char dat;
	
	I2C_SDA = 1;
	for(mask=0x80; mask!=0; mask>>=1)
	{
	   I2CDelay();
		I2C_SCL = 1;
		if(I2C_SDA == 0)
			dat &= ~mask;
		else
			dat |= mask;
		I2CDelay();
		I2C_SCL = 0;
		
		return dat;
	}
}

 多字节读写

#include 


extern void InitLcd1602();
extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str);
extern void I2CStart();
extern void I2CStop();
extern unsigned char I2CReadACK();
extern unsigned char I2CReadNAK();
extern bit I2CWrite(unsigned char dat);
void E2Read(unsigned char *buf, unsigned char addr, unsigned char len);
void E2Write(unsigned char *buf, unsigned char addr, unsigned char len);
void MemToStr(unsigned char *str, unsigned char *src, unsigned char len);


void main()
{
    unsigned char i;
		unsigned char buf[5];
    unsigned char str[20];
	
	InitLcd1602();
	E2Read(buf, 0x90, sizeof(buf));
	MemToStr(str, buf, sizeof(buf));
	LcdShowStr(0, 0, str);
	for(i = 0;i < sizeof(buf);i++)
	{
	    buf[i] = buf[i] + 1 + i;
	}
     E2Write(buf, 0x90, sizeof(buf));
    while (1);
}

void MemToStr(unsigned char *str, unsigned char *src, unsigned char len)
{
  unsigned char tmp;
  while(len--)
	{
	  tmp = *src>>4;
		if(tmp <= 9)
		*str++ = tmp + '0';
		else
			*str++ = tmp - 10 +'A';
		tmp = *str & 0x0f;
		if(tmp <= 9)
			*str++ = tmp - 10 - 'A';
		*str++ =' ';
		src++;
	}
	*str = '\0';
}

void E2Read(unsigned char *buf, unsigned char addr, unsigned char len)
{
		do{
		I2CStart();
			  if (I2CWrite(0x50<<1)) 
        {
            break;
        }
		}while(1);
	I2CWrite(addr);
	 I2CStart(); 	
		I2CWrite((0x50<<1)|0x01);
		while(len > 1)
		{
		  *buf++ = I2CReadACK();
			len--;
		}
		*buf = I2CReadACK();
		I2CStop();
}

void E2Write(unsigned char *buf, unsigned char addr, unsigned char len)
{
		while(len--)
		{
		do{
		I2CStart();
			  if (I2CWrite(0x50<<1)) 
        {
            break;
        }
			I2CStop();
		}while(1);
		I2CWrite(addr++);
		I2CWrite(*buf++);
		I2CStop();
		}
}
        //按页连续写入
        I2CWrite(addr);           
        while (len > 0)
        {
            I2CWrite(*buf++);    
            len--;                
            addr++;               
            if ((addr&0x07) == 0) 
            {                    
                break;            
            }
        }
        I2CStop();
    }

#循循渐进学51单片机#IIC总线与EEPROM#not.13_第4张图片
3、将前边学的交通灯进行改进,用EEPROM保存红灯和绿灯倒计时时间,并且可以通过UART改变红灯和绿灯倒计时时间。


4、使用按键、1602液晶、EEPROM做一个简单的密码锁程序。

你可能感兴趣的:(51学习记录,51单片机,嵌入式硬件,单片机,学习方法,笔记)