一、定时器设置
首先,无论是实时时钟工程,还是倒计时工程,都离不开定时器的使用。至于定时器的调节,学过的同学肯定都会,没学过的可以看下面的步骤。
先打开stc-isp软件。
然后找到定时器计算器,
然后开始设置定时器时间,这里需要注意几项需要调整的数据。
当然上面的设置并不绝对,这个定时器模式的设置,我记得之前看过一篇博客,博主使用的8位自动重载模式。这里新学的萌新(当然我也是萌新)也可以直接使用我代码中的定时器设置函数,复制下来放到一个地方或者做成头文件,以便以后使用。如果有同学没有学过怎么做头文件、怎么做模块化编程的话,可以在评论区留言,我再写一篇模块化编程的教程。
下面是我用过的一个博主的定时器设置
void DSQ_INIT()
{
TMOD=0x11;//定时器方式
EA=1;//开总中断
ET0=1;//开T0中断
TR0=0;//定时器0计时
TF0=0;//清除TF0标志
TH0=0x3c; //设置初值,定时时间为50ms
TL0=0xb0;
}
二、时间函数(即秒数自减、分秒转换)
我们这里可以看到,设置定时器的时间是50ms,而我们需要的时间是1s,这很显然并不符合。那么怎么解决呢?很简单,让它运行20次即可。
void Timer()interrupt 1
{
static int i=0;
TL0 = 0xb0;
TH0 = 0x3c;
i++;
if(i>19)//总共1s,因为定时器设置的是50ms,i>19,意味着20*50ms=1s
{
i=0;
if(min>0||sec>0)
{
if(sec==0)
{
sec=60;
min--;
}
if(sec>0)
{
sec--;
}
}
}
}
既然讲到这里了,那么顺便说一下,这个函数是一个时间控制的函数,它的效果有:使定时器运行20次,凑成1s;当秒数为0时,使秒数归为60,分钟数减一(至于为什么秒数不是变为59,而是变为60,因为我当时自己运行并烧录后发现,如果改为59的话,在秒数为0的下一秒,秒数就会变为58,很明显是下方紧接着一个“sec--”,可以看下图,由于秒数先一步变成59,所以下面的if判断几乎同时进行而非相隔1s,由此就导致造成的效果就是秒数多减了1,故我这里将秒数回归60,使秒数在0的下一秒可以变为59.);
接着上面说,圈红的地方就是这个函数的第3个功能,即秒数大于0时,秒数自减1,再结合函数运行总时间为1s,也就是每1s减秒数1,这个函数到这里就完成了。
三、数码管位选、段选(时间显示函数)
相信既然准备做倒计时工程了,那么同学们一定学过数码管位选、段码之类了,这里便不再多说,上代码!
unsigned char table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x40};//数码管段选,即段码
void Show_time()
{
P0=table[min/10%10]; //分,第一位,数码管显示数值
LSA=0;LSB=0;LSC=1;delay(1); //数码管位选
P0=table[min%10];
LSA=1;LSB=1;LSC=0;delay(1);
P0=table[10];
LSA=0;LSB=1;LSC=0;delay(1);
P0=table[sec/10%10]; //秒,第一位
LSA=1;LSB=0;LSC=0;delay(1);
P0=table[sec%10];
LSA=0;LSB=0;LSC=0;delay(1);
P0=0x00;
}
四、倒计时自定义时间函数讲解
由于本编程只涉及到分钟、秒钟两单位,因此只需要设置秒数加1、秒数减1、分钟数加1、分钟数减1四种调节方式即可。这一模块离不开独立按键的应用。
1.秒数加1、减1函数
我们都知道,按下一个独立按键,其实就相当于这个独立按键变为低电平,赋0值,比如下面:
sbit KEY4=P3^3;
void Sec_ADD()//按下K4,使秒数加1。
{
if(KEY4==0)
{
delay(20);
while(!KEY4);
Show_time();
sec++;
delay(20);
miao=sec;
if(sec>59)
{
min++;
sec=0;
delay(20);
fen=min;
miao=sec;
}
}
}
按下KEY4按键,KEY4为0,程序运行进入if判断内,之后才可以进行之后的程序。
这里的“while(!KEY4)”,作用是,当KEY4为0时,即被按下时,陷入死循环,一直循环,直到松开,起到的作用其实就是当你的手松开的时候,它才会进行下面的程序,也就是检测你的手是否松开。
这里的程序,这个函数的作用是,按下独立按键KEY4,使秒数+1;且“if(sec>59)”这一判断的作用就是当秒数变为60时,秒数变为00;
下面是减1函数,其实逻辑上和加1函数是差不多的。
void Sec_SUB()//按下K3,使秒数减一。
{
if(KEY3==0)
{
delay(20);
while(!KEY3);
if(min>0||sec>0)
{
sec--;
delay(20);
miao=sec;
if(sec==0&&min!=0)
{
min--;
sec=59;
delay(20);
fen=min;
miao=sec;
}
}
}
}
这里涉及到一个语句:“sec=59”,这个语句就是当秒数为零时,再按秒数减1按键,秒数将变为59.
这里的“fen=min;miao=sec;”之类的语句,是为之后重置倒计时做准备的,“fen、miao”相当于一个暂时的储存器,重置后,将“fen、miao”中储存的数值赋给“min”和“sec”。细心的同学可能会发现,我在每一个时间调节函数里面都有这种类似的语句,就是为了能够把最新的时间储存进去。
2.终止与重置函数
具体应用看下面的函数:
void stop()//终止程序,2,3一起按下,并自己调的回归初始值
{
if(KEY2==0)
{
delay(20);
if(KEY3==0)
{
delay(20);
while(!KEY3);
Show_time();
sec=miao; //为终止程序后,倒计时回归自己调的初始值做准备。
min=fen;
_Delay(5000);
P0=0xff;
while(1)
{
if(KEY2==0)
{
delay(20);
while(KEY2!=0);
break;
}
}
}
}
}
这里的“sec=miao;min=fen;”语句就是将之前储存的值重新赋给“sec”和“min”,起到一个数值重置到所调节数值的作用。
可能还会有其它更简单的方法,但是,我毕竟是萌新,只能想到通过这种中介的方法,可能相对来说会有些麻烦。
3.暂停函数
暂停函数相对来说不用动太多脑子,就是关闭或者说暂停定时器的计时就可以了。下面直接上个代码:
void Pause()//暂停键
{
if(KEY2==0)
{
delay(10);
if(KEY2==0)
{
TR0=0;
}
}
if(min==0&&sec==0)
{
TR0=0;
led=0;
}
}
“TR0”是控制计时器计时的,具体可以听听b站上江科大的讲解,很清晰,也易于理解。
下面是链接: 51单片机入门教程-2020版 程序全程纯手打 从零开始入门_哔哩哔哩_bilibili
至于计时的开始按键、开始函数,在主函数里面,最后一步会提到,慢慢来~~
4.分钟的加1、减1
由于独立按键的数量有限,因此我这里控制分钟的函数用了两个独立按键,例如独立按键2,4同时按下,分钟加1.
下面是代码:
void Min_ADD()//K2,K4一起按下,分钟加1;
{
if(KEY2==0)
{
delay(20);
if(KEY4==0){
delay(20);
while(!KEY4);
Show_time();
min++;
delay(20);
fen=min;
}
}
}
这函数的解释:按下2按键后,“delay(20);”语句作用是延时消抖,然后,同时按下4,也可以一直按着2,再按4,由于是两个按键,很难完美协调,不过不影响使用,大家可以自己烧录尝试一下,就理解我说的不好协调是什么意思了>-< ->_->。
这里其实如果理解了分钟加1,那么分钟减1就照葫芦画瓢就可以了,我最后会发一遍完整代码,方便大家学习和调试。
5.蜂鸣器函数
首先需要定义上bee蜂鸣器的针脚(我习惯用来代替蜂鸣器的缩写,随便定义,不影响,方便理解就行,比如“feng、ming、di啥的”),语句是“sbit bee=P2^5;”,蜂鸣器链接的针脚是P2^5.
蜂鸣器这一模块,如果只是单纯想让它响是很简单的,但是放音乐可能会难一些,具体学习仍旧可以看江科大视频,链接在上面,不过个人建议是网上找些代码自己学学,视频蛮长的,有些浪费时间。
下面是代码:
void Bee()//蜂鸣器
{
if(sec==3&&min==0||sec==2&&min==0||sec==1&&min==0)//当分钟为0,且秒为3、2、1时,蜂鸣器响
{
bee=~bee;
}
}
这里用到了“&&”,“与”的意思,其实就是“且”,语句“sec==3&&min==0”的意思就是分钟为0的同时,秒钟为3;至于“||”,是“或”的意思,那么整体意思就是,当分钟为0时,秒钟为3或2或1时,蜂鸣器响。
6.主函数(启动、运行函数)
在这个函数中,其实就是把定时器启动计时,把之前所写的所有相关函数加进去,再设置一个启动按键就可以了。
我这里设置的启动按键时KEY1.
//主函数
void main()
{
DSQ_INIT(); //定时器
while(1)
{
Show_time();
if(KEY1==0&&(KEY3!=0)) //开始按键,即KEY1按下,倒计时开始运行。
{
if(min>0||sec>0)
{
TR0=1;
}
delay(5);
while(KEY1)
{
Show_time();
Pause();
Bee();
Sec_ADD();
Sec_SUB();
Min_SUB();
Min_ADD();
stop();
}
}
Pause();
Bee();
Sec_SUB();
Sec_ADD();
Min_SUB();
Min_ADD();
stop();
}
}
五、完整代码
温馨提示~~会很长,毕竟函数比较多,要有耐心。
#include <REGX52.H>
unsigned char min=0;
unsigned char sec=00;
unsigned char miao;
unsigned char fen;
sbit KEY1=P3^1;
sbit KEY2=P3^0;
sbit KEY3=P3^2;
sbit KEY4=P3^3;
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;
sbit bee=P2^5;
sbit led=P2^6;
sbit KEY_S5=P3^4;
unsigned char table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x40};//数码管段选,即段码
void delay(unsigned int z)
{
unsigned int x,y;
for(x=z;x>0;x--)
for(y=114;y>0;y--);
}
void _Delay(int x) //@12.000MHz
{
while(x--)
{
unsigned char i, j;
i = 12;
j = 169;
do
{
while (--j);
} while (--i);
}
}
void DSQ_INIT()
{
TMOD=0x11;//定时器方式为方式一
EA=1;//开总中断
ET0=1;//开T0中断
TR0=0;//定时器0计时
TF0=0;//清除TF0标志
TH0=0x3c; //设置初值,定时时间为50ms
TL0=0xb0;
}
void Timer()interrupt 1
{
static int i=0;
TL0 = 0xb0;
TH0 = 0x3c;
i++;
if(i>19)//总共1s,因为定时器设置的是50ms,i>19,意味着20*50ms=1s
{
i=0;
if(min>0||sec>0)
{
if(sec==0)
{
sec=60;
min--;
}
if(sec>0)
{
sec--;
}
}
}
}
void Show_time()
{
P0=table[min/10%10]; //分,第一位,数码管显示数值
LSA=0;LSB=0;LSC=1;delay(1); //数码管位选
P0=table[min%10];
LSA=1;LSB=1;LSC=0;delay(1);
P0=table[10];
LSA=0;LSB=1;LSC=0;delay(1);
P0=table[sec/10%10]; //秒,第一位
LSA=1;LSB=0;LSC=0;delay(1);
P0=table[sec%10];
LSA=0;LSB=0;LSC=0;delay(1);
P0=0x00;
}
//拓展:自定义倒计时
void Sec_ADD()//按下K4,使秒数加1。
{
if(KEY4==0)
{
delay(20);
while(!KEY4);
Show_time();
sec++;
delay(20);
miao=sec;
if(sec>59)
{
min++;
sec=0;
delay(20);
}
}
}
void Sec_SUB()//按下K3,使秒数减一。
{
if(KEY3==0)
{
delay(20);
while(!KEY3);
if(min>0||sec>0)
{
sec--;
delay(20);
miao=sec;
if(sec==0&&min!=0)
{
min--;
sec=59;
delay(20);
fen=min;
miao=sec;
}
}
}
}
void Pause()//暂停键
{
if(KEY2==0)
{
delay(10);
if(KEY2==0)
{
TR0=0;
}
}
if(min==0&&sec==0)
{
TR0=0;
led=0;
}
}
void Min_ADD()//K2,K4一起按下,分钟加1;
{
if(KEY2==0)
{
delay(20);
if(KEY4==0){
delay(20);
while(!KEY4);
Show_time();
min++;
delay(20);
fen=min;
}
}
}
void Min_SUB()//1,3一起按下,分钟减1;
{
if(KEY1==0)
{
delay(20);
if(KEY3==0){
delay(20);
while(!KEY3);
Show_time();
if(min!=0)
{
min--;
delay(20);
fen=min;
}
}
}
}
void stop()//终止程序,2,3一起按下,并自己调的回归初始值
{
if(KEY2==0)
{
delay(20);
if(KEY3==0)
{
delay(20);
while(!KEY3);
Show_time();
sec=miao; //为终止程序后,倒计时回归自己调的初始值做准备。
min=fen;
_Delay(5000);
P0=0xff;
while(1)
{
if(KEY2==0)
{
delay(20);
while(KEY2!=0);
break;
}
}
}
}
}
//拓展:最后三秒报警提示
void Bee()//蜂鸣器
{
if(sec==3&&min==0||sec==2&&min==0||sec==1&&min==0)//当分钟为0,且秒为3、2、1时,蜂鸣器响
{
bee=~bee;
}
}
//主函数
void main()
{
DSQ_INIT(); //定时器
while(1)
{
Show_time();
if(KEY1==0&&(KEY3!=0)) //开始按键,即KEY1按下,倒计时开始运行。
{
if(min>0||sec>0)
{
TR0=1;
}
delay(5);
while(KEY1)
{
Show_time();
Pause();
Bee();
Sec_ADD();
Sec_SUB();
Min_SUB();
Min_ADD();
stop();
}
}
Pause();
Bee();
Sec_SUB();
Sec_ADD();
Min_SUB();
Min_ADD();
stop();
}
}
六、效果视频
这个嘛,目前单片机没在手里,等我回了宿舍再更新一下文章,上传一下视频。
emmm……到宿舍了,但是我刚尝试了一下,我不会插入视频,有没有大佬教一教我。
a361923bc29c1bb6a2e639b19aece-CSDN直播
82343d698cae0c341fae11fca12985-CSDN直播
会了会了,视频链接,不晓得为啥有直播这俩字,我明明上传的视频。
七、片外话
要动手!要动手!要动手!重要的事情说三遍。
另外就是,尽量不要直接复制粘贴,那样对学习没有帮助,要尝试自己去做一做,哪怕做一个比这个简单很多的一个倒计时。
学单片机,做工程运行成功后超有成就感的好伐,我要继续学32单片机去喽~~
注:我在学这一方面时曾借鉴过一位优秀博主的编程代码:cara23456(博主名称)
Cara23456-CSDN博客(博主博客链接)