hour--; delay1ms(300); if(hour==0) hour=23; while(RED==0); } }
timevalue=(((hour)/10)<<4|(hour)); WriteSet1302(0x84,timevalue); delay1ms(1); display_Hour(hour); Write_Address(0x43); delay1ms(5); if(hour>=24) hour=0; delay1ms(5); if(CANL==0) {
mode=0;
Write_com(0x0c); break; }
if(SET==0) break; }
WriteSet1302(0x8e,0x80);
46
}
void minuteset(void) //调分 {
unsigned char timevalue,minute; delay1ms(500);
WriteSet1302(0x8e,0x00); timevalue=ReadSet1302(0x83);
minute=(((timevalue&0x70)>>4)*10+(timevalue&0x0f)); while(1) {
if(ADD==0) {
delay1ms(10); if(ADD==0) {
minute++;
if(minute>=60) timevalue=0; while(ADD==0); } }
if(RED==0) {
delay1ms(10);
47
if(RED==0) {
minute--; delay1ms(300);
if(minute==0) minute=59; while(RED==0); } }
timevalue=((minute/10)<<4|(minute)); WriteSet1302(0x82,timevalue); delay1ms(1);
display_Minute(minute); Write_Address(0x46); delay1ms(5);
if(minute>=60) minute=0; delay1ms(5); if(CANL==0) {
mode=0;
Write_com(0x0c); break; }
if(SET==0) break;
48
}
WriteSet1302(0x8e,0x80); }
void secondset(void) //秒归零 {
unsigned char second; delay1ms(500);
WriteSet1302(0x8e,0x00); while(1) {
if(ADD==0) {
delay1ms(10); if(ADD==0) {
second=0;
WriteSet1302(0x80,0x00); while(ADD==0); } }
delay1ms(1);
display_Second(second); Write_Address(0x49);
49
delay1ms(5); if(CANL==0) {
mode=0;
Write_com(0x0c); break; }
if(SET==0) break; }
WriteSet1302(0x8e,0x80); }
void yearset(void) //调年 {
unsigned char datevalue,year; delay1ms(500);
WriteSet1302(0x8e,0x00); datevalue=ReadSet1302(0x8d);
year=(((datevalue&0x70)>>4)*10+(datevalue&0x0f)); while(1) {
if(ADD==0) {
delay1ms(10);
50
目录
一 设计任务与要求 …………………………………………………………… …………3
二 核心芯片功能介绍…………………………………………………………………… 3
2.1 AT89S52 主要性能………………………………………………………………. 4 2.2 AT89S52的引脚及功能…………………………………………………………4
2.3 DS1302时钟电路 …………………………………………………………………6
三 电路与程序设计………………………………………………………….…………… 12
3.1时间程序的设计………………………………………………………..………….12
3.2时间调整程序设计………………………………………………………………..12 3.3
手动和自动控制小型电动机设计…………..………………………………14
3.4 高温报警设计 ??????????????????????????14 3.5 闹钟设计 ??????????????????????????14 3.6
调试部分 ………………………………………………………………………...15
3.7 Protues仿真图及程序 ………………………………………………………… 16
Protues
总体仿真图 …………………………………………………………… 16
3.7.1 欢迎界面 …………………………………………………………… 16
3.7.2 主界面 …………………………………………………………… 17 3.7.3 温度显示界面…………………………………………………………… 17 3.7.4 温度设置界面…………………………………………………………… 17
1
3.7.5 闹钟设置界面…………………………………………………………… 18
四 结果分析 …………………………………………………………………………..……… 18
五 功能完善与扩展 ………………………………………………………………..……… 19
六 总结与体会 ………………………………………………………………………………..19 七 参考文献… …………………………………………………………………………………19
2
二 、核心芯片功能介绍
2.1、 AT89S52具有下列主要性能[5]:
·8KB可改编程序Flash存储器(可经受1000次的写入/擦除周期) ·全静态工作:0Hz~24MHz ·三级程序存储器保密 ·128×8字节内部RAM ·32条可编程I/O线
·2个16位定时器/计数器 ·6个中断源
·可编程串行通道 ·片内时钟振荡器 2.2、AT89S52的引脚及功能
AT89S52单片机的管脚说明如图4-2所示。
1234567891011121314151617181920P1.0P1.1P1.2P1.3P1.4P1.5P1.6P1.7RSTP3.0(RXD)P3.1(TXD)P3.2(INT0)P3.3(INT1)P3.4(T0)P3.5(T1)P3.6(WR)P3.7(RD)XTAL2XTAL1GNDVCCP0.0(AD0)P0.1(AD1)P0.2(AD2)P0.3(AD3)P0.4(AD4)P0.5(AD5)P0.6(AD6)P0.7(AD7)EA/VPPALE/PROGPSENP2.7(A15)P2.6(A14)P2.5(A13)P2.4(A12)P2.3(A11)P2.2(A10)P2.1(A9)P2.0(A8)4039383736353433323130292827262524232221
图4-2 AT89S52的管脚
(1)、 主要电源引脚 ①VCC 电源端 ②GND 接地端
(2)、 外接晶体引脚XTAL1和XTAL2
①XTAL1 接外部晶体的一个引脚。在单片机内部,它是构成片内振荡器的反相放大器的输入端。当采用外部振荡器时,该引脚接收振荡器的信号,既把此信号直接接到内部时钟发生器的输入端。
3
②XTAL2 接外部晶体的另一个引脚。在单片机内部,它是上述振荡器的反相放大器的输出端。采用外部振荡器时,此引脚应悬浮不连接。
(3)、 控制或与其它电源复用引脚RST、ALE//PROG、/PSEN和/EA/VPP
①RST 复位输入端。 当振荡器运行时,在该引脚上出现两个机器周期的高电平将使单片机复位。
②ALE//PROG 当访问外部存储器时,ALE(地址锁存允许)的输出用于锁存地址的低位字节。即使不访问外部存储器,ALE端仍以不变的频率(此频率为振荡器频率的1/6)周期性地出现正脉冲信号。因此,它可用作对外输出的时钟,或用于定时目的。然而要注意的是:每当访问外部数据存储器时,将跳过一个ALE脉冲。在对Flash存储器编程期间,该引脚还用于输入编程脉冲(/PROG)[6]。 ③/PSEN 程序存储允许(/PSEN)输出是外部程序存储器的读选通信号。当AT89S52/LV52由外部程序存储器取指令(或常数)时,每个机器周期两次/PSEN有效(既输出2个脉冲)。但在此期间内,每当访问外部数据存储器时,这两次有效的/PSEN信号将不出现。
④/EA/VPP 外部访问允许端。要使CPU只访问外部程序存储器(地址为0000H~FFFFH),则/EA端必须保持低电平(接到GND端)。当/EA端保持高电平(接VSS端)时,CPU则执行内部程序存储器中的程序。
(4)、 输入/输出引脚 P0.0~ P0.7、P1.0~P1.7、P2.0~ P2.7 和P3.0~P3.7 ①P0端口(P0.0~ P0.7) P0是一个8位漏极开路型双向I/O端口。作为输出口用时,每位能以吸收电流的方式驱动8个TTL输入,对端口写1时,又可作高阻抗输入端用。
②P1端口(P1.0~ P1.7) P1是一个带有内部上拉电阻的8位双向I/O端口。P1的输出缓冲器可驱动(吸收或输出电流方式)4个TTL输入。对端口写1时,通过内部的上拉电阻把端口拉到高电位,这时可用作输入口。作输入口时,因为有内部的上拉电阻,那些被外部信号拉低的引脚会输出一个电流。 ③P2端口 (P2.0~P2.7) P2是一个带有内部上拉电阻的8位双向I/O端口。P2的输出缓冲器可驱动(吸收或输出电流方式)4个TTL输入。对端口写1时,通过内部的上拉电阻把端口拉到高电位,这时可用作输入口。P2作输入口使用时,因为有内部的上拉电阻,那些被外部信号拉低的引脚会输出一个电流。
④P3端口(P3.0~P3.7) P3口管脚是8个带内部上拉电阻的双向I/O口,可接收输出4个TTL门电流。当P3口写入“1”后,它们被内部上拉为高电平,并用作输入。作为输入,由于外部下拉为低电平,P3口将输出电流,这是由于上拉的缘故。
P3口也可作为AT89S52的一些特殊功能,这些特殊功能见表4-1[7]。
表4-1 P3端口的特殊功能 端口引脚
兼 用 功 能 4
P3.0 P3.1 P3.2 P3.3 P3.4 P3.5 P3.6 P3.7 RXD (串行输入口) TXD (串行输出口) /INT0 (外部中断0) /INT1 (外部中断1) T0 ( 定时器0的外部输入) T1 (定时器1的外部输入) /WR (外部数据存储器写选通) /RD (外部数据存储器读选通)
2.3 、DS1302芯片介绍
低功耗时钟芯片DS1302可以对年、月、日、时、分、秒进行计时,且具有闰年补偿等多种功能。DS1302用于数据记录,特别是对某些具有特殊意义的数据点的记录上,能实现数据与出现该数据的时间同时记录。这种记录对长时间的连续测控系统结果的分析以及对异常数据出现的原因的查找有重要意义[8]。
采用DS1302作为记录测控系统中的数据记录,其软硬件设计简单,时间记录准确,既避免了连续记录的大工作量,又避免了定时记录的盲目性,给连续长时间的测量、控制系统的正常运行及检查都来了很大的方便,可广泛应用于长时间连续的测控系统中。在测量控制系统中,特别是长时间无人职守的测控系统中,经常需要记录某些具有特殊意义的数据及其出现的时间。记录及分析这些特殊意义的数据,对测控系统的性能分析及正常运行具有重要的意义。传统的数据记录方式是隔时采样或定时采样,没有具体的时间记录,因此只能记录数据而无法准确记录其出现的时间;若采用单片机计时,一方面需要采用计数器,占用硬件资源,另一方面需要设置中断、查询等,同样耗费单片机的资源,而且某些测控系统可能不允许。而在系统中采用DS1302则能很好地解决这个问题。 (1) 、DS1302的性能特性
·实时时钟,可对秒、分、时、日、周、月以及带闰年补偿的年进行计数; ·用于高速数据暂存的31×8位RAM; ·最少引脚的串行I/O;
·2.5~5.5V 电压工作范围; ·2.5V时耗电小于300nA;
·用于时钟或RAM数据读/写的单字节或多字节(脉冲方式)数据传送方式; ·简单的3线接口;
·可选的慢速充电(至VCC1)的能力。
DS1302时钟芯片包括实时时钟/日历和31字节的静态RAM。它经过一个简单的串行接口与微处理器通信。实时时钟/日历提供秒、分、时、日、周、月和年等
5
void display_houra(unsigned char x) //闹钟小时部分显示 {
unsigned char i,j; i=x/10; j=x;
Write_Address(0x44); Write_Date(digit[i]); Write_Date(digit[j]); }
void display_minutea(unsigned char x)//闹钟分钟部分显示 {
unsigned char i,j; i=x/10; j=x;
Write_Address(0x47); Write_Date(digit[i]); Write_Date(digit[j]); }
void display_Time(void) //显示实时时间 {
unsigned char value,day,month,year; Write_com(0x0c); value=ReadSet1302(0x81);
41
seconds=(((value&0x70)>>4)*10+(value&0x0f)); display_Second(seconds); value=ReadSet1302(0x83);
minutes=(((value&0x70)>>4)*10+(value&0x0f)); display_Minute(minutes); value=ReadSet1302(0x85);
hours=(((value&0x70)>>4)*10+(value&0x0f)); display_Hour(hours); value=ReadSet1302(0x87);
day=(((value&0x70)>>4)*10+(value&0x0f)); display_Day(day); value=ReadSet1302(0x89);
month=(((value&0x70)>>4)*10+(value&0x0f)); display_Month(month); value=ReadSet1302(0x8D);
year=(((value&0xf0)>>4)*10+(value&0x0f)); display_Year(year); }
void displaystar(void) //显示欢迎界面 {
unsigned char i,j; Write_Address(0x0f);
while(displaywelcome[i]!='\\0')
42
{
Write_Date(displaywelcome[i]); i++; delay1ms(1); } i=0;
Write_Address(0x4f); while(displaywish[i]!='\\0') {
Write_Date(displaywish[i]); i++; delay1ms(1); } j=0;
while(++j<=30) {
Write_com(0x18);//循环左移 delay1ms(700); }
Write_com(0x01); delay1ms(10); }
void gbdisplay(unsigned char address) //
时间调整时光标闪烁43
{
Write_Address(address); delay1ms(5); Write_com(0x0f); delay1ms(5); }
void displaymaxt(unsigned char x) //显示最大温度 {
unsigned char i,j,k; Write_com(0x0c); delay1ms(2); Write_Address(0x44); i=x/100; j=x/10; k=x;
Write_Date(digit[i]); Write_Date(digit[j]); Write_Date(digit[k]); }
/*****************时间调整部分*********************/ void hourset(void) //调时 {
unsigned char timevalue,hour;
44
delay1ms(500); //防止多次触发
WriteSet1302(0x8e,0x00);//将写保护去掉,确保能正常将调整后的数值写入DS1302
timevalue=ReadSet1302(0x85); //读取此时的数值 hour=(((timevalue&0x70)>>4)*10+(timevalue&0x0f)); while(1) {
if(ADD==0) {
delay1ms(50); if(ADD==0) {
hour++; delay1ms(300); while(ADD==0); } }
if(RED==0) {
delay1ms(50); if(RED==0) {
45
delaynus(2); for(i=0;i<8;i++) {
date>>=1; if(DATE==1) date|=0x80; SCLK=1; delaynus(2); SCLK=0; delaynus(2); }
return date; }
unsigned char ReadSet1302(unsigned char cmd)//根据命令读取1302相应的值 {
unsigned char date; REST=0; SCLK=0; REST=1; Write1302(cmd); delaynus(2); date=Read1302();
31
SCLK=1; REST=0; return date; }
void IntDS1302(void) //DS1302初始化 {
unsigned char flag;
flag= ReadSet1302(0x81);
if(flag&0x80) { //判断时钟芯片是否关闭
WriteSet1302(0x8E,0x00); //根据写状态寄存器命令字,写入不保护指令
WriteSet1302(0x80,((0/10)<<4|(0))); //根据写秒寄存器命令字,写入秒的初始值
WriteSet1302(0x82,((0/10)<<4|(0))); //根据写分寄存器命令字,写入分的初始值
WriteSet1302(0x84,((0/10)<<4|(0))); //根据写小时寄存器命令字,写入小时的初始值
WriteSet1302(0x86,((0/10)<<4|(0))); //根据写日寄存器命令字,写入日的初始值
WriteSet1302(0x88,((0/10)<<4|(0))); //根据写月寄存器命令字,写入月的初始值
WriteSet1302(0x8c,((10/10)<<4|(10))); //根据写年寄存器命令字,写
32
入年的初始值
WriteSet1302(0x90,0xa5); //打开充电功能 选择2K电阻充电方式
WriteSet1302(0x8E,0x80); //根据写状态寄存器命令字,写入保护指令 } }
/***************液晶显示模块*****************/
sbit E=P2^7; sbit BF=P0^7; /*液晶忙检测*/ bit BusyTest(void) {
bit result; RS=0; RW=1; E=1; _nop_(); _nop_(); _nop_(); result=BF; _nop_();
33
_nop_(); _nop_(); E=0;
return result; }
/*写指令*/
void Write_com(unsigned char command) {
while(BusyTest()!=0); RS=0; RW=0; E=0; _nop_(); _nop_(); _nop_(); P0=command; _nop_(); _nop_(); _nop_(); E=1; _nop_(); _nop_(); _nop_();
34
E=0; }
/*写地址*/
void Write_Address(unsigned char address) {
Write_com(address|0x80); delay1ms(1); } /*写数据*/
void Write_Date(unsigned char date) { RS=1; RW=0; E=0; _nop_(); _nop_(); _nop_(); P0=date; _nop_(); _nop_(); _nop_(); E=1; _nop_();
35
_nop_(); _nop_(); E=0; delay1ms(1); }
/*初始化*/ void Lcd_Int(void) {
Write_com(0x38); delay1ms(1); Write_com(0x38); delay1ms(1); Write_com(0x06); delay1ms(1); Write_com(0x0c); delay1ms(1);
Write_com(0x01); }
void displaymainpart(void)//显示液晶主要部分(不变化部分){
36
Write_Address(0x01); delay1ms(1); Write_Date('D'); Write_Date('A'); Write_Date('T'); Write_Date('E'); Write_Date(':'); delay1ms(1); Write_Address(0x0a); delay1ms(1); Write_Date('-'); Write_Address(0x0d); Write_Date('-'); Write_Address(0x42); /*Write_Date('T'); Write_Date('I'); Write_Date('M'); Write_Date('E'); Write_Date(':');*/ Write_Address(0x44);
37
Write_Date('-'); Write_Address(0x47); Write_Date('-'); Write_Address(0x06); Write_Date('2'); Write_Date('0'); }
void display_Second(unsigned char second) //{
unsigned char i,j; i=second/10; j=second; Write_Address(0x48); Write_Date(digit[i]); Write_Date(digit[j]); delay1ms(1); }
void display_Minute(unsigned char minute) //{
unsigned char i,j; i=minute/10; j=minute; Write_Address(0x45);
在液晶上显示秒在液晶上显示分38
Write_Date(digit[i]); Write_Date(digit[j]); delay1ms(1); }
void display_Hour(unsigned char hour) //在液晶上显示时 {
unsigned char i,j; i=hour/10; j=hour;
Write_Address(0x42); Write_Date(digit[i]); Write_Date(digit[j]); delay1ms(1); }
void display_Day(unsigned char day) //{
unsigned char i,j; i=day/10; j=day;
Write_Address(0x0e); Write_Date(digit[i]); Write_Date(digit[j]); delay1ms(1);
在液晶上显示日 39
}
void display_Month(unsigned char month) //在液晶上显示月 {
unsigned char i,j; i=month/10; j=month;
Write_Address(0x0b); Write_Date(digit[i]); Write_Date(digit[j]); delay1ms(1); }
void display_Year(unsigned char year) //{
unsigned char i,j; i=year/10; j=year;
Write_Address(0x08); Write_Date(digit[i]); Write_Date(digit[j]); delay1ms(1); }
在液晶上显示年 40