基于ds1302的电子日历系统在硬件选择上主要采用AT89S51作为主控制系统;DS1302提供时钟;DS18B20作为数字式温度传感器;LCD1602液晶屏作为显示。
AT89C51单片机
单片机最小系统
AT89C51单片机采用Flash ROM,内部具有4KB ROM存储空间,相对于本设计而言程序空间完全够用。能于3V的超低压工作,而且与MCS-51系列单片机完全兼容,而且运用于电路设计中时具备ISP在线编程技术,当在对电路进行调试时,由于程序的错误修改或对程序的新增功能需要烧入程序时,避免芯片的多次拔插对芯片造成的损坏。
ds1302
DS1302与单片机的连接
DS130是美国DALLAS公司推出的一种高性能、低功耗、带RAM的实时时钟电路,它可以对年、月、日、周日、时、分、秒进行计时,具有闰年补偿功能,工作电压为2.5V~5.5V。采用三线接口与CPU进行同步通信,并可采用突发方式一次传送多个字节的时钟信号或RAM数据。DS1302内部有一个31&TImes;8的用于临时性存放数据的RAM寄存器。DS1302是DS1202的升级产品,与DS1202兼容,但增加了主电源/后背电源双电源引脚,同时提供了对后背电源进行涓细电流充电的能力。主要特点是采用串行数据传输,可为掉电保护电源提供可编程的充电功能,并且可以关闭充电功能。采用普通32.768kHz晶振。而且DS1302有使用寿命长,误差小等优点。
1602LCM点阵液晶显示屏
LCM1602与单片机的连接
LCD液晶显示屏,液晶显示屏的显示功能强大,可显示大量文字,图形,显示多样,清晰可见,对于电子万年历而言,一个1602的液晶屏即可,价格也还能接受,需要的接口线较多,但会给调试带来诸多方便,
数字式温度传感器DS18B20
DS18B20管脚连线
此类传感器为数字式传感器而且仅需要一条数据线进行数据传输,易于与单片机连接,可以避免A/D模数转换模块,降低硬件成本,简化系统电路。另外,数字式温度传感器还具有测量精度高、测量范围广等优点。
DS1302原理及说明
(1) 时钟芯片DS1302的工作原理
DS1302在每次进行读、写程序前都必须初始化,先把SCLK端置 “0”,接着把RST端置“1”,最后才给予SCLK脉冲;读/写时序如下图7所示。图6为DS1302的控制字,此控制字的位7必须置1,若为0则不能把对DS1302进行读写数据。对于位6,若对程序进行读/写时RAM=1,对时间进行读/写时,CK=0。位1至位5指操作单元的地址。位0是读/写操作位,进行读操作时,该位为1;该位为0则表示进行的是写操作。控制字节总是从最低位开始输入/输出的。表2为DS1302的日历、时间寄存器内容:“CH”是时钟暂停标志位,当该位为1时,时钟振荡器停止,DS1302处于低功耗状态;当该位为0时,时钟开始运行。“WP”是写保护位,在任何的对时钟和RAM的写操作之前,WP必须为0。当“WP”为1时,写保护位防止对任一寄存器的写操作。
(2) DS1302的控制字
DS1302的控制字如图6所示。控制字节的高有效位(位7)必须是逻辑1,如果它为0,则不能把数据写入DS1302中,位6如果0,则表示存取日历时钟数据,为1表示存取RAM数据;位5至位1指示操作单元的地址;最低有效位(位0)如为0表示要进行写操作,为1表示进行读操作,控制字节总是从最低位开始输出。
DS1302的控制字
(3) 数据输入输出
在控制指令字输入后的下一个SCLK时钟的上升沿时,数据被写入DS1302,数据输入从低位即位0开始。同样,在紧跟8位的控制指令字后的下一个SCLK脉冲的下降沿读出DS1302的数据,读出数据时从低位0位到高位7。
DS1302读与写的时序图
(4) DS1302的寄存器
DS1302有12个寄存器,其中有7个寄存器与日历、时钟相关,存放的数据位为BCD码形式,其日历、时间寄存器及其控制字见图。
DS1302的日历、时间寄存器
此外,DS1302 还有年份寄存器、控制寄存器、充电寄存器、时钟突发寄存器及与RAM相关的寄存器等。时钟突发寄存器可一次性顺序读写除充电寄存器外的所有寄存器内容。 DS1302与RAM相关的寄存器分为两类:一类是单个RAM单元,共31个,每个单元组态为一个8位的字节,其命令控制字为C0H~FDH,其中奇数为读操作,偶数为写操作;另一类为突发方式下的RAM寄存器,此方式下可一次性读写所有的RAM的31个字节,命令控制字为FEH(写)、FFH(读)。
系统设计
主程序流程图
时间调整程序流程图
主程序:
#include《reg51.h》
#define uint unsigned int
#define uchar unsigned char
uchar a,miao,shi,fen,ri,yue,nian,week,key1n,temp;
#define yh 0x80
#define er 0x80+0x40
sbit DQ = P3^5;
sbit RS=P1^7;
sbit E=P2^4;
sbit RW=P1^6;
sbit IO=P1^5;
sbit SCLK=P1^4;
sbit RST=P2^2;
sbit ACC0=ACC^0;
sbit ACC7=ACC^7;
sbit key1=P1^0;
sbit key2=P1^2;
sbit key3=P1^3;
uchar code tab1[]={“20 - - ”};
uchar code tab2[]={“ : : ”};
void delay(uint xms)
{
uint x,y;
for(x=xms;x》0;x--) ;
for(y=110;y》0;y--);
}
//液晶写入指令函数
write_1602com(uchar com)
{
RS=0;
RW=0;
P0=com;
delay(1);
E=1;
delay(1);
E=0;
}
//液晶写入数据函数
write_1602dat(uchar dat)
{
RS=1;
RW=0;
P0=dat;
delay(1);
E=1;
delay(1);
E=0;
}
//液晶初始化函数
lcd_init()
{
write_1602com(0x38);
write_1602com(0x0c);
write_1602com(0x06);
write_1602com(0x01);
write_1602com(yh+1);
for(a=0;a《14;a++)
{
write_1602dat(tab1[a]);
delay(3);
}
write_1602com(er+2);
for(a=0;a《8;a++)
{
write_1602dat(tab2[a]);
delay(3);
}
}
/***************DS1302有关子函数********************/
//写一个字节
void write_byte(uchar dat)
{
ACC=dat;
RST=1;
for(a=8;a》0;a--)
{
IO=ACC0;
SCLK=0;
SCLK=1;
ACC=ACC》》1;
}
}
//读一个字节
uchar read_byte()
{
RST=1;
for(a=8;a》0;a--)
{
ACC7=IO;
SCLK=1;
SCLK=0;
ACC=ACC》》1;
}
return (ACC);
}
//向1302芯片写函数,指定写入地址,数据
void write_1302(uchar add,uchar dat)
{
RST=0;
SCLK=0;
RST=1;
write_byte(add);
write_byte(dat);
SCLK=1;
RST=0;
}
//从1302读数据函数,指定读取数据来源地址
uchar read_1302(uchar add)
{
uchar temp;
RST=0;
SCLK=0;
RST=1;
write_byte(add);
temp=read_byte(); SCLK=1;
RST=0; return(temp);
}
//BCD码转十进制函数,输入BCD,返回十进制
uchar BCD_Decimal(uchar bcd)
{
uchar Decimal;
Decimal=bcd》》4;
return(Decimal=Decimal*10+(bcd&=0x0F));
}
//1302芯片初始化子函数(2012-06-26,00:00:00,week2)
void ds1302_init()
{
RST=0;
SCLK=0;
write_1302(0x8e,0x00);
write_1302(0x80,0x00);
write_1302(0x82,0x00);
write_1302(0x84,0x00);
write_1302(0x8a,0x02);
write_1302(0x86,0x26);
write_1302(0x88,0x06);
write_1302(0x8c,0x12);
write_1302(0x8e,0x80);
}
//时分秒显示子函数
void write_sfm(uchar add,uchar dat)
{
uchar gw,sw;
gw=dat%10;
sw=dat/10;
write_1602com(er+add);
write_1602dat(0x30+sw);
write_1602dat(0x30+gw);
}
//年月日显示子函数
void write_nyr(uchar add,uchar dat)
{
uchar gw,sw;
gw=dat%10;
sw=dat/10;
write_1602com(yh+add);
write_1602dat(0x30+sw);
write_1602dat(0x30+gw);
}
//写星期函数
void write_week(uchar week)
{
write_1602com(yh+0x0c);
switch(week
{
case 1:
write_1602dat(‘M’);
write_1602dat(‘O’);
write_1602dat(‘N’);
break;
case 2:
write_1602dat(‘T’);
write_1602dat(‘U’);
write_1602dat(‘E’);
break;
case 3:
write_1602dat(‘W’);
write_1602dat(‘E’);
write_1602dat(‘D’);
break;
case 4:
write_1602dat(‘T’
write_1602dat(‘H’);
write_1602dat(‘U’);
break;
case 5:
write_1602dat(‘F’);
write_1602dat(‘R’);
write_1602dat(‘I’);
break;
case 6:
write_1602dat(‘S’);
write_1602dat(‘T’);
write_1602dat(‘A’);
break;
case 7:
write_1602dat(‘S’);
write_1602dat(‘U’);
write_1602dat(‘N’);
break;
}
}
//*键盘扫描有关函数 void keyscan()
{
if(key1==0)
{
delay(9);
if(key1==0)
{
delay(20);
while(!key1);
key1n++;
if(key1n==9) key1n=1;
switch(key1n)
{
case 1:
TR0=0;
write_1602com(er+0x09);
write_1602com(0x0f);
temp=(miao)/10*16+(miao)%10;
write_1302(0x8e,0x00);
write_1302(0x80,0x80|temp);
write_1302(0x8e,0x80);
break; case 2: write_1602com(er+6);
break;
case 3:
write_1602com(er+3);
break;
case 4:
write_1602com(yh+0x0e);
break;
case 5:
write_1602com(yh+0x0a);
break;
case 6:
write_1602com(yh+0x07);
break;
case 7:
write_1602com(yh+0x04);
break;
case 8:
write_1602com(0x0c);
TR0=1;
temp=(miao)/10*16+(miao)%10;
write_1302(0x8e,0x00);
write_1302(0x80,0x00|temp);
write_1302(0x8e,0x80);
break;
}
}
}
if(key1n!=0)
{
if(key2==0)
{
delay(10);
if(key2==0)
{
delay(20);
while(!key2);
switch(key1n) {
case 1:miao++;
if(miao==60)
miao=0;
write_sfm(0x08,miao);
temp=(miao)/10*16+(miao)%10;
write_1302(0x8e,0x00);
write_1302(0x80,temp);
write_1302(0x8e,0x80);
write_1602com(er+0x09);
break;
case 2:fen++;
if(fen==60)
fen=0;
write_sfm(0x05,fen);
temp=(fen)/10*16+(fen)%10;
write_1302(0x8e,0x00);
write_1302(0x82,temp);
write_1302(0x8e,0x80);
write_1602com(er+6);
break; case 3:shi++;
if(shi==24)
shi=0;
write_sfm(2,shi);
temp=(shi)/10*16+(shi)%10;
write_1302(0x8e,0x00);
write_1302(0x84,temp);
write_1302(0x8e,0x80);
write_1602com(er+3);
break; case 4:week++;
if(week==8) week=1;
write_1602com(yh+0x0C);
write_week(week);
temp=(week)/10*16+(week)%10;
write_1302(0x8e,0x00);
write_1302(0x8a,temp);
write_1302(0x8e,0x80);
write_1602com(yh+0x0e);
break; case 5:ri++;
if(ri==32)
ri=1;
write_nyr(9,ri);
temp=(ri)/10*16+(ri)%10;
write_1302(0x8e,0x00);
write_1302(0x86,temp);
write_1302(0x8e,0x80);
write_1602com(yh+10);
break;
case 6:yue++;
if(yue==13)
yue=1;
write_nyr(6,yue);
temp=(yue)/10*16+(yue)%10;
write_1302(0x8e,0x00);
write_1302(0x88,temp);
write_1302(0x8e,0x80);
write_1602com(yh+7);
break;
case 7:nian++;
if(nian==100) nian=0;
write_nyr(3,nian);
temp=(nian)/10*16+(nian)%10;
write_1302(0x8e,0x00);
write_1302(0x8c,temp);
write_1302(0x8e,0x80);
write_1602com(yh+4);
break;
}
}
}
if(key3==0)
{
delay(10);
if(key3==0)
{
delay(20);
while(!key3);
switch(key1n)
{
case 1:miao--;
if(miao==-1)
miao=59;
write_sfm(0x08,miao);
temp=(miao)/10*16+(miao)%10;
write_1302(0x8e,0x00);
write_1302(0x80,temp);
write_1302(0x8e,0x80);
write_1602com(er+0x09);
break; case 2:fen--;
if(fen==-1)
fen=59;
write_sfm(5,fen);
temp=(fen)/10*16+(fen)%10;
write_1302(0x8e,0x00);
write_1302(0x82,temp);
write_1302(0x8e,0x80);
write_1602com(er+6);
break;
case 3:shi--;
if(shi==-1)
shi=23;
write_sfm(2,shi);
temp=(shi)/10*16+(shi)%10;
write_1302(0x8e,0x00);
write_1302(0x84,temp);
write_1302(0x8e,0x80);
write_1602com(er+3);
break; case 4:week--;
if(week==0)
week=7;
write_1602com(yh+0x0C);
write_week(week);
temp=(week)/10*16+(week)%10;
write_1302(0x8e,0x00);
write_1302(0x8a,temp);
write_1302(0x8e,0x80);
write_1602com(yh+0x0e);
break; case 5:ri--;
if(ri==0)
ri=31;
write_nyr(9,ri);
temp=(ri)/10*16+(ri)%10;
write_1302(0x8e,0x00);
write_1302(0x86,temp);
write_1302(0x8e,0x80);
write_1602com(yh+10);
break; case 6:yue--;
if(yue==0)
yue=12;
write_nyr(6,yue);
temp=(yue)/10*16+(yue)%10;
write_1302(0x8e,0x00);
write_1302(0x88,temp);
write_1302(0x8e,0x80);
write_1602com(yh+7);
break;
case 7:nian--;
if(nian==-1) nian=99;
write_nyr(3,nian);
temp=(nian)/10*16+(nian)%10;
write_1302(0x8e,0x00);
write_1302(0x8c,temp);
write_1302(0x8e,0x80);
write_1602com(yh+4);
break;
}
}
}
}
}
//定时器、计数器设置函数
void init()
{
TMOD=0x11;
TH0=0;
TL0=0;
EA=1;
ET0=1;
TR0=1;
}
/******************************* 延时函数 ********************************
* 功能:在11.059MHz的晶振条件下调用本函数需要24μs ,然后每次计数需16μs
**************************************************************************/
void DS18_delay(int useconds)
{
int s;
for (s=0; s《useconds;s++);
}
/******************************* 复位函数 *******************************
* 功能:完成单总线的复位操作
* 复位时间为480μs,因此延时时间为(480-24)/16 = 28.5,取29μs。
* 经过70μs之后检测存在脉冲,因此延时时间为(70-24)/16 = 2.875,取3μs。
**************************************************************************/
unsigned char ow_reset(void)
{
unsigned char presence;
DQ = 0;
DS18_delay(29);
DQ = 1;
DS18_delay(3);
presence = DQ;
DS18_delay(25);
return(presence);
}
/****************************** 位写入函数 *******************************
* 功能:向单总线写入1位值:bitval
*************************************************************************/
void write_bit(char bitval) { DQ = 0;
if(bitval==1) DQ =1;
DS18_delay(5);
DQ = 1;
}
/**************************** 字节写入函数 *******************************
* 功能:向单总线写入一个字节值:val
*************************************************************************/
void ds18write_byte(char val)
{
unsigned char i;
unsigned char temp;
for (i=0; i《8; i++) {
temp = val》》i;
temp &= 0x01;
write_bit(temp);
}
DS18_delay(5);
}
/**************************** 位读取函数 ********************************
* 功能:从单总线上读取一位信号,所需延时时间为15μs,因此无法调用前面定义 * 的DS18_delay()函数,而采用一个for()循环来实现延时。 *
***********************************************************************/
unsigned char read_bit(void)
{
unsigned char i;
DQ = 0;
DQ = 1;
for (i=0; i《3; i++);
return(DQ);
}
/**************************** 字节读取函数 *******************************
*功能:从单总线读取一个字节的值*
*************************************************************************/
unsigned char DSread_byte(void)
{
unsigned char i; unsigned char value = 0; for (i=0;i《8;i++)
{
if(read_bit()) value|=0x01《《i;
DS18_delay(6);
}
return(value);
}
//*******************主函数**************************
void main() { lcd_init();
ds1302_init();
init();
delay(80);
while(1)
{
keyscan();
}
}
//取得并显示日历和时间
voidTImer0() interrupt 1
{
miao = BCD_Decimal(read_1302(0x81));
fen = BCD_Decimal(read_1302(0x83));
shi = BCD_Decimal(read_1302(0x85));
ri = BCD_Decimal(read_1302(0x87));
yue = BCD_Decimal(read_1302(0x89));
nian=BCD_Decimal(read_1302(0x8d));
week=BCD_Decimal(read_1302(0x8b));
write_sfm(8,miao);
write_sfm(5,fen);
write_sfm(2,shi);
write_nyr(9,ri);
write_nyr(6,yue);
write_nyr(3,nian);
write_week(week);
}