12864与24C04的原理和使用方法——以电子密码锁为例(下篇)

上次简单介绍了一下12864的原理,这次就把实验中用到的另一个24c04芯片简单说一说。相比于12864,24c04就简单一点,它的操作原理相对于显示图像来说更容易理解,没有那么绕的地方。我会尽量将它的原理讲的通俗易懂一些,以便于大家理解和使用。

24c04的原理(含IIC简单介绍)

24c04属于AT24c系列存储芯片的一种,其芯片容量为512Byte,在官方手册上写为了4Kbit,其实是一个意思(1Byte=8bit,以1024为单位转换值)。除此之外,24c04与其他的系列还是存在比较大差别的。

在我对24c04编程的过程中,总结出了两个关键的问题,相信大家只要搞懂了这两个问题,24c04的使用就不在话下:①如何使用IIC进行读写?时序怎样分配?②24c04的组成与操作特性是什么样的?响应机制又是怎样的?该如何根据其特点进行程序编写?

下面先来讲讲第一个问题。24c04本质上是一种存储器件,因此我们必然需要一种操作方法来对其进行读取和写入,这种操作方法就是IIC。这里没用过IIC的话问题也不大,我就先开个小标题简单说一说IIC,已经懂的朋友也可以直接跳到下面的24c04介绍部分。

IIC的操作原理

因为本人不喜欢所谓的设计背景之类的套话,这里就直接介绍IIC原理。

简单来说,IIC其实就是两根线,一根负责时序(SCL),也就是类似于我们所熟悉的时钟;一根负责数据(SDA),也就是传输对应的数据。所有的设备都挂在这两根线上,其中,有一台设备为主机,剩余设备为从机,通常我们用单片机做主机。这些设备根据接收到的已经规定过信号的含义(两根线发送的不同的高低电平的变化)来表示不同的操作。

例如,IIC的起始信号表达方法就是:SCL(时钟总线)保持高电平期间,SDA(数据总线)由高电平向低电平变化产生的一个下降沿。只要挂在IIC上的设备接收到了上述这个状态,就认为IIC开始工作了。所以,IIC的使用实际上就是赋值语句与适当的nop()延时函数的组合。具体的高低电平变化则需要根据官方给出的时序图来决定,这里给出起始和停止的时序图,给大家熟悉熟悉:

12864与24C04的原理和使用方法——以电子密码锁为例(下篇)_第1张图片
可以看到,停止的表达方法为:SCL(时钟总线)保持高电平期间,SDA(数据总线)由低电平向高电平变化产生的一个上升沿。也就是说,当我的数据发送完毕后想要停止IIC,就需要先把SDA拉为低电平,SCL拉为高电平,然后再把SDA从低电平拉为高电平。

当然,IIC的操作远不止这两种,大家可以百度“IIC时序图详解”,查看相关的操作,这里不再做赘述。

24c04的原理介绍

关于24c04的基本原理方面,我提取了一下官方数据手册上比较有用的一些信息并加以整合,方便大家观看,如下:
12864与24C04的原理和使用方法——以电子密码锁为例(下篇)_第2张图片
12864与24C04的原理和使用方法——以电子密码锁为例(下篇)_第3张图片
大致翻译一下,意思是24c04分2大页,每大页16小页,每小页16字节,即总容量为512字节,每个储存空间单位都有自己的地址编号。然后这个芯片的A0口是不接的,发送的device addres(设备地址,可以认为具有标识芯片、携带操作行为与地址设定功能)的前四位固定为1010,之后接上A2、A1(这里直接取0),A0位被取消并用P0取代,用于辨别当前操作的是在哪个大页。也就是说,24c04每次最多支持连续16字节的写入,即一小页。后来实测时感觉有点问题,写满了8字节后指针似乎就翻转到开始处了,后8个字节会覆盖掉前8个字节,所以程序里还是只连续写了8个字节。

了解了这些,就可以阅读下面有关24c04的使用方法了。

24c04的使用方法

按例,我会把自己的代码放出来,然后在比较重要的地方做一下注释。这里的代码是经过本人实测可以使用的,所以请大家放心阅读。在这个C文件中,因为涉及到了IIC的知识,所以大家需要了解一下写入的原理,但是并不难,大家只要理解了上面我举的例子就很容易用起来:

#include 
#include 

#define DeviceAdress_WriteMode_Page1 0xA0
#define DeviceAdress_WriteMode_Page2 0xA2
#define DeviceAdress_ReadMode_Page1 0xA1
#define DeviceAdress_ReadMode_Page2 0xA3
#define I2Cdelay() {_nop_();_nop_();_nop_();_nop_();}

sbit SDA = P1^0;
sbit SCL = P1^1;//这里的端口可以根据实际需要修改

void I2CStart(void)//IIC启动,不明白的话可以结合上面给出的时序图分析
{
	SDA=1; 	
	SCL=1;     
	I2Cdelay();	
	SDA=0;         
	I2Cdelay();
	SCL=0;
}

void I2CStop(void)//IIC停止
{
	SCL=0; 
	SDA=0; 
 	I2Cdelay();
 	SCL=1;
 	I2Cdelay();
 	SDA=1;
 	I2Cdelay();
}

bit I2CWrite(unsigned char dat)//IIC总线写操作,返回一个从机应答值(用于判断写入是否成功)
{
 	bit ack;
 	unsigned char mask;//用来探测某一位的掩码变量

	for(mask=0x80;mask!=0;mask>>=1)
	{
  		if((mask&dat)==0)//该位的值输出到SDA上
   			SDA = 0;
  		else
   			SDA = 1;
  		I2Cdelay();
  		SCL = 1;
  		I2Cdelay();
  		SCL = 0;//完成一个位的周期
 	}
 	SDA = 1;//一个字节发完后,释放SDA,用于检测从机应答
 	I2Cdelay();
 	SCL = 1;
 	ack = SDA;//此时的SDA即为从机应答值
 	I2Cdelay();
 	SCL = 0;
 	return(~ack);
}

unsigned char I2CReadnak()//IIC总线读操作,发送非应答信号,大致原理与写操作类似
{
	unsigned char mask,dat;
	SDA = 1;
 
 	for(mask=0x80;mask!=0;mask>>=1)
 	{
  		I2Cdelay();
  		SCL = 1;
  		if(SDA == 0)
   			dat &= ~mask;
  		else
   			dat |= mask;
  		I2Cdelay();
  		SCL = 0;
 	}
 	SDA = 1;
 	I2Cdelay();
 	SCL = 1;
 	I2Cdelay();
 	SCL = 0;
 	return dat;
}

unsigned char I2CReadack()//IIC总线读操作,发送应答信号
{
	unsigned char mask,dat;
	SDA = 1;
 
 	for(mask=0x80;mask!=0;mask>>=1)
 	{
  		I2Cdelay();
  		SCL = 1;
  		if(SDA == 0)
   			dat &= ~mask;
  		else
   			dat |= mask;
  		I2Cdelay();
  		SCL = 0;
 	}
 	SDA = 0;
 	I2Cdelay();
 	SCL = 1;
 	I2Cdelay();
 	SCL = 0;
 	return dat;
}

void Read(unsigned char *buf,unsigned char addr,unsigned char len)//buf-数据接收指针,addr-C04中写入数据的起始地址,len-读取长度
{
	do{//查询当前是否能够进行读写操作
		I2CStart();
  		if(I2CWrite(DeviceAdress_WriteMode_Page1))//应答则跳出循环,否则进行下一次查询
   			break;
  		I2CStop();
 	  }while(1);
 	I2CWrite(addr);
 	I2CStart();
 	I2CWrite(DeviceAdress_ReadMode_Page1);
 	while(len>1)
 	{
 		*buf++ = I2CReadack();
  		len--;
 	}
 	*buf = I2CReadnak();
 	I2CStop();
}

void Write(unsigned char *buf,unsigned char addr,unsigned char len)//buf-源数据指针,addr-C04中的起始地址,len-写入长度
{
	while(len--)
	{
  		do{
   			I2CStart();
   			if(I2CWrite(DeviceAdress_WriteMode_Page1))
   			{
    				break;
   			}
   			I2CStop();
  		}while(1);
  		I2CWrite(addr++);
  		I2CWrite(*buf++);
  		I2CStop();
 	}
}

可以看到,在操作函数中,首先是IIC的起始信号,接着不管是读还是写操作,都要先将device adress的0位设为写,这样是为了写入要发送数据的存储位置或读取的数据地址,这之后,如果是写操作,就可以直接写数据了,不用更改模式;如果是读操作,则需要再IIC启动一次,并在这次中将device adress的0位设为读,之后再进行读取。操作完毕后,IIC停止即可。

因为这一次所提供的函数可以直接调取使用,没有什么特殊的注意事项或者难理解的地方,所以就不再给出实际应用的例子了。希望这篇文章能够帮助到有困扰的朋友。

你可能感兴趣的:(51及常用外设)