中斷是單片機系統(tǒng)的重點中的重點因為有了中斷單片機_第1頁
中斷是單片機系統(tǒng)的重點中的重點因為有了中斷單片機_第2頁
中斷是單片機系統(tǒng)的重點中的重點因為有了中斷單片機_第3頁
中斷是單片機系統(tǒng)的重點中的重點因為有了中斷單片機_第4頁
中斷是單片機系統(tǒng)的重點中的重點因為有了中斷單片機_第5頁
已閱讀5頁,還剩9頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)

文檔簡介

1、 中斷是單片機系統(tǒng)的重點中的重點,因為有了中斷,單片機就具備了快速協(xié)調(diào)多模塊工作的能力,大家對本章節(jié)內(nèi)容要多研究,最終要完全理解并且掌握。6.1 C語言的數(shù)組6.1.1 數(shù)組的基本概念我們第四章學(xué)過變量的基本類型,比如char、int等等。這種類型描述的數(shù)據(jù)是比較有限的,當(dāng)我們要處理非常大量數(shù)據(jù)的時候,就可以用到數(shù)組了,比如我們上節(jié)課的那個數(shù)碼管的真值表,我們就可以用一個數(shù)組來表達。從概念上講,數(shù)組是具有相同數(shù)據(jù)類型的有序數(shù)據(jù)的組合,一般來講,數(shù)組定義后滿足以下三個條件。(1)具有相同的數(shù)據(jù)類型;(2)具有相同的名字;(3)在存儲器中是被連續(xù)存放的。    比如我

2、們上節(jié)課定義的那個數(shù)碼管真值表,如果我們把關(guān)鍵字code去掉,數(shù)組元素將被保存在RAM中,在程序中可讀可寫,同時我們也可以在中括號里邊標(biāo)明這個數(shù)組元素的個數(shù),比如:unsigned  char LedChar16 = 0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8e; 在這個數(shù)組中的每個值都稱之為數(shù)組的一個元素,這些元素都具備相同的數(shù)據(jù)類型就是unsigned char型,他們有一個共同的名字LedChar,不管放到RAM中還是FLASH中,他們都是存放在一塊連續(xù)的

3、存儲空間里的。有一點要特別注意,這個數(shù)組一共有16(中括號里面的數(shù)值)個元素,但是數(shù)組的元素的表達方式下標(biāo)是從0開始,因此實際上上邊這個數(shù)組的首個元素LedChar0的值是0xC0,而LedChar15的值是0x8e,下標(biāo)從0到15一共是16個元素。LedChar這個數(shù)組只有一個下標(biāo),我們稱之為一維數(shù)組,還有兩個下標(biāo)或者多個下標(biāo)的,我們稱之為多維數(shù)組。比如unsigned char a23;表示這是一個2行3列的二維數(shù)組。在大多數(shù)情況下我們使用的是一維數(shù)組,對于初學(xué)來說,我們先來研究一維數(shù)組,多維數(shù)組遇到了再了解。6.1.2 數(shù)組的聲明一維數(shù)組的聲明格式如下:數(shù)據(jù)類型   

4、數(shù)組名數(shù)組長度;(1)數(shù)組的數(shù)據(jù)類型聲明的是該數(shù)組的每個元素的類型,即一個數(shù)組中的元素具有相同的數(shù)據(jù)類型。(2)數(shù)組名的聲明要符合C語言固定的標(biāo)識符的聲明要求,只能由字母、數(shù)字、下劃線這三種符號組成,且第一個字符只能是字母或者下劃線。(3)方括號中的數(shù)組長度是一個常量或常量表達式,并且必須是正整數(shù)。6.1.3 數(shù)組的初始化數(shù)組在進行聲明的同時可以進行初始化操作,格式如下:數(shù)據(jù)類型   數(shù)組名數(shù)組長度 = 初值列表;還是以上節(jié)課我們用的數(shù)碼管的真值表為例來講解注意事項。    unsigned  char LedChar16 = 0xC

5、0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8e; (1)初值列表里的數(shù)據(jù)之間要用逗號隔開。(2)初值列表里的初值的數(shù)量必須小于或者等于數(shù)組長度,當(dāng)小于數(shù)組長度時,數(shù)組的后邊沒有賦初值的元素由系統(tǒng)自動賦值0。(3)若給數(shù)組的所有元素賦初值,可以省略數(shù)組的長度,上節(jié)課的例子中我們實際上已經(jīng)省略了數(shù)組的長度。(4)系統(tǒng)為數(shù)組分配連續(xù)的存儲單元的時候,數(shù)組元素的相對次序由下標(biāo)來決定,就是說LedChar0、LedChar1. . LedChar15是按照順序排下來的。6.1.4 數(shù)組的使用和賦值在

6、C語言程序中,是不能一次使用整個數(shù)組的,只能使用單個數(shù)組元素。一個數(shù)組元素相當(dāng)于一個變量,使用數(shù)組元素的時候與使用相同數(shù)據(jù)類型的變量的方法一樣。比如這個LedChar這個數(shù)組,如果沒加code關(guān)鍵字,那么它可讀可寫,我們可以寫成a = LedChar0這樣來把數(shù)組的一個元素的值送個a這個變量,也可以寫成LedChar0 = a這樣把a這個變量的值送給數(shù)組的一個元素,以下三點要注意:(1)引用數(shù)組的時候,那個方括號里的數(shù)字代表的是數(shù)組元素的下標(biāo),而數(shù)組初始化的時候方括號里的數(shù)字代表的是這個數(shù)組元素的個數(shù)。(2)數(shù)組元素的方括號里的下標(biāo)可以是整型常數(shù),整型變量或者表達式,而數(shù)組初始化的時候方括號里

7、的數(shù)字必須是常數(shù)不能是變量。(3)數(shù)組整體賦值只可以在初始化的時候操作,功能程序只能對單個元素賦值。6.2 if語句if語句已經(jīng)不陌生了,前邊程序我們其實已經(jīng)用過了,這里我們系統(tǒng)的介紹一下,方便后邊的深入學(xué)習(xí)。if語句有兩個關(guān)鍵字:if和else,把這兩個關(guān)鍵字翻譯一下就是:“如果”和“否則”。if語句一共有三種格式,我們分別來看。1.if語句的默認(rèn)形式。if (條件表達式)      語句 1;    其執(zhí)行過程是,if(如果)條件表達式的值為“真”,則執(zhí)行語句1;如果條件表達式的值為“假”,則不執(zhí)行語句1。真和假的概念不再贅述,參

8、考第五章。    這里要提醒一句,C語言一個分號表示一句語句的結(jié)束,因此如果if后邊只有一條執(zhí)行語句的時候,可以省略大括號,但是如果有多條執(zhí)行語句的話,必須加上大括號。    我們上節(jié)課的語句就很好理解了if(16 =j) j = 0;,如果j等于16的時候,括號里的值才是“真”,那么就執(zhí)行j=0這一句,如果j不等于16,那么里邊就為“假”,就不執(zhí)行這一句。2.if.else語句    有些情況下,我們除了判斷一下if括號里的是否滿足條件,執(zhí)行相應(yīng)的語句,在不滿足條件的時候,我們又要執(zhí)行另外相應(yīng)的語句,這個時候就用到了if.else語

9、句,它的基本的語法形式是:if (條件表達式)    語句 1;else    語句 2;比如上節(jié)課的后半段程序我們也可以寫成:                P0 = LedCharj;    /把數(shù)組里的對應(yīng)值送給P0                if(15 = j)       

10、 /當(dāng)顯示到F后,歸0重新開始                j = 0;                else                j+;    這個程序大家可以改改下載進去試試,程序邏輯大家自己動腦分析一下,我就不解釋了。   

11、60;      3.if.else if語句    if.esle語句是一個二選一的語句,或者執(zhí)行if條件下的語句,或者執(zhí)行else條件下的語句。還有一種多選一的用法就是if.else if語句。他的基本語法格式是:if (條件表達式1)            語句 1;else if (條件表達式2)        語句 2; else if (條件表達式3)     &#

12、160;  語句 3; . .else                       語句 n;他的執(zhí)行過程是:依次判斷條件表達式的值,當(dāng)出現(xiàn)某個值為“真”時,則執(zhí)行相對應(yīng)的語句,然后跳出整個if的語句塊,執(zhí)行“語句n”后邊的程序;如果所有的表達式都為“假”,則執(zhí)行“語句n”后,再執(zhí)行“語句n”后邊的程序。if語句在C語言編程的過程中使用頻率很高,用法也簡單,所以必須要熟練掌握。6.3 switch語句用if.else

13、語句在處理多分支的時候,分支太多就會顯得不方便,且容易出現(xiàn)if和else配對出現(xiàn)錯誤的情況,在C語言中提供了另外一種多分支選擇的語句switch語句,它的基本語法格式如下:switch (表達式)    case 常量表達式1:執(zhí)行語句1;    case 常量表達式2:執(zhí)行語句2;     .    case 常量表達式n:執(zhí)行語句n;    default: 執(zhí)行語句n+1;它的執(zhí)行過程是:首先計算“表達式”的值,然后從第一個case開始,與“常量表達式x”進行比較,如果與當(dāng)前常量表達式的值

14、不相等,那么就不執(zhí)行冒號后邊的程序,一旦發(fā)現(xiàn)和一個常量表達式的值相等了,那么他會執(zhí)行之后所有的,注意是所有的“執(zhí)行語句”,顯然這不是我們想要的結(jié)果。在C語言中,有一條break語句,作用是跳出當(dāng)前循環(huán)語句,不管是for和while循環(huán),還是switch循環(huán),都可以用其搭配使用跳出循環(huán)。switch語句一共有n+1種可能,而我們希望要的是一條多選一的語句,只執(zhí)行其中一條然后直接退出該循環(huán),不再執(zhí)行下邊的任何語句,這個時候就需要用到break語句,比如我們在switch表達式上加上break語句,如下:switch (表達式)    case 常量表達式1:執(zhí)行語句1;brea

15、k;    case 常量表達式2:執(zhí)行語句2;break;    .    case 常量表達式n:執(zhí)行語句n;break;    default:語句n+1;加了這個break語句后,一旦“常量表達式x”與“表達式”相等了,那就執(zhí)行“執(zhí)行語句x”,執(zhí)行完畢后,由于有了break,直接跳出switch語句,執(zhí)行switch語句循環(huán)后邊的程序了,這樣就可以避免執(zhí)行不必要的語句。了解了這個switch語句,我們將會在本章程序中使用鞏固。6.4 數(shù)碼管的動態(tài)顯示6.4.1 動態(tài)顯示的基本原理我們在上一章學(xué)習(xí)數(shù)碼管靜態(tài)顯

16、示的時候說到,74HC138只能在同一時刻導(dǎo)通一個三極管,而我們的數(shù)碼管是靠了6個三極管來控制,那我們?nèi)绾蝸碜寯?shù)碼管同時顯示呢?這就用到了我們這節(jié)課的動態(tài)顯示。多個數(shù)碼管顯示數(shù)字的時候,我們實際上是輪流點亮數(shù)碼管(一個時刻內(nèi)只有一個數(shù)碼管是亮的),利用人眼的視覺暫留現(xiàn)象(也叫余輝效應(yīng)),就可以做到看起來是所有數(shù)碼管都同時亮了,這就是動態(tài)掃描顯示的含義。例如:我們有2個數(shù)碼管,我們要顯示“12”這個數(shù)字,讓高位的位選三極管導(dǎo)通,然后給它賦值“1”,延時一定時間后讓低位的位選三極管導(dǎo)通,然后給它賦值“2”。把這個流程以一定的速度循環(huán)運行就可以讓數(shù)碼管顯示出“12”,由于交替速度非??欤巳庋圩R別到

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

18、時候,都是取一個接近10ms,又比較規(guī)整的值就行了。我們板子上有6個數(shù)碼管,我們下面用程序來驗證一下數(shù)碼管動態(tài)顯示程序。#include <reg52.h>               /包含寄存器的庫文件                   sbit  ADDR0 = P10;sbit  ADDR1 = P11;sbit&#

19、160; ADDR2 = P12;sbit  ADDR3 = P13;sbit  ENLED = P14;unsigned char code LedChar =    /用數(shù)組來表示數(shù)碼管真值表    0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,    0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8e,;void main()        unsigned int counte

20、r = 0;    unsigned char j = 0;    unsigned long stopwatch = 0;    unsigned char LedNumber6 = 0;    ENLED = 0; ADDR3 = 1;P0 = 0XFF;   /74HC138和P0初始化部分    TMOD = 0x01; /設(shè)置定時器0為模式1    TH0  = 0xFC;    TL0 

21、0;= 0x67; /定時值初值,定時1ms    TR0  = 1;    /打開定時器0    while(1)            if(1 = TF0)             /判斷定時器0是否溢出                

22、0;   TF0 = 0;            TH0 = 0xFC;        /一旦溢出后,重新賦值            TL0 = 0x67;            counter+;         &#

23、160;  if(1000 = counter)     /判斷定時器0溢出是否達到50次                            counter = 0;                stopwatch+;     

24、60;            /秒表數(shù)值一秒加1                LedNumber0 = stopwatch%10;                LedNumber1 = stopwatch/10%10;           

25、60;    LedNumber2 = stopwatch/100%10;                     LedNumber3 = stopwatch/1000%10;  /數(shù)碼管顯示值計算                LedNumber4 = stopwatch/10000%10;   

26、;             LedNumber5 = stopwatch/100000%10;                      if (0=j)          ADDR0=0; ADDR1=0; ADDR2=0; j+; P0=LedCharLedNumber0;     

27、     else if (1=j)          ADDR0=1; ADDR1=0; ADDR2=0; j+; P0=LedCharLedNumber1;           else if (2=j)          ADDR0=0; ADDR1=1; ADDR2=0; j+; P0=LedCharLedNumber2;       &#

28、160;   else if (3=j)          ADDR0=1; ADDR1=1; ADDR2=0; j+; P0=LedCharLedNumber3;           else if (4=j)          ADDR0=0; ADDR1=0; ADDR2=1; j+; P0=LedCharLedNumber4;         

29、60; else if (5=j)          ADDR0=1; ADDR1=0; ADDR2=1; j=0; P0=LedCharLedNumber5;                           /數(shù)碼管動態(tài)刷新部分    這程序,大家自己抄到Keil中,然后邊抄邊理解,最終下載到實驗板上實驗一下效果。其中下邊的if.else語句就是每1m

30、s快速的刷新一個數(shù)碼管,這樣6個數(shù)碼管整體刷新一遍的時間就是6ms,視覺上就是6個數(shù)碼管無閃爍的同時亮起來了。另外一個簡單知識點這個地方也提一下,其實屬于小學(xué)三年級知識,但是很多同學(xué)剛接觸C語言,可能遇到了也會發(fā)懵。就是在數(shù)碼管顯示值計算這個地方,相信小學(xué)我們沒學(xué)小數(shù)之前,除法運算里邊有“被除數(shù)”、“除數(shù)”、“商”、“余數(shù)”這四個概念年。而在我們C語言中,“/”等同于數(shù)學(xué)里的除法運算,而“%”等同于我們小學(xué)學(xué)的求余數(shù)運算。如果是123456這個數(shù)字,我們要正常顯示在數(shù)碼管上,個位顯示,就是直接對10取余數(shù),這個“6”就出來了,十位數(shù)字就是先除以10,然后再對10取余數(shù),以此類推,就把6個數(shù)字全

31、部顯示出來了。對于多選一的動態(tài)刷新數(shù)碼管的方式,我們?nèi)绻胹witch會有更好的效果,大家來看一下我們用switch語句完成的情況。#include <reg52.h>               /包含寄存器的庫文件                   sbit  ADDR0 = P10;sbit  ADDR1 = P11;

32、sbit  ADDR2 = P12;sbit  ADDR3 = P13;sbit  ENLED = P14;unsigned char code LedChar =    /用數(shù)組來表示數(shù)碼管真值表    0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,    0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8e,;void main()        unsigned int

33、counter = 0;    unsigned char j = 0;    unsigned long stopwatch =0;    unsigned char LedNumber6=0;    ENLED = 0; ADDR3 = 1;P0 = 0XFF;   /74HC138和P0初始化部分    TMOD = 0x01; /設(shè)置定時器0為模式1    TH0  = 0xFC;    TL0 &

34、#160;= 0x67; /定時值初值,定時1ms    TR0  = 1; /打開定時器0    while(1)            if(1 = TF0)             /判斷定時器0是否溢出                   &

35、#160;TF0 = 0;            TH0 = 0xFC;        /一旦溢出后,重新賦值            TL0 = 0x67;            counter+;            

36、;if(1000 = counter)     /判斷定時器0溢出是否達到1000次                            counter = 0;                stopwatch+;       

37、;           /秒表數(shù)值一秒加1                LedNumber0 = stopwatch%10;                LedNumber1 = stopwatch/10%10;             

38、;   LedNumber2 = stopwatch/100%10;                     LedNumber3 = stopwatch/1000%10;  /數(shù)碼管顯示值計算                LedNumber4 = stopwatch/10000%10;    

39、           LedNumber5 = stopwatch/100000%10;                        switch(j)                        case

40、 0: ADDR0=0; ADDR1=0; ADDR2=0; j+; P0=LedCharLedNumber0;break;             case 1: ADDR0=1; ADDR1=0; ADDR2=0; j+; P0=LedCharLedNumber1;break;            case 2: ADDR0=0; ADDR1=1; ADDR2=0; j+; P0=LedCharLedNumber2;break;&

41、#160;           case 3: ADDR0=1; ADDR1=1; ADDR2=0; j+; P0=LedCharLedNumber3;break;            case 4: ADDR0=0; ADDR1=0; ADDR2=1; j+; P0=LedCharLedNumber4;break;            case 5: ADDR0

42、=1; ADDR1=0; ADDR2=1; j=0; P0=LedCharLedNumber5;break;            default: break;                                       /數(shù)碼管動態(tài)刷新部分  

43、60; 大家是否能感覺到switch語句比if.else語句顯得要整齊的多?6.4.2 數(shù)碼管消隱處理不知道細(xì)心的同學(xué)能否發(fā)現(xiàn),我們的兩次數(shù)碼管動態(tài)刷新顯示的時候似乎并不是那么完美,第一個小問題,大家仔細(xì)看,數(shù)碼管的不應(yīng)該顯示的段,似乎有微微的發(fā)亮,這種現(xiàn)象叫做“鬼影”,這個“鬼影”嚴(yán)重影響了我們的視覺效果,我們該如何解決呢?同 學(xué)們今后可能會遇到各種各樣的問題,可能有很多我是沒有講過的問題,遇到問題怎么辦呢?大家要相信,你作為初學(xué)者,遇到的問題肯定不是第一個遇到的,肯定 有前輩會遇到同類問題,他們一般會在網(wǎng)上發(fā)表各種帖子,各種討論,所以大家遇到問題,首先解決方法就應(yīng)該形成一個到網(wǎng)上搜索的條件

44、反射,這個問題大家可以 到網(wǎng)上搜:“數(shù)碼管消隱”或者“數(shù)碼管鬼影解決”,多找相關(guān)關(guān)鍵詞搜索,會搜索也是一種能力。大家在網(wǎng) 上搜了一下會發(fā)現(xiàn),解決這類問題的普遍兩個方法,其中之一是延時,延時之后我們?nèi)庋劬涂赡芸床坏竭@個“鬼影”了。但是延時是一個非常拙劣的手段,且不說延 時多久能讓我們看不到“鬼影”,延時后,我們的數(shù)碼管亮度會普遍降低。我們解決問題呢,不能只知其然,不知其所以然,所以我們首先要弄懂為什么會出現(xiàn)“鬼 影”?!肮碛啊钡某霈F(xiàn),主要是因為我們數(shù)碼管位選和段選產(chǎn)生的瞬態(tài)所造成的。舉個簡單例子,我們在數(shù)碼管動態(tài)刷新的那部分程序中,實際上每一個數(shù)碼管點亮的持續(xù)時間是1ms的時間,1ms后進行下個

45、數(shù)碼管的切換。在進行數(shù)碼管切換的時候,比如我們從case 5要切換到case 0的時候,case 5的位選用的是ADDR0=1; ADDR1=0; ADDR2=1;假如此刻case5也就是最高位數(shù)碼管對應(yīng)的值是0。我們要切換成的case 0的數(shù)碼管位選是ADDR0=0; ADDR1=0; ADDR2=0;而對應(yīng)的數(shù)碼管的值假如是1。因為我們的C語言程序是一句一句順序往下執(zhí)行的,每一條語句都會占用一定的時間,即使這個時間非常非常短暫。但是當(dāng)我們把“ADDR0=1”改變成“ADDR0=0”的時候,這個瞬間存在了一個中間狀態(tài)ADDR0=0; ADDR1=0; ADDR2=1;在這個瞬間上,我們就給c

46、ase 4對應(yīng)的數(shù)碼管DS5瞬間賦值了0。當(dāng)我們?nèi)繉懲炅薃DDR0=0; ADDR1=0; ADDR2=0;后,這個時候,我們的P0還沒有正式賦值,而P0此刻卻保持了前一次的值,也就是在這個瞬間,我們又給case 0對應(yīng)的數(shù)碼管DS1賦值了一個0。直到我們把case 0后邊的語句全部完成后,我們的刷新才正式完成。而在這個刷新過程中,有2次瞬間我們給了錯誤的數(shù)碼管賦值,雖然很弱(因為亮的時間很短),但是我們還是能夠發(fā)現(xiàn)。那 弄懂了原理后,解決起來就不是困難的事情了,我們只要避開這個瞬態(tài)就可以了。不產(chǎn)生瞬態(tài)的方法是,我們在進行刷新的賦值語句期間,避免一切數(shù)碼管的賦值即 可。方法有兩個,一個方法是

47、刷新之前關(guān)閉所有的段,改變好了位選后,再打開段即可;第二個方法是關(guān)閉數(shù)碼管的位,賦值過程都做好后,再重新打開即可。這個 不是很難,答案我都公布一下。關(guān)閉段:在switch(j)這句程序之前,加一句P0=0XFF;這樣就把數(shù)碼管所有的段都關(guān)閉了,當(dāng)把“ADDR”的值全部搞定后,再給P0賦對應(yīng)的值即可。關(guān)閉位:在switch(j)這句程序之前,加上一句ENLED=1;等到把“ADDR=0; ADDR1=0; ADDR2=0; P0=LedCharLedNumber0;這幾條刷新程序全部寫完后,再加上一句ENLED=0;然后再進行break操作即可。這個地方稍微有點邏輯思路在里邊,大家一定要理解深刻

48、,深刻理解,徹底弄明白,把這個瞬態(tài)弄明白,后邊很多牽扯到此類情況的問題,我們都可以一并搞定。上邊的數(shù)碼管程序還有第二個問題,大家仔細(xì)看,我們的數(shù)碼管上的數(shù)字每一秒變化一次,變化的時候,不參加變化的數(shù)碼管可能出現(xiàn)一次抖動,這個抖動沒有什么專業(yè)的名字,我們就稱之為數(shù)碼管抖動吧。這種數(shù)碼管抖動是什么原因造成的呢?為何在數(shù)據(jù)改變的時候才抖動呢?我們來看我們的程序。我們的程序在定時到1秒的時候,執(zhí)行了“數(shù)碼管顯示值計算”這個過程,一個32位的除法運算,實際上是比較耗費時間的,至于這一段程序占用了多少時間,大家可以通過第四章講的Debug進入看看這段程序運行一共占據(jù)了多少時間。由于達到1秒的時候,程序多運

49、行了這么一段,導(dǎo)致了某個數(shù)碼管的點亮?xí)r間比其他情況下要長一些,時間是1ms+程序消耗時間,于此同時,其它的數(shù)碼管就熄滅了5ms+程序消耗時間,如果這個程序消耗時間非常短,那么可以忽略不計,但很明顯,現(xiàn)在這段程序已經(jīng)比較長了,嚴(yán)重影響我們的視覺效果了,所以我們要采取另外一種思路去解決這個問題。6.5 中斷的學(xué)習(xí)6.5.1 中斷的產(chǎn)生背景比如此刻我正在廚房用煤氣燒一壺水,燒開一壺水剛好需要10分鐘。我是一個主體,燒水是一個目的,而且我只能時 時刻刻在這里燒水,因為一旦水開了,溢出來澆滅煤氣的話,有可能引發(fā)一場災(zāi)難。而這個時候呢,我聽到了電視里傳來天龍八部的主題歌,馬上就要開演了, 我真想奪門而出,

50、去看我最喜歡的電視劇。然而,聽到這個水壺發(fā)出的“咕嘟”的聲音,我清楚:除非水開了,否則我是無法享受我喜歡的電視劇的。這里邊主體只有我一個,而我要做的有兩件事情,一個是看電視,一個是燒水,而電視和燒水是兩個獨立的客體,他們是同時進行的。其中燒水需要10分鐘,但不需要了解燒水的過程的,只需要得到水燒開的這樣一個結(jié)果就行了,提下水壺和關(guān)閉煤氣只需要幾秒的時間而已。所以我們采取的辦法就是:燒水的時候,定上一個鬧鐘,定時10分鐘,然后我就可以安心看電視了。當(dāng)10分鐘時間到了,鬧鐘響了,此刻水也燒開了,我就過去把煤氣滅掉,然后繼續(xù)回來看電視就可以了。這個場景和單片機有什么關(guān)系呢?在單片機的程序處理過程中也

51、有很多類似的場景,當(dāng)單片機正在專心致志的做一件事情的時候(如看電視),總會有一件或者多件緊迫或者不緊迫的事情發(fā)生,需要我們?nèi)リP(guān)注,有一些需要我們停下手頭的工作去馬上完成(比如水開了),只有處理完,才能回頭繼續(xù)完成剛才的工作(看電視)。如果在這個地方用上了單片機的中斷機制,不僅僅我擁有了處理意外情況的能力,而且如果我能夠充分發(fā)揮這個機制的妙用,就可以“同時”完成多個任務(wù)了。如果還是一知半解關(guān)于中斷更詳細(xì)的介紹可以看這里: 6.5.2 定時器中斷應(yīng)用方法在第五章我們學(xué)過定時器,而實際上定時器一般用法都是采取中斷方式來做的,我是故意在第五章用查詢法,就是使用if(TR0 =0)這 樣的語句先講定時器

52、,目的是明確告訴同學(xué)們,定時器和中斷不是一回事,定時器是單片機模塊的一個資源,確確實實存在的一個模塊,而中斷,是單片機的一種運 行機制。尤其是初學(xué)者們,很多人會誤以為定時器和中斷是一個東西,只有定時器才會觸發(fā)中斷,但實際上很多事件都會觸發(fā)中斷的,除了“燒水”,還有“有人按 門鈴”,“來電話了”等等。標(biāo)準(zhǔn)51中與中斷相關(guān)的寄存器,一共有2個,其中1個是中斷使能寄存器,另外1個是中斷優(yōu)先級寄存器,這里先介紹中斷使能寄存器。隨著一些增強型51單片機的問世,可能會有增加的寄存器,大家這些理解了這里所講的,其他的通過自己研讀數(shù)據(jù)手冊全部可以理解明白并且使用起來。表6-1 IE-中斷使能寄存器(地址:A8

53、H)       可位尋址;復(fù)位值:0x00;復(fù)位源:任何復(fù)位位76543210符號EA-ET2ESET1EX1ET0EX0表6-2 IE-中斷使能寄存器的位描述位符號描述7EA總中斷使能位,相當(dāng)于總開關(guān)6-5ET2定時器2中斷使能4ES串口中斷使能3ET1定時器1溢出中斷使能2EX1外部中斷1使能1ET0定時器0中斷使能0EX0外部中斷0使能中斷使能寄存器IE控制了6個中斷使能,其中第6位暫時不用,第七位是總開關(guān),相當(dāng)于我們家里或者學(xué)生宿舍里的那個電源總閘門。而0到5位這6個相當(dāng)于每個分開關(guān)。那么也就是說,我們只要用到中斷,就要寫EA = 1這一句,打

54、開中斷總開關(guān),然后用到哪個分中斷,再打開相對應(yīng)的位就可以了。我們現(xiàn)在就把第五章學(xué)的定時器的程序進行改寫,使用中斷實現(xiàn)出來,把數(shù)碼管的抖動問題也同時一并處理掉。#include <reg52.h>               /包含寄存器的庫文件                   sbit  ADDR0 = P10;sbit  

55、;ADDR1 = P11;sbit  ADDR2 = P12;sbit  ADDR3 = P13;sbit  ENLED = P14;unsigned char code LedChar =    /用數(shù)組來表示數(shù)碼管真值表    0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,    0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8e,;unsigned char LedNumber6 = 0; /定義全局變量un

56、signed char j = 0;unsigned int counter = 0;void main()    unsigned long stopwatch =0;    ENLED = 0; ADDR3 = 1; P0 = 0XFF;   /74HC138和P0初始化部分    TMOD = 0x01;  /設(shè)置定時器0為模式1    TH0  = 0xFC;    TL0  = 0x67; 

57、60;/定時值初值,定時1ms    TR0  = 1;     /打開定時器0    EA = 1;       /打開中中斷    ET0 = 1;      /打開定時器0中斷    while(1)            if(1000 = counter)   

58、0; /判斷定時器0溢出是否達到1000次                    counter = 0;            stopwatch+;            LedNumber0 = stopwatch%10;       

59、;     LedNumber1 = stopwatch/10%10;            LedNumber2 = stopwatch/100%10;            LedNumber3 = stopwatch/1000%10;            LedNumber4 = stopwatch/10000%10;&

60、#160;           LedNumber5 = stopwatch/100000%10;            void InterruptTimer0() interrupt 1               /中斷函數(shù)的特殊寫法,數(shù)字1為中斷入口號    TH0 = 0xFC;   /溢出后進入中

61、斷重新賦值    TL0 = 0x67;    counter+;   /計數(shù)值counter加1    P0 = 0xFF;   /消隱    switch(j)            case 0: ADDR0=0; ADDR1=0; ADDR2=0; j+; P0=LedCharLedNumber0; break;         

62、case 1: ADDR0=1; ADDR1=0; ADDR2=0; j+; P0=LedCharLedNumber1; break;        case 2: ADDR0=0; ADDR1=1; ADDR2=0; j+; P0=LedCharLedNumber2; break;        case 3: ADDR0=1; ADDR1=1; ADDR2=0; j+; P0=LedCharLedNumber3; break;     

63、60;  case 4: ADDR0=0; ADDR1=0; ADDR2=1; j+; P0=LedCharLedNumber4; break;        case 5: ADDR0=1; ADDR1=0; ADDR2=1; j=0; P0=LedCharLedNumber5; break;        default: break;        /動態(tài)刷新   大家可以先把這個程序了解明

64、白,下載到單片機里邊實驗一下,看看實際效果。是否可以看出來,近乎完美的顯示效果經(jīng)過我們的努力終于做成功了。那下面我們還要來解析一下我們的這個程序。在我們這個程序中,有兩個函數(shù),一個是主函數(shù),一個是中斷函數(shù)。主函數(shù)main()我們就不用說了,重點強調(diào)一下中斷函數(shù),中斷函數(shù)的格式是固定的,首先中斷函數(shù)前邊void表示函數(shù)返回空,即中斷函數(shù)不返回任何值,函數(shù)名字是InterruptTimer0(),這個函數(shù)名字只要符合函數(shù)命名規(guī)則的前提下我們就可以隨便起,我這樣起名字是為了方便區(qū)分和記憶,而后是interrupt這個關(guān)鍵字不能錯,這個是中斷特有的關(guān)鍵字,另外后邊還有個數(shù)字1,這個數(shù)字1怎么來的呢?我們先來看一個表格。表6-3 中斷查詢序列描述中斷標(biāo)志向量地址中斷使能默認(rèn)優(yōu)先級外部中斷0IE00003HEX01(最高)T0中斷TF0000BHET02外部中斷1IE10013HEX13T1中斷TF1001BHET14UART中斷TI/RI0023HES5T2中斷TF2/EXF2002BHET26這個表格同樣不需要大家記住,需要的時候過來查就可以了。我們現(xiàn)在看第二行T0中斷,它的中斷標(biāo)志是TF0,也就是當(dāng)TF0變成1

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論