51单片机实现的秒表

功能描述

  1. 使用6段数码管显示时间 分钟-秒-100毫秒 (十分钟以内,0.1秒一位)
  2. 记录8组数据 使用LED灯代表那组数据
  3. 使用三个按键 K1开始/暂停 K2重置 K3计次/记录(说明:运行状态下,不能重置,k3增加一条记录点亮一个LED,暂停状态下,可重置,k3从第一条记录查看并点亮相应LED灯)

实现分析

  1. 定时器 产生固定周期中断(16 13 8位定时器)
  2. 数码管动态刷新(软延时、定时器)
  3. 按键消抖(软延时、定时器)
  4. 记录 使用数组或字符串?使用LED表示那组数据
  5. 按键功能定义和状态(开始状态计次、重置失效,暂停状态重置、记录激活)

最终实现

  1. 16位定时器,定时2ms(由于单片机晶振为11.0592MHz所以有0.01%误差 中断相应也会导致误差)
  2. 6位数码管 分-秒-0.1s,最大10分钟
  3. 按键消抖使用定时器 10ms
  4. 8组记录 对应8个LED灯

相关说明

P1对应8个LED灯
六位共阴数码管 由P0连接两个锁存器 控制位选和段选
效果如下图


51单片机实现的秒表_第1张图片
946af505-2a92-49c7-a2ee-97d403e05523.jpg
#include 
unsigned char code leddata[]= {  0x3F,  0x06,  0x5B,  0x4F, //0 1 2 3
                                 0x66,  0x6D,  0x7D,  0x07,  //4 5 6 7
                                 0x7F,  0x6F,  0x77,  0x7C,  //8 9 A b
                                 0x39,  0x5E,  0x79,  0x71,  //c d E F
                                 0x76,  0x38,  0x37,  0x3E,  //H L n u
                                 0x73,  0x5C,  0x40,  0x00,  //P o - 清屏
                              }; 
#define digitData P0          //数码管数据
#define startBtn    0       //按键值
#define resetBtn    1
#define counterBtn  2
sbit DX = P2^6;//段选
sbit WX = P2^7;//位选
sbit kaiShi = P3^4;//开始、暂停按键
sbit chongZhi = P3^5;//重置按键
sbit jiCi = P3^6;//计次、记录查看按键
unsigned int recode[8] = {0};//记录数据
unsigned char recodeNum = 0;//记录数据值位置
unsigned char showRe = 0; //查看哪项记录值
unsigned int times = 0; //当前计时时间
unsigned int saveTimes = 0;//暂停查看记录时保存时间数据
unsigned char counter = 0;//定时器次数,构成100ms
unsigned char shuanXi = 0,sXW = 1;//刷新数码管
unsigned char runState = 0;//0表示停止,1表示运行
unsigned char keyTemp[3]= {0}; //按键按下状态持续时间

//六位数码管显示 dat:显示内容下标 wei:哪一个数码管
void display(unsigned char dat,unsigned char wei)//wei 1-6
{
    digitData = 0xff; //关掉显示 消除重影
    WX = 1;
    WX = 0;
    //如果不先关闭显示,不论先段选还位选(位选前关闭都不行),而且必须先送段选,再送位选,
    //先位选则当前位置会显示上次残留段选数据 导致重影
    digitData = leddata[dat];//段选
    DX = 1;
    DX = 0; 
    digitData = ~(1<<(wei-1));//位选
    WX = 1;
    WX = 0;
}
//判断当前按键
unsigned char scanKey()
{
    P3 = 0x70;//k1 k2 k3给高

    if(kaiShi==0)//开始键按下
    {
        if(keyTemp[0] == 0) keyTemp[0]=1 ;//按键按下 开始记延时
    }
    else
    {
        keyTemp[0] = 0; //有抖动重新延时
    }

    if(chongZhi==0)
    {
        if(keyTemp[1] == 0) keyTemp[1]=1 ;
    }
    else
    {
        keyTemp[1] = 0;
    }

    if(jiCi==0)
    {
        if(keyTemp[2] == 0) keyTemp[2]=1 ;
    }
    else
    {
        keyTemp[2] = 0;
    }
    if(keyTemp[0]>5) return startBtn;  //5次 5*2ms 超过10ms 消抖
    if(keyTemp[1]>5) return resetBtn;
    if(keyTemp[2]>5) return counterBtn;
    return 0xFF; //没有按键
}
void Timer0Init(void)       //2毫秒@11.0592MHz
{
    //AUXR &= 0x7F;     //定时器时钟12T模式
    TMOD &= 0xF0;       //设置定时器模式
    TMOD |= 0x01;       //设置定时器模式
    TL0 = 0xCF;     //设置定时初值
    TH0 = 0xF8;     //设置定时初值
    TF0 = 0;        //清除TF0标志
    TR0 = 1;        //定时器0开始计时
    ET0 = 1;         //运行定时器0中断
    EA = 1;         //开全局中断
}

void main()
{
    unsigned char keyValue = 0xFF,keyT = 0xFF;//按键值
    unsigned char weiXian[6] = {0,22,0,0,22,0};
    Timer0Init();
    while(1)
    {
        if(shuanXi)//刷新sXW位置的数码管中
        {
            if(times>=6000) //十分钟
            {
                times = 0;
            }
            weiXian[0] = times/600;//分钟
            weiXian[2] = times/10%60/10;
            weiXian[3] = times/10%60%10;
            weiXian[5] = times%10;
            display(weiXian[sXW-1],sXW);
            shuanXi = 0;
        }
        keyT = scanKey();//当前按键值
        if(keyT!=keyValue)//当前按键值与上次按键值不同,则表示按键状态改变。需要做按键处理
        {
            keyValue = keyT;
            //P1 = keyValue;
            switch(keyValue)//按键处理
            {
            case startBtn://开始暂停按下
                //if(runState) TR0 = 0; else TR0 = 1;
                //runState ? (TR0 = 0) : (TR0 = 1);
                runState = !runState;//改变状态
                if(runState)//恢复时间
                {
                    times = saveTimes;
                }
                else//保存时间
                {
                    saveTimes = times;
                }

                break;
            case resetBtn:  //重置
                if(!runState)
                {
                    times = 0;
                    saveTimes = 0;
                    recodeNum = 0;
                    P1 = 0xFF;
                }
                break;
            case counterBtn:  //记录或查看记录
                if(runState)//运行时,记录
                {
                    if(recodeNum<8)
                    {
                        recode[recodeNum++] = times;
                        P1 = (0xFF<= recodeNum)
                    {
                        showRe = 0;//循环查看记录
                    }
                }
                break;
            }
        }
    }

}
void T0_2ms() interrupt 1
{
    //定时器重新赋初值 放在终端服务程序最开始有利于降低定时中断误差
    TL0 = 0xCF;     //设置定时初值
    TH0 = 0xF8;     //设置定时初值
    if(++counter>=50 && runState)//运行状态下100ms 加0.1秒
    {
        times++;
        counter = 0;
    }
    shuanXi = 1;//数码管周期刷新
    if(++sXW>6) //六位循环刷新
    {
        sXW = 1;
    }
    if(keyTemp[0]) keyTemp[0]++;//按键持续
    if(keyTemp[1]) keyTemp[1]++;//按键持续
    if(keyTemp[2]) keyTemp[2]++;//按键持续

}

你可能感兴趣的:(51单片机实现的秒表)