课程设计基于单片机AT89C52电子时钟课程设计说明书.docx
《课程设计基于单片机AT89C52电子时钟课程设计说明书.docx》由会员分享,可在线阅读,更多相关《课程设计基于单片机AT89C52电子时钟课程设计说明书.docx(26页珍藏版)》请在冰点文库上搜索。
![课程设计基于单片机AT89C52电子时钟课程设计说明书.docx](https://file1.bingdoc.com/fileroot1/2023-5/16/2a789990-d2b7-45d9-9994-1c975880a8ef/2a789990-d2b7-45d9-9994-1c975880a8ef1.gif)
一、课程设计内容
单片机课程设计报告
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
个机器周期以上的高电平,将使单片机复位。
·EA/VPP ——片外程序存储器访问允许信号。
欲使CPU仅访问外部程序存
储器(地址为0000H-FFFFH),EA端必须保持低电平(接地),如果EA端为
高电平(接Vcc端),CPU则执行内部程序中的指令。
·LAE/PROG
——当访问外部程序存储器或数据存储器时,ALE(地址允许锁
存)输出脉冲用于锁存地址的低8位位数字节。
一般情况下,ALE仍以时钟振荡频率的1/6输出固定的脉冲信号,因此它可对外输出时钟或用于定时目的。
要注意的是:
每当访问外部数据存储器时将跳过一个ALE脉冲。
对Flash存储器编程期间,该引脚还用于输入编程脉冲(第二功能)。
·PSEN:
程序储存允许(PSEN)输出是外部程序存储器的读选通信号,当
AT89C52
由外部存储器取指令(或数据)时,每个机器周期两次PSEN有效,即输出两
个脉冲。
在此期间,当访问外部数据存储器,将跳过两次PSEN信号。
·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
INT0 (外部中断0)
P3.3
INT1 (外部中断1)
P3.4
T0 (定时/计数器0)
P3.5
T1 (定时/计数器1)
P3.6
WR (外部数据存储器写选通)
P3.7
RD (外部数据存储器读选通)
三、设计方案
1)实物图
2)实验程序流程图
T0初始化,中断初始化,启动定时器
·主程序
开 始
调用compare()函数比较现在显示的时间是否和闹钟时间相同
调用子程序keyhandle()根据键盘扫描的返回值进行处理
调用启停子程序
keysure()
键盘扫描
while
(1)死循环调用子程
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。
·键盘扫描函数
键盘是否
真的被按下?
是
否
调用scankey(),根据按下键位返回值给keyhandle()处理
调用keysure()函数用以扫描键盘是否真正被按下
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;
}
·中断子程序
手动重装初值
循环次数加一
Y
分位清0
循环数i到100
否?
N
Y
N
60S到否?
Y
N
60分到否?
时位加1
分位加1
秒个位清0
秒个位加1
N
24时到
了?
天数加1
时位清0
Y
调用display()LED动态扫描函数
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};
unsignedchar codeb[]={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
}
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下来就什么都不学,连程序都看不懂就交了。
但也是说说而已,该怎么着始终还是会怎么着的。
本课程设计纯属梦呓,存在许多缺点,如有不爽,敬请无视。