单片机课程设计报告
基于单片机的数字时钟
姓名:
班级:
学号:
一、 前言
利用实验板上的4个LED数码管,设计带有闹铃、秒表功能的数字时钟。
功能要求:
a) 计时并显示(LED)。由于实验板上只有4位数码管,可设计成显示“时分”和显示“分秒”并可切换。
b) 时间调整功能。利用4个独立按钮,实现时钟调整功能。这4个按钮的功能为工作模式切换按钮(MODE),数字加(INC),数字减(DEC)和数字移位(SHITF)。
c) 定闹功能。利用4个独立按钮设定闹钟时间,时间到以蜂鸣器响、继电器动作作为闹铃。
d) 秒表功能。最小时间单位0.01秒。
二、 硬件原理分析
1. 电源部分
电源部份采用两种输入接口(如上图)。
a) 外电源供电,采用2.1电源座,可接入电源DC5V,经单向保护D1接入开关S1。
b) USB供电,USB供电口输入电源也经D1单向保护,送到开关S1。
注:两路电源输入是并连的,因此只选择一路就可以了,以免出问题。
S1为板子工作电源开关,按下后接通电源,提供VCC给板子各功能电路。电路采用两个滤波电容,给板子一个更加稳定的工作电源。LED为电源的指示灯,通电后LED灯亮。
2. 蜂鸣器
蜂鸣器分为有源和无源两种,有源即两引脚有一个直流电源就可以长鸣,无源则需要一个1K左右的脉冲才可以蜂鸣,因此对于按键的提示音及报警蜂鸣使用有源来得方便。有源也可以当无源使用,而无源则不能当有源使用,当然用有源蜂鸣器作音乐发声会失真厉害。
如上图:单片机P15输出高低电平经R21连接三极管B极,控制三极管的导通与截止,从而控制蜂鸣器的工作。低电平时三极管导通,蜂鸣器得电蜂鸣,高电平时三极管截止,蜂鸣器失电关闭蜂鸣。
3. 数码管
电路使用一个四位共阳型数码管,四个公共阳级由三极管放大电流来驱动,三极管由P10-P13控制开与关。数码管的阴级由P0口经过电阻限流连接。
例如,要十位的数码管工作,P12输出0,使三极管Q12导通,8脚得电,当P0口相应位有输出0时,点亮相应的LED灯组合各种字符数字。
4. 独立键盘
如上图,由六个按键组成,每个按键的一端连接IO口,另一端直接连接GND(0电位)。六个按键分别接入P37-P32,只要按下按键,相应位的IO口位将被拉为低电平(0),程序可以判断相应位是否为0来确认按键己按下。
5. 动态显示
动态显示驱动电路是单片机应用中最常用的显示方式。
动态显示接口电路是把所有显示器的8个笔划段的同名段连在一起,而每一个显示器的公共极COM是各自独立地受I/O线控制。CPU向字段输出口送出字形码时,所有显示器接收到相同的字形码,但究竟是哪个显示器亮,则取决于COM端。
在轮流点亮扫描过程中,每位显示器的点亮时间是极为短暂的(约1ms),但由于人眼的视觉暂留现象及发光二极管的余辉效应,尽管实际上各位显示器并非同时点亮,但只要扫描的速度足够快,给人的印象就是一组稳定的显示数据,不会有闪烁感。
动态扫描显示必须由CPU不断地调用显示程序,才能保证持续不断的显示。在实际工作中,当然不可能只显示,这样在两次调用显示程序之间的时间间隔就不一定了,如果时间间隔比较长,就会使显示不连续。
三、 软件设计
l 主程序
将按键1设定为功能选择键,每按一次按键1,进入相应的功能模式。
l 分秒显示
分秒显示模式下,数码管显示时钟时间的分和秒。当按键1按下后,进入下一模式。
l 时分显示
时分显示模式下,数码管显示时钟时间的时和分。当按键1按下后,进入下一模式。
l 时间调整
时间调整模式下,仅调整时钟的时、分。
数码管闪烁的为当前调整的位子,按键2为加,按键3为减,按键4为移位键。
l 闹钟
闹钟模式下,可设置闹钟的时、分。
数码管闪烁的为当前调整的位子,按键2为加,按键3为减,按键4为移位键。
当闹钟时间调整完毕之后,按下按键5,确认闹钟时间。随后等待时钟时间到达闹钟时间。时钟时间到达闹钟时间后,蜂鸣器工作。
l 秒表
秒表模式下,按下按键4,秒表开始计时;再次按下按键4,秒表暂停。
按下按键2或按键3,秒表清零。
四、 软件调试
在软件调试过程中碰到了很多问题。其中比较重要的几个问题如下:
1、时钟调整子程序中,时、分的十位数、个位数加减不符合常识。比如在加减时,小时可以达到27、28等数值,而这是不符合常识的。
在查看代码后发现,可以在加、减过程中设置if函数判断当前所处位置,调整当前数值所能达到的最大值或最小值。
修改后代码如下:
else if(keyval==0xbf) //按键2 加
{
if(pos==0)
{
dispbuf[pos]++;
if(dispbuf[pos]==3)
{
dispbuf[pos]=0;
}
}
else if(pos==1)
{
if(dispbuf[0]==0||dispbuf[0]==1)
{
dispbuf[pos]++;
dispbuf[pos]=dispbuf[pos]%10;
}
if(dispbuf[0]==2)
{
dispbuf[pos]++;
if(dispbuf[pos]>=4)
{
dispbuf[pos]=0;
}
}
}
else if(pos==2)
{
dispbuf[pos]++;
if(dispbuf[pos]==6)
{
dispbuf[pos]=0;
}
}
else if(pos==3)
{
dispbuf[pos]++;
dispbuf[pos]=dispbuf[pos]%10;
}
}
else if(keyval==0xdf) //按键3 减
{
if(pos==0)
{
if(dispbuf[pos]==0)dispbuf[pos]=2;
else dispbuf[pos]--;
}
else if(pos==1)
{
if(dispbuf[0]==0||dispbuf[0]==1)
{
if(dispbuf[pos]==0)dispbuf[pos]=9;
else dispbuf[pos]--;
}
if(dispbuf[0]==2)
{
if(dispbuf[pos]>=4&&dispbuf[pos]<=9)
{
dispbuf[pos]=3;
}
else if(dispbuf[pos]==0)
{
dispbuf[pos]=3;
}
else
{
dispbuf[pos]--;
}
}
}
else if(pos==2)
{
if(dispbuf[pos]==0)dispbuf[pos]=5;
else dispbuf[pos]--;
}
else if(pos==3)
{
if(dispbuf[pos]==0)dispbuf[pos]=9;
else dispbuf[pos]--;
}
}
2、秒表模式下,无法很好地区分当前计时所达到的具体数值。在秒的个位数位子设置一个小数点,使秒表的显示更加直观。
修改后代码如下:
在程序开始处,添加如下代码:
unsigned char code segtab_dot[]=
{0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10}; //带小数点的数码管代码
在void t0int(void) interrupt 1 using 1之中添加如下代码:
if(mode==4&&P1==0xfd) //秒表处显示小数点的if语句
{
P0=segtab_dot[dispbuf[dispindex]];
}
else
{
P0=segtab[dispbuf[dispindex]];
}
3、在秒表运行一遍后,进入其他模式操作完毕,再次进入秒表模式下发现秒表无法清零。检查代码后发现,在闹钟模式下,进入秒表模式时,未对秒表清零。
修改后代码如下:
if(keyval==0x7f)//1号按键 模式 进入秒表模式
{
mode=4;
old_ss=0;
old_ms=0;
miaobiao_ss=old_ss;
miaobiao_ms=old_ms;
dispbuf[0]=miaobiao_ss/10;
dispbuf[1]=miaobiao_ss%10;
dispbuf[2]=miaobiao_ms/10;
dispbuf[3]=miaobiao_ms%10;
clocksing=0;
}
附完整实验代码:
/***********************************************************************
基于单片机的数字时钟
功能:时钟、闹钟、秒表
模式0:时钟显示模式-分、秒
模式0下,数码管显示时钟时间-分、秒。
按键说明:
按键1:选择下一模式。
模式1:时钟显示模式-时、分
模式1下,数码管显示时钟时间-时、分。
按键说明:
按键1:选择下一模式。
模式2:时钟调整模式
模式2下,调整时钟时间,仅调整时、分时间。
按键说明:
按键1:选择下一模式。
按键2:增加选中数码管的数值。
按键3:减少选中数码管的数值。
按键4:移位键,按键选择下一数码管。
模式3:闹钟模式
模式3下,设置闹铃时间之后,等时钟到达相应时间,发出铃声。
按键说明:
按键1:选择下一模式。
按键2:增加选中数码管的数值。
按键3:减少选中数码管的数值。
按键4:移位键,按键选择下一数码管。
按键5:设置闹铃时间后,按键确认,数码管显示当前时钟时间。
模式4:秒表模式
模式4为秒表模式,可记录0-59秒。最小时间单位为0.01秒。
按键说明:
按键1:选择下一模式。
按键2:清除秒表当前数值。
按键3:清除秒表当前数值。
按键4:开始、暂停秒表。
***********************************************************************/
#include <reg52.h>
unsigned char dispbuf[4]={0,0,0,0};
unsigned char code segtab[]=
{0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; //不带小数点的数码管代码
unsigned char code segtab_dot[]=
{0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10}; //带小数点的数码管代码
unsigned char code bittab[]={0xfe,0xfd,0xfb,0xf7};
unsigned char dispindex;
unsigned char mode;
unsigned char pos;
unsigned char keyval;
unsigned char hh,mm,ss;
unsigned char miaobiao_ms=0,miaobiao_ss=0; //秒表
unsigned char old_ms,old_ss; //保存秒表时间
unsigned char clock_hh,clock_mm; //用于存放所设闹钟时间
unsigned int clock_shanshuo; //用于控制闹钟调整时间时是否闪烁 0:闪烁 1:不闪烁
sbit clocksing=P1^5; //闹钟所用蜂鸣器
unsigned int cnt; //用于10ms转1s计数
unsigned char cnt1; //用于10ms转0.5s处计数(用于闪烁)
unsigned char cnt2; //用于秒表处计时
unsigned char keyscan(void);
void delay(unsigned int t);
void keypro(void);
bit flash;
/******************主函数******************/
void main(void)
{
TMOD=0x11;
TH0=(0-2000)/256;
TL0=(0-2000)%256;
ET0=1;
TR0=1;
TH1=(0-2000)/256;
TL1=(0-2000)%256;
ET1=1;
TR1=0; //秒表 先不开
EA=1;
mode=0;
hh=02;
mm=03;
ss=15;
while(1)
{
if(mode==3)
{
if(hh==clock_hh)
{
if(mm==clock_mm)
{
clocksing=0;
}
}
else
{
clocksing=1;
}
}
else
{
clocksing=1;
}
if(keyscan()==0xff) continue;
keypro();
}
}
/******************键扫描程序******************/
unsigned char keyscan(void)
{
P3=0xff;
keyval=P3;
if(keyval==0xff)
{
return 0xff;
}
delay(10000);
if(keyval!=P3)
{
return 0xff;
}
else
{
while(P3!=0xff);
return keyval;
}
}
/******************时钟中断及显示程序******************/
void t0int(void) interrupt 1 using 1
{
TH0=(0-2000)/256; //2ms
TL0=(0-2000)%256;
P0=0xff;
P1=0xff;
P1=bittab[dispindex];
if(mode==4&&P1==0xfd) //秒表处显示小数点的if语句
{
P0=segtab_dot[dispbuf[dispindex]];
}
else
{
P0=segtab[dispbuf[dispindex]];
}
if((dispindex==pos)&&(flash==1)&&(mode==2||(mode==3&&clock_shanshuo==0))) //闪烁控制程序
{
P0=0xff;
P1=0xf0;
}
dispindex++;
dispindex=dispindex&0x03;
cnt++;
cnt1++;
if(cnt1>=250) //0.5ms闪烁
{
cnt1=0;
flash=~flash;
}
if(cnt>=500) //2ms*500=1s
{
cnt=0;
ss++;
if(ss>=60)
{
ss=0;
mm++;
if(mm>=60)
{
mm=0;
hh++;
if(hh>=24)
{
hh=0;
}
}
}
if(mode==0)
{
dispbuf[0]=mm/10;
dispbuf[1]=mm%10;
dispbuf[2]=ss/10;
dispbuf[3]=ss%10;
}
else if(mode==1||(mode==3&&clock_shanshuo==1))
{
dispbuf[0]=hh/10;
dispbuf[1]=hh%10;
dispbuf[2]=mm/10;
dispbuf[3]=mm%10;
}
}
}
/******************按键选择,进入不同模式******************/
void keypro(void)
{
switch(mode)
{
case 0://显示分秒
if(keyval==0x7f)
{
mode=1;
dispbuf[0]=hh/10;
dispbuf[1]=hh%10;
dispbuf[2]=mm/10;
dispbuf[3]=mm%10;
}
break;
case 1://显示时分
if(keyval==0x7f)
{
mode=2;
dispbuf[0]=hh/10;
dispbuf[1]=hh%10;
dispbuf[2]=mm/10;
dispbuf[3]=mm%10;
}
break;
case 2://时间调整(只调整时分)
if(keyval==0x7f) //按键1 调整模式 进入闹钟模式
{
mode=3;
clock_hh=0;
clock_mm=0;
clock_shanshuo=0;
hh=dispbuf[0]*10+dispbuf[1]; //保存调整后的时间
mm=dispbuf[2]*10+dispbuf[3];
dispbuf[0]=clock_hh/10; //显示闹钟的初始时间
dispbuf[1]=clock_hh%10;
dispbuf[2]=clock_mm/10;
dispbuf[3]=clock_mm%10;
TR0=1;
}
else if(keyval==0xbf) //按键2 加
{
if(pos==0)
{
dispbuf[pos]++;
if(dispbuf[pos]==3)
{
dispbuf[pos]=0;
}
}
else if(pos==1)
{
if(dispbuf[0]==0||dispbuf[0]==1)
{
dispbuf[pos]++;
dispbuf[pos]=dispbuf[pos]%10;
}
if(dispbuf[0]==2)
{
dispbuf[pos]++;
if(dispbuf[pos]>=4)
{
dispbuf[pos]=0;
}
}
}
else if(pos==2)
{
dispbuf[pos]++;
if(dispbuf[pos]==6)
{
dispbuf[pos]=0;
}
}
else if(pos==3)
{
dispbuf[pos]++;
dispbuf[pos]=dispbuf[pos]%10;
}
}
else if(keyval==0xdf) //按键3 减
{
if(pos==0)
{
if(dispbuf[pos]==0)dispbuf[pos]=2;
else dispbuf[pos]--;
}
else if(pos==1)
{
if(dispbuf[0]==0||dispbuf[0]==1)
{
if(dispbuf[pos]==0)dispbuf[pos]=9;
else dispbuf[pos]--;
}
if(dispbuf[0]==2)
{
if(dispbuf[pos]>=4&&dispbuf[pos]<=9)
{
dispbuf[pos]=3;
}
else if(dispbuf[pos]==0)
{
dispbuf[pos]=3;
}
else
{
dispbuf[pos]--;
}
}
}
else if(pos==2)
{
if(dispbuf[pos]==0)dispbuf[pos]=5;
else dispbuf[pos]--;
}
else if(pos==3)
{
if(dispbuf[pos]==0)dispbuf[pos]=9;
else dispbuf[pos]--;
}
}
else if(keyval==0xef) //按键4 移位
{
pos++;
pos=pos&03;
}
break;
case 3: //闹钟模式
if(keyval==0x7f) //按键1 调整模式 进入秒表模式
{
mode=4;
old_ss=0;
old_ms=0;
miaobiao_ss=old_ss;
miaobiao_ms=old_ms;
dispbuf[0]=miaobiao_ss/10;
dispbuf[1]=miaobiao_ss%10;
dispbuf[2]=miaobiao_ms/10;
dispbuf[3]=miaobiao_ms%10;
}
else if(keyval==0xbf) //按键2 加
{
clock_shanshuo=0;
if(pos==0)
{
dispbuf[pos]++;
if(dispbuf[pos]==3)
{
dispbuf[pos]=0;
}
}
else if(pos==1)
{
if(dispbuf[0]==0||dispbuf[0]==1)
{
dispbuf[pos]++;
dispbuf[pos]=dispbuf[pos]%10;
}
if(dispbuf[0]==2)
{
dispbuf[pos]++;
if(dispbuf[pos]>=4)
{
dispbuf[pos]=0;
}
}
}
else if(pos==2)
{
dispbuf[pos]++;
if(dispbuf[pos]==6)
{
dispbuf[pos]=0;
}
}
else if(pos==3)
{
dispbuf[pos]++;
dispbuf[pos]=dispbuf[pos]%10;
}
}
else if(keyval==0xdf) //按键3 减
{
clock_shanshuo=0;
if(pos==0)
{
if(dispbuf[pos]==0)dispbuf[pos]=2;
else dispbuf[pos]--;
}
else if(pos==1)
{
if(dispbuf[0]==0||dispbuf[0]==1)
{
if(dispbuf[pos]==0)dispbuf[pos]=9;
else dispbuf[pos]--;
}
if(dispbuf[0]==2)
{
if(dispbuf[pos]>=4&&dispbuf[pos]<=9)
{
dispbuf[pos]=3;
}
else if(dispbuf[pos]==0)
{
dispbuf[pos]=3;
}
else
{
dispbuf[pos]--;
}
}
}
else if(pos==2)
{
if(dispbuf[pos]==0)dispbuf[pos]=5;
else dispbuf[pos]--;
}
else if(pos==3)
{
if(dispbuf[pos]==0)dispbuf[pos]=9;
else dispbuf[pos]--;
}
}
else if(keyval==0xef) //按键4 移位
{
clock_shanshuo=0;
pos++;
pos=pos&03;
}
else if(keyval==0xf7) //按键5 设置闹钟时间
{
clock_shanshuo=1;
clock_hh=dispbuf[0]*10+dispbuf[1]; //保存所设闹钟时间
clock_mm=dispbuf[2]*10+dispbuf[3];
dispbuf[0]=hh/10;
dispbuf[1]=hh%10;
dispbuf[2]=mm/10;
dispbuf[3]=mm%10;
}
break;
case 4: //秒表模式
if(keyval==0x7f) //按键1 调整模式 进入时钟显示模式
{
mode=0;
TR1=0; //关闭秒表
dispbuf[0]=mm/10;
dispbuf[1]=mm%10;
dispbuf[2]=ss/10;
dispbuf[3]=ss%10;
}
else if(keyval==0xef) //按键4 开始、暂停
{
old_ss=miaobiao_ss;
old_ms=miaobiao_ms;
TR1=~TR1;
}
else if(keyval==0xbf||keyval==0xdf) //按键2或按键3 清零
{
miaobiao_ss=0;
miaobiao_ms=0;
dispbuf[0]=miaobiao_ss/10;
dispbuf[1]=miaobiao_ss%10;
dispbuf[2]=miaobiao_ms/10;
dispbuf[3]=miaobiao_ms%10;
}
break;
default:
break;
}
}
/******************延时程序******************/
void delay(unsigned int t)
{
while(t--);
}
/******************中断程序,用于秒表******************/
void t1int(void) interrupt 3 using 3
{
TH1=(0-2000)/256;
TL1=(0-2000)%256;
cnt2++;
if(cnt2>=5)
{
cnt2=0;
miaobiao_ms++;
if(miaobiao_ms>=100)
{
miaobiao_ms=0;
miaobiao_ss++;
if(miaobiao_ss>=60)
{
miaobiao_ss=0;
}
}
dispbuf[0]=miaobiao_ss/10;
dispbuf[1]=miaobiao_ss%10;
dispbuf[2]=miaobiao_ms/10;
dispbuf[3]=miaobiao_ms%10;
}
}
五、 学习体会
通过本次单片机课程设计,不仅加深了我对单片机理论课程知识的认识,而且通过将理论与实践相结合,使我真正地全面理解单片机的功能。
在刚开始编程时,没有一点思路,通过ftp上所提供的例程,慢慢理清了思路、基本了解了程序大致需要那几部分,基本确定编程思想。
在课程设计的整个过程中也遇到了很多问题,但本着遇到问题解决问题的原则,通过查找资料和询问老师、同学的办法,基本解决了所遇到问题。整个课程设计过程学到了不少通过理论学习没法学到的东西,真正增强了自己的能力。
课程设计的三天时间,虽然辛苦但是收获巨大。
单片机课程设计电子钟一实现功能1能够实现准确计时以数字形式显示时分秒的时间2小时以24小时计时形式分秒计时为60进位能够调节时钟时…
单片机课程设计报告基于单片机的数字钟班级姓名学号设计时间单片机课程设计正文一引言现代社会科技飞速发展人们生活节奏加快时间就是金钱时…
科技创新实践[数字时钟设计][用AT89S52单片机设计电子时钟的实验报告]目录单片机的个性化电子钟设计报告1一、设计背景1二、作…
西安建筑科技大学课程设计论文第1章绪论1设计要求1系统可以按秒进行计时2数字时钟可以显示小时0023分钟0059和秒00593可通…
JINGCHUUNIVERSITYOFTECHNOLOGY单片机原理及应用课程设计报告题目电子时钟专业电气工程及其自动化班级20x…
学院:专业班级:姓名:指导教师:设计时间:51单片机课程设计一、设计任务与要求1.任务:制作并调试51单片机学习板2.要求:(1)…
51单片机课程设计报告学院:专业班级:姓名:指导教师:设计时间:51单片机课程设计一、设计任务与要求1.任务:制作并调试51单片机…
江南大学课程名称设计题目班级姓名指导教师物联网工程学院课程设计报告单片机原理及应用基于单片机的步进电机控制器设计号评分20xx年6…
一6该课程设计是利用MCS51单片机内部的定时计数器中断系统以及行列键盘和LED显示器等部件设计一个单片机电子时钟设计的电子时钟通…
内容提要单片微型计算机简称单片机是典型的嵌入式微控制器常用英文字母的缩写MCU表示单片机它最早是被用在工业控制领域单片机由芯片内仅…
湖南工业职业技术学院项目制作报告书项目名称新大屏幕数字钟制作所属课程系别专业班级学生姓名学号项目指导老师电子邮箱联系QQ20xx1…