鍍金池/ 教程/ 數(shù)據(jù)庫/ 6.4 單片機數(shù)碼管動態(tài)顯示程序[帶解釋]
2.1 單片機內(nèi)部資源(內(nèi)部結(jié)構(gòu))
5.1 單片機邏輯電路與邏輯運算
6.1 C 語言數(shù)組
5.7 單片機數(shù)碼管的靜態(tài)顯示
4.8 單片機 LED 流水燈程序[詳細]
6.7 單片機中斷的優(yōu)先級
5.2 單片機定時器介紹
1. 如何學(xué)習(xí)單片機
1.3 單片機學(xué)習(xí)的準備工作
4.3 C 語言基本運算符
5.5 單片機中 LED 數(shù)碼管的介紹
2.6 第一個單片機程序(C 語言編寫)
3.5 單片機中 74HC138 三八譯碼器的應(yīng)用
3.2 單片機中去耦電容的應(yīng)用
6.4 單片機數(shù)碼管動態(tài)顯示程序[帶解釋]
1.5 Keil uVision4簡單使用教程
第一章問題匯總
1.2 如何學(xué)習(xí)單片機
4.4 C 語言 for 循環(huán)語句
6.5 單片機數(shù)碼管顯示消隱
單片機上的發(fā)光二極管(LED 燈)
5. 單片機定時器與數(shù)碼管基礎(chǔ)
5.3 單片機定時器的寄存器
5.6 單片機數(shù)碼管的真值表
6.2 C 語言 if 語句
3.3 三極管的的概念及其工作原理
4. C 語言基礎(chǔ)及流水燈的實現(xiàn)
點亮你的 LED 燈
4.6 C 語言函數(shù)的簡單介紹
2.4 51單片機特殊功能寄存器和位定義
6.6 單片機中斷系統(tǒng)
3.1 電磁干擾 EMI
4.1 二進制、十進制和十六進制
4.5 C 語言 while 循環(huán)語句
3. 單片機硬件基礎(chǔ)知識學(xué)習(xí)
2.5 使用 Keil uVision 新建一個工程
1.4 單片機開發(fā)環(huán)境搭建--Keil uVision4安裝教程
4.7 單片機延時方法(Keil 軟件延時)
6. 單片機的中斷與數(shù)碼管動態(tài)顯示
3.6 單片機 LED 燈閃爍程序
6.3 C 語言 switch 語句
1.1 學(xué)習(xí)什么單片機
4.2 C 語言變量類型和范圍
5.4 單片機中定時器的應(yīng)用
2.7 將程序下載到單片機
3.4 單片機中三極管的應(yīng)用
2.2 單片機最小系統(tǒng)[配圖][超詳細]

6.4 單片機數(shù)碼管動態(tài)顯示程序[帶解釋]

我們在上一章學(xué)習(xí)數(shù)碼管靜態(tài)顯示的時候說到,74HC138 只能在同一時刻導(dǎo)通一個三極管,而我們的數(shù)碼管是靠了6個三極管來控制,那我們?nèi)绾蝸碜寯?shù)碼管同時顯示呢?這就用到了動態(tài)顯示的概念。

多個數(shù)碼管顯示數(shù)字的時候,我們實際上是輪流點亮數(shù)碼管(一個時刻內(nèi)只有一個數(shù)碼管是亮的),利用人眼的視覺暫留現(xiàn)象(也叫余輝效應(yīng)),就可以做到看起來是所有數(shù)碼管都同時亮了,這就是動態(tài)顯示,也叫做動態(tài)掃描。

例如:有2個數(shù)碼管,我們要顯示“12”這個數(shù)字,先讓高位的位選三極管導(dǎo)通,然后控制段選讓其顯示“1”,延時一定時間后再讓低位的位選三極管導(dǎo)通,然后控制段選讓其顯示“2”。把這個流程以一定的速度循環(huán)運行就可以讓數(shù)碼管顯示出“12”,由于交替速度非???,人眼識別到的就是“12”這兩位數(shù)字同時亮了。

那么一個數(shù)碼管需要點亮多長時間呢?也就是說要多長時間完成一次全部數(shù)碼管的掃描呢(很明顯:整體掃描時間=單個數(shù)碼管點亮?xí)r間*數(shù)碼管個數(shù))?答案是:10 ms 以內(nèi)。當(dāng)電視機和顯示器還處在 CRT(電子顯像管)時代的時候,有一句很流行的廣告語——“100 Hz無閃爍”,沒錯,只要刷新率大于 100 Hz,即刷新時間小于 10 ms,就可以做到無閃爍,這也就是我們的動態(tài)掃描的硬性指標。那么你也許會問,有最小值的限制嗎?理論上沒有,但實際上做到更快的刷新卻沒有任何進步的意義了,因為已經(jīng)無閃爍了,再快也還是無閃爍,只是徒然增加 CPU 的負荷而已(因為1秒內(nèi)要執(zhí)行更多次的掃描程序)。所以,通常我們設(shè)計程序的時候,都是取一個接近 10 ms,又比較規(guī)整的值就行了。我們開發(fā)板上有6個數(shù)碼管,那么我們現(xiàn)在就來著手寫一個數(shù)碼管動態(tài)掃描的程序,實現(xiàn)兼驗證上面講的動態(tài)顯示原理。

我們的目標還是實現(xiàn)秒表功能,只不過這次有6個位了,最大可以計到999999秒。那么現(xiàn)在要實現(xiàn)的這個程序相對于前幾章的例程來說就要復(fù)雜的多了,既要處理秒表計數(shù),又要處理動態(tài)掃描。在編寫這類稍復(fù)雜的程序時,建議初學(xué)者們先用程序流程圖來把程序的整個流程理清,在動手寫程序之前先把整個程序的結(jié)構(gòu)框架搭好,把每一個環(huán)節(jié)要實現(xiàn)的功能先細化出來,然后再用程序代碼一步一步的去實現(xiàn)出來。這樣就可以避免無處下筆的迷茫感了。如圖6-1就是本例的程序流程圖,大家先根據(jù)流程圖把程序的執(zhí)行經(jīng)過在大腦里走一遍,然后再看接下來的程序代碼,體會一下流程圖的作用,看是不是能幫助你更順暢的理清程序流程。

http://wiki.jikexueyuan.com/project/mcu-tutorial-one/images/65.png" alt="" />

圖6-1 數(shù)碼管動態(tài)顯示秒表程序流程圖

#include <reg52.h>

sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;

unsigned char code LedChar[] = {  //數(shù)碼管顯示字符轉(zhuǎn)換表
    0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
    0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
};
unsigned char LedBuff[6] = { //數(shù)碼管顯示緩沖區(qū),初值 0xFF 確保啟動時都不亮
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};

void main(){
    unsigned char i = 0;  //動態(tài)掃描的索引

    unsigned int cnt = 0; //記錄 T0 中斷次數(shù)
    unsigned long sec = 0; //記錄經(jīng)過的秒數(shù)

    ENLED = 0;  //使能 U3,選擇控制數(shù)碼管
    ADDR3 = 1; //因為需要動態(tài)改變 ADDR0-2 的值,所以不需要再初始化了

    TMOD = 0x01;  //設(shè)置 T0 為模式1
    TH0 = 0xFC;  //為 T0 賦初值 0xFC67,定時 1 ms
    TL0 = 0x67;
    TR0 = 1;  //啟動 T0

    while (1){
        if (TF0 == 1){  //判斷 T0 是否溢出
            TF0 = 0;  //T0 溢出后,清零中斷標志
            TH0 = 0xFC;  //并重新賦初值
            TL0 = 0x67;
            cnt++;  //計數(shù)值自加1

            if (cnt >= 1000){ //判斷 T0 溢出是否達到1000次
                cnt = 0;  //達到1000次后計數(shù)值清零
                sec++;  //秒計數(shù)自加1

                //以下代碼將 sec 按十進制位從低到高依次提取并轉(zhuǎn)為數(shù)碼管顯示字符
                LedBuff[0] = LedChar[sec%10];
                LedBuff[1] = LedChar[sec/10%10];
                LedBuff[2] = LedChar[sec/100%10];
                LedBuff[3] = LedChar[sec/1000%10];
                LedBuff[4] = LedChar[sec/10000%10];
                LedBuff[5] = LedChar[sec/100000%10];
            }
            //以下代碼完成數(shù)碼管動態(tài)掃描刷新
            if (i == 0)
            { ADDR2=0; ADDR1=0; ADDR0=0; i++; P0=LedBuff[0]; }
            else if (i == 1)
            { ADDR2=0; ADDR1=0; ADDR0=1; i++; P0=LedBuff[1]; }
            else if (i == 2)
            { ADDR2=0; ADDR1=1; ADDR0=0; i++; P0=LedBuff[2]; }
            else if (i == 3)
            { ADDR2=0; ADDR1=1; ADDR0=1; i++; P0=LedBuff[3]; }
            else if (i == 4)
            { ADDR2=1; ADDR1=0; ADDR0=0; i++; P0=LedBuff[4]; }
            else if (i == 5)
            { ADDR2=1; ADDR1=0; ADDR0=1; i=0; P0=LedBuff[5]; }
        }
    }
}

這段程序,大家自己抄到 Keil 中,然后邊抄邊結(jié)合程序流程圖來理解,最終下載到實驗板上看一下運行結(jié)果。其中下邊的 if...else 語句就是每 1 ms 快速的刷新一個數(shù)碼管,這樣6個數(shù)碼管整體刷新一遍的時間就是 6 ms,視覺感官上就是6個數(shù)碼管同時亮起來了。

在 C 語言中, /”等同于數(shù)學(xué)里的除法運算,而“%”等同于我們小學(xué)學(xué)的求余數(shù)運算,這個前邊已有介紹。如果是123456這個數(shù)字,我們要正常顯示在數(shù)碼管上,個位顯示,就是直接對10取余數(shù),這個“6”就出來了,十位數(shù)字就是先除以10,然后再對10取余數(shù),以此類推,就把6個數(shù)字全部顯示出來了。

對于多選一的動態(tài)刷新數(shù)碼管的方式,我們?nèi)绻?switch 會有更好的效果,大家來看一下我們用 switch 語句完成的情況。

#include <reg52.h>

sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;

unsigned char code LedChar[] = { //數(shù)碼管顯示字符轉(zhuǎn)換表
    0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
    0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
};
unsigned char LedBuff[6] = { //數(shù)碼管顯示緩沖區(qū),初值 0xFF 確保啟動時都不亮
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};

void main(){
    unsigned char i = 0;  //動態(tài)掃描的索引
    unsigned int cnt = 0;  //記錄 T0 中斷次數(shù)
    unsigned long sec = 0;  //記錄經(jīng)過的秒數(shù)

    ENLED = 0;  //使能 U3,選擇控制數(shù)碼管
    ADDR3 = 1;  //因為需要動態(tài)改變 ADDR0-2 的值,所以不需要再初始化了
    TMOD = 0x01; //設(shè)置 T0 為模式1
    TH0 = 0xFC; //為 T0 賦初值 0xFC67,定時 1 ms
    TL0 = 0x67;
    TR0 = 1;  //啟動 T0

    while (1){
        if (TF0 == 1){  //判斷 T0 是否溢出
            TF0 = 0;  //T0 溢出后,清零中斷標志
            TH0 = 0xFC;  //并重新賦初值
            TL0 = 0x67;
            cnt++;  //計數(shù)值自加1

            if (cnt >= 1000){  //判斷 T0 溢出是否達到1000次
                cnt = 0;  //達到1000次后計數(shù)值清零
                sec++;  //秒計數(shù)自加1

                //以下代碼將 sec 按十進制位從低到高依次提取并轉(zhuǎn)為數(shù)碼管顯示字符
                LedBuff[0] = LedChar[sec%10];
                LedBuff[1] = LedChar[sec/10%10];
                LedBuff[2] = LedChar[sec/100%10];
                LedBuff[3] = LedChar[sec/1000%10];
                LedBuff[4] = LedChar[sec/10000%10];
                LedBuff[5] = LedChar[sec/100000%10];
            }
            //以下代碼完成數(shù)碼管動態(tài)掃描刷新
            switch (i){
                case 0: ADDR2=0; ADDR1=0; ADDR0=0; i++; P0=LedBuff[0]; break;
                case 1: ADDR2=0; ADDR1=0; ADDR0=1; i++; P0=LedBuff[1]; break;
                case 2: ADDR2=0; ADDR1=1; ADDR0=0; i++; P0=LedBuff[2]; break;
                case 3: ADDR2=0; ADDR1=1; ADDR0=1; i++; P0=LedBuff[3]; break;
                case 4: ADDR2=1; ADDR1=0; ADDR0=0; i++; P0=LedBuff[4]; break;
                case 5: ADDR2=1; ADDR1=0; ADDR0=1; i=0; P0=LedBuff[5]; break;
                default: break;
            }
        }
    }
}

程序完成的功能是一模一樣的,但大家看一下,switch 語句是不是比 if...else 語句顯得要整齊清爽呢。