代码功能:
1.自动开始计数,一共5个数码管来显示时间。一位数码管显示0-9,对应分度值是0.1s;两位数码管显示00-59,对应分度值1s;两位数码管显示00-59,对应分度值1min;上电后自动开始计时模式。
2.按下开关K1,计时暂停;按下开关K2,计时清0,从头开始计时;按下开关K3,显示520.1314,再次按下开关回到计时模式,显示520.1314期间计时仍在进行。
代码如下
delay.h delay.c main.c SMG.h SMG.c部分
cpp
//main.c
#include<regx52.h>
#include"function.h"
#include"delay.h"
#include"SMG.h"
void main(){
Init_T0();
while(1){
//一直检测是否有按键按下,从而执行下面三个函数
pause();
clear();
love();
//这个flag变量其实没有必要写,但这里我想借这个别的文件中的变量来说明一个知识点
//因为当不显示520.1314的时候flag==1是肯定的,就会自动腾出来数码管来执行clock函数显示时间
if(flag==1){
clock();
}
}
}
//delay.h
#ifndef __delay_H__
#define __delay_H__
void delay(unsigned int t);
#endif
//delay.c
#include"delay.h"
void delay(unsigned int t){
while(t--);
}
//SMG.h
#ifndef __SMG_H__
#define __SMG_H__
//传入显示哪个数码管,显示哪个数字,显示不显示小数点三个参数
void SMG_show(unsigned char pos,unsigned char num,unsigned char dot);
#endif
//SMG.c
#include<regx52.h>
#include"delay.h"
#include"SMG.h"
sbit A1=P2^2;
sbit B1=P2^3;
sbit C1=P2^4;
//这里是共阴数码管,定义了两个数组,分别是不显示点和显示点的
//第二的SMGdot[]数组第11个数据表示单独仅一个小数点
//用来显示520.1314中让一个数码管仅仅显示一个.
//最需要注意的是pos和C1,C2,C3对应顺序
unsigned char SMGndot[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
unsigned char SMGdot[11]={0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef,0x80};
void SMG_show(unsigned char pos,unsigned char num,unsigned char dot){
switch(pos){
case 1:C1=0;B1=0;A1=0;break;
case 2:C1=0;B1=0;A1=1;break;
case 3:C1=0;B1=1;A1=0;break;
case 4:C1=0;B1=1;A1=1;break;
case 5:C1=1;B1=0;A1=0;break;
case 6:C1=1;B1=0;A1=1;break;
case 7:C1=1;B1=1;A1=0;break;
case 8:C1=1;B1=1;A1=1;break;
}
if(dot==1){
P0=SMGdot[num];
}
else{
P0=SMGndot[num];
}
}
function.h function.c部分
cpp
//function.h
#ifndef __function_H__
#define __function_H__
extern unsigned int s;
extern unsigned int min;
extern unsigned int ms10;//0.5s
extern unsigned char flag;//控制显示静态520还是动态时钟
void Init_T0();
void pause();
void clear();
void love();
void clock();
#endif
//function.c
#include<regx52.h>
#include"delay.h"
#include"function.h"
#include"SMG.h"
unsigned int s=0;
unsigned int min=0;
unsigned int ms10=0;
unsigned char flag=1;
void clock(){
SMG_show(1,ms10/2,0);
delay(50);
SMG_show(2,s%10,1);
delay(50);
SMG_show(3,s/10,0);
delay(50);
SMG_show(4,min%10,1);
delay(50);
SMG_show(5,min/10,0);
delay(50);
}
void Init_T0(){
TMOD=0x01;
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
ET0=1;
EA=1;
TR0=1;
}
void service_T0()interrupt 1{
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
ms10++;
if(ms10==20){
s++;
ms10=0;
}
if(s==60){
min++;
s=0;
}
}
void pause(){
if(P3_1==0){
delay(20);
if(P3_1==0){
while(P3_1==0);
TR0=~TR0;//注意让定时器暂停我们改变的是TR0
}
}
}
void clear(){
if(P3_0==0){
delay(20);
if(P3_0==0){
while(P3_0==0);
ms10=0;
s=0;
min=0;
}
}
}
void love(){
//这小段代码是检测按键有没有按下,从而改变变量flag的值
if(P3_2==0){
delay(100);
if(P3_2==0){
while(P3_2==0);
if(flag==0){
flag=1;
}
if(flag==1){
flag=0;
}
//1.这段代码是在flag==0时一直让数码管显示数字,
因为这里多位数码管的显示都是利用不断循环刷新,视觉暂留效果,
所以我们要写到while(flag==0){}里面,目的是:没有再次按下按键K3的时候一直运行这段代码,
一直刷新才能一直显示8位数字。同时在这个函数中也要加上一段按键检测的代码,
来检测有没有按键再次按下,不然就永远困在这个while循环了。
//2.所以我们说刚刚在main.c中的if(flag==1)执行时钟代码时没有用的其实,完全可以不加
if(flag==0){
while(flag==0){
if(P3_2==0){
delay(100);
if(P3_2==0){
while(P3_2==0);
flag=1;
}
}
SMG_show(8,5,0);delay(50);
SMG_show(7,2,0);delay(50);
SMG_show(6,0,0);delay(50);
SMG_show(5,10,1);delay(50);
SMG_show(4,1,0);delay(50);
SMG_show(3,3,0);delay(50);
SMG_show(2,1,0);delay(50);
SMG_show(1,4,0);delay(50);
}
}
}
}
}
收获
1.定时器0暂停为什么是对TR0操作?
从图中我们可以看出来想让定时器计数暂停就要让控制信号K来断开,所以最好的方式就是通过TRx来控制定时器的启动和停止,这样同时也能保留当前加1计数器中的数字不变。如果是改变了我们IE寄存器的使能端,只能是让定时器中断过去不了,但前面计数仍在继续。
2.全局变量如何跨文件使用
在上面代码中我们在function.c文件中使用了flag全局变量,但是我们还想在main.c中使用这个全局变量,我们应该如何写代码?
其实这和让函数在被别的文件中使用差不多,都是在.h中声明,在.c中实现,在别的文件中用到的时候包含.h头文件就可以了,但是为什么变量的声明前面要额外加extern呢?
在 C 语言中,使用
extern
关键字来声明变量时,是为了告诉编译器该变量是在其他文件中定义的,需要在当前文件中进行引用。而对于函数的声明,通常不需要使用extern
关键字是因为函数的声明本身就隐含了extern
的含义。
3.并不是所有在.c文件中使用的全局变量和函数都要在.h文件添加声明
我们只用将那些,我们想让外面可以看到全局变量和函数在.h文件中声明。对于比如SMGndot[10]数组,又比如中断服务函数(中断服务函数都是自动调用的,一般就不会写到.h中,供应别的函数使用),这些变量和函数没必要或者我们不想提供给别的文件使用就可以不用在.h文件中添加声明。对于flag这个全局变量我们在main.c中要用到,就需要在.h文件添加声明了
4.巩固了学过的知识
在这个代码中使用到了独立按键,需要用到独立按键的去抖动处理。
使用到了数码管,需要我们通过74HC138译码器来实现对8个数码管的循环提供信号,利用视觉再留达到同时显示的效果,同时回顾了这里使用的是switch,代码上要注意的是C1对应译码器的高位,还要注意其他对应问题,这里我们还是用了两个数组来保存字段码。
使用到了定时器的知识,需要我们来进行一开始定时器的各个变量的初始化,定时器的启动和暂停,要编写中断服务函数。
5.加强了模块化编程
这里我们将暂停,清除,显示8位静态码,显示时间变化,定时器初始化,中断服务函数,这些功能函数写到了一个文件中。(再想说一下,并不是所有的这些数组,或者函数必须要在.h文件中添加说明)
将数码管具体显示实现也单独写到了一个文件中
这样写更清晰明了。
6.残留的缺点
就是这个在使用clear,pause函数的时候,在按下按键之后就不能保持原来正在计数的状态,因为函数会卡在while(P3_1==0)这些地方,在我们松开按键之后才能变为暂停的数字,中间会有一点没有显示的时候,当然快速按下按键然后松开就几乎可以达到连贯的感觉。
在我刚刚写完上面的那段话的时候我忽然就想到了解决方法,代码如下,就是卡在while(P3_1==0)的时候让它来执行clock();同样在clear函数中和love函数中也可做此优化。
while(P3_0==0){clock();};//clear
while(P3_2==0){clock();};//love
这样在按下后但没松开时可以保持原状态
cpp
void pause(){
if(P3_1==0){
delay(20);
if(P3_1==0){
while(P3_1==0){clock();};
TR0=~TR0;
}
}
}