[实验任务]
利用24C08断电以后存储的数据不消失的特点,可以做一个断电保护装置。首先利用单片机做一个0-99秒的自动计时器。然后随机关断电源,在
通电以后计时器接着断电前的状态继续计时。
[实验原理]
首先简单的说明以下I2C总线,I2C总线是一种串行数据总线,只有二根信号线,一根是双向的数据线SDA,另一根是时钟线SCL。在
I2C总线上传送的一个数据字节由八位组成。总线对每次传送的字节数没有限制,但每个字节后必须跟一位应答位。数据传送首先传送最高位(MSB),数据传送按图1所示格式进行。首先由主机发出启动信号“S”(SDA在SCL高电平期间由高电平跳变为低电平),然后由主机发送一个字节的数据。启动信号后的第一个字节数据具有特殊含义:高七位是从机的地址,第八位是传送方向位,0表示主机发送数据(写),1表示主机接收数据(读)。被寻址到的从机设备按传送方向位设置为对应工作方式。标准I2C总线的设备都有一个七位地址,所有连接在I2C总线上的设备都接收启动信号后的第一个字节,并将接收到的地址与自己的地址进行比较,如果地址相符则为主机要寻访的从机,应在第九位答时钟脉冲时向SDA线送出低电平作为应答。除了第一字节是通用呼叫地址或十位从机地址之外第二字节开始即数据字节。数据传送完毕,由主机发出停止信号“P”(SDA在SCL高电平期间由低电平跳变为高电平)。
AT24C系列串行E2PROM具有I2C总线接口功能,功耗小,淼缭吹缪?根据不同型号2.5V~6.0V),工作电流约为3mA,静态电流随电源电压不同为30μA~110μA,AT24C系列串行E2PROM参数如下
型号 容量 器件寻址字节(8位) 一次装载字节数
AT24C01 128×8 1010A2A1A0 R/W 4
AT24C02 256×8 1010A2A1A0 R/W 8
AT24C04 512×8
1010A2A1P0 R/W 16
AT24C08 1024×8 1010A2P1P0 R/W 16
AT24C16 2048×8 1010P2P1P0 R/W 16
由于I2C总线可挂接多个串行接口器件,在I2C总线中每个器件应有唯一的器件地址,按I2C总线规则,器件地址为7位数据(即一个I2C总线系统中理论上可挂接128个不同地址的器件),它和1位数据方向位构成一个器件寻址字节,最低位D0为方向位(读/写)。器件寻址字节中的最高4位(D7~D4)为器件型号地址,不同的I2C总线接口器件的型号地址是厂家给定的,如AT24C系列E2PROM的型号地址皆为1010,器件地址中的低3位为引脚地址A2
A1 A0,对应器件寻址字节中的D3、D2、D1位,在硬件设计时由连接的引脚电平给定。
对AT24C系列
E2PROM的读写操作完全遵守I2C总线的主收从发和主发从收的规则。
[C语言源程序]
#include <AT89X52.H>
#include <stdio.h>
#include <absacc.h>
unsigned char code table[] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f,};
unsigned char sec; //定义计数值,每过1秒,sec加1
unsigned int tcnt; //定时中断次数
bit write = 0; //写24C08的标志;
sbit gewei = P2 ^ 0; //个位选通定义
sbit shiwei = P2 ^ 1; //十位选通定义
/////////24C08读写驱动程序////////////////////
sbit scl = P3 ^ 4; // 24c08 SCL
sbit sda = P3 ^ 5; // 24c08 SDA
void delay1(unsigned char x)
{
unsigned int i;
for (i = 0; i < x; i++);
}
void flash()
{
;
}
void x24c08_init() //24c08初始化子程序
{
scl = 1;
flash();
sda = 1;
flash();
}
void start() //启动I2C总线
{
sda = 1;
flash();
scl = 1;
flash();
sda = 0;
flash();
scl = 0;
flash();
}
void stop() //停止I2C总线
{
sda = 0;
flash();
scl = 1;
flash();
sda = 1;
flash();
}
void writex(unsigned char j) //写一个字节
{
unsigned char i, temp;
temp = j;
for (i = 0; i < 8; i++) {
temp = temp << 1;
scl = 0;
flash();
sda = CY;
flash();
scl = 1;
flash();
}
scl = 0;
flash();
sda = 1;
flash();
}
unsigned char readx() //读一个字节
{
unsigned char i, j, k = 0;
scl = 0;
flash();
sda = 1;
for (i = 0; i < 8; i++) {
flash();
scl = 1;
flash();
if (sda == 1) {
j = 1;
}
else {
j = 0;
}
k = (k << 1) | j;
scl = 0;
}
flash();
return (k);
}
void clock() // I2C总线时钟
{
unsigned char i = 0;
scl = 1;
flash();
while ((sda == 1) && (i < 255)) {
i++;
}
scl = 0;
flash();
}
////////从24c02的地址address中读取一个字节数据/////
unsigned char x24c08_read(unsigned char address)
{
unsigned char i;
start();
writex(0xa0);
clock();
writex(address);
clock();
start();
writex(0xa1);
clock();
i = readx();
stop();
delay1(10);
return (i);
}
//////向24c02的address地址中写入一字节数据info/////
void x24c08_write(unsigned char address, unsigned char info)
{
EA = 0;
start();
writex(0xa0);
clock();
writex(address);
clock();
writex(info);
clock();
stop();
EA = 1;
delay1(50);
}
/////////////24C08读写驱动程序完/////////////////////
void Delay(unsigned int tc) //延时程序
{
while (tc != 0) {
unsigned int i;
for (i = 0; i < 100; i++);
tc--;
}
}
void LED() //LED显示函数
{
shiwei = 0;
P0 = table[sec / 10];
Delay(8);
shiwei = 1;
gewei = 0;
P0 = table[sec % 10];
Delay(5);
gewei = 1;
}
void t0(void) interrupt 1 using 0 //定时中断服务函数
{
TH0 = (65536 - 50000) / 256; //对TH0 TL0赋值
TL0 = (65536 - 50000) % 256; //重装计数初值
tcnt++; //每过250ust tcnt加一
if (tcnt == 20) { //计满20次(1秒)时
tcnt = 0; //重新再计
sec++;
write = 1; //1秒写一次24C08
if (sec == 100) { //定时100秒,在从零开始计时
sec = 0;
}
}
}
void main(void)
{
TMOD = 0x01; //定时器工作在方式1
ET0 = 1;
EA = 1;
x24c08_init(); //初始化24C08
sec = x24c08_read(2); //读出保存的数据赋于sec
TH0 = (65536 - 50000) / 256; //对TH0 TL0赋值
TL0 = (65536 - 50000) % 256; //使定时器0.05秒中断一次
TR0 = 1; //开始计时
while (1) {
LED();
if (write == 1) { //判断计时器是否计时一秒
write = 0; //清零
x24c08_write(2, sec); //在24c08的地址2中写入数据sec
}
}
}
[硬件电路图]