1)设计任务:完成倒计时时钟的设计。

2)指标要求

(1)能够分钟级的倒计时,分钟和秒显示。

(2)倒计时的起始值可以设置。

(3)具有暂停和清零按钮,倒计时结束报警。

(4)供电电压3.3V/5V。

获取该程序的方式:

2、关注微信公众号下载:

     ① 关注微信公众号:Tech云  

     ②

课程设计题九: 倒计时时钟设计_按键扫描

3、可以关注点赞并在下方评论,我给你邮箱发过去。

一、Protues仿真图:

课程设计题九: 倒计时时钟设计_数码管显示_02

课程设计题九: 倒计时时钟设计_protues仿真_03

课程设计题九: 倒计时时钟设计_protues仿真_04

课程设计题九: 倒计时时钟设计_定时器计数_05

二、程序源码:

因为注释非常的全,这里就不再进行讲解了。

/*******************************************************************************
================================================================================
【平 台】STC89C51_sumjess平台
【编 写】sumjess
【软件版本】V2.0
【最后更新】2019年06月10日
【相关信息参考下列地址】
【网 站】
http://www.51hei.com/bbs/mcu-2-1.html
---------------------------------------------------------------------------------
【dev.env.】MDK4.02及以上版本
【Target 】STC89C51
第一次修订:2019/05/09
第二次修订:2019/05/21
第三次修订:2019/06/10
【problem 】
(1)库内补充的不全面;
(2)库内解释部分不全面;
(3)库内还存在一定的bug;
【direction】
下一步的目标就是把库继续集成!
【explain 】
为了方便使用,我也自己写了很多的库,和优化了算法和表示方式!
【warning】
目前程序中暂无错误 !
---------------------------------------------------------------------------------
没有完美的代码,只有不断的奉献,大家一起努力;
赠人玫瑰手留余香,欢迎大家反馈bug!
================================================================================
********************************************************************************/
#include<reg52.h>

#define uchar unsigned char
#define uint unsigned int

#define duan P1 //段选
#define wei P3 //位选
sbit key1 = P2^3; //设置
sbit key2 = P2^4; //加
sbit key3 = P2^5; //减
sbit key4 = P2^6; //启动
sbit key5 = P2^7; //手动
sbit out = P2^2; //输出控制 ,低电平有效
sbit di = P2^0; //蜂鸣器

bit flag=0,flag1=0,shan=0;
uchar knum,shan_cont,i;
uchar shi=0,fen=0,miao=0; //时分秒
uint cont,cont1,cont2;
uchar code duanma[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};//共阴段码表
// 0 1 2 3 5 6 7 8 9
uchar code weima[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80}; //位码 加反相器用
// 1 2 3 4 5 6 7 8
uchar LedBuff[]={0,0,0,0,0,0,0,0}; //数码管显示缓冲区

void init() //定时器初始化
{
TMOD |= 0x01; //设置定时器0为工作方式1(M1M0为01)
TH0 = (65536-1000)/256; //1ms初值 @12MHz
TL0 = (65536-1000)%256;
EA = 1; //开总中断
ET0 = 1; //开定时器0中断
TR0 = 1; //启动定时器0
}

void dis() //显示函数
{
LedBuff[0] = duanma[miao%10];
LedBuff[1] = duanma[miao/10];
LedBuff[2] = 0x40;
LedBuff[3] = duanma[fen%10];
LedBuff[4] = duanma[fen/10];
LedBuff[5] = 0x40;
LedBuff[6] = duanma[shi%10];
LedBuff[7] = duanma[shi/10];
}


void keysan() //按键扫描函数
{
static bit kf1=0,kf2=0,kf3=0; //自锁标志,这里一定要加static(或者用全局变量),否则按键不灵
static uint i=0,j=0,k=0,l=0,m=0; //消抖延时变量
//启动/暂停键//
if(knum==0) //只要时分秒都不等于0
{
if(key4 == 1) //启动 暂停键
{
m=0; //按键消抖计时变量清零
kf2=0; //按键自锁标志清零

}
else if(kf2 == 0 ) //有按键按下,且是第一次被按下
{
++m; //消抖
if( (m>20) && ((miao != 0) || (fen != 0) || (shi != 0)) )
{
//时分秒都为0时,不能启动输出,只要其中之一不为0,就能启动输出
m=0; //按键消抖计时变量清零
kf2=1; //自锁按键置位,避免一直触发
flag = !flag; //启动、暂停计时标志
out = !out; //LED状态取反
}
}
}

//启动/暂停键//
if(out==1) //加这句,只有在输出关闭的时候,才能设置计时时间,在倒计时的过程中是不能设置的
{
if(key1==1) //设置 //IO是高电平,说明按键没有被按下,这时要及时清零一些标志位
{
i=0; //按键消抖计时变量清零
kf1=0; //按键自锁标志清零
}
else if(kf1 == 0) //有按键按下,且是第一次被按下
{
++i; //消抖
if(i>12)
{
i=0; //按键消抖计时变量清零
kf1=1; //自锁按键置位,避免一直触发
knum++; //调节时分秒--->123
if(knum==4) knum = 0; //按第四次关闭设置
}
}
}
//加1按键//
if(key2==0) //加1按键 快速加数
{
j++;
if(j >= 3000) //长按,数码管扫描放中断里面,这里的值就要取得大才行
{ //因为没用delay延时,CPU跑的很快
if(knum==1)
{
miao++; //秒钟加1
if(miao==60) miao = 0; //超过60s变成0
}

if(knum==2)
{
fen++; //分钟加1
if(fen==60) fen=0; //超过60变0
}

if(knum==3)
{
shi++; //小时加1
if(shi==24) shi=0; //超过24变0
}

j=1000; //这里j的值可以设置连加的快慢,j的值越大就越快
} //因为初值越大,加到3000的时间就越短
}
else //慢速加数
{
if(knum==1)
{
miao++; //秒钟加1
if(miao==60) miao = 0; //超过60s变成0
}

if(knum==2)
{
fen++; //分钟加1
if(fen==60) fen=0; //超过60变0
}

if(knum==3)
{
shi++; //小时加1
if(shi==24) shi=0; //超过24变0
}
j=0;

}
//减1按键//
if(key3==0) //减1按键 快速减数
{
k++;
if(k >= 3000) //长按,数码管扫描放中断里面,这里的值就要取得大才行
{ //因为没用delay延时,CPU跑的很快
if(knum==1)
{
miao--; //秒钟减1
if(miao==255) miao = 59; //小于0就等于59
}
if(knum==2)
{
fen--; //分钟减1
if(fen==255) fen=59; //小于0就等于59
}
if(knum==3)
{
shi--; //时钟减1
if(shi==255) shi=23; //小于0就等于23
}
k=1000;
}
}
else
{
if(k>500 && k<3000) //短按
{
if(knum==1)
{
miao--; //秒钟减1
if(miao==255) miao = 59; //小于0就等于59
}
if(knum==2)
{
fen--; //分钟减1
if(fen==255) fen=59; //小于0就等于59
}
if(knum==3)
{
shi--; //时钟减1
if(shi==255) shi=23; //小于0就等于23
}
}
k=0; //消抖变量清零,为下次按键做准备
}

}


void main()
{
init(); //定时器初始化
out = 1;
while(1)
{
keysan(); //按键扫描函数
dis(); //显示函数
if(shi==0 && fen==0 && miao==0 && flag == 1 ) //倒计时时间到
{
flag=0; //停止计时
flag1=1; //启动蜂鸣器标志置1
out=1; //关闭输出
}
}
}

void timer() interrupt 1
{
TH0 = (65536-1000)/256; //1毫秒初值
TL0 = (65536-1000)%256;
//1s计时//
if(flag)
{
cont++;
if(cont == 1000) //1s到
{
cont = 0; //清0
miao--; //秒数减1
if(miao==255) //如果小于0,就是59
{
miao=59;
fen--; //分钟数减1
if(fen==255) //如果小于0,就是59
{
fen=59;
shi--; //小时减1
if(shi==255) shi=0; //时钟小于0,就是0
if(shi==0) fen=59; //时钟小于0,就是255
if(fen==0 && shi==0) fen=0;//时钟分钟都为0,就是0
}
}
}
}
//蜂鸣器控制//
if(flag1) //倒计时时间到,启动蜂鸣器
{
cont1++;
if(cont1==500) //500ms蜂鸣器响一次
{
cont1=0;
di=!di;
cont2++;
if(cont2==20)//500*20=10000ms=10s,蜂鸣器响10s后停止
{
cont2=0;
flag1=0;
}
}
}
//启动/暂停键//
if(knum != 0)
{
shan_cont++;
if(shan_cont==100) //设置时,100ms闪烁时间 可根据实际效果调整
{
shan_cont=0;
shan = !shan;
}
}
/**********************************数码管扫描函数**********************************/
i++; //挨个显示
if(i>=8) i=0; //清0
duan = 0x00;
switch(i)
{
case 0:
if(knum==1) //如果设置键第一次按下
{
if(shan==1) {wei = 0x00; duan = LedBuff[0];} //设置秒位闪烁
else {wei = weima[0]; duan = LedBuff[0];}
}
else{wei = weima[0]; duan = LedBuff[0];} break; //否则正常显示
case 1:
if(knum==1) //如果设置键第一次按下
{
if(shan==1){wei = 0x00; duan = LedBuff[1];} //设置秒位闪烁
else{wei = weima[1]; duan = LedBuff[1];}
}
else{wei = weima[1]; duan = LedBuff[1];} break; //否则正常显示

case 2: wei = weima[2]; duan = LedBuff[2]; break; //显示-
case 3:
if(knum==2) //如果设置键第二次按下
{
if(shan==1){wei = 0x00; duan = LedBuff[3];} //设置分位闪烁
else{wei = weima[3]; duan = LedBuff[3];}
}
else{wei = weima[3]; duan = LedBuff[3];} break;//否则正常显示

case 4:
if(knum==2) //如果设置键第二次按下
{
if(shan==1){wei = 0x00; duan = LedBuff[4];} //设置分位闪烁
else{wei = weima[4]; duan = LedBuff[4];}
}
else{wei = weima[4]; duan = LedBuff[4];} break; //否则正常显示
case 5: wei = weima[5]; duan = LedBuff[5]; break; //显示-
case 6:
if(knum==3) //如果设置键第三次按下
{
if(shan==1){wei = 0x00; duan = LedBuff[6];} //设置时位闪烁
else{wei = weima[6]; duan = LedBuff[6];}
}
else{wei = weima[6]; duan = LedBuff[6];} break;//否则正常显示
case 7:
if(knum==3) //如果设置键第三次按下
{
if(shan==1){wei = 0x00; duan = LedBuff[7];} //设置时位闪烁
else{wei = weima[7]; duan = LedBuff[7];}
}
else{wei = weima[7]; duan = LedBuff[7];} break;//否则正常显示
default: break;

}
}