新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > 第27節:在定時中斷里動態掃描數碼管的程序

        第27節:在定時中斷里動態掃描數碼管的程序

        作者: 時間:2016-11-22 來源:網絡 收藏
        開場白:

        上一節講了在主函數循環中動態掃描數碼管的程序,但是該程序有一個隱患,在一些項目中 ,主函數循環中的任務越多,就意味著在某一瞬間,每顯示一位數碼管停留的時間就會越久,一旦超過某個值,會嚴重影響顯示的效果。這一節要教會大家兩個知識點:

        本文引用地址:http://www.104case.com/article/201611/319781.htm

        第一個:如何把動態掃描數碼管的程序放在定時中斷里,徹底解決上節的顯示隱患。

        第二個:在定時中斷里的重裝初始值不能太大,否則動態掃描數碼管的速度就不夠。我把原來常用的初始值2000改成了500。

        具體內容,請看源代碼講解。

        (1)硬件平臺:基于朱兆祺51單片機學習板。用兩片74HC595動態驅動八位共陰數碼管。

        (2)實現功能:

        開機后顯示 8765.4321 的內容,注意,其中有一個小數點。

        (3)源代碼講解如下:

        #include "REG52.H"

        void initial_myself();

        void initial_peripheral();

        void delay_short(unsigned int uiDelayShort);

        void delay_long(unsigned int uiDelaylong);

        //驅動數碼管的74HC595

        void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01);

        void display_drive(); //顯示數碼管字模的驅動函數

        //驅動LED的74HC595

        void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01);

        void T0_time(); //定時中斷函數

        sbit beep_dr=P2^7; //蜂鳴器的驅動IO口

        sbit led_dr=P3^5; //作為中途暫停指示燈 亮的時候表示中途暫停

        sbit dig_hc595_sh_dr=P2^0; //數碼管的74HC595程序

        sbit dig_hc595_st_dr=P2^1;

        sbit dig_hc595_ds_dr=P2^2;

        sbit hc595_sh_dr=P2^3; //LED燈的74HC595程序

        sbit hc595_st_dr=P2^4;

        sbit hc595_ds_dr=P2^5;

        unsigned char ucDigShow8; //第8位數碼管要顯示的內容

        unsigned char ucDigShow7; //第7位數碼管要顯示的內容

        unsigned char ucDigShow6; //第6位數碼管要顯示的內容

        unsigned char ucDigShow5; //第5位數碼管要顯示的內容

        unsigned char ucDigShow4; //第4位數碼管要顯示的內容

        unsigned char ucDigShow3; //第3位數碼管要顯示的內容

        unsigned char ucDigShow2; //第2位數碼管要顯示的內容

        unsigned char ucDigShow1; //第1位數碼管要顯示的內容

        unsigned char ucDigDot8; //數碼管8的小數點是否顯示的標志

        unsigned char ucDigDot7; //數碼管7的小數點是否顯示的標志

        unsigned char ucDigDot6; //數碼管6的小數點是否顯示的標志

        unsigned char ucDigDot5; //數碼管5的小數點是否顯示的標志

        unsigned char ucDigDot4; //數碼管4的小數點是否顯示的標志

        unsigned char ucDigDot3; //數碼管3的小數點是否顯示的標志

        unsigned char ucDigDot2; //數碼管2的小數點是否顯示的標志

        unsigned char ucDigDot1; //數碼管1的小數點是否顯示的標志

        unsigned char ucDigShowTemp=0; //臨時中間變量

        unsigned char ucDisplayDriveStep=1; //動態掃描數碼管的步驟變量

        unsigned char ucDisplayUpdate=1; //更新顯示標志

        //根據原理圖得出的共陰數碼管字模表

        code unsigned char dig_table[]=

        {

        0x3f, //0 序號0

        0x06, //1 序號1

        0x5b, //2 序號2

        0x4f, //3 序號3

        0x66, //4 序號4

        0x6d, //5 序號5

        0x7d, //6 序號6

        0x07, //7 序號7

        0x7f, //8 序號8

        0x6f, //9 序號9

        0x00, //不顯示 序號10

        };

        void main()

        {

        initial_myself();

        delay_long(100);

        initial_peripheral();

        while(1)

        {

        ;

        }

        }

        /* 注釋一:

        * 動態驅動數碼管的原理是,在八位數碼管中,在任何一個瞬間,每次只顯示其中一位數碼管,另外的七個數碼管

        * 通過設置其公共位com為高電平來關閉顯示,只要切換畫面的速度足夠快,人的視覺就分辨不出來,感覺八個數碼管

        * 是同時亮的。以下dig_hc595_drive(xx,yy)函數,其中第一個形參xx是驅動數碼管段seg的引腳,第二個形參yy是驅動

        * 數碼管公共位com的引腳。

        */

        void display_drive()

        {

        //以下程序,如果加一些數組和移位的元素,還可以壓縮容量。但是鴻哥追求的不是容量,而是清晰的講解思路

        switch(ucDisplayDriveStep)

        {

        case 1: //顯示第1位

        ucDigShowTemp=dig_table[ucDigShow1];

        if(ucDigDot1==1)

        {

        ucDigShowTemp=ucDigShowTemp|0x80; //顯示小數點

        }

        dig_hc595_drive(ucDigShowTemp,0xfe);

        break;

        case 2: //顯示第2位

        ucDigShowTemp=dig_table[ucDigShow2];

        if(ucDigDot2==1)

        {

        ucDigShowTemp=ucDigShowTemp|0x80; //顯示小數點

        }

        dig_hc595_drive(ucDigShowTemp,0xfd);

        break;

        case 3: //顯示第3位

        ucDigShowTemp=dig_table[ucDigShow3];

        if(ucDigDot3==1)

        {

        ucDigShowTemp=ucDigShowTemp|0x80; //顯示小數點

        }

        dig_hc595_drive(ucDigShowTemp,0xfb);

        break;

        case 4: //顯示第4位

        ucDigShowTemp=dig_table[ucDigShow4];

        if(ucDigDot4==1)

        {

        ucDigShowTemp=ucDigShowTemp|0x80; //顯示小數點

        }

        dig_hc595_drive(ucDigShowTemp,0xf7);

        break;

        case 5: //顯示第5位

        ucDigShowTemp=dig_table[ucDigShow5];

        if(ucDigDot5==1)

        {

        ucDigShowTemp=ucDigShowTemp|0x80; //顯示小數點

        }

        dig_hc595_drive(ucDigShowTemp,0xef);

        break;

        case 6: //顯示第6位

        ucDigShowTemp=dig_table[ucDigShow6];

        if(ucDigDot6==1)

        {

        ucDigShowTemp=ucDigShowTemp|0x80; //顯示小數點

        }

        dig_hc595_drive(ucDigShowTemp,0xdf);

        break;

        case 7: //顯示第7位

        ucDigShowTemp=dig_table[ucDigShow7];

        if(ucDigDot7==1)

        {

        ucDigShowTemp=ucDigShowTemp|0x80; //顯示小數點

        }

        dig_hc595_drive(ucDigShowTemp,0xbf);

        break;

        case 8: //顯示第8位

        ucDigShowTemp=dig_table[ucDigShow8];

        if(ucDigDot8==1)

        {

        ucDigShowTemp=ucDigShowTemp|0x80; //顯示小數點

        }

        dig_hc595_drive(ucDigShowTemp,0x7f);

        break;

        }

        ucDisplayDriveStep++;

        if(ucDisplayDriveStep>8) //掃描完8個數碼管后,重新從第一個開始掃描

        {

        ucDisplayDriveStep=1;

        }

        /* 注釋二:

        * 如果直接是單片機的IO口引腳驅動的數碼管,由于驅動的速度太快,此處應該適當增加一點delay延時或者

        * 用計數延時的方式來延時,目的是在八位數碼管中切換到每位數碼管顯示的時候,都能停留一會再切換到其它

        * 位的數碼管界面,這樣可以增加顯示的效果。但是,由于朱兆祺51學習板是間接經過74HC595驅動數碼管的,

        * 在單片機驅動74HC595的時候,dig_hc595_drive函數本身內部需要執行很多指令,已經相當于delay延時了,

        * 因此這里不再需要加delay延時函數或者計數延時。

        */

        }

        //數碼管的74HC595驅動函數

        void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01)

        {

        unsigned char i;

        unsigned char ucTempData;

        dig_hc595_sh_dr=0;

        dig_hc595_st_dr=0;

        ucTempData=ucDigStatusTemp16_09; //先送高8位

        for(i=0;i<8;i++)

        {

        if(ucTempData>=0x80)dig_hc595_ds_dr=1;

        else dig_hc595_ds_dr=0;

        /* 注釋三:

        * 注意,此處的延時delay_short必須盡可能小,否則動態掃描數碼管的速度就不夠。

        */

        dig_hc595_sh_dr=0; //SH引腳的上升沿把數據送入寄存器

        delay_short(1);

        dig_hc595_sh_dr=1;

        delay_short(1);

        ucTempData=ucTempData<<1;

        }

        ucTempData=ucDigStatusTemp08_01; //再先送低8位

        for(i=0;i<8;i++)

        {

        if(ucTempData>=0x80)dig_hc595_ds_dr=1;

        else dig_hc595_ds_dr=0;

        dig_hc595_sh_dr=0; //SH引腳的上升沿把數據送入寄存器

        delay_short(1);

        dig_hc595_sh_dr=1;

        delay_short(1);

        ucTempData=ucTempData<<1;

        }

        dig_hc595_st_dr=0; //ST引腳把兩個寄存器的數據更新輸出到74HC595的輸出引腳上并且鎖存起來

        delay_short(1);

        dig_hc595_st_dr=1;

        delay_short(1);

        dig_hc595_sh_dr=0; //拉低,抗干擾就增強

        dig_hc595_st_dr=0;

        dig_hc595_ds_dr=0;

        }

        //LED燈的74HC595驅動函數

        void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01)

        {

        unsigned char i;

        unsigned char ucTempData;

        hc595_sh_dr=0;

        hc595_st_dr=0;

        ucTempData=ucLedStatusTemp16_09; //先送高8位

        for(i=0;i<8;i++)

        {

        if(ucTempData>=0x80)hc595_ds_dr=1;

        else hc595_ds_dr=0;

        hc595_sh_dr=0; //SH引腳的上升沿把數據送入寄存器

        delay_short(1);

        hc595_sh_dr=1;

        delay_short(1);

        ucTempData=ucTempData<<1;

        }

        ucTempData=ucLedStatusTemp08_01; //再先送低8位

        for(i=0;i<8;i++)

        {

        if(ucTempData>=0x80)hc595_ds_dr=1;

        else hc595_ds_dr=0;

        hc595_sh_dr=0; //SH引腳的上升沿把數據送入寄存器

        delay_short(1);

        hc595_sh_dr=1;

        delay_short(1);

        ucTempData=ucTempData<<1;

        }

        hc595_st_dr=0; //ST引腳把兩個寄存器的數據更新輸出到74HC595的輸出引腳上并且鎖存起來

        delay_short(1);

        hc595_st_dr=1;

        delay_short(1);

        hc595_sh_dr=0; //拉低,抗干擾就增強

        hc595_st_dr=0;

        hc595_ds_dr=0;

        }

        void T0_time() interrupt 1

        {

        TF0=0; //清除中斷標志

        TR0=0; //關中斷

        display_drive(); //數碼管字模的驅動函數

        /* 注釋四:

        * 注意,此處的重裝初始值不能太大,否則動態掃描數碼管的速度就不夠。我把原來常用的2000改成了500。

        */

        TH0=0xfe; //重裝初始值(65535-500)=65035=0xfe0b

        TL0=0x0b;

        TR0=1; //開中斷

        }

        void delay_short(unsigned int uiDelayShort)

        {

        unsigned int i;

        for(i=0;i

        {

        ; //一個分號相當于執行一條空語句

        }

        }

        void delay_long(unsigned int uiDelayLong)

        {

        unsigned int i;

        unsigned int j;

        for(i=0;i

        {

        for(j=0;j<500;j++) //內嵌循環的空指令數量

        {

        ; //一個分號相當于執行一條空語句

        }

        }

        }

        void initial_myself() //第一區 初始化單片機

        {

        led_dr=0; //關閉獨立LED燈

        beep_dr=1; //用PNP三極管控制蜂鳴器,輸出高電平時不叫。

        hc595_drive(0x00,0x00); //關閉所有經過另外兩個74HC595驅動的LED燈

        TMOD=0x01; //設置定時器0為工作方式1

        TH0=0xfe; //重裝初始值(65535-500)=65035=0xfe0b

        TL0=0x0b;

        }

        void initial_peripheral() //第二區 初始化外圍

        {

        /* 注釋五:

        * 讓數碼管顯示的內容轉移到以下幾個變量接口上,方便以后編寫更上一層的窗口程序。

        * 只要更改以下對應變量的內容,就可以顯示你想顯示的數字。初學者應該仔細看看display_drive等函數,

        * 了解來龍去脈,就可以知道本驅動程序的框架原理了。

        */

        ucDigShow8=8; //第8位數碼管要顯示的內容

        ucDigShow7=7; //第7位數碼管要顯示的內容

        ucDigShow6=6; //第6位數碼管要顯示的內容

        ucDigShow5=5; //第5位數碼管要顯示的內容

        ucDigShow4=4; //第4位數碼管要顯示的內容

        ucDigShow3=3; //第3位數碼管要顯示的內容

        ucDigShow2=2; //第2位數碼管要顯示的內容

        ucDigShow1=1; //第1位數碼管要顯示的內容

        ucDigDot8=0;

        ucDigDot7=0;

        ucDigDot6=0;

        ucDigDot5=1; //顯示第5位的小數點

        ucDigDot4=0;

        ucDigDot3=0;

        ucDigDot2=0;

        ucDigDot1=0;

        EA=1; //開總中斷

        ET0=1; //允許定時中斷

        TR0=1; //啟動定時中斷

        }

        總結陳詞:

        有的朋友會質疑,很多教科書上說,定時中斷函數里面的內容應該越少越好,你把動態驅動數碼管的函數放在中斷里面,難道不會影響其它任務的執行嗎?我的回答是,大部分的小項目都不會影響,只有少數實時性要求非常高的項目會影響,而對于這類項目,我的做法是從一開始設計硬件電路板的時候,就應該放棄用動態掃描數碼管的方案,而是應該選數碼管專用驅動芯片來實現靜態驅動。因為動態掃描數碼管本來就不適合應用在實時性非常高的項目。

        前面這兩節都講了數碼管的驅動程序,要在此基礎上,做一些項目中經常遇到的界面應用,我們該怎么寫程序?欲知詳情,請聽下回分解-----數碼管通過切換窗口來設置參數。



        評論


        技術專區

        關閉
        主站蜘蛛池模板: 斗六市| 加查县| 乐至县| 长岭县| 鸡西市| 南汇区| 镇巴县| 浮山县| 康乐县| 卢氏县| 秦安县| 屯留县| 郑州市| 兴山县| 孟州市| 麦盖提县| 鸡泽县| 成安县| 鄂尔多斯市| 屏南县| 仪征市| 民和| 巴楚县| 乌什县| 子洲县| 广饶县| 沭阳县| 二连浩特市| 乐都县| 聊城市| 天祝| 湖北省| 莎车县| 集安市| 慈利县| 榕江县| 乌鲁木齐县| 抚州市| 德令哈市| 都安| 苗栗市|