一、设计背景
近年来随着科技的飞速发展,单片机的应用正在不断的走向深入。本文阐述了51单片机通过级联74HC595实现倒计时秒表设计,倒计时精度达0.05s,解决了传统的由于倒计时精度不够造成的误差和不公平性,是各种体育竞赛的必备设备之一。本设计采用51单片机为中心器件,利用其定时器/计数器定时和记数的原理,使用两片74HC595级联实现LED数码管的控制以及利用外部中断来设计倒计时器。将软、硬件有机地结合起来,使得系统能够实现八位LED显示,倒计时精度为0.05秒,能正确地进行倒计时,复位后倒计时重新回到初始状态。其中软件系统采用C语言编写程序,包括显示程序,定时中断服务,外部中断服务程序,延时程序等,并在KEIL5中调试运行,硬件系统利用Protues强大的功能来实现,简单易于观察,在仿真中就可以观察到实际的工作状态。
二、实现功能
以51单片机为控制核心,设计一种可调倒计时秒表。整个系统包括51单片机芯片、晶振电路、时钟电路、功能按键以及74HC595数码管控制显示电路。该系统利用共阳数码管实时显示时、分、秒倒计时,通过独立按键键盘设计控制倒计时秒表的启停、定时时间的设计。可具体实现以下功能:
(1) 按下设置键后,在数字闪烁的过程中,采用启动外部中断,能对增、减按键产生响应,。
(2) 按下启动键后,启动定时器;按下暂停键后,停止定时器。
(3)·定时器定时50ms,20次定时可产生1秒钟计时。
(4) 倒计时结束后,停止定时器。
(5) 最大定时99时59分59秒。
三、仿真演示
初始化定时时间0时0分0秒。
按下时间设置按键可以设置倒计时时间,通过连续按下时间设置按键可在时、分、秒设置来回切换,利用闪烁效果提示当前设置单位。
设置好倒计时时间后,利用启动键和停止键实现倒计时秒表的启停。
四、源程序
#include <REGX52.H>
#include <intrins.h>#define false 0
#define true 1
#define uchar unsigned char //0——255
#define uint unsigned int //0——65535
#define ulong unsigned long //0——4294967295sbit SDA1 = P3^6; //串行数据输入,对应595的14脚SER
sbit SCL1 = P3^7; //移位寄存器时钟输入,对应595的11脚SCK
sbit SCL2 = P2^4; //存储寄存器时钟输入,对应595的12脚RCKsbit start_key = P3^0; //启动按钮
sbit pause_key = P3^1; //停止按钮
sbit set_key = P3^5; //停止按钮uchar state; //状态:0-停止,1-启动,2-暂停
ulong ms = 0; //存储毫秒值uchar shi = 0; // 小时数
uchar fen = 0; //分钟数
uchar miao = 0; //秒数
uchar set_flag = 0;uchar smg_c = 0; //数码管闪烁时间计数
bit smg_f = 0; //数码管闪烁标志unsigned char code disp[17]= {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x58,0x5e,0x79,0x71,0x40}; /***数码***///0 1 2 3 4 5 6 7 8 9
unsigned char code wei[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};void displayTime(void); //刷新时间显示
void disPlay(uchar num, uchar loc); //数码管、led显示函数
void Exint_Init(void);
void Timer_Init(void);
void delay2ms(void);
void Delay1ms(unsigned int n);void Delay1ms(unsigned int n)
{unsigned char i, j; for (; n>0; n--)for(i=0;i<10;i++)for(j=0;j<33;j++);
}void delay2ms(void)
{unsigned char i,j;for(i=133;i>0;i--)for(j=6;j>0;j--);
}void c595_in(unsigned char Data)
{unsigned char i;for(i = 0; i < 8; i++) //循环8次,刚好移完8位{SCL1 = 0; //先将移位寄存器控制引脚置为低 _nop_();if((Data & 0x80)== 0x80)SDA1 =1;elseSDA1 =0;Data <<= 1; //将数据的次高位移到最高位SCL1 = 1; //再置为高,产生移位时钟上升沿,上升沿时数据寄存器的数据移位_nop_();}
}void c595_out(void)
{SCL2 = 0; //先将存储寄存器引脚置为低_nop_();SCL2 = 1; //再置为高,产生移位时钟上升沿,上升沿时移位寄存器的数据进入数据存储寄存器,更新显示数据。
}void Timer_Init(void)
{TMOD=0x11; //0110 0001 //方式一TH0= (65536-50000)/256;TL0= (65536-50000)%256; // 50msTH1= (65536-50000)/256;TL1= (65536-50000)%256; // 50msTR0=1; //开启T0TR1=1; //开启T1//ET0=1; //T0中断允许//ET1=1; //T1中断允许EA=1; //总中断开启
}void T0_INT0(void) interrupt 1
{ ms++;if(ms == 20) //1s时间到{ms = 0;if(miao > 0){miao--;}if(miao == 0) //秒数计时为0{if(fen == 0) //没有分钟数{if(shi == 0){TR0 = 0;} //判断是否还有时数,若没有,倒计时结束,停止计时 else{ shi--;fen = 60;}}if(fen > 0) //有分钟数{fen--;miao = 59;}}}
}void T0_INT1(void) interrupt 3
{ smg_c++;if(smg_c == 10){smg_c = 0;smg_f = ~smg_f;}
}/*********外部中断0、1初始化***********/
void Exint_Init()
{IT0 = 1;IT1 = 1; //下降沿触发EX0 = 1;EX1 = 1;PX0 = 1; // 中断优先级设置PX1 = 0;
}/*********外部中断0服务函数***********/
void Exint0_Service() interrupt 0
{if(set_flag == 3) {miao++;if(miao == 60){miao = 59;}}if(set_flag == 2) {fen++;if(fen == 60){fen = 59;} //按键加}if(set_flag == 1) {shi++;if(shi == 100) {shi = 99;}}
}/*********外部中断1服务函数***********/
void Exint1_Service() interrupt 2
{if(set_flag == 3) {if(miao > 0){miao--;}}if(set_flag == 2) {if(fen > 0){fen--;} //按键减}if(set_flag == 1) {if(shi > 0){shi--;}}
}void displayTime(void) //刷新时间显示
{if(((smg_f == 0) && (set_flag == 1))|| (set_flag == 0) || (set_flag == 2) || (set_flag == 3)){c595_in(wei[0]); //先传位码c595_in(disp[shi/10]); //再传段码c595_out();delay2ms();c595_in(wei[1]); //先传位码c595_in(disp[shi%10]); //再传段码 c595_out();delay2ms();} c595_in(wei[2]); //先传位码c595_in(disp[16]); //再传段码 c595_out();delay2ms();if(((smg_f == 0) && (set_flag == 2))|| (set_flag == 0) || (set_flag == 1) || (set_flag == 3)){c595_in(wei[3]); //先传位码c595_in(disp[fen/10]); //再传段码c595_out();delay2ms();c595_in(wei[4]); //先传位码c595_in(disp[fen%10]); //再传段码c595_out();delay2ms();}c595_in(wei[5]); //先传位码c595_in(disp[16]); //再传段码 c595_out();delay2ms();if(((smg_f == 6) && (set_flag == 3))|| (set_flag == 0) || (set_flag == 1) || (set_flag == 2)){c595_in(wei[6]); //先传位码c595_in(disp[miao/10]); //再传段码c595_out();delay2ms();c595_in(wei[7]); //先传位码c595_in(disp[miao%10]); //再传段码c595_out();delay2ms();}
}void main(void)
{Timer_Init();Exint_Init();while(1){displayTime(); //刷新时间显示//检测启动按钮if(start_key == 0) //按下了启动、停止按钮{Delay1ms(10); //延时消抖if(start_key == 0){ET0 = 1; //T0中断允许打开,开始计时set_flag = 0;TR0 = 1;while(start_key == 0) displayTime(); //防止按键重复检测}}//检测停止按钮if(pause_key == 0) //已启动计时,并且暂停按钮按下{Delay1ms(10); //延时消抖if(pause_key == 0){ET0 = 0; while(pause_key == 0) displayTime(); //防止按键重复检测}}if(set_key == 0) //已启动计时,并且暂停按钮按下{Delay1ms(10); //延时消抖if(set_key == 0){ET1=1; set_flag++;if(set_flag == 4){set_flag = 1;}while(set_key == 0) displayTime(); //防止按键重复检测}}}
}
仿真源文件与keil工程百度网盘链接:https://pan.baidu.com/s/1L-FwKgHhRAUiHs6nwD4cBA
提取码:fqjd