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連接兩個鎖存器 控制位選和段選
效果如下圖


946af505-2a92-49c7-a2ee-97d403e05523.jpg
#include <reg51.h>
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);//點亮recodeNum個燈
                    }
                }
                else//暫停查看記錄
                {
                    P1 = (0xFF ^(1 << showRe));//點亮showRe位置的燈
                    times = recode[showRe++];
                    if(showRe >= 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]++;//按鍵持續

}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 一、基本知識 1. 按鍵分類與輸入原理 按鍵按照結構原理科分為兩類,一類是觸點式開關按鍵,如機械式開關、導電橡膠式...
    Murrey_Xiao閱讀 14,188評論 1 8
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,923評論 18 139
  • 國家電網公司企業標準(Q/GDW)- 面向對象的用電信息數據交換協議 - 報批稿:20170802 前言: 排版 ...
    庭說閱讀 11,149評論 6 13
  • 聯想樂檬K3 Note在產品定位上來說就是樂檬K3的大屏幕加強版本,在市場上的競爭對手很明顯就是魅藍Note與紅米...
    馮偉文閱讀 1,930評論 1 2
  • intro key-value pairs merge method merge is used for merg...
    bookinstock_閱讀 268評論 0 0