17798690课程设计基于单片机AT89C52电子时钟课程设计说明书.docx
《17798690课程设计基于单片机AT89C52电子时钟课程设计说明书.docx》由会员分享,可在线阅读,更多相关《17798690课程设计基于单片机AT89C52电子时钟课程设计说明书.docx(28页珍藏版)》请在冰点文库上搜索。
17798690课程设计基于单片机AT89C52电子时钟课程设计说明书
单片机课程设计报告
一、课程设计内容
1)显示时间功能,能正确显示“时”、“分”。
2)显示日期功能,能显示“月”、“日”。
3)闹钟功能,可按设定的时间闹时。
4)具有校准月、日、时、分的功能。
二、元器件介绍
本次课程设计我使用的单片机是至强51蓝精灵版,而实验中使用到的关键元器件主要有:
STC85C52RC,4*4按键,蜂鸣器,数码管等。
下面是STC85C52RC的简单介绍:
AT89C52是本设计最核心的部件,它是美国ATMEL公司生产的低电压,高性能CMOS8位单片机,片内含8Kbytes的可反复擦写的Flash只读程序存储器和256bytes的随机存取数据存储器(RAM),器件采用ATMEL公司的高密度、非易失性存储技术生产,与标准MCS-51指令系统及8052产品引脚兼容,片内置通用8位中央处理器(CPU)和Flash存储单元,功能强大。
AT89C52单片机适用于许多较为复杂的控制应用场合。
下图是AT89C52最常见的一种封装。
如上图所示,AT89C52共有40个管脚,其各个功能如下:
·VCC——运行时加+5V
·VSS——接地
·XTAL1——振荡器反相放大器及内部时钟发生器的输入端
·XTAL2——振荡器反相放大器的输出端
·RST——复位输入,高电平有效,在晶振工作时,在RST引脚上作用2个机器周期以上的高电平,将使单片机复位。
·
/VPP——片外程序存储器访问允许信号。
欲使CPU仅访问外部程序存储器(地址为0000H-FFFFH),
端必须保持低电平(接地),如果
端为高电平(接Vcc端),CPU则执行内部程序中的指令。
·LAE/
——当访问外部程序存储器或数据存储器时,ALE(地址允许锁存)输出脉冲用于锁存地址的低8位位数字节。
一般情况下,ALE仍以时钟振荡频率的1/6输出固定的脉冲信号,因此它可对外输出时钟或用于定时目的。
要注意的是:
每当访问外部数据存储器时将跳过一个ALE脉冲。
对Flash存储器编程期间,该引脚还用于输入编程脉冲(第二功能)。
·
:
程序储存允许(
)输出是外部程序存储器的读选通信号,当AT89C52
由外部存储器取指令(或数据)时,每个机器周期两次
有效,即输出两个脉冲。
在此期间,当访问外部数据存储器,将跳过两次
信号。
·P0口——是一组8位漏极开路双向I/O口,也即地址/数据总线复用口。
作为输出口用时,每位能吸收电流的方式驱动8个TTL逻辑门电路,对端口P0写“1”时,可作为高阻抗输入端用。
·P1口——是一个内部带上拉电阻的8位准双向I/O口。
在对EPROM型单片机编程和验证程序时,它接收低8位地址。
P1能驱动(吸收或输出电流)4个LSTTL电路。
P1.0还被用作定时器/计数器2的外部计数输入端,即专用功能T2。
P1.1被用作专用功能端T2EX,即定时器T2的外部控制端。
参见下表。
P1.1和P1.1的第二功能
引脚号
功能特性
P1.0
T2(定时/计数器2外部计数脉冲输入),时钟输出
P1.1
T2XE(定时/计数器2捕获/重装载触发和方向控制)
·P2口——是一个带有内部上拉电阻的8位准双向I/O口,P2的输出缓冲级可驱动(吸收或输出电流)4个TTL逻辑门电路。
对端口写“1”,通过内部的上拉电阻,某个引脚外部信号拉低是会输出一个电流。
·P3口——是一组带有内部上拉电阻的8位准双向I/O口。
P3口输出缓冲级可驱动(吸收或输出电流)4个TTL逻辑门电路。
对P3口写入“1”时,它们被内部上拉电阻拉高并可作为输入端口。
此时,被外部拉低的P3口将用上拉电阻输出电流。
P3口除了作为一般的I/O口线外,更重要的用途是它的第二功能,如下表所示:
P3口各位的第二功能
端口引脚
第二功能
P3.0
RXD(串行输入口)
P3.1
TXD(串行输出口)
P3.2
(外部中断0)
P3.3
(外部中断1)
P3.4
T0(定时/计数器0)
P3.5
T1(定时/计数器1)
P3.6
(外部数据存储器写选通)
P3.7
(外部数据存储器读选通)
三、设计方案
1)实物图
2)实验程序流程图
·主程序
voidmain()
{
init();
while
(1)
{
keysure();
keyhandle();
compare();
}
}
·初始化函数
voidinit()
{TMOD=0x01;
TH0=0xdc;
TL0=0x00;
EA=1;
ET0=1;
TR0=1;
keytemp=8;//这是为了还没有扫描按键时给keyhandle()函数处理用的//
}
实验中只是用了T0这一个定时器,也只是使用了T0这个中断。
实验中设置TMOD=0x01使T0工作于方式1。
并且由公式t=(65535-M)*6/12MHz,而t为中断溢出时间,为10ms,所以得出初值M=3340=0xdc。
·键盘扫描函数
voidkeysure()
{staticbitflag1;
P1=0xf0;
if(P1!
=0xf0)
{
Delay_1ms(15);
if(P1!
=0xf0)
{
keytemp=scankey();flag1=1;
}
}
·键盘返回值函数scankey()
ucharscankey(void)//键盘扫描后返回值//
{
uchari,j,temp,Buffer[4]={0xef,0xdf,0xbf,0x7f};
for(j=0;j<4;j++)
{
P1=Buffer[j];
_nop_();
_nop_();
_nop_();
temp=0x01;
for(i=0;i<4;i++)
{
if(!
(P1&temp))
{
return(i+j*4);
}
temp<<=1;//temp从0x01一直位移到10000000//
}
}
return(0);
}
·键盘处理程序
voidkeyhandle()
{if(keytemp==8)//此显示当前时间//
{s2=min;
s1=hour;
who=1;
led1=0;
led2=1;
}
if(keytemp==9)//此时显示闹钟//
{s1=nhour;
s2=nmin;
who=2;
led1=1;
led2=0;
}
if(keytemp==10)//此时显示日期//
{s1=month;
s2=day;
who=3;
}
if(keytemp==12)//此左移//
{which=1;
led4=1;
led3=0;
switch(who)//返回显示,不然就没有循环动态扫描了//
{
case1:
keytemp=8;break;
case2:
keytemp=9;break;
case3:
keytemp=10;break;
default:
break;
}
}
if(keytemp==13)//此右移//
{which=2;
led4=0;
led3=1;
switch(who)
{
case1:
keytemp=8;break;
case2:
keytemp=9;break;
case3:
keytemp=10;break;
default:
break;
}
}
if(keytemp==14&&flag==1)//此做加数//
{if(which==1)
switch(who)
{
case1:
hour++;keytemp=8;
if(hour>59)
hour=0;
break;
case2:
nhour++;keytemp=9;
if(nhour>24)
nhour=0;
break;
case3:
month++;keytemp=10;
if(month>12)
month=0;
break;
default:
break;
}
if(which==2)//此减数//
switch(who)
{
case1:
min++;keytemp=8;
if(min>59)
min=0;
break;
case2:
nmin++;keytemp=9;
if(nmin>59)
nmin=0;
break;
case3:
day++;keytemp=10;
if(day>30)
day=0;
break;
default:
break;
}
flag=0;
}
if(keytemp==15&&flag==1)//此减数//
{if(which==1)
switch(who)
{
case1:
hour--;keytemp=8;
if(hour<0)
hour=23;
break;
case2:
nhour--;keytemp=9;
if(nhour<0)
nhour=23;
break;
case3:
month--;keytemp=10;
if(month<0)
month=12;
break;
default:
break;
}
if(which==2)
switch(who)
{
case1:
min--;keytemp=8;
if(min<0)
min=59;
break;
case2:
nmin--;keytemp=9;
if(nmin<0)
nmin=59;
break;
case3:
day--;keytemp=10;
if(day<0)
day=30;
break;
default:
break;
}
flag=0;
}
flag=0;
}
·中断子程序
voidtiem0(void)interrupt1
{staticunsignedchari=0;
TH0=0xdc;
TL0=0x00;
i++;
if(i>199)
{sec++;
i=0;
}
if(sec>59)
{sec=0;
min++;
}
if(min>59)
{min=0;
hour++;
}
if(hour>23)
{day++;
hour=0;
}
display();//每次中断程序中都调用一次LED扫描程序//
}
LED扫描子程序
voiddisplay()
{staticchari=0;
chardisbuf[4];
disbuf[1]=s1%10;
disbuf[0]=s1/10;
disbuf[3]=s2%10;
disbuf[2]=s2/10;
P0=0xff;
P2=b[i];
P0=duanma[disbuf[i]];
i++;
if(i>3)
i=0;
if((sec&0x01)&&i==2)
P0&=0x7f;
}
3)实验中使用电路模块电路图
·LED显示模块
·蜂鸣器模块
·键盘扫描模块
D0~D7分别对应IO口的P1.0~P1.7
以下是全部源程序
#include
#defineucharunsignedchar
#defineuintunsignedint
#include
ucharcodeduanma[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,
0x82,0xf8,0x80,0x90,0xbf};
unsignedcharcodeb[]={0xfe,0xfd,0xfb,0xf7};
charsec=0,min=0,hour=2,nsec=0,nmin=1,nhour,month=12,day=1,keytemp;
bitflag=0;
charwho=0;
charwhich=1;
sbitled1=P3^0;
sbitled2=P3^1;
sbitled3=P3^2;
sbitled4=P3^3;
sbitSPK=P3^6;//蜂鸣器的接口//
#defineuintunsignedint
chars1,s2;
voiddisplay()
{staticchari=0;
chardisbuf[4];
disbuf[1]=s1%10;
disbuf[0]=s1/10;
disbuf[3]=s2%10;
disbuf[2]=s2/10;
P0=0xff;
P2=b[i];
P0=duanma[disbuf[i]];
i++;
if(i>3)
i=0;
if((sec&0x01)&&i==2)
P0&=0x7f;
}
voidinit()
{TMOD=0x01;
TH0=0xdc;
TL0=0x00;
EA=1;
ET0=1;
TR0=1;
keytemp=8;//这是为了没有扫描到按键时给keyhandle()函数处理用的//
}
voidtiem0(void)interrupt1
{staticunsignedchari=0;
TH0=0xdc;
TL0=0x00;
i++;
if(i>199)
{sec++;
i=0;
}
if(sec>59)
{sec=0;
min++;
}
if(min>59)
{min=0;
hour++;
}
if(hour>23)
{day++;
hour=0;
}
display();
}
ucharscankey(void)
{
uchari,j,temp,Buffer[4]={0xef,0xdf,0xbf,0x7f};
for(j=0;j<4;j++)
{
P1=Buffer[j];
_nop_();
_nop_();
_nop_();
temp=0x01;
for(i=0;i<4;i++)
{
if(!
(P1&temp))
{
return(i+j*4);
}
temp<<=1;
}
}
return(0);
}
voidkeyhandle()
{if(keytemp==8)//此显示时间//
{s2=min;
s1=hour;
who=1;
led1=0;
led2=1;
}
if(keytemp==9)//此时显示闹钟//
{s1=nhour;
s2=nmin;
who=2;
led1=1;
led2=0;
}
if(keytemp==10)//此时显示日期//
{s1=month;
s2=day;
who=3;
}
if(keytemp==12)//此左移//
{which=1;
led4=1;
led3=0;
switch(who)//返回显示,不然就没有循环动态扫描了//
{
case1:
keytemp=8;break;
case2:
keytemp=9;break;
case3:
keytemp=10;break;
default:
break;
}
}
if(keytemp==13)//此右移//
{which=2;
led4=0;
led3=1;
switch(who)
{
case1:
keytemp=8;break;
case2:
keytemp=9;break;
case3:
keytemp=10;break;
default:
break;
}
}
if(keytemp==14&&flag==1)//此加数//
{if(which==1)
switch(who)
{
case1:
hour++;keytemp=8;
if(hour>59)
hour=0;
break;
case2:
nhour++;keytemp=9;
if(nhour>24)
nhour=0;
break;
case3:
month++;keytemp=10;
if(month>12)
month=0;
break;
default:
break;
}
if(which==2)//此减数//
switch(who)
{
case1:
min++;keytemp=8;
if(min>59)
min=0;
break;
case2:
nmin++;keytemp=9;
if(nmin>59)
nmin=0;
break;
case3:
day++;keytemp=10;
if(day>30)
day=0;
break;
default:
break;
}
flag=0;
}
if(keytemp==15&&flag==1)//此减数//
{if(which==1)
switch(who)
{
case1:
hour--;keytemp=8;
if(hour<0)
hour=23;
break;
case2:
nhour--;keytemp=9;
if(nhour<0)
nhour=23;
break;
case3:
month--;keytemp=10;
if(month<0)
month=12;
break;
default:
break;
}
if(which==2)
switch(who)
{
case1:
min--;keytemp=8;
if(min<0)
min=59;
break;
case2:
nmin--;keytemp=9;
if(nmin<0)
nmin=59;
break;
case3:
day--;keytemp=10;
if(day<0)
day=30;
break;
default:
break;
}
flag=0;
}
flag=0;
}
voidDelay_1ms(uinti)
{
ucharx,j;
for(j=0;j
for(x=0;x<=148;x++);
}
voidkeysure()
{staticbitflag1;
P1=0xf0;
if(P1!
=0xf0)
{
Delay_1ms(15);
if(P1!
=0xf0)
{
keytemp=scankey();flag1=1;
}
}
elseif(flag1)
flag=1;
}
voidcompare()
{if(nmin==min&&nhour==hour&&nsec==sec)
SPK=0;
if(keytemp==11)
SPK=1;
}
voidmain()
{
init();
while
(1)
{
keysure();
keyhandle();
compare();
}
}
四、总结
总结就删掉啦,上传这篇东西希望能帮到有需要的童鞋们,但最好就不要down下来就什么都不学,连程序都看不懂就交了。
但也是说说而已,该怎么着始终还是会怎么着的。
本课程设计纯属梦呓,存在许多缺点,如有不爽,敬请无视。