相比起学校教材所用的8031+锁存器+存储芯片的组合搭建(不过貌似这种组合只有教科书才用),8952+AT24CXX的组合已经完全够用而且可以很明确地将AT24CXX功能定位在掉电数据存储。
自己在进行电子钟的编程中,将AT24C02作为了闹钟定时保存的存储,因为操作方便,很适合作为程序附加功能的拓展,比如电子密码储存部件等等。对于没接触过的人来说,唯一头痛的就是I^2C
总线的软件模拟编程,虽然只有SCL和SDA两条通讯线以及高低电平,上下沿几种状态加以组合,但是单调得难以理解,尤其延时应该是多少,应答怎么实现这些问题都很困扰。I^2C总线是AT24CXX硬件自带,而常见8952不自带的,所以进行交互通讯时,需要在8952上运行软件模拟。相关的原理解释网上很多,这里建立在理解了原理的基础上,进行程序分析:在保证程序能正常工作的前提下,进行了延时最短处理
//24C02的初始化
void c02_init()
{
scl=1;
sda=1;
}
//开启I^C总线
void start()
{
sda=1;
nop();
scl=1;
nop();
sda=0;
nop();
scl=0;
nop();
}
//停止I^2C总线
void stop()
{
sda=0;
nop();
scl=1;
nop();
sda=1;
nop();
}
//前面总线开关操作和初始化好理解,看着说明书的原理波形就能够写出
//发送8位数据
//之前还真不知道数据是这样一位位赋值传递的
void s_byte(uchar s_dat)
{
uchar i;
for(i=0;i<8;i++)
{
s_dat<=1;//最高位再左移就会写入到 CY
scl=0;
nop();
sda=CY;//如此类推将每次左移读CY值,直到读完8位
scl=1;//SCL=1,保留数据。SCL=0,改变数据。
nop();
}
scl=0;
nop();
sda=1;
nop();
}
//读取8位数据
uchar r_byte()
{
uchar i,k=0;
for(i=0;i<8;i++)
{
scl=1;
nop();
k<=1;
if(sda)
{
k++;
}
scl=0;
nop();
}
return k;
}
//从件应答
void ack()
{
uchar i;
scl=1;
nop();
scl=0;
//参考了很多程序还是这个好理解,有应答时SDA==0跳出,没有应答等待i累加完毕也退出。
//但是应答这样用貌似没有什么意义。在无应答时,是不是该转入其他操作?
while((sda==1)&&i<(200))i++;
scl=0;
nop();
}
//写入函数
void c02_write(uchar w_add,uchar w_dat)
{
start();
s_byte(0xa0);
ack();
s_byte(w_add);
ack();
s_byte(w_dat);
ack();
stop();
delay(5);
}
//读取函数,这个是任意地址的读取函数。包含了当前地址读取的操作
uchar c02_read(uchar r_add)
{
start();
//这里要注意,向从件写入一个地址(r_add),让接收器件自己比对是否相同.故先送一个写入驱动(0xa0)
s_byte(0xa0);
ack();
s_byte(r_add);
ack();
//进行读写入驱动前,都要重新开启总线 start();
start();
//确定了地址之后,才进行真正的写入操作。
s_byte(0xa1);
ack();
return r_byte();
stop();
delay(10);
}
其中应答很多人说可有可无,假如EEPROM是作为实现单一功能的主要元件,比如密码锁的存储器,必须做到功能的尽善尽美,我觉得是要的。假如像我一样作为电子钟许多附加程序的其中一个,为了节省运算时间,无奈地不应答也能接受吧?吧?吧?不知道哩……
最后使用write(w_add,w_dat);和read(r_add);就可以方便调用了。记得在这两个函数使用前先调用初始化函数。