用户工具

站点工具


侧边栏

抱歉,您没有权限增加页面
4.单片机入门教程:15.iic总线24c02-eeprom

单片机入门教程之IIC总线24C02

一:实验原理:
EEPROM (Electrically Erasable Programmable Read-Only Memory),即电可擦除可编程只读存储器,一种掉电后数据不丢失的存储芯片。用来保存用户数据,运行过程中可以修改,比如电子时钟闹铃的时间。也许你在想,这和单片机的程序存储器有啥区别,区别就是你的程序下载进去以后,你写好的闹铃时间是固定的,想要修改闹铃时间必须在代码中改然后再编译下载进去,外部用个EEPROM芯片就很方便了,你设定的值可以通过单片机中的EEPROM芯片读写程序写进去,这个存储器是由单片机来擦除和编程的,24C02就是个EPPROM芯片。

24C02是一个8脚的芯片,容量为2KB,分为32页,每页8字节,共256字节。容量大一点的还有24C04/24C08等。一百万次编程/擦除周期,可保存数据100年。24C02与单片机的接口非常简单,使用两线串行接口,IIC总线协议。A0-A2为地址输入,WP为写保护,SDA为串行数据,SCL为串行时钟。

IIC总线是PHLIPS公司推出的一种串行总线,是具备多主机系统所需的包括总线裁决和高低速器件同步功能的高性能串行总线。IIC总线只有两根双向信号线。一根是数据线SDA,另一根是时钟线SCL。接到IIC总线上的器件都有唯一的地址,这个地址用来识别器件,从而使IIC总线可以挂多个芯片。如下图:

IIC总线必须通过上拉电阻接到VCC。当总线空闲时,两根线均为高电平。连到总线上的任一器件输出的低电平,都将使总线的信号变低。如下图:

想要读写24C02,我们先要把IIC总线的时序部分搞懂,总线在传送数据过程中共有三种类型的型号,分别是:开始信号,终止信号和应答信号。
1.开始信号:SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。
2.终止信号:SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据。

3.发送器每发送一个字节,就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。 应答信号为低电平时,表示接收器已经成功地接收了该字节;对于反馈有效应答位ACK的要求是,接收器在第9个时钟脉冲之前的低电平期间将SDA线拉低,并且确保在该时钟的高电平期间为稳定的低电平。 如果接收器是主控器,则在它收到最后一个字节后,发送一个非应答NACK信号,以通知被控发送器结束数据发送,并释放SDA线,如图:

在I2C总线上传送的每一位数据都有一个时钟脉冲相对应,即在SCL串行时钟的配合下,在SDA上逐位地串行传送每一位数据,每个时钟送一位数据。进行数据传送时,在SCL呈现高电平期间,SDA上的电平必须保持稳定,低电平为数据0,高电平为数据1。只有在SCL为低电平期间,才允许SDA上的电平改变状态。如图:

有了上面的时序我们就可以写IIC时序部分的代码了,先来看看24C02数据手册上给的一个总的时序图,可以看到这些信号是有时间范围的,超过或小于可能会读写失败,所以要遵循这些时间:

二:实验现象与解释:
IIC总线时序模拟,读写24C02 EEPROM,写入流水灯数组在读出来。

来看代码吧,可以看到上面的时序图,我们需要一个微秒级的延时函数,如下: 代码完整工程在stc51 discovery开发板【实验例程】【进阶实验】【实验四十三】

//时间=time*2us
void Delay_us(uint time)//@11.0592MHz 
{
   while(time--)
   {
      _nop_();
   }
}

下面再来看IIC的开始信号:

//IIC 起始信号
//在时钟线保持为高电平时,数据线由高电平向低电平变化
void IIC_Start(void)	
{
 	IIC_SDA=1;
 	Delay_us(3);
 	IIC_SCL=1;
 	Delay_us(3);
 	IIC_SDA=0;
 	Delay_us(3);
 	IIC_SCL=0;
 	Delay_us(3);
}

开始信号的时序图如下,和上面总的时序图一样,分解出来更容易理解:

下面再来看IIC的终止信号:

//IIC 终止信号
//在时钟线保持为高电平时,数据线由低电平向高电平变化
void IIC_Stop(void)
{
	 IIC_SDA=0; 
	 Delay_us(3);
	 IIC_SCL=1;
	 Delay_us(3); 
	 IIC_SDA=1;
	 Delay_us(3); 
	 IIC_SCL=0;
	 Delay_us(3);
}

终止信号时序图如下,和上面总的时序图一样,分解出来更容易理解:

下面再来看非应答信号,非应答是一个高电平信号,由主机发出,告诉从机我接收完了。

//主机发送非应答 
//主机如果不再进行读取从机数据时,发送SDA=1,给从机非应答
void IIC_Master_NOACK(void)
{
  IIC_SDA=1;
  Delay_us(3);
  IIC_SCL=1;
  Delay_us(3);
  IIC_SCL=0;
  Delay_us(3);
}

非应答时序图如下:

下面再来看应答信号,是一个低电平信号,是由从机发出的,告诉主机我接收完了。

//主机检测从机应答 
//主机写入数据后,等待从机应答,SDA=0成功
void IIC_SLave_ACK(void)	 
{

   uchar i;
   IIC_SCL=1;
   Delay_us(3);
   while((IIC_SDA==1)&&(i<250))	//从机应答SDA=0;忙SDA=1
   { 
     i++;
     Delay_us(1);
	 
   }
   IIC_SCL=0;
}

应答时序如下:

再来看主机向从机写入一个字节数据。

//主机向从机写入一个字节,Input 要写入的数据
void IIC_Write_Byte(uchar Input)
{
 	uchar i;
 	for(i=0;i<8;i++)
	{
		IIC_SDA=Input&0x80;
		IIC_SCL=1;	//稳定数据
		Delay_us(6);
		IIC_SCL=0;  //允许数据变化,准备传送下一位
		Delay_us(6);
		Input=Input<<1;
	}
  // IIC_SLave_ACK();
}

最后再来看主机读取从机一个字节数据

   //主机向从机读取一个字节,return 读取到的字节
uchar IIC_Read_Byte()
{
 	uchar i;
	uchar Readbyte=0;
	IIC_SDA=1;	//释放数据线
 	for(i=0;i<8;i++)
	{
		Readbyte<<=1;	
		IIC_SCL=1;	//稳定数据
		Delay_us(6);
		Readbyte|=IIC_SDA;
		IIC_SCL=0; //允许数据变化,准备接收下一位
		Delay_us(6);
	}
 	return Readbyte; 
}

OK,IIC时序部分已经完成,接下来就可以读写24C02了,读写之前先来看看24C02的读写地址问题,看个图:

其中A0-A2是芯片管脚,通过接高低电平来自定义,板子上这三个脚都接到了地,所以都是0,最后一位R/W,这一位控制着写地址和读地址,置1为读数据,置0为写数据,即24C02的读地址为:10100001=0xa1; 写地址为:10100000=0xa0;

读写地址搞清楚了,接下先来看如何向24C02中写数据,24C02分为按字节写和按页写,我们只弄按字节写的部分,手册上给出的一个按字节写流程如下:
可以看到,首先发送START开始信号,然后发送DEVICE ADDRESS(写地址0xa0),然后检测ACK应答,然后发送你要往24C02的哪个WORD ADDRESS(存储地址)(0x00-0xff)中写数据,然后检测ACK应答,然后在发送你要写的DATA(数据),再检测ACK应答,最后发送STOP信号。一波写数据就完成了。代码如下:

//向24C02按地址写一字节,24C02共256个字节 256*8=2K
//address:选择要写的地址0-255  date:要写的数据
void WriteDate(uchar address,uchar date) //24c02字节写
{
		//写规则时序见英文手册11页figure8
  IIC_Start();
  IIC_Write_Byte(0xa0);//写器件地址 见AT24C02英文手册11页figure7
  IIC_SLave_ACK();
  IIC_Write_Byte(address);	//写地址 
  IIC_SLave_ACK();
  IIC_Write_Byte(date);    //写数据 
  IIC_SLave_ACK();
  IIC_Stop();
}

再来看从24C02读出一个字节数据,读分为随机读和按当前地址读,我们只看按地址读,读的流程如下:
首先是发送START开始信号,然后发送读的器件地址(0xa1),然后在发送你想读哪个地址(0x00-0xff)上的数据,接着检测ACK,然后就是读出来的数据了,最后发送NO ACK和STOP信号。

//向24C02按地址读一字节,address:选择要读的地址
uchar ReadData(uchar address)	   //24C02选择读 
{
    uchar temp;//读规则时序见AT24C02英文手册12页figure10
    IIC_Start();
    IIC_Write_Byte(0xa0);   //写器件地址 
    IIC_SLave_ACK();
    IIC_Write_Byte(address);   //写数据地址 
    IIC_SLave_ACK();
    IIC_Start();
    IIC_Write_Byte(0xa1);   //写要读的地址 
    IIC_SLave_ACK();
    temp=IIC_Read_Byte();  //读取数据 
    IIC_Master_NOACK();
    IIC_Stop();
    return temp;

}

OK,结束了,读写函数都造好了,接下来就可以测试下了,把8个字节的流水灯数组写进去,然后在读出来看看有没有效果。对于IIC一些概念一定要搞清楚。

void main(void)
{
  uchar i;
  LEDK=0; 
  IO_Init();

  for(i=0;i<8;i++) //将流水灯数据写进去
  {
	WriteDate(i,LED[i]);//写数据
	delay_ms(20);
  }

  while(1)
  {
	for(i=0;i<8;i++) //将流水灯数组读出来  
	{
	  P0=ReadData(i);
	  delay_ms(1000);
	}      

  }

}
  代码完整工程在stc51 discovery开发板【实验例程】【进阶实验】【实验四十三】
4.单片机入门教程/15.iic总线24c02-eeprom.txt · 最后更改: 2017/03/08 15:39 由 wawooo