1,应用mcs-51单片机设计电子跑表
2,选用4位LED数码显示,跑表计时时间0–99.99s循环,另有一键可循环控制暂停–继续–结束计时–清零功能
要求:实验视频,硬件电路图以及注释,编程方法(说明程序编写的原理和方法),程序清单以及注释,电路图和程序流程图
;定义端口和IO口寄存器
sbit start_button=P3^0 ;开始按钮
sbit reset_button=P3^1 ;重置按钮
sbit led_digit_1 = P1^0 ;第一位LED数码管
sbit led_digit_2 = P1^1 ;第二位LED数码管
sbit led_digit_3 = P1^2 ;第三位LED数码管
sbit led_digit_4 = P1^3 ;第四位LED数码管
sbit led_segments = P0 ;LED数码管段控制
; 定义常量
us equ 2 ; 定时器2的预分频值
digit_count equ 100 ; 跑表计时时间,单位为毫秒
; 定义变量
sdata xdata digits[4] ; 数码管显示的数字
sbit xdata is_paused = 0x4000 ; 是否已暂停标志
sbit xdata is_reset = 0x4001 ; 是否已重置标志
data ddata time_counter_low ; 计时器低字节计数器
data ddata time_counter_high ; 计时器高字节计数器
; 入口点
start:
; 初始化 IO 端口
InitSegments:
led_segments = 0FFH;
led_digit_1=0;
led_digit_2=0;
led_digit_3=0;
led_digit_4=0;
; 初始化定时器2
InitTimer:
TH1 = 256 - digit_count / us; ; 计算定时器值,us 表示微秒
TL1 = TH1;
TMOD = 20H ; 定时器2工作在模式2,TH1是高字节,TL1是低字节
TR1 = 1 ; 开始定时器2计时
; 循环显示跑表时间
Loop:
; 扫描按钮状态
if(is_reset)
{
; 重置按钮按下,跑表时间清零
time_counter_low = 0;
time_counter_high = 0;
reset_button = 0;
is_reset = 0;
}
if(is_paused)
{
; 暂停状态
start_button = 0;
display_time();
}
else
{
; 运行状态
start_button = 1;
increment_time();
display_time();
if(time_counter_high >= 10)
{
; 计时器已满,停止计时
time_counter_high = 9;
time_counter_low = 99;
is_paused = 1;
}
}
goto Loop; ; 返回循环
; 计时器累加
increment_time:
time_counter_low = time_counter_low + 1;
if(time_counter_low == 0)
{
time_counter_high = time_counter_high + 1;
}
return;
; 显示跑表时间
display_time:
digits[0] = time_counter_high / 10;
digits[1] = time_counter_high % 10;
digits[2] = time_counter_low / 10;
digits[3] = time_counter_low % 10;
display_segments();
return;
; 显示数码管的段值
display_segments:
led_segments = 0x3f;
led_digit_1=1;
led_segments = get_digit_segments(digits[0]);
delay_us(50);
led_digit_1=0;
led_segments = 0x3f;
led_digit_2=1;
led_segments = get_digit_segments(digits[1]);
delay_us(50);
led_digit_2=0;
led_segments = 0x3f;
led_digit_3=1;
led_segments = get_digit_segments(digits[2]);
delay_us(50);
led_digit_3=0;
led_segments = 0x3f;
led_digit_4=1;
led_segments = get_digit_segments(digits[3]);
delay_us(50);
led_digit_4=0;
return;
; 获取数码管段控制值
get_digit_segments:
mov r0, #0
mov a, dpl
anl a, #0FH
add a, #0F0H
mov r0, a
ret
; 微秒级延时子程序
delay_us:
mov r7, dpl
delay_us_1:
mov r6, #us / 3
delay_us_2:
mov r5, #us / 3
delay_us_3:
djnz r5, delay_us_3
djnz r6, delay_us_2
djnz r7, delay_us_1
ret
使用 P3^0 和 P3^1 连接开始和重置按钮,使用 P1^0 - P1^3 和 P0 端口连接 LED 数码管,本程序使用定时器2进行计时,定时器工作在模式2,TH1 是高字节,TL1 是低字节,计算定时器值,每次增加一个毫秒,到达时间上限后停止计时,支持暂停和恢复操作以及重置跑表功能。
由于本程序是模拟计时器进行计时的,在实际运用过程中时间精度可能不够高,您可以根据实际情况进行调整。
对于应用 mcs-51 单片机设计电子跑表,您需要完成以下几个步骤:
硬件设计和调试。将 4 位 LED 数码显示器与单片机连接起来,并添加按键等外设。根据需求,您需要编写相应的硬件驱动程序,例如数码管显示、按键检测等。在硬件调试过程中,请注意检查各个电路元件是否正确连接,以及电源电压是否稳定。
编写程序。通过 Keil C51 等集成开发环境,编写跑表程序。主要包括计时功能、暂停/继续功能、结束计时功能和清零功能。在编写程序时,您需要注意时间精度、按键检测的 debounce 等问题。
调试和测试。在程序编写完成后,您需要进行系统的调试和测试。尝试各种场景下的操作,如开始计时、暂停计时、继续计时、结束计时、清零计时等。
以下是一份基本的程序框架供参考:
#include <reg52.h>
#include <intrins.h>
sbit KEY_START = P1^0; // 开始/继续按钮
sbit KEY_PAUSE = P1^1; // 暂停按钮
sbit KEY_STOP = P1^2; // 停止按钮
sbit KEY_RESET = P1^3; // 清零按钮
sbit DIG_1 = P2^0; // 第一位数码管
sbit DIG_2 = P2^1; // 第二位数码管
sbit DIG_3 = P2^2; // 第三位数码管
sbit DIG_4 = P2^3; // 第四位数码管
unsigned int cnt = 0; // 计时器计数值,初始为0
unsigned char flag_start = 0; // 开始/继续标志位
unsigned char flag_pause = 0; // 暂停标志位
unsigned char flag_stop = 0; // 停止标志位
unsigned char flag_reset = 0; // 清零标志位
void init_timer() {
TMOD = 0x01; // 定时器0工作在模式1(16位自动重装载)
TH0 = 0xFC; // 定时器初值为65536-50000=15536,定时50000us,即50ms
TL0 = 0x00;
ET0 = 1; // 定时器0中断使能
EA = 1; // 总中断使能
TR0 = 1; // 启动定时器0
}
void display(unsigned int num) {
unsigned int digit[4];
digit[0] = num % 10;
digit[1] = num / 10 % 10;
digit[2] = num / 100 % 10;
digit[3] = num / 1000;
DIG_1 = 0; P0 = digit[0]; DIG_1 = 1;
DIG_2 = 0; P0 = digit[1]; DIG_2 = 1;
DIG_3 = 0; P0 = digit[2]; DIG_3 = 1;
DIG_4 = 0; P0 = digit[3]; DIG_4 = 1;
}
void main() {
init_timer(); // 定时器初始化
while (1) {
if (!KEY_START && !flag_start) { // 开始/继续计时
flag_start = 1;
flag_pause = 0;
flag_stop = 0;
flag_reset = 0;
}
if (!KEY_PAUSE && flag_start && !flag_pause) { // 暂停计时
flag_pause = 1;
}
if (!KEY_PAUSE && flag_start && flag_pause) { // 继续计时
flag_pause = 0;
}
if (!KEY_STOP && flag_start && !flag_stop) { // 结束计时
flag_stop = 1;
}
当检测到停止按钮被按下,并且当前正在计时,且之前未标记过停止,则将标志位置为 1。
在主程序中需要添加定时器中断处理函数,如下所示:
void timer0_isr() interrupt 1 {
TH0 = 0xFC; // 定时器初值重置
TL0 = 0x00;
if (flag_start && !flag_pause && !flag_stop && !flag_reset) {
cnt++; // 计时器加一
if (cnt > 9999) cnt = 0; // 计时器溢出
display(cnt); // 在数码管上显示计数值
}
}
该函数会在定时器每次计时完成时被调用。如果已经开始计时、未暂停、未停止并且未清零,则将计数器 cnt 加 1,并将其显示在数码管上。如果计数器超过了 9999,则将其归零。
至此,您可以继续完善其他功能,比如暂停/继续计时和清零计数器。在程序编写过程中,还需要注意处理按键抖动问题,避免错误操作。另外,您还需要将程序烧录到单片机上进行测试,并调试硬件电路以及程序逻辑。
一个简易的,可以借鉴下
电子跑表程序:
/************************ mian.c *************************/
#include <reg52.h>
#include "1602.h"
#include "main.h"
#include "delay.h"
#include "control.h"
#include "key.h"
uchar code tablea[]=" 2020-12-31 Thu ";
uchar code tableb[]=" 00:00:00 ";
uchar code tablec[]="0 -- 00:00:00:00";
uchar code tabled[]=" 00:00:00:00 ";
extern uint mode,imode;
void main()
{
uchar num;
init();
while(1)
{
keyscan() ;
if (mode==CLOCK)
{
/***********************************/
if(imode)
{
write_com(0x80);
for(num=0;num<=15;num++)
{
write_date(tablea[num]);
delay(5);
}
write_com(0x80+0x40);
for(num=0;num<=15;num++)
{
write_date(tableb[num]);
delay(5);
}
imode=0;
}
/***********************************/
keyscan_clock();
if(TR0==1)show_clock();
}
if (mode==RUN)
{
/***********************************/
if(imode)
{
write_com(0x80);
for(num=0;num<=15;num++)
{
write_date(tabled[num]);
delay(5);
}
write_com(0x80+0x40);
for(num=0;num<=15;num++)
{
write_date(tablec[num]);
delay(5);
}
imode=0;
}
/***********************************/
keyscan_run();
show_run();
}
}
}
/********************** main.h ********************************/
#ifndef __MAIN_H_
#define __MAIN_H_
#define uchar unsigned char
#define uint unsigned int
#endif
/************************ 1602.c *****************************/
#include <reg52.h>
#include "delay.h"
#include "1602.h"
sbit rs=P1^0;
sbit lcden=P1^2;
sbit rw=P1^1;
void write_com(uchar com)
{
rs=0;
lcden=0;
P0=com;
delay(5);
lcden=1;
delay(5);
lcden=0;
}
void write_date(uchar date)
{
rs=1;
lcden=0;
P0=date;
delay(5);
lcden=1;
delay(5);
lcden=0;
}
void write_sfm(uchar hang,uchar add,uchar date)
{
uchar shi,ge;
shi=date/10;
ge=date%10;
if (hang==1)
write_com(0x80+add);
if(hang==2)
write_com(0x80+0x40+add);
write_date(0x30+shi);
write_date(0x30+ge);
}
/******************** 1602.h *******************************/
#ifndef __1602_H_
#define __1602_H_
#include "main.h"
void write_com(uchar com);
void write_date(uchar date);
void write_sfm(uchar hang,uchar add,uchar date);
#endif
/************************ delay.c ****************************/
#include "delay.h"
void delay(uint z)
{
uint x,y;
for(x=z;x>0;x--)
for(y=800;y>0;y--);
}
/************************* delay.h ***********************/
#ifndef __DELAY_H__
#define __DELAY_H__
#include "main.h"
void delay(uint z);
#endif
/********************** key.c ********************************/
#include <reg52.h>
#include "key.h"
#include "main.h"
#include "delay.h"
#include "1602.h"
sbit s1=P2^0; //时钟调整按键
sbit s2=P2^1; //时钟加按键/计时清零键
sbit s3=P2^2; //时钟减按键/计时开始或停止
sbit time_cap=P2^3; //计时捕获按键
sbit mode_change=P2^4; //模式切换按键
uint mode=CLOCK; // mode : 模式标志位
extern uchar count,miao,shi,fen; //时钟参数
extern uchar msecond,second,minute,hour; //跑表参数
uchar s1num; // 调整时钟用的状态标志
uint s1_state=0,s3_state=0;
uint site=0,ssite=0; //计时保存排名位
long time[15]; //计时保存数组
uint flag=0,imode=0;
void keyscan(void) //模式按键扫描
{
if(mode_change==0)
{
delay(5);
if(mode_change==0) //模式转换huan
{
flag++;
if(flag%2==0)
{
mode=CLOCK;
imode=1;
}
else
{
mode=RUN;
imode=2;
}
while(!mode_change);
}
}
}
/*
keyscan_clock : 时钟模式下按键扫描
输入 : 无
输出 : 无
*/
void keyscan_clock(void)
{
if(s1==0)
{
delay(5);
if(s1==0)
{ s1num++;
while(!s1);
if(s1num==1)
{
TR0=0;
write_com(0x80+0x40+10);
write_com(0x0f);
}
}
if(s1num==2)
{
write_com(0x80+0x40+7);
}
if(s1num==3)
{
write_com(0x80+0x40+4);
}
if(s1num==4)
{
s1num=0;
write_com(0x0c);
TR0=1;
}
}
if(s1num!=0)
{
if(s2==0)
{
delay(5);
if(s2==0)
{
while(!s2);
if(s1num==1)
{
miao++;
if(miao==60)
miao=0;
write_sfm(2,10,miao);
write_com(0x80+0x40+10);
}
if(s1num==2)
{
fen++;
if(fen==60)
fen=0;
write_sfm(2,7,fen);
write_com(0x80+0x40+7);
}
if(s1num==3)
{
shi++;
if(shi==24)
shi=0;
write_sfm(2,4,shi);
write_com(0x80+0x40+4);
}
}
}
if(s3==0)
{
delay(5);
if(s3==0)
{
while(!s3);
if(s1num==1)
{
miao--;
if(miao==-1)
miao=59;
write_sfm(2,10,miao);
write_com(0x80+0x40+10);
}
if(s1num==2)
{
fen--;
if(fen==-1)
fen=59;
write_sfm(2,7,fen);
write_com(0x80+0x40+7);
}
if(s1num==3)
{
shi--;
if(shi==-1)
shi=23;
write_sfm(2,4,shi);
write_com(0x80+0x40+4);
}
}
}
}
}
/*
keyscan_run : 跑表模式下按键扫描
输入 : 无
输出 : 无
*/
void keyscan_run(void)
{
if(time_cap==0)
{
delay(5);
if(time_cap==0)
{
if(site<15)
{
site++;
time[site]=hour*1000000+minute*10000+second*100+msecond;
write_sfm(2,0,site);
write_sfm(2,5,time[site]/1000000);
write_sfm(2,8,time[site]%1000000/10000);
write_sfm(2,11,time[site]%10000/100);
write_sfm(2,14,time[site]%100);
}
}
}
if(s1==0) //计时清零
{
uint i=0;
delay(5);
if( s1==0 && s3_state%2==0 )
{
msecond=second=minute=hour=0;
for(i=0;i<15;i++)
{
time[i]=0;
}
site=0;
write_sfm(2,0,0);
write_sfm(2,5,0);
write_sfm(2,8,0);
write_sfm(2,11,0);
write_sfm(2,14,0);
}
}
if(s2==0) // 查询数据
{
delay(5);
if(s2==0)
{
ssite++;
if(ssite>site) ssite=1;
write_sfm(2,0,ssite);
write_sfm(2,5,time[ssite]/1000000);
write_sfm(2,8,time[ssite]%1000000/10000);
write_sfm(2,11,time[ssite]%10000/100);
write_sfm(2,14,time[ssite]%100);
}
}
if(s3==0) // 开始 / 停止 计时
{
delay(5);
if(s3==0)
{
s3_state++;
if(s3_state%2)
{
TR1=1;
}
if(s3_state%2==0)
{
TR1=0;
}
while(!s3);
}
}
}
/********************** key.h ********************************/
#ifndef __KEY_H_
#define __KEY_H_
#define CLOCK 0 //时钟模式
#define RUN 1 //跑表模式
void keyscan_clock(void); //时钟模式按键扫描
void keyscan(void); //状态模式扫描
void keyscan_run(void); //跑表模式扫描
#endif
/***************** control.c ******************************/
#include "control.h"
#include "1602.h"
#include "delay.h"
uchar count,miao,shi,fen; //时钟参数
uchar code table[]=" 2020-12-31 Thu "; //初始化LCD见界面参数
uchar code table1[]=" 00:00:00 ";
uchar msecond,second,minute,hour; //跑表参数
//初始化,LCD及定时器
void init(void)
{
uchar num;
// lcden=0;
// fen=59;
// miao=53;
// shi=23;
write_com(0x38);
write_com(0x0c);
write_com(0x06);
write_com(0x01);
write_com(0x80);
for(num=0;num<=15;num++)
{
write_date(table[num]);
delay(5);
}
write_com(0x80+0x40);
for(num=0;num<=15;num++)
{
write_date(table1[num]);
delay(5);
}
TMOD=0x11;
TH0 = 0x88;
TL0 = 0x00;
EA=1;
ET0=1;
TR0=1;
TH1 = 0x0E8; //定时器1 10ms
TL1 = 0x00;
ET1=1;
TR1 = 0;
}
//定时器2中断服务函数,用于时钟进行
void timer0() interrupt 1
{
TH0 = 0x88;
TL0 = 0x00;
count++;
if(count==20)
{
count=0;
miao++;
if(miao==60)
{
miao=0;
fen++;
if(fen==60)
{
fen=0;
shi++;
if(shi==24)
{
shi=0;
}
}
}
}
}
//定时器1中断服务函数,用于跑表计时
void timer1() interrupt 3
{
TH1 = 0x0E8; //定时器1 10ms
TL1 = 0x00;
msecond++;
if(msecond==100)
{
msecond=0;
second++;
if(second==60)
{
second=0;
minute++;
if(minute==60)
{
minute=0;
hour++;
if(hour==24)
hour=0;
}
}
}
}
//更新时钟函数
void show_clock(void)
{
write_sfm(2,4,shi);
write_sfm(2,7,fen);
write_sfm(2,10,miao);
}
//更新跑表计时函数
void show_run(void)
{
write_sfm(1,2,hour);
write_sfm(1,5,minute);
write_sfm(1,8,second);
write_sfm(1,11,msecond);
}
/*************************** control.h ********************/
#ifndef __CONTROL_H_
#define __CONTROL_H_
void init(void);
void show_clock(void);
void show_run(void);
#endif
电子跑表程序:
/************************ mian.c *************************/
#include <reg52.h>
#include "1602.h"
#include "main.h"
#include "delay.h"
#include "control.h"
#include "key.h"
uchar code tablea[]=" 2020-12-31 Thu ";
uchar code tableb[]=" 00:00:00 ";
uchar code tablec[]="0 -- 00:00:00:00";
uchar code tabled[]=" 00:00:00:00 ";
extern uint mode,imode;
void main()
{
uchar num;
init();
while(1)
{
keyscan() ;
if (mode==CLOCK)
{
/***********************************/
if(imode)
{
write_com(0x80);
for(num=0;num<=15;num++)
{
write_date(tablea[num]);
delay(5);
}
write_com(0x80+0x40);
for(num=0;num<=15;num++)
{
write_date(tableb[num]);
delay(5);
}
imode=0;
}
/***********************************/
keyscan_clock();
if(TR0==1)show_clock();
}
if (mode==RUN)
{
/***********************************/
if(imode)
{
write_com(0x80);
for(num=0;num<=15;num++)
{
write_date(tabled[num]);
delay(5);
}
write_com(0x80+0x40);
for(num=0;num<=15;num++)
{
write_date(tablec[num]);
delay(5);
}
imode=0;
}
/***********************************/
keyscan_run();
show_run();
}
}
}
/********************** main.h ********************************/
#ifndef __MAIN_H_
#define __MAIN_H_
#define uchar unsigned char
#define uint unsigned int
#endif
/************************ 1602.c *****************************/
#include <reg52.h>
#include "delay.h"
#include "1602.h"
sbit rs=P1^0;
sbit lcden=P1^2;
sbit rw=P1^1;
void write_com(uchar com)
{
rs=0;
lcden=0;
P0=com;
delay(5);
lcden=1;
delay(5);
lcden=0;
}
void write_date(uchar date)
{
rs=1;
lcden=0;
P0=date;
delay(5);
lcden=1;
delay(5);
lcden=0;
}
void write_sfm(uchar hang,uchar add,uchar date)
{
uchar shi,ge;
shi=date/10;
ge=date%10;
if (hang==1)
write_com(0x80+add);
if(hang==2)
write_com(0x80+0x40+add);
write_date(0x30+shi);
write_date(0x30+ge);
}
/******************** 1602.h *******************************/
#ifndef __1602_H_
#define __1602_H_
#include "main.h"
void write_com(uchar com);
void write_date(uchar date);
void write_sfm(uchar hang,uchar add,uchar date);
#endif
/************************ delay.c ****************************/
#include "delay.h"
void delay(uint z)
{
uint x,y;
for(x=z;x>0;x--)
for(y=800;y>0;y--);
}
/************************* delay.h ***********************/
#ifndef __DELAY_H__
#define __DELAY_H__
#include "main.h"
void delay(uint z);
#endif
/********************** key.c ********************************/
#include <reg52.h>
#include "key.h"
#include "main.h"
#include "delay.h"
#include "1602.h"
sbit s1=P2^0; //时钟调整按键
sbit s2=P2^1; //时钟加按键/计时清零键
sbit s3=P2^2; //时钟减按键/计时开始或停止
sbit time_cap=P2^3; //计时捕获按键
sbit mode_change=P2^4; //模式切换按键
uint mode=CLOCK; // mode : 模式标志位
extern uchar count,miao,shi,fen; //时钟参数
extern uchar msecond,second,minute,hour; //跑表参数
uchar s1num; // 调整时钟用的状态标志
uint s1_state=0,s3_state=0;
uint site=0,ssite=0; //计时保存排名位
long time[15]; //计时保存数组
uint flag=0,imode=0;
void keyscan(void) //模式按键扫描
{
if(mode_change==0)
{
delay(5);
if(mode_change==0) //模式转换huan
{
flag++;
if(flag%2==0)
{
mode=CLOCK;
imode=1;
}
else
{
mode=RUN;
imode=2;
}
while(!mode_change);
}
}
}
/*
keyscan_clock : 时钟模式下按键扫描
输入 : 无
输出 : 无
*/
void keyscan_clock(void)
{
if(s1==0)
{
delay(5);
if(s1==0)
{ s1num++;
while(!s1);
if(s1num==1)
{
TR0=0;
write_com(0x80+0x40+10);
write_com(0x0f);
}
}
if(s1num==2)
{
write_com(0x80+0x40+7);
}
if(s1num==3)
{
write_com(0x80+0x40+4);
}
if(s1num==4)
{
s1num=0;
write_com(0x0c);
TR0=1;
}
}
if(s1num!=0)
{
if(s2==0)
{
delay(5);
if(s2==0)
{
while(!s2);
if(s1num==1)
{
miao++;
if(miao==60)
miao=0;
write_sfm(2,10,miao);
write_com(0x80+0x40+10);
}
if(s1num==2)
{
fen++;
if(fen==60)
fen=0;
write_sfm(2,7,fen);
write_com(0x80+0x40+7);
}
if(s1num==3)
{
shi++;
if(shi==24)
shi=0;
write_sfm(2,4,shi);
write_com(0x80+0x40+4);
}
}
}
if(s3==0)
{
delay(5);
if(s3==0)
{
while(!s3);
if(s1num==1)
{
miao--;
if(miao==-1)
miao=59;
write_sfm(2,10,miao);
write_com(0x80+0x40+10);
}
if(s1num==2)
{
fen--;
if(fen==-1)
fen=59;
write_sfm(2,7,fen);
write_com(0x80+0x40+7);
}
if(s1num==3)
{
shi--;
if(shi==-1)
shi=23;
write_sfm(2,4,shi);
write_com(0x80+0x40+4);
}
}
}
}
}
/*
keyscan_run : 跑表模式下按键扫描
输入 : 无
输出 : 无
*/
void keyscan_run(void)
{
if(time_cap==0)
{
delay(5);
if(time_cap==0)
{
if(site<15)
{
site++;
time[site]=hour*1000000+minute*10000+second*100+msecond;
write_sfm(2,0,site);
write_sfm(2,5,time[site]/1000000);
write_sfm(2,8,time[site]%1000000/10000);
write_sfm(2,11,time[site]%10000/100);
write_sfm(2,14,time[site]%100);
}
}
}
if(s1==0) //计时清零
{
uint i=0;
delay(5);
if( s1==0 && s3_state%2==0 )
{
msecond=second=minute=hour=0;
for(i=0;i<15;i++)
{
time[i]=0;
}
site=0;
write_sfm(2,0,0);
write_sfm(2,5,0);
write_sfm(2,8,0);
write_sfm(2,11,0);
write_sfm(2,14,0);
}
}
if(s2==0) // 查询数据
{
delay(5);
if(s2==0)
{
ssite++;
if(ssite>site) ssite=1;
write_sfm(2,0,ssite);
write_sfm(2,5,time[ssite]/1000000);
write_sfm(2,8,time[ssite]%1000000/10000);
write_sfm(2,11,time[ssite]%10000/100);
write_sfm(2,14,time[ssite]%100);
}
}
if(s3==0) // 开始 / 停止 计时
{
delay(5);
if(s3==0)
{
s3_state++;
if(s3_state%2)
{
TR1=1;
}
if(s3_state%2==0)
{
TR1=0;
}
while(!s3);
}
}
}
/********************** key.h ********************************/
#ifndef __KEY_H_
#define __KEY_H_
#define CLOCK 0 //时钟模式
#define RUN 1 //跑表模式
void keyscan_clock(void); //时钟模式按键扫描
void keyscan(void); //状态模式扫描
void keyscan_run(void); //跑表模式扫描
#endif
/***************** control.c ******************************/
#include "control.h"
#include "1602.h"
#include "delay.h"
uchar count,miao,shi,fen; //时钟参数
uchar code table[]=" 2020-12-31 Thu "; //初始化LCD见界面参数
uchar code table1[]=" 00:00:00 ";
uchar msecond,second,minute,hour; //跑表参数
//初始化,LCD及定时器
void init(void)
{
uchar num;
// lcden=0;
// fen=59;
// miao=53;
// shi=23;
write_com(0x38);
write_com(0x0c);
write_com(0x06);
write_com(0x01);
write_com(0x80);
for(num=0;num<=15;num++)
{
write_date(table[num]);
delay(5);
}
write_com(0x80+0x40);
for(num=0;num<=15;num++)
{
write_date(table1[num]);
delay(5);
}
TMOD=0x11;
TH0 = 0x88;
TL0 = 0x00;
EA=1;
ET0=1;
TR0=1;
TH1 = 0x0E8; //定时器1 10ms
TL1 = 0x00;
ET1=1;
TR1 = 0;
}
//定时器2中断服务函数,用于时钟进行
void timer0() interrupt 1
{
TH0 = 0x88;
TL0 = 0x00;
count++;
if(count==20)
{
count=0;
miao++;
if(miao==60)
{
miao=0;
fen++;
if(fen==60)
{
fen=0;
shi++;
if(shi==24)
{
shi=0;
}
}
}
}
}
//定时器1中断服务函数,用于跑表计时
void timer1() interrupt 3
{
TH1 = 0x0E8; //定时器1 10ms
TL1 = 0x00;
msecond++;
if(msecond==100)
{
msecond=0;
second++;
if(second==60)
{
second=0;
minute++;
if(minute==60)
{
minute=0;
hour++;
if(hour==24)
hour=0;
}
}
}
}
//更新时钟函数
void show_clock(void)
{
write_sfm(2,4,shi);
write_sfm(2,7,fen);
write_sfm(2,10,miao);
}
//更新跑表计时函数
void show_run(void)
{
write_sfm(1,2,hour);
write_sfm(1,5,minute);
write_sfm(1,8,second);
write_sfm(1,11,msecond);
}
/*************************** control.h ********************/
#ifndef __CONTROL_H_
#define __CONTROL_H_
void init(void);
void show_clock(void);
void show_run(void);
#endif
关于这方面的原理图,程序例子网上都是一大堆的
结合ChatGPT参考建议作答:
1、程序编写的原理和方法
该电子跑表的设计基于MCS-51单片机,使用4位LED数码显示,计时时间为0-99.99秒循环。同时,该跑表还具有暂停、继续、结束计时和清零功能。
2、程序的主要思路是使用定时器中断来实现计时功能,并使用按键中断来实现暂停、继续、结束计时和清零功能。具体实现步骤如下:
1.初始化定时器和按键中断。
2.在定时器中断服务程序中,每隔10ms更新计时器的值,并将计时器的值转换为BCD码,然后将BCD码输出到LED数码管上。
3.在按键中断服务程序中,根据按键的不同,执行相应的操作,如暂停、继续、结束计时和清零。
4.在主函数中,启用中断并等待中断事件的发生。
5.程序清单以及注释
#include <reg51.h>
#define uchar unsigned char
#define uint unsigned int
sbit key1 = P3^2; // 暂停/继续按键
sbit key2 = P3^3; // 结束计时按键
sbit key3 = P3^4; // 清零按键
uchar code table[] = { // 数码管显示表
0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07,
0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71
};
uchar timer[4] = {0, 0, 0, 0}; // 计时器,初始值为0
void init_timer() { // 初始化定时器
TMOD = 0x01; // 定时器0,模式1
TH0 = 0x3c; // 定时器初值,10ms
TL0 = 0xb0;
ET0 = 1; // 允许定时器中断
TR0 = 1; // 启动定时器
}
void init_interrupt() { // 初始化中断
EA = 1; // 允许中断
EX0 = 1; // 允许外部中断0
IT0 = 1; // 下降沿触发
}
void display() { // 数码管显示
uchar i;
for (i = 0; i < 4; i++) {
P2 = 0xff; // 关闭所有数码管
P0 = table[timer[i]]; // 显示当前数码
P2 = ~(0x01 << i); // 打开当前数码管
delay(1); // 延时1ms
}
}
void pause() { // 暂停计时
TR0 = 0; // 停止定时器
while (key1 == 0); // 等待按键释放
}
void resume() { // 继续计时
TR0 = 1; // 启动定时器
}
void stop() { // 结束计时
TR0 = 0; // 停止定时器
timer[0] = timer[1] = timer[2] = timer[3] = 0; // 计时器清零
display(); // 显示清零后的数码
}
void clear() { // 清零计时
timer[0] = timer[1] = timer[2] = timer[3] = 0; // 计时器清零
display(); // 显示清零后的数码
}
void delay(uint ms) { // 延时函数
uint i, j;
for (i = 0; i < ms; i++)
for (j = 0; j < 110; j++);
}
void main() {
init_timer(); // 初始化定时器
init_interrupt(); // 初始化中断
while (1); // 等待中断事件的发生
}
void timer_isr() interrupt 1 { // 定时器中断服务程序
TH0 = 0x3c; // 重新赋初值
TL0 = 0xb0;
timer[3]++; // 计时器加1
if (timer[3] == 10) { // 判断是否达到10ms
timer[3] = 0;
timer[2]++;
if (timer[2] == 10) { // 判断是否达到1s
timer[2] = 0;
timer[1]++;
if (timer[1] == 10) { // 判断是否达到10s
timer[1] = 0;
timer[0]++;
if (timer[0] == 10) // 判断是否达到99.99s
timer[0] = 0;
}
}
}
display(); // 显示当前计时器的值
}
void key1_isr() interrupt 0 { // 暂停/继续按键中断服务程序
delay(10); // 延时去抖
if (key1 == 0) { // 判断按键是否按下
pause(); // 暂停计时
while (1) { // 等待按键事件的发生
if (key1 == 0) { // 判断按键是否按下
delay(10); // 延时去抖
if (key1 == 0) // 再次判断按键是否按下
resume(); // 继续计时
break;
}
if (key2 == 0) { // 判断是否按下结束计时按键
delay(10); // 延时去抖
if (key2 == 0) { // 再次判断是否按下结束计时按键
stop(); // 结束计时
break;
}
}
}
}
}
void key2_isr() interrupt 0 { // 结束计时按键中断服务程序
delay(10); // 延时去抖
if (key2 == 0) { // 判断按键是否按下
stop(); // 结束计时
}
}
void key3_isr() interrupt 0 { // 清零按键中断服务程序
delay(10); // 延时去抖
if (key3 == 0) { // 判断按键是否按下
clear(); // 清零计时
}
}