基于51单片机(普中开发板)的四位数码管秒表设计
说明
今天学校布置的课后作业,利用51单片机做一个秒表,本来是做了个8位数码管的秒表,但后来想了下秒表计时一般都不会用到以小时为单位,就把小时的部分以及毫秒的部分去掉了,就留下了分钟跟秒。
每个开发板的电路连接不一定是一样的,我所使用这块开发板是普中科技的,连接数码管的位选是接了一个138译码器的,如果是购买的普中科技的开发板可以直接复制程序就能使用,如果开发板的数码管的位选是接了锁存器的,那么就要相应地更改。
如果不理解138芯片使用方法的,我也要把138芯片的真值表附在下面了,顺便说下它的用法:
74HC138译码器可接受3位二进制加权地址输入(A0, A1和A2),并当使能时,提供8个互斥的低有效输出(Y0至Y7)。74HC138特有3个使能输入端:两个低有效(E1和E2)和一个高有效(E3)。除非E1和E2置低且E3置高,否则74HC138将保持所有输出为高。74HC138正常工作的话,一次只能输出一个低电平,其他全为高电平。
要求:
- 按下按钮S1,秒表开始计时;
- 按下按钮S2,秒表暂停计时(停留在计时的数);
- 按下按钮S3,秒表清零;
- 计时精确到0.5s
电路图
程序
程序已经过测试,可以正常使用,注释非常多,比较容易理解。
/****************************************************************************************
秒表实验
实验现象:按下按钮S1开始计时,最大为59.59。按下按钮S2定时器暂停,按下按钮S3清零
*****************************************************************************************/
#include <reg52.h>
#define uint unsigned int //对数据类型进行声明定义
#define uchar unsigned char
sbit LS138_A = P2^2; //138芯片A2引脚
sbit LS138_B = P2^3; //138芯片A1引脚
sbit LS138_C = P2^4; //138芯片A0引脚
uchar DT_s = 0; //秒计时
uchar DT_min = 0; //分计时
uchar code duan[] = {0x3F,0x06,0x5B,0x4F, //共阴数码管无小数点 显示0~9
0x66,0x6D,0x7D,0x07,
0x7F,0x6F
};
uchar code duanpoint[] = {0xbf,0x86,0xdb,0xcf, //共阴数码管有小数点 显示0~9
0xe6,0xed,0xfd,0x87,
0xff,0xef,0xf7,0xfc,
0xb9,0xde,0xf9,0xf1
};
/************************************************
函数名 :delay1ms
函数功能 :t=1,大约延时1ms
************************************************/
void delay1ms(uint t)
{
uint i,j;
for(i=0;i<t;i++)
{
for(j=0;j<120;j++);
}
}
/************************************************
函数名 :Timer0Init
函数功能 :定时/计数器0中断初始化
*************************************************/
void Timer0Init()
{
TMOD = 0x01; //选择T0定时/计数器,工作在方式1,16位计数器
TH0 = 0xFC; //计数初始值,计数从64536开始,计1000个数,完成一次计数,时间为1ms
TL0 = 0x18;
ET0 = 1; //定时/计数器0中断允许位
EA = 1; //总中断
}
/************************************************
函数名 :S3
函数功能 :按下按钮S3时,秒表归零
*************************************************/
void S3()
{
DT_s = 0; //秒归零
DT_min = 0; //分归零
TR0 = 0; //运行控制位清0,关闭定时器
}
/**********************************************************
函数名 :ScanKey
函数功能 :按键扫描函数,循环扫描哪个键被按下
**********************************************************/
void ScanKey()
{
uchar Key; //临时变量
Key = P3&0x07; //只需要三个按键,分别接的P3^1,P3^2,P3^3,故把P3高五位被屏蔽
if(Key!=0x07) //判断哪个键被按下
{
delay1ms(10); //消抖10ms
Key = P3&0x07; //再次赋值,避免此时按键状态改变
if(Key!=0x07) //确认按键被按下
{
switch(Key)
{
case 0x05: //S1(P3^2)被按下
TR0 = 1; //定时器0运行控制位为1,启动定时器0
break;
case 0x06: //S2(P3^1)被按下
TR0 = 0; //定时器0运行控制位为0,关闭定时器0
break;
case 0x03: //S3(P3^3)被按下
S3();
break;
}
}
while(Key!=0x07) //松手检测
{
Key = P3&0x07;
}
}
}
/*******************************************************************
函数名 :DigDisplay
函数功能 :数码管动态扫描函数,循环扫描8个数码管显示
********************************************************************/
void DigDisplay(uchar s,uchar min)
{
LS138_A = 0; //秒个位位选
LS138_B = 0;
LS138_C = 0;
P0 = duan[s%10]; //发送段码,显示秒个位
delay1ms(5); //间隔一段时间扫描
LS138_A = 1; //秒十位位选
LS138_B = 0;
LS138_C = 0;
P0 = duan[s/10]; //发送段码显示秒十位
delay1ms(5); //间隔一段时间扫描
LS138_A = 0; //分的个位位选
LS138_B = 1;
LS138_C = 0;
P0 = duanpoint[min%10]; //发送段码,显示分的个位
delay1ms(5); //间隔一段时间扫描
LS138_A = 1; //分的十位位选
LS138_B = 1; //间隔一段时间扫描
LS138_C = 0;
P0 = duan[min/10]; //发送段码,显示分的十位
delay1ms(5); //间隔一段时间扫描
P0 = 0x00; //消隐
}
/**********************************************************
函数名 :主函数
函数功能 :无
**********************************************************/
void main(void)
{
P0 = 0x00; //读端口前写1
P3 = 0xFF; //读端口前写1
Timer0Init(); //定时器中断初始化函数
while(1)
{
DigDisplay(DT_s,DT_min); //数码管显示函数
ScanKey(); //按键扫描函数
}
}
/**********************************************************
函数名 :Timer0
函数功能 :定时器计数
**********************************************************/
void Timer0() interrupt 1
{
static uint count_s;
static uint count_min;
TH0 = 0xFC; //计数值初始化,从64536开始计数,计满时为65536,溢出时即为 1ms
TL0 = 0x18;
count_s++; //秒计数
count_min++; //分计数
if(count_s==1000) //计数到1s时,秒计数器开始工作
{
count_s = 0; //秒计数清零
DT_s++; //显示秒计数值自增
if(DT_s>59) //秒数最大为59
{
DT_s = 0;
}
}
if(count_min==60000) //计数到60000ms时,秒计数器开始工作
{
count_min = 0; //分计数清零
DT_min++; //显示分计数值自增
if(DT_min>59) //分数最大为59
{
DT_min = 0;
}
}
}