资源描述
综合性、设计性实验报告
姓名: *** 学号: ***********
姓名: *** 学号: ***********
专业 通信工程 班级 *** 级 班
实验课程名称 单片机原理及应用
指导教师及职称 *******
开课学期 * 至 *学年 第* 学期
上课时间 *****
*******学院教务处编印
第 1 页
一、实验设计方案
实验名称:可调节数字电子钟设计
实验时间: ********
小组合作: 是● 否○
小组成员:
1、实验目的:
1)学习数码管的基本知识。
2)学会如何控制动态数码管扫描显示。
3)掌握定时器和外部中断的综合应用
2、实验设备及材料:
硬件设备:LH-M20单片机实验箱
操作系统:win7
开发工具:PROTEUS、KEIL C
3、实验内容:
①问题描述
利用共阴极的6/8位LED数码管进行显示,然后用定时器实现定时,实现一个数字电子钟,实现时、分、秒,并用“.”或“-”隔开显示。通过外部按键实现对数字电子钟控制,即能适时地设置时间,并完成相应的程序。另可扩展设计电路实现秒表及闹钟等附加功能。
②编程任务
1. 利用通过修改电路实现对数字电子钟实现控制,即能适时地设置时间,并完成相应的程序。
2.利用通过修改电路实现一个电子秒表,启动秒表,暂停秒表,秒表清零,秒表显示到毫秒。
③样例
1. 定时1秒,采用16位定时50ms,共定时20次才可达到1秒,进一步可实现分时。
2. 利用小延时或者另一个定时器实现对8个数码管的动态扫描显示。
3. 时间设置采用外部中断。
4、实验方法步骤及注意事项:
①实验步骤
1、根据实验任务书所提出的要求,明确自己的实验目的和实验预期结果。
2、确定可调数字时钟的具体功能:
3、Proteus画硬件仿真电路图。
4、思考各功能模块的实现流程,明确各模块具体的过程,画出程序流程图
5、KEIL环境编程实现
6、下载程序,调试程序和仿真电路,观察并记录实验现象。
②解题思路
1. 如何实现定时1秒,1分,1小时
2. 如何实现对动态数码管的动态扫描显示
3. 如何实现多功能的电子表。
数码管动态扫描显示:将数码管显示抽象成时、分、秒、“-”,通过P0和74HC573锁存器复用输出显示段码和选择位码。将编写一个显示函数,根据输入的段码和位码显示两个数码管(实质是一位的循环显示),然后在不同功能模块的显示函数中调用该显示函数,来实现不同的显示需求和闪烁控制。
各功能模块的切换: 使用外部中断来更新相应的按键变量,主程序根据按键变量进行不同模式间的切换
走时调时功能的实现:利用51单片机的定时器0实现中断计时,在中断函数现实走时的时间更新。在主循环中不断扫描按键,根据按键变量来更新时间。
年月日显示和调节: 编写相应的年月日显示函数,在按键按下时,有时间显示转为日期显示,按键释放时,恢复正常的时间显示。进入调节年月日模式后,根据扫描的按键值来更新年月日变量,进行日期的修改。
闹钟:定义闹钟使用标志,和闹钟时间变量。在开启闹钟后,树立闹钟使用标志,主循环中会不断判断闹钟变量和当前时间是否相等,若相等就进行响铃,当按键按下取消响铃。
秒表:秒表计时使用定时器1,中断周期为10MS,秒表精确到0.01S。扫描启动/停止键,清零键,退出键。
5.参考文献:
《单片机原理及接口技术》张毅刚主编,人民邮电出版社,2008
《单片机原理及应用》(第二版), 曹巧媛 主编,电子工业出版社, 2004
指导老师对实验设计方案的意见:
指导老师签名:
年 月 日
二、实验报告
1、实验目的、设备及材料、实验内容、实验方法步骤见实验设计方案
2、 实验程序(请自行填写,可拉大表格)
#include<reg52.h>
#define uchar unsigned char
#define uint unsigned int
#define DataPort P0 //定义数据端口
sbit duanma_ea=P2^2;//定义锁存使能端口 段锁存
sbit weima_ea=P2^3;// 位锁存
sbit BEEP=P1^0; //蜂鸣器发生频率输出
char set; //菜单键
sbit select=P1^3; //选择键
sbit add=P1^1; //增加键
sbit sub=P1^2; //减少键
bit mod_flag; //菜单选择标志
bit alarm_flag; //闹钟启用标志
bit alarm_ri_flag; //闹钟响铃标志
char h_shi,h_ge, m_shi,m_ge, s_shi,s_ge; //全局的秒分时位变量
char year_shi,year_ge, month_shi,month_ge, day_shi,day_ge;//全局年月日
char ah_shi,ah_ge, am_shi,am_ge, as_shi,as_ge; //全局的闹钟秒分时位变量
char sm_shi,sm_ge, ss_shi,ss_ge, s1s_shi,s2s_ge; //全局秒表计时
uchar set_num,select_num; //设置键按下次数计数变量,根据此变量来翻转状态
char shi,fen,miao,count; //全局的时分秒数变量
char year,month,day; //全局的年月日数变量
char ashi,afen,amiao; //全局的闹钟时分秒数变量
char sfen,smiao,ssmiao; //全局的闹钟时分秒数变量
uchar code dofly_DuanMa[11]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x40};// 显示段码值0~9和“-”隔离号
uchar code dofly_WeiMa[8]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//分别对应相应的数码管点亮,即位码
void custom_delayms(uint); //带参数的ms级延时声明
void part_display(uchar ,uchar ,uchar ,uchar );//部分数码管显示函数声明
void display_time(uchar);//数码管扫描函数声明
void display_date(uchar set_flash);//显示日期
void display_alarm_time(uchar set_flash);//显示闹钟时间
void display_Stopwacth();//秒表计时显示
void KeyScan(void);//键盘扫描
void init_timer0();//定时器0初始化
void init_timer1();//定时器1初始化
void Init_EX_();
void Di();
void Du();
void Date_handle();//日期处理函数,对于闰年和大小月的计算
/*------------------------------------------------
主函数
------------------------------------------------*/
void main(void)
{
Init_EX_(); //外部中断初始化
init_timer0(); //定时器0初始化
init_timer1(); //定时器1初始化
DataPort=0 ; //输出口置零,等待数据
set_num=0; //菜单键计数
year=16;month=12;day=24;
year_shi=1;year_ge=6; month_shi=1;month_ge=2;day_shi=2;day_ge=4;//设定默认的年月日
while(1) //主循环
{
if(mod_flag==1) //进入设置模式
{
switch(set_num)
{
case 1:display_time(select_num); break;//调节时分秒 显示
case 2:display_date(select_num); break;//调节年月日显示
case 3:display_alarm_time(select_num);break;//调节闹钟显示
case 4:display_Stopwacth(); break;//秒表显示
}
}
else //正常时间显示
{
if(alarm_flag==1) //如果开启闹钟
{
if( (ashi==shi) && (afen==fen) && (amiao==miao) )alarm_ri_flag=1;//时间相等,开启闹铃
if(alarm_ri_flag==1){Di();Du();}//响铃
if(add==0||sub==0)alarm_ri_flag=0; //按下增,减键就取消响铃
}
if(select==0) //正常显示时间时,按下选择键,显示年月日
display_date(select_num);
else
display_time(select_num); //正常显示年月日
}
KeyScan();//键盘扫描
}
}
/*------------------------------------------------
部分数码管显示函数,给定两位数据,两位指定时分秒的功能号
------------------------------------------------*/
void part_display(uchar set_shi,uchar set_ge,uchar set_H_M_S_ten,uchar set_H_M_S_a)
{
DataPort=0; //数据端口置0,保证每次数据的更新都是从0状态改变,防止重叠。
duanma_ea=1; //段使能
DataPort=dofly_DuanMa[set_shi];//送显示数据
duanma_ea=0; //锁存段数据
custom_delayms(2); //延时2MS防止消影现象
DataPort=0;
weima_ea=1; //位使能
DataPort=dofly_WeiMa[set_H_M_S_ten]; //送位数据,对应为显示数据
weima_ea=0; //锁存位数据
custom_delayms(2); //延时2MS防止消影现象
DataPort=0;
duanma_ea=1;
DataPort=dofly_DuanMa[set_ge];
duanma_ea=0;
custom_delayms(2);
DataPort=0;
weima_ea=1;
DataPort=dofly_WeiMa[set_H_M_S_a];
weima_ea=0;
custom_delayms(2);
}
/*时分秒扫描显示函数,给定功能号,可以设定时分秒的闪烁状态。*/
void display_time(uchar set_flash)
{
static uchar flash1,flash2,flash3,n1,n2,n3;
switch(set_flash) //闪烁状态控制
{
case 3:flash1=25;flash2=0;flash3=0;break;
case 2:flash1=0;flash2=25;flash3=0;break;
case 1:flash1=0;flash2=0;flash3=25;break;
case 0:flash1=0;flash2=0;flash3=0;break;
}
part_display(10,10,2,5); //显示“-”
if(n1>=flash1) //可调节n*和flash*的值,改变显示的闪烁频率
{part_display(h_shi,h_ge,0,1);}
n1++;
if(n1==50)n1=0;
if(n2>=flash2)
{part_display(m_shi,m_ge,3,4);}
n2++;
if(n2==50)n2=0;
if(n3>=flash3)
{part_display(s_shi,s_ge,6,7);}
n3++;
if(n3==50)n3=0;
}
/*年月日扫描显示函数,给定功能号,可以设定年月日的闪烁状态。*/
void display_date(uchar set_flash)
{
static uchar flash1,flash2,flash3,n1,n2,n3;
switch(set_flash) //闪烁状态控制
{
case 3:flash1=25;flash2=0;flash3=0;break;
case 2:flash1=0;flash2=25;flash3=0;break;
case 1:flash1=0;flash2=0;flash3=25;break;
case 0:flash1=0;flash2=0;flash3=0;break;
}
part_display(10,10,2,5); //显示“-”
if(n1>=flash1) //可调节n*和flash*的值,改变显示的闪烁频率
{part_display(year_shi,year_ge,0,1);}
n1++;
if(n1==50)n1=0;
if(n2>=flash2)
{part_display(month_shi,month_ge,3,4);}
n2++;
if(n2==50)n2=0;
if(n3>=flash3)
{part_display(day_shi,day_ge,6,7);}
n3++;
if(n3==50)n3=0;
}
/*闹钟时分秒扫描显示函数,给定功能号,可以设定时分秒的闪烁状态。*/
void display_alarm_time(uchar set_flash)
{
static uchar flash1,flash2,flash3,n1,n2,n3;
switch(set_flash) //闪烁状态控制
{
case 3:flash1=25;flash2=0;flash3=0;break;
case 2:flash1=0;flash2=25;flash3=0;break;
case 1:flash1=0;flash2=0;flash3=25;break;
case 0:flash1=0;flash2=0;flash3=0;break;
}
part_display(10,10,2,5); //显示“-”
if(n1>=flash1) //可调节n*和flash*的值,改变显示的闪烁频率
{part_display(ah_shi,ah_ge,0,1);}
n1++;
if(n1==50)n1=0;
if(n2>=flash2)
{part_display(am_shi,am_ge,3,4);}
n2++;
if(n2==50)n2=0;
if(n3>=flash3)
{part_display(as_shi,as_ge,6,7);}
n3++;
if(n3==50)n3=0;
}
/*秒表描显示函数*/
void display_Stopwacth()
{
part_display(10,10,2,5); //显示“——”
part_display(sm_shi,sm_ge,0,1);
part_display(ss_shi,ss_ge,3,4);
part_display(s1s_shi,s2s_ge,6,7);
}
/*初始化定时器0函数,设置定时器初值,打开中断,计时开启*/
void init_timer0()
{
TMOD=0x11;
TH0=(65536-50000)/256; //定时中断周期50MS
TL0=(65536-50000)%256;
EA=1;
ET0=1;
TR0=1;
}
/*初始化定时器0函数,设置定时器初值,打开中断,计时开启*/
void init_timer1()
{
TMOD=0x11;
TH1=(65536-10000)/256; //定时中断周期10MS
TL1=(65536-10000)%256;
EA=1;
ET1=0;
TR1=0;
}
/*初始化外部中断*/
void Init_EX_()
{
EX0=1;
IT0=1; //边沿触发
EX1=1;
IT1=1;
}
/*外部中断1*/
void SUB_KEY() interrupt 2 //菜单键按下,触发
{
set_num++;
select_num=0; //选择键置0
mod_flag=1; //树立设置模式标志,进入设置显示模式
Di(); //按键声
}
/*定时器0中断函数,时分秒,累计计时*/
void timer_delay() interrupt 1
{
TH0=(65536-50000)/256; //定时中断周期50MS
TL0=(65536-50000)%256;
count++;
if(count==20)
{
count=0;
miao++;
if(miao==60)
{
miao=0;
fen++;
if(fen==60)
{
fen=0;
shi++;
if(shi==24)
{
shi=0;
day++;
if(day>27)Date_handle();//万年历算法
day_shi=day/10;
day_ge=day%10;
}
h_shi=shi/10;
h_ge=shi%10;
//时变化,改变输出时数
}
m_shi=fen/10;
m_ge=fen%10;//分变化,改变输出分数
}
s_shi=miao/10;
s_ge=miao%10;//秒变化,改变输出秒数
}
}
/*定时器1中断函数,秒表计时*/
void stopwacth_delay() interrupt 3
{
TH1=(65536-10000)/256; //定时中断周期10MS
TL1=(65536-10000)%256;
ssmiao++;
if(ssmiao==100)
{
ssmiao=0;
smiao++;
if(smiao==60)
{
smiao=0;
sfen++;
if(sfen==60)
{
sfen=0;
}
sm_shi=sfen/10;
sm_ge=sfen%10;//分变化,改变输出分数
}
ss_shi=smiao/10;
ss_ge=smiao%10;//分变化,改变输出分数
}
s1s_shi=ssmiao/10;
s2s_ge=ssmiao%10;//秒变化,改变输出秒数
}
/*定义MS延时函数,用于数码管扫描时的延时处理,以及按键设置时需要选定数码管显示*/
void custom_delayms(unsigned int xms)
{
unsigned int i,j;
for(i=xms;i>0;i--)
for(j=110;j>0;j--);
}
/*蜂鸣器*/
void Di()
{
uchar i;
for(i=0;i<100;i++)
{
custom_delayms(1); //产生500HZ的信号来使喇叭发生
BEEP=~BEEP;
}
}
void Du()
{
uchar i;
for(i=0;i<50;i++)
{
custom_delayms(2); //产生500HZ的信号来使喇叭发生
BEEP=~BEEP;
}
}
/*万年历计算函数*/
void Date_handle()
{
if(month==2&&year%4==0&&day==30)day=1;
else if(month==2&&year%4!=0&&day==29)day=1;
else if((month==4||month==6||month==9||month==11)&&day==31)day=1;
else if(day==32)day=1;
if(day==1)
{
month++;
if(month==13)
{
month=1;
year++;
if(year==100)year=0;
year_shi=year/10;
year_ge=year%10;
}
month_shi=month/10;
month_ge =month%10;
}
}
/*------------------------------------------------
按键扫描函数,根据按键状态翻转
------------------------------------------------*/
void KeyScan()
{
if(set_num==1)//进入时间调时分秒模式
{
if(select==0)
{
// custom_delayms(5); //延时消抖
if(select==0)
{
select_num++;
while(!select)
{display_time(0);}
Di();
TR0=0;
ET0=0;//首先恢复计时,开启计时中断(定时器0)
switch(select_num) //闪烁状态控制
{
case 1:break;//display(1); //秒位置闪烁
case 2:break;//display(2); //分位置闪烁
case 3:break;//display(3); //时位置闪烁
case 4:select_num=0;set_num=0;mod_flag=0;
TR0=1;
ET0=1;//首先恢复计时,开启计时中断(定时器0)
break; //选择键清零
}
}
}
if(select_num!=0)
{
if(add==0)
{
while(!add)display_time(0);
Di();
switch(select_num) //调节闪烁位置数据
{
case 1:miao++;if(miao==60)miao=0;
s_shi=miao/10;
s_ge=miao%10;//每调节一次送回显示
break;
case 2:fen++;if(fen==60)fen=0;
m_shi=fen/10;
m_ge=fen%10;//每调节一次送回显示
break;
case 3:shi++;if(shi==24)shi=0;
h_shi=shi/10;
h_ge=shi%10;
break;
}
}
if(sub==0)
{
while(!sub)display_time(0);
Di();
switch(select_num) //调节闪烁位置数据
{
case 1:miao--;if(miao==-1)miao=59;
s_shi=miao/10;
s_ge=miao%10;
break;
case 2:fen--;if(fen==-1)fen=59;
m_shi=fen/10;
m_ge=fen%10;
break;
case 3:shi--;if(shi==-1)shi=23;
h_shi=shi/10;
h_ge=shi%10;
break;
}
}
}
}
if(set_num==2)//调节年月日模式
{
if(select==0)
{
// custom_delayms(5); //延时消抖
if(select==0)
{
select_num++;
while(!select)display_date(0);
Di();
switch(select_num) //闪烁状态控制
{
case 1:break;//display(1); //秒位置闪烁
case 2:break;//display(2); //分位置闪烁
case 3:break;//display(3); //时位置闪烁
case 4:select_num=0;set_num=0;mod_flag=0;
break; //选择键清零
}
}
}
if(select_num!=0)
{
if(add==0)
{
while(!add)display_date(0);
Di();
switch(select_num) //调节闪烁位置数据
{
case 1:day++;if(day==32)day=1;
day_shi=day/10;
day_ge=day%10;//每调节一次送回显示
break;
case 2:month++;if(month==13)month=1;
month_shi=month/10;
month_ge=month%10;//每调节一次送回显示
break;
case 3:year++;if(year==100)year=0;
year_shi=year/10;
year_ge=year%10;
break;
}
}
if(sub==0)
{
while(!sub)display_date(0);
Di();
switch(select_num) //调节闪烁位置数据
{
case 1:day--;if(day==0)day=31;
day_shi=day/10;
day_ge=day%10;
break;
展开阅读全文