《电子DIY》之使用51单片机驱动EEPROM器件AT24C02、IIC通信、Proteus仿真

一,IIC介绍

1,历史来源:IIC(Inter-Integrated Circuit)其实是IICBus简称,所以中文应该叫集成电路总线,它是一种串行通信总线,使用多主从架构,由飞利浦公司在1980年代为了让主板、嵌入式系统或手机用以连接低速周边设备而发展。
2,I2C串行总线一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL。所有接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。
(SDA、SCL外接上拉电阻)

3,IIC讲解:
各信号讲解:
开始信号:
开始信号:MCU使其SCL时钟保持电平,然后让SDA数据信号由高变低
在这里插入图片描述
停止信号:
停止信号:MCU使其SCL时钟保持电平,然后让SDA数据信号由低变高
在这里插入图片描述
数据传输信号:
SDA上的数据只能在SCL为电平期间翻转变化,在SCL为高电平期间必须保持稳定,IIC设备只在SCL为电平期间采集SDA数据
在这里插入图片描述
应答信号:
每个接收设备在寻址时都有义务收到每个消息后产生一个确认字节。 主设备必须产生一个额外的时钟与该确认位相关的脉冲。
确认器件必须在确认时钟脉冲期间拉低SDA线,以使SDA线在确认相关时钟脉冲的高电平期间稳定为LOW。 当然,必须考虑建立和保持时间。 主机必须通过不在从机时钟输出的最后一个字节上不生成确认位来向从机发出数据结束信号。 在这种情况下,从机必须将数据线保持为高电平才能使能
主机产生STOP条件。

简而言之:
如果外接IIC设备在收到信号后紧接着在第9个周期把SDA拉低,那么MCU认为外接IIC设备数据已经成功收到。

注:IIC初始化状态为:SDA拉高,SCL拉高,即SDA=SCL=1

二,EEPROM器件24C02介绍

EEPROM:电可擦编程只读存储器

1,在Proteus中,24C02器件选择:
在这里插入图片描述
2,24C02介绍
串行E2PROM是基于I2C-BUS 的存储器件,遵循二线制协议,由于其具有接口方便,体积小,数据掉电不丢失等特点,在仪器仪表及工业自动化控制中得到大量的应用。
特点
1.宽范围的工作电压1.8v~5.5v
2.低电压技术:
1mA典型工作电流
1uA典型待机电流
3.储存器组织结构
4.2线串行接口,完全兼容I2C总线
5.施密特触发输入噪声抑制
6.硬件数据写保护
7.内部与周期(最大5ms)
8.自动递增地址
9.可按照字节写
10.esd保护大于2.5kV
11.高可靠性:
–擦写寿命:100万次 数据保持时间:100年
12.无铅工艺,符合RoHS标准
(摘自百度)

(1)功能框图:
在这里插入图片描述
(2)主要引脚定义

SDA                   串行地址/数据I / O
SCL                   串行时钟
WP                    写保护输入
VCC                   电源正极
GND                   电源地
A0-A2                 地址

注: WP:

该引脚必须连接到VSS或VCC。如果连接到VSS(即接地),则启用正常的内存操作(读/写整个内存)。如果绑定到VCC,则禁止写操作,整个内存将被写保护。 读取操作不受影响。

3,时序
图1:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
4,访问24C02B

产生启动条件后,总线主机发送从机地址,该从机地址由24C01B / 02B的4位设备代码 (1010) 组成,后跟3个无关位。从机地址的第位确定主机是否设备要读取或写入24C01B / 02B,格式如下:
在这里插入图片描述

向24C02写数据过程:
(1)写一页数据:
在这里插入图片描述
(2)写一字节数据:
在这里插入图片描述
数据
在这里插入图片描述
(上述时序摘自芯片手册)

对于上述讲解似乎不好理解,

总结如下:

24C02通过IIC通信,通信时钟频率最大值100KHZ(见图1),供电典型值5V

1,写过程:(写一字节)
(1),主机(即MCU)首先发出开始信号
(2),发出写24C02的寻址字节1010 A2A1A0 W B,此处A2A1A0=000
所以:发出字节 1010 0000B
(3),等待从机应答
(4),发送待写数据的地址
(5),等待从机应答
(6),发送待写数据
(7),等待从机应答
(8),发送停止位,结束这次写过程

2,读过程:(读一字节)
(1),主机(即MCU)首先发出开始信号
(2),发出写24C02的寻址字节1010 A2A1A0 W B,此处A2A1A0=000
所以:发出字节 1010 0000B
(3),等待从机应答
(4),发送待读数据的地址
(5),等待从机应答
(6),重新发送开始信号
(7),发出写24C02的寻址字节1010 A2A1A0 R B,此处A2A1A0=000
所以:发出字节 1010 0001B
(8),等待从机应答
(9),读取数据
(10),发送非应答信号
(11),发送停止位,结束这次读过程
(读多字节和写多字节类似)

三,仿真电路图

在这里插入图片描述

四,驱动24C02B程序源码

要求:向24C02B器件写入字符,当按键“写入”按下时,向24C02写数据同时写入指示灯亮,当按键“读取”按下时,从24C02B器件读出数据显示在LCD1602显示屏上同时读取指示灯亮

程序源码:


#include "reg52.h"
#include "intrins.h"

sbit RS = P2^0;   //定义端口 
sbit RW = P2^1;
sbit EN = P2^2;
sbit SCK = P2^3;
sbit SDA = P2^4;
sbit LED = P2^5;
sbit WriteLED = P2^6;
sbit ReadLED  = P2^7;

#define LCD_RS_CLR RS=0 
#define LCD_RS_SET RS=1
#define LCD_RW_CLR RW=0 
#define LCD_RW_SET RW=1 
#define LCD_EN_CLR EN=0
#define LCD_EN_SET EN=1
#define LCD_DataPort P0

#define u8  unsigned char
#define u16 unsigned int

#define EEPROM24C02_ADDR 0XA0
#define READ  1
#define WRITE 0

const u8 DataBuffer[25]={" author: liuxianfei0810 "};
const u8 DataBuffer1[8]={" author:"};
const u8 DataBuffer2[16]={" liuxianfei0810 "};

u8 cnt=0x30;
u8 addr=32;
u8 dat[3]=" ";
/////////////////////函数声明区//////////////////
void delay_us(u16 t);
void delay_ms(u16 t);

void LCD_Write_String(u8 x,u8 y,u8 *s);
bit LCD_Check_Busy(void);
void LCD_Write_Command(u8 commmand);
void LCD_Write_Data(u8 dat);
void LCD_Clear(void);
void LCD_Write_Char(u8 x,u8 y,u8 dat);
void LCD_Init(void);
//void LCD_Write_String_Number(u8 posx,u8 posy,u8 startlen,u8 endlen,u8 *s);
//void LCD_Write_StringWantonly(u8 posx,u8 len,u8 *s) ;

void IIC_Start(void);
void IIC_Stop(void);
u8   IIC_Wait_Ack(void);
void IIC_Ack(void);
void IIC_NAck(void);
static void IIC_WriteByte(u8 dat);
static u8   IIC_ReadByte(void);

void EEPROM24C02_Init(void);
u8   Read_24c02_ByteData(u8 address);
long Read_24c02_LenByte(u16 Addr,u8 Len);
void Read_24c02_Buff(u16 Addr,u8 *pBuffer,u16 Number);
void Write_24c02_ByteData(u8 address,u8 information);
void Write_24c02_LenByte(u16 Addr,long Data,u8 Len);
void Write_24c02_Buff(u16 Addr,u8 *pBuffer,u16 Number);
u8 EEPROM24c02_Check(void);
////////////////////////END//////////////////////

void Write_24c02_LenByte(u16 Addr,long Data,u8 len)
{  	
	u8 i;
	for(i=0;i<len;i++)
	{
		Write_24c02_ByteData(Addr+i,(Data>>(8*i))&0xff);
	}												    
}

long Read_24c02_LenByte(u16 Addr,u8 Len)
{  	
	u8 t;
	long temp=0;
	for(t=0;t<Len;t++)
	{
		temp<<=8;
		temp+=Read_24c02_ByteData(Addr+Len-t-1); 	 				   
	}
	return temp;												    
}

void Read_24c02_Buff(u16 Addr,u8 *pBuffer,u16 Number)
{
	while(Number)
	{
		*pBuffer++=Read_24c02_ByteData(Addr++);	
		Number--;
	}
}  

void Write_24c02_Buff(u16 Addr,u8 *pBuffer,u16 Number)
{
	while(Number--)
	{
		Write_24c02_ByteData(Addr,*pBuffer);
		Addr++;
		pBuffer++;
	}
}

u8 EEPROM24c02_Check(void)
{
	u8 temp;
	temp=Read_24c02_ByteData(51);		   
	if(temp==51)return 0;		   
	else
	{
		Write_24c02_ByteData(51,51);
	    temp=Read_24c02_ByteData(51);	  
		if(temp==51)
			return 0;
	}
	return 1;											  
}

void Write_24c02_ByteData(u8 address,u8 information)
{
   IIC_Start();
   IIC_WriteByte(EEPROM24C02_ADDR|WRITE);
   IIC_Wait_Ack();
   IIC_WriteByte(address);
   IIC_Wait_Ack();
   IIC_WriteByte(information);
   IIC_Wait_Ack();
   IIC_Stop();
   delay_ms(10);
}

u8 Read_24c02_ByteData(u8 address)
{
   u8 dat;
   IIC_Start();
   IIC_WriteByte(EEPROM24C02_ADDR|WRITE);
   IIC_Wait_Ack();
   IIC_WriteByte(address);
   IIC_Wait_Ack();
   IIC_Start();
   IIC_WriteByte(EEPROM24C02_ADDR|READ);
   IIC_Wait_Ack();
   dat=IIC_ReadByte();
   IIC_NAck();
   IIC_Stop();
   return  dat;
}

static u8 IIC_ReadByte(void)   
{
   u8 i,dat=0;
   for (i=0;i<8;i++)
   {  
		SCK=0; 
		_nop_();
		_nop_();
		SCK=1;
		dat<<=1;
      	if(SDA==1) 
			dat++;
      	else;
			_nop_();
	}
	return dat;
}

static void IIC_WriteByte(u8 dat) 
{
	u8 i,temp;
	temp=dat;
	SCK=0;
	for (i=0;i<8;i++)
	{
		if((temp&0x80)>>7)
			SDA=1;
		else
			SDA=0;
		temp<<=1; 	  
		_nop_();
		_nop_();
		SCK=1;
		_nop_();
		_nop_();
		SCK=0;	
		_nop_();
		_nop_();
	}
}

void IIC_Start(void)        
{
	SDA=1;
	SCK=1;
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	SDA=0;
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	SCK=0;
}

void IIC_Stop(void)         
{
	SDA=0;
	SCK=1;
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	SDA=1;
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	SDA=0;
	SCK=0;
}

u8 IIC_Wait_Ack(void)
{
	u8 ErrorTime=0;	 
	SDA=1;
	_nop_();	   
	SCK=1;
	_nop_();	 
	while(SDA)
	{
		ErrorTime++;
		if(ErrorTime>250)
		{
			IIC_Stop();
			return 1;
		}
	}
	SCK=0;//时钟输出0 	   
	return 0;  
} 

//产生ACK应答
void IIC_Ack(void)
{
	SCK=0;
	SDA=0;
	_nop_();
	_nop_();
	SCK=1;
	_nop_();
	_nop_();
	SCK=0;
}
//不产生ACK应答		    
void IIC_NAck(void)
{
	SCK=0;
	SDA=1;
	_nop_();
	_nop_();
	SCK=1;
	_nop_();
	_nop_();
	SCK=0;
}

void EEPROM24C02_Init(void)  
{
	SCK=1;
	SDA=1;
	delay_us(2);
}

void delay_us(u16 t)
{   
	while(--t)
		_nop_();
}

void delay_ms(u16 t)
{ 
	while(t--)
	{
	 //大致延时1mS
	 delay_us(245);
	 delay_us(245);
	}
}


bit LCD_Check_Busy(void) 
{ 
	LCD_DataPort=0xFF; 
	LCD_RS_CLR; 
	LCD_RW_SET; 
	LCD_EN_CLR; 
	_nop_(); 
	LCD_EN_SET;
	return (bit)(LCD_DataPort&0x80);
}


void LCD_Write_Command(u8 commmand) 
{  
//	while(LCD_Check_Busy()); 
	LCD_RS_CLR; 
	LCD_RW_CLR; 
	LCD_EN_SET; 
	LCD_DataPort=commmand; 
	_nop_(); 
	LCD_EN_CLR;
}


void LCD_Write_Data(u8 dat) 
{ 
//	while(LCD_Check_Busy());
	LCD_RS_SET; 
	LCD_RW_CLR; 
	LCD_EN_SET; 
	LCD_DataPort=dat; 
	_nop_();
	LCD_EN_CLR;
}

void LCD_Clear(void) 
{ 
	LCD_Write_Command(0x01); 
	delay_ms(5);
}

//void LCD_Write_String_Number(u8 posx,u8 posy,u8 startlen,u8 endlen,u8 *s) 
//{  
//	u8 i=0,len;  
//	
//	len=endlen-startlen;
//	s[endlen+1]='\0';
//	if (posy==0) 
//	{     
//	 	LCD_Write_Command(0x80+posx);     //表示第一行
//	}
//	else 
//	{      
//		LCD_Write_Command(0xC0+posx);      //表示第二行
//	} 
//	for(i=startlen;i<len;i++)     
//  	{
//		LCD_Write_Data(s[i]);        
//	}
//}
//
// 
//void LCD_Write_StringWantonly(u8 posx,u8 len,u8 *s) 
//{     
//  	u8 i=0;
//	LCD_Clear();
//	LCD_Write_Command(0x80+posx);     
//	while (s[i]!='\0') 
//	{    
//		if(i==len)
//			LCD_Write_Command(0xC0+posx); 	 
//		LCD_Write_Data(s[i]);     
//		i++;     
//	}
//}

void LCD_Write_String(u8 posx,u8 posy,u8 *s) 
{     
	if (posy==0) 
	{     
	 	LCD_Write_Command(0x80+posx);     //表示第一行
	}
	else 
	{      
		LCD_Write_Command(0xC0+posx);      //表示第二行
	}        
	while (*s!='\0') 
	{     
		LCD_Write_Data(*s);     
		s++;     
	}
}

void LCD_Write_Char(u8 posx,u8 posy,u8 dat) 
{     
	if (!posy) 
	{     
		LCD_Write_Command(0x80+posx);     
	}    
	else 
	{     
		LCD_Write_Command(0xC0+posx);     
	}        
	LCD_Write_Data(dat);  
}

void LCD_Init(void) 
{
	LCD_Write_Command(0x38);    /*显示模式设置*/ 
	delay_ms(5); 
	LCD_Write_Command(0x38); 
	delay_ms(5); 
	LCD_Write_Command(0x38); 
	delay_ms(5); 
	LCD_Write_Command(0x38);  
	LCD_Write_Command(0x08);    /*显示关闭*/ 
	LCD_Write_Command(0x01);    /*显示清屏*/ 
	LCD_Write_Command(0x06);    /*显示光标移动设置*/ 
	delay_ms(5); 
	LCD_Write_Command(0x0C);    /*显示开及光标设置*/
}
   

int main(void) 
{ 
	u8 *pointer1;
	LCD_Init(); 
	LCD_Clear();//清屏
	LED=1;
	IE=0X85;
	IP=0X40;
	TCON=0X05;
	WriteLED=ReadLED=1;
	EEPROM24C02_Init();
	LCD_Write_String(0,0," Access 24c02");
	while(EEPROM24c02_Check())
	{
		LED=0;	
	}
	LED=1;
	Write_24c02_Buff(0,DataBuffer,25);
	Read_24c02_Buff(0,pointer1,25);
	LCD_Write_String(0,1,&pointer1[8]);
	if(Read_24c02_ByteData(1)=='a')	;
		LED=0;
	while (1) 
	{  
		delay_ms(100);
	}
	return 0;
}

void Interrupt0(void) interrupt 0 
{
  	 LCD_Clear();
	 LCD_Write_String(0,0," Write Data: ");
	 Write_24c02_ByteData(addr,cnt);
	 dat[1]= cnt;
 	 LCD_Write_String(1,1,dat);

     WriteLED=0;
	 ReadLED=1;
	 cnt++;
	 addr++;
	 if(addr==100)
	 {
	 	cnt=0x30;
	 	addr=0;	
	 }
}

//预留
void Timer0(void) interrupt 1 
{
}

void Interrupt2(void) interrupt 2 
{
	WriteLED=1;
	ReadLED=0;
	LCD_Clear();
	LCD_Write_String(0,0," Read Data: ");
	dat[1]= Read_24c02_ByteData(addr-1);
//	if(dat[1]==cnt-1)
//		LED=1;
	LCD_Write_String(1,1,dat);
}

五,仿真图

1,开机初始化
在这里插入图片描述

2,写入数据
在这里插入图片描述
3,读出数据
在这里插入图片描述

仿真视频:

《电子DIY》之使用51单片机驱动EEPROM器件AT24C02、IIC通信、Proteus仿真

猜你喜欢

转载自blog.csdn.net/liuxianfei0810/article/details/105929410