<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > 《手把手教你學(xué)51單片機(jī)-C語(yǔ)言》之六 中斷與數(shù)碼管動(dòng)態(tài)顯示

          《手把手教你學(xué)51單片機(jī)-C語(yǔ)言》之六 中斷與數(shù)碼管動(dòng)態(tài)顯示

          作者: 時(shí)間:2016-11-19 來(lái)源:網(wǎng)絡(luò) 收藏

          中斷單片機(jī)系統(tǒng)重點(diǎn)中的重點(diǎn),因?yàn)橛辛酥袛?,單片機(jī)就具備了快速協(xié)調(diào)多模塊工作的能力,可以完成復(fù)雜的任務(wù)。本章將首先帶領(lǐng)大家學(xué)習(xí)一些必要的C語(yǔ)言基礎(chǔ)知識(shí),然后講解數(shù)碼管動(dòng)態(tài)顯示的原理,并最終借助于中斷系統(tǒng)來(lái)完成實(shí)用的數(shù)碼管顯示程序。大家對(duì)本章節(jié)內(nèi)容要多多研究,要完全掌握并能熟練運(yùn)用。

          本文引用地址:http://www.ex-cimer.com/article/201611/318571.htm

          1.1C語(yǔ)言的數(shù)組

          1.1.1數(shù)組的基本概念

          第四章已經(jīng)學(xué)過(guò)變量的基本類型,比如char、int等等。這種類型描述的都是單個(gè)具有特定意義的數(shù)據(jù),當(dāng)我們要處理?yè)碛型愐饬x但是卻包含很多個(gè)數(shù)據(jù)的時(shí)候,就可以用到數(shù)組了,比如我們上節(jié)課那個(gè)數(shù)碼管的真值表,就是用一個(gè)數(shù)組來(lái)表達(dá)的。

          從概念上講,數(shù)組是具有相同數(shù)據(jù)類型的有序數(shù)據(jù)的組合,一般來(lái)講,數(shù)組定義后滿足以下三個(gè)條件。

          1、具有相同的數(shù)據(jù)類型;

          2、具有相同的名字;

          3、在存儲(chǔ)器中是被連續(xù)存放的。

          比如我們上節(jié)課定義的那個(gè)數(shù)碼管真值表,如果我們把關(guān)鍵字code去掉,數(shù)組元素將被保存在RAM中,在程序中可讀可寫,同時(shí)我們也可以在中括號(hào)里邊標(biāo)明這個(gè)數(shù)組所包含的元素個(gè)數(shù),比如:

          unsignedcharLedChar[16]={

          0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,

          0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E

          };

          在這個(gè)數(shù)組中的每個(gè)值都稱之為數(shù)組的一個(gè)元素,這些元素都具備相同的數(shù)據(jù)類型就是unsignedchar型,他們有一個(gè)共同的名字LedChar,不管放到RAM中還是FLASH中,他們都是存放在一塊連續(xù)的存儲(chǔ)空間里的。

          有一點(diǎn)要特別注意,這個(gè)數(shù)組一共有16(中括號(hào)里面的數(shù)值)個(gè)元素,但是數(shù)組的單個(gè)元素的表達(dá)方式——下標(biāo)是從0開始,因此實(shí)際上上邊這個(gè)數(shù)組的首個(gè)元素LedChar[0]的值是0xC0,而LedChar[15]的值是0x8E,下標(biāo)從0到15一共是16個(gè)元素。

          LedChar這個(gè)數(shù)組只有一個(gè)下標(biāo),我們稱之為一維數(shù)組,還有兩個(gè)下標(biāo)和多個(gè)下標(biāo)的,我們稱之為二維數(shù)組和多維數(shù)組。比如unsignedchara[2][3];表示這是一個(gè)2行3列的二維數(shù)組。在大多數(shù)情況下我們使用的是一維數(shù)組,對(duì)于初學(xué)來(lái)說(shuō),我們先來(lái)研究一維數(shù)組,多維數(shù)組等遇到了再來(lái)了解。

          1.1.2數(shù)組的聲明

          一維數(shù)組的聲明格式如下:

          數(shù)據(jù)類型數(shù)組名[數(shù)組長(zhǎng)度];

          1、數(shù)組的數(shù)據(jù)類型聲明的是該數(shù)組的每個(gè)元素的類型,即一個(gè)數(shù)組中的元素具有相同的數(shù)據(jù)類型。

          2、數(shù)組名的聲明要符合C語(yǔ)言固定的標(biāo)識(shí)符的聲明要求,只能由字母、數(shù)字、下劃線這三種符號(hào)組成,且第一個(gè)字符只能是字母或者下劃線。

          3、方括號(hào)中的數(shù)組長(zhǎng)度是一個(gè)常量或常量表達(dá)式,并且必須是正整數(shù)。

          1.1.3數(shù)組的初始化

          數(shù)組在進(jìn)行聲明的同時(shí)可以進(jìn)行初始化操作,格式如下:

          數(shù)據(jù)類型數(shù)組名[數(shù)組長(zhǎng)度]={初值列表};

          還是以上節(jié)課我們用的數(shù)碼管的真值表為例來(lái)講解注意事項(xiàng)。

          unsignedcharLedChar[16]={

          0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,

          0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E

          };

          1、初值列表里的數(shù)據(jù)之間要用逗號(hào)隔開;

          2、初值列表里的初值的數(shù)量必須等于或小于數(shù)組長(zhǎng)度,當(dāng)小于數(shù)組長(zhǎng)度時(shí),數(shù)組的后邊沒(méi)有賦初值的元素由系統(tǒng)自動(dòng)賦值為0。

          3、若給數(shù)組的所有元素都賦初值,那么可以省略數(shù)組的長(zhǎng)度,上節(jié)課的例子中我們實(shí)際上已經(jīng)省略了數(shù)組的長(zhǎng)度。

          4、系統(tǒng)為數(shù)組分配連續(xù)的存儲(chǔ)單元的時(shí)候,數(shù)組元素的相對(duì)次序由下標(biāo)來(lái)決定,就是說(shuō)LedChar[0]、LedChar[1]……LedChar[15]是按照順序緊挨著依次排下來(lái)的。

          1.1.4數(shù)組的使用和賦值

          在C語(yǔ)言程序中,是不能一次使用整個(gè)數(shù)組的,只能使用數(shù)組的單個(gè)元素。一個(gè)數(shù)組元素相當(dāng)于一個(gè)變量,使用數(shù)組元素的時(shí)候與使用相同數(shù)據(jù)類型的變量的方法是一樣的。比如LedChar這個(gè)數(shù)組,如果沒(méi)加code關(guān)鍵字,那么它可讀可寫,我們可以寫成a=LedChar[0]這樣來(lái)把數(shù)組的一個(gè)元素的值送個(gè)a這個(gè)變量,也可以寫成LedChar[0]=a這樣把a(bǔ)這個(gè)變量的值送給數(shù)組中的一個(gè)元素,以下三點(diǎn)要注意:

          1、引用數(shù)組的時(shí)候,那個(gè)方括號(hào)里的數(shù)字代表的是數(shù)組元素的下標(biāo),而數(shù)組初始化的時(shí)候方括號(hào)里的數(shù)字代表的是這個(gè)數(shù)組中元素的總數(shù)。

          2、數(shù)組元素的方括號(hào)里的下標(biāo)可以是整型常數(shù),整型變量或者表達(dá)式,而數(shù)組初始化的時(shí)候方括號(hào)里的數(shù)字必須是常數(shù)不能是變量。

          3、數(shù)組整體賦值只能在初始化的時(shí)候進(jìn)行,程序執(zhí)行代碼中只能對(duì)單個(gè)元素賦值。

          1.2if語(yǔ)句

          到目前為止,我們對(duì)if語(yǔ)句應(yīng)該已經(jīng)不陌生了,前邊程序已用過(guò)多次了,這里我們系統(tǒng)的介紹一下,方便后邊的深入學(xué)習(xí)。if語(yǔ)句有兩個(gè)關(guān)鍵字:if和else,把這兩個(gè)關(guān)鍵字翻譯一下就是:“如果”和“否則”。if語(yǔ)句一共有三種格式,我們分別來(lái)看。

          1、if語(yǔ)句的默認(rèn)形式:

          if(條件表達(dá)式)

          {

          語(yǔ)句1;

          }

          其執(zhí)行過(guò)程是,if(即如果)條件表達(dá)式的值為“真”,則執(zhí)行語(yǔ)句1;如果條件表達(dá)式的值為“假”,則不執(zhí)行語(yǔ)句1。真和假的概念不再贅述,參考第五章。

          這里要提醒大家一點(diǎn),C語(yǔ)言一個(gè)分號(hào)表示一條語(yǔ)句的結(jié)束,因此如果if后邊只有一條執(zhí)行語(yǔ)句的時(shí)候,可以省略大括號(hào),但是如果有多條執(zhí)行語(yǔ)句的話,必須加上大括號(hào)。

          那么現(xiàn)在,我們上節(jié)課的語(yǔ)句就很好理解了:

          if(sec>=16)

          {

          sec=0;

          }

          當(dāng)sec的值大于或等于16的時(shí)候,括號(hào)里的值才是“真”,那么就執(zhí)行sec=0這一句,當(dāng)sec的值小于16時(shí),那么括號(hào)里就為“假”,就不執(zhí)行這一句。

          2、if...else語(yǔ)句

          有些情況下,我們除了要在括號(hào)里條件滿足時(shí)執(zhí)行相應(yīng)的語(yǔ)句外,在不滿足該條件的時(shí)候,也要執(zhí)行一些另外的語(yǔ)句,這時(shí)候就用到了if...else語(yǔ)句,它的基本語(yǔ)法形式是:

          if(條件表達(dá)式)

          {

          語(yǔ)句1;

          }

          else

          {

          語(yǔ)句2;

          }

          比如上節(jié)課的最后一段程序我們也可以寫成:

          P0=LedChar[sec];

          if(sec>=15)

          {

          sec=0;

          }

          else

          {

          Sec++;

          }

          這個(gè)程序大家可以修改下載到單片機(jī)里驗(yàn)證一下,程序邏輯大家自己動(dòng)腦筋分析,注意條件表達(dá)式內(nèi)16到15的變化,想一下為什么,我就不多解釋了。

          3、if....elseif語(yǔ)句

          if...esle語(yǔ)句是一個(gè)二選一的語(yǔ)句,或者執(zhí)行if分支后的語(yǔ)句,或者執(zhí)行else分支后的語(yǔ)句。還有一種多選一的用法就是if...elseif語(yǔ)句。他的基本語(yǔ)法格式是:

          if(條件表達(dá)式1){語(yǔ)句1;}

          elseif(條件表達(dá)式2){語(yǔ)句2;}

          elseif(條件表達(dá)式3){語(yǔ)句3;}

          ......

          else{語(yǔ)句n;}

          他的執(zhí)行過(guò)程是:依次判斷條件表達(dá)式的值,當(dāng)出現(xiàn)某個(gè)值為“真”時(shí),則執(zhí)行相對(duì)應(yīng)的語(yǔ)句,然后跳出整個(gè)if的語(yǔ)句塊,執(zhí)行“語(yǔ)句n”后面的程序;如果所有的表達(dá)式都為“假”,則執(zhí)行else分支的“語(yǔ)句n”后,再執(zhí)行“語(yǔ)句n”后邊的程序。

          if語(yǔ)句在C語(yǔ)言編程中使用頻率很高,用法也不復(fù)雜,所以必須要熟練掌握。

          1.3switch語(yǔ)句

          用if....else語(yǔ)句在處理多分支的時(shí)候,分支太多就會(huì)顯得不方便,且容易出現(xiàn)if和else配對(duì)出現(xiàn)錯(cuò)誤的情況,在C語(yǔ)言中提供了另外一種多分支選擇的語(yǔ)句——switch語(yǔ)句,它的基本語(yǔ)法格式如下:

          switch(表達(dá)式)

          {

          case常量表達(dá)式1:語(yǔ)句1;

          case常量表達(dá)式2:語(yǔ)句2;

          ......

          case常量表達(dá)式n:語(yǔ)句n;

          default:語(yǔ)句n+1;

          }

          它的執(zhí)行過(guò)程是:首先計(jì)算“表達(dá)式”的值,然后從第一個(gè)case開始,與“常量表達(dá)式x”進(jìn)行比較,如果與當(dāng)前常量表達(dá)式的值不相等,那么就不執(zhí)行冒號(hào)后邊的語(yǔ)句x,一旦發(fā)現(xiàn)和某個(gè)常量表達(dá)式的值相等了,那么它會(huì)執(zhí)行之后所有的語(yǔ)句,如果直到最后一個(gè)“常量表達(dá)式n”都沒(méi)有找到相等的值,那么就執(zhí)行default后的“語(yǔ)句n+1”。請(qǐng)?zhí)貏e注意一點(diǎn),當(dāng)找到一個(gè)相等的case分支后,會(huì)執(zhí)行該分支以及之后所有分支的語(yǔ)句,很明顯這不是我們想要的結(jié)果。

          在C語(yǔ)言中,有一條break語(yǔ)句,作用是跳出當(dāng)前的循環(huán)語(yǔ)句,包括for循環(huán)和while循環(huán),同時(shí),它還能用來(lái)結(jié)束switch語(yǔ)句塊。switch的分支語(yǔ)句一共有n+1種,而我們通常希望的都是選擇其中的一個(gè)分支來(lái)執(zhí)行,執(zhí)行完后就結(jié)束整個(gè)switch語(yǔ)句,而繼續(xù)執(zhí)行switch后面的語(yǔ)句,此時(shí)就可以通過(guò)在每個(gè)分支后加上break語(yǔ)句來(lái)實(shí)現(xiàn)了。如下:

          switch(表達(dá)式)

          {

          case常量表達(dá)式1:語(yǔ)句1;break;

          case常量表達(dá)式2:語(yǔ)句2;break;

          ......

          case常量表達(dá)式n:語(yǔ)句n;break;

          default:語(yǔ)句n+1;break;

          }

          加了這個(gè)break語(yǔ)句后,一旦“常量表達(dá)式x”與“表達(dá)式”的值相等了,那么就執(zhí)行“語(yǔ)句x”,執(zhí)行完畢后,由于有了break則直接跳出switch語(yǔ)句,繼續(xù)執(zhí)行switch語(yǔ)句后面的程序了,這樣就可以避免執(zhí)行不必要的語(yǔ)句。了解了這個(gè)switch語(yǔ)句后,我們馬上會(huì)在本章程序中使用鞏固它。

          1.4數(shù)碼管的動(dòng)態(tài)顯示

          1.4.1動(dòng)態(tài)顯示的基本原理

          我們?cè)谏弦徽聦W(xué)習(xí)數(shù)碼管靜態(tài)顯示的時(shí)候說(shuō)到,74HC138只能在同一時(shí)刻導(dǎo)通一個(gè)三極管,而我們的數(shù)碼管是靠了6個(gè)三極管來(lái)控制,那我們?nèi)绾蝸?lái)讓數(shù)碼管同時(shí)顯示呢?這就用到了動(dòng)態(tài)顯示的概念。

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

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

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

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

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

          #include

          sbitADDR0=P1^0;

          sbitADDR1=P1^1;

          sbitADDR2=P1^2;

          sbitADDR3=P1^3;

          sbitENLED=P1^4;

          unsignedcharcodeLedChar[]={//數(shù)碼管顯示字符轉(zhuǎn)換表

          0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,

          0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E

          };

          unsignedcharLedBuff[6]={//數(shù)碼管顯示緩沖區(qū),初值0xFF確保啟動(dòng)時(shí)都不亮

          0xFF,0xFF,0xFF,0xFF,0xFF,0xFF

          };

          voidmain()

          {

          unsignedchari=0;//動(dòng)態(tài)掃描的索引

          unsignedintcnt=0;//記錄T0中斷次數(shù)

          unsignedlongsec=0;//記錄經(jīng)過(guò)的秒數(shù)

          ENLED=0;//使能U3,選擇控制數(shù)碼管

          ADDR3=1;//因?yàn)樾枰獎(jiǎng)討B(tài)改變ADDR0-2的值,所以不需要再初始化了

          TMOD=0x01;//設(shè)置T0為模式1

          TH0=0xFC;//為T0賦初值0xFC67,定時(shí)1ms

          TL0=0x67;

          TR0=1;//啟動(dòng)T0

          while(1)

          {

          if(TF0==1)//判斷T0是否溢出

          {

          TF0=0;//T0溢出后,清零中斷標(biāo)志

          TH0=0xFC;//并重新賦初值

          TL0=0x67;

          cnt++;//計(jì)數(shù)值自加1

          if(cnt>=1000)//判斷T0溢出是否達(dá)到1000次

          {

          cnt=0;//達(dá)到1000次后計(jì)數(shù)值清零

          sec++;//秒計(jì)數(shù)自加1

          //以下代碼將sec按十進(jìn)制位從低到高依次提取并轉(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ù)碼管動(dòng)態(tài)掃描刷新

          if(i==0)

          {ADDR2=0;ADDR1=0;ADDR0=0;i++;P0=LedBuff[0];}

          elseif(i==1)

          {ADDR2=0;ADDR1=0;ADDR0=1;i++;P0=LedBuff[1];}

          elseif(i==2)

          {ADDR2=0;ADDR1=1;ADDR0=0;i++;P0=LedBuff[2];}

          elseif(i==3)

          {ADDR2=0;ADDR1=1;ADDR0=1;i++;P0=LedBuff[3];}

          elseif(i==4)

          {ADDR2=1;ADDR1=0;ADDR0=0;i++;P0=LedBuff[4];}

          elseif(i==5)

          {ADDR2=1;ADDR1=0;ADDR0=1;i=0;P0=LedBuff[5];}

          }

          }

          }

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

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

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

          #include

          sbitADDR0=P1^0;

          sbitADDR1=P1^1;

          sbitADDR2=P1^2;

          sbitADDR3=P1^3;

          sbitENLED=P1^4;

          unsignedcharcodeLedChar[]={//數(shù)碼管顯示字符轉(zhuǎn)換表

          0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,

          0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E

          };

          unsignedcharLedBuff[6]={//數(shù)碼管顯示緩沖區(qū),初值0xFF確保啟動(dòng)時(shí)都不亮

          0xFF,0xFF,0xFF,0xFF,0xFF,0xFF

          };

          voidmain()

          {

          unsignedchari=0;//動(dòng)態(tài)掃描的索引

          unsignedintcnt=0;//記錄T0中斷次數(shù)

          unsignedlongsec=0;//記錄經(jīng)過(guò)的秒數(shù)

          ENLED=0;//使能U3,選擇控制數(shù)碼管

          ADDR3=1;//因?yàn)樾枰獎(jiǎng)討B(tài)改變ADDR0-2的值,所以不需要再初始化了

          TMOD=0x01;//設(shè)置T0為模式1

          TH0=0xFC;//為T0賦初值0xFC67,定時(shí)1ms

          TL0=0x67;

          TR0=1;//啟動(dòng)T0

          while(1)

          {

          if(TF0==1)//判斷T0是否溢出

          {

          TF0=0;//T0溢出后,清零中斷標(biāo)志

          TH0=0xFC;//并重新賦初值

          TL0=0x67;

          cnt++;//計(jì)數(shù)值自加1

          if(cnt>=1000)//判斷T0溢出是否達(dá)到1000次

          {

          cnt=0;//達(dá)到1000次后計(jì)數(shù)值清零

          sec++;//秒計(jì)數(shù)自加1

          //以下代碼將sec按十進(jìn)制位從低到高依次提取并轉(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ù)碼管動(dòng)態(tài)掃描刷新

          switch(i)

          {

          case0:ADDR2=0;ADDR1=0;ADDR0=0;i++;P0=LedBuff[0];break;

          case1:ADDR2=0;ADDR1=0;ADDR0=1;i++;P0=LedBuff[1];break;

          case2:ADDR2=0;ADDR1=1;ADDR0=0;i++;P0=LedBuff[2];break;

          case3:ADDR2=0;ADDR1=1;ADDR0=1;i++;P0=LedBuff[3];break;

          case4:ADDR2=1;ADDR1=0;ADDR0=0;i++;P0=LedBuff[4];break;

          case5:ADDR2=1;ADDR1=0;ADDR0=1;i=0;P0=LedBuff[5];break;

          default:break;

          }

          }

          }

          }

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

          1.4.2數(shù)碼管顯示消隱

          不知道同學(xué)們是否發(fā)現(xiàn)了,我們的這兩個(gè)數(shù)碼管動(dòng)態(tài)顯示程序的運(yùn)行效果似乎并不是那么完美,第一個(gè)小問(wèn)題,大家仔細(xì)看,數(shù)碼管的不應(yīng)該亮的段,似乎有微微的發(fā)亮,這種現(xiàn)象叫做“鬼影”,這個(gè)“鬼影”嚴(yán)重影響了我們的視覺效果,我們?cè)撊绾谓鉀Q呢?

          同學(xué)們?cè)诮窈罂赡軙?huì)遇到各種各樣的實(shí)際問(wèn)題,可能很多都是我們沒(méi)有講過(guò)的,遇到問(wèn)題怎么辦呢?大家要相信,你作為初學(xué)者,遇到的問(wèn)題肯定不是第一個(gè)遇到的,肯定有前輩已經(jīng)遇到過(guò)相同的或類似的問(wèn)題,他們一般都會(huì)在網(wǎng)上發(fā)表各種帖子,各種討論,所以大家遇到問(wèn)題,首先就應(yīng)該形成一個(gè)到網(wǎng)上搜索的條件反射,這個(gè)問(wèn)題大家可以到網(wǎng)上搜:“數(shù)碼管消隱”或者“數(shù)碼管鬼影解決”,多找相關(guān)關(guān)鍵詞搜索試試,會(huì)搜索也是一種能力。

          大家在網(wǎng)上搜了一下會(huì)發(fā)現(xiàn),解決這類問(wèn)題的方法有兩個(gè),其中之一是延時(shí),延時(shí)之后我們?nèi)庋劬涂赡芸床坏竭@個(gè)“鬼影”了。但是延時(shí)是一個(gè)非常拙劣的手段,且不說(shuō)延時(shí)多久能讓我們看不到“鬼影”,延時(shí)后,我們的數(shù)碼管亮度會(huì)普遍降低。我們解決問(wèn)題呢,不能只知其然,還要知其所以然,那么我們首先就來(lái)弄明白為什么會(huì)出現(xiàn)“鬼影”。

          “鬼影”的出現(xiàn),主要是在數(shù)碼管位選和段選產(chǎn)生的瞬態(tài)造成的。舉個(gè)簡(jiǎn)單例子,我們?cè)跀?shù)碼管動(dòng)態(tài)顯示的那部分程序中,實(shí)際上每一個(gè)數(shù)碼管點(diǎn)亮的持續(xù)時(shí)間是1ms的時(shí)間,1ms后進(jìn)行下個(gè)數(shù)碼管的切換。在進(jìn)行數(shù)碼管切換的時(shí)候,比如我們從case5要切換到case0的時(shí)候,case5的位選用的是ADDR0=1;ADDR1=0;ADDR2=1;假如此刻case5也就是最高位數(shù)碼管對(duì)應(yīng)的值是0,我們要切換成的case0的數(shù)碼管位選是ADDR0=0;ADDR1=0;ADDR2=0;而對(duì)應(yīng)的數(shù)碼管的值假如是1。又因?yàn)镃語(yǔ)言程序是一句一句順序往下執(zhí)行的,每一條語(yǔ)句的執(zhí)行都會(huì)占用一定的時(shí)間,即使這個(gè)時(shí)間非常非常短暫。但是當(dāng)我們把“ADDR0=1”改變成“ADDR0=0”的時(shí)候,這個(gè)瞬間存在了一個(gè)中間狀態(tài)ADDR0=0;ADDR1=0;ADDR2=1;在這個(gè)瞬間上,我們就給case4對(duì)應(yīng)的數(shù)碼管DS5瞬間賦值了0。當(dāng)我們?nèi)繉懲炅薃DDR0=0;ADDR1=0;ADDR2=0;后,這個(gè)時(shí)候,我們的P0還沒(méi)有正式賦值,而P0此刻卻保持了前一次的值,也就是在這個(gè)瞬間,我們又給case0對(duì)應(yīng)的數(shù)碼管DS1賦值了一個(gè)0。直到我們把case0后邊的語(yǔ)句全部完成后,我們的刷新才正式完成。而在這個(gè)刷新過(guò)程中,有2個(gè)瞬間我們給錯(cuò)誤的數(shù)碼管賦了值,雖然很弱(因?yàn)榱恋臅r(shí)間很短),但是我們還是能夠發(fā)現(xiàn)。

          那么搞明白了原理后,解決起來(lái)就不是困難的事情了,我們只要避開這個(gè)瞬間錯(cuò)誤就可以了。不產(chǎn)生瞬間錯(cuò)誤的方法是,在進(jìn)行位選切換期間,避免一切數(shù)碼管的賦值即可。方法有兩個(gè),一個(gè)方法是刷新之前關(guān)閉所有的段,改變好了位選后,再打開段即可;第二個(gè)方法是關(guān)閉數(shù)碼管的位,賦值過(guò)程都做好后,再重新打開即可。這個(gè)不是很難,答案我都公布一下。

          關(guān)閉段:在switch(i)這句程序之前,加一句P0=0xFF;這樣就把數(shù)碼管所有的段都關(guān)閉了,當(dāng)把“ADDR”的值全部搞定后,再給P0賦對(duì)應(yīng)的值即可。

          關(guān)閉位:在switch(i)這句程序之前,加上一句ENLED=1;等到把ADDR2=0;ADDR1=0;ADDR0=0;i++;P0=LedBuff[0];這幾條刷新程序全部寫完后,再加上一句ENLED=0;然后再進(jìn)行break操作即可。

          這個(gè)地方邏輯思路上稍微有點(diǎn)復(fù)雜,大家一定要理解深刻,深刻理解,徹底弄明白,把這個(gè)瞬間的問(wèn)題弄明白了,后邊很多牽扯到此類情況的問(wèn)題,我們都可以一并搞定。

          上邊的數(shù)碼管程序還有第二個(gè)問(wèn)題,大家仔細(xì)看,我們的數(shù)碼管上的數(shù)字每一秒變化一次,變化的時(shí)候,不參加變化的數(shù)碼管可能出現(xiàn)一次抖動(dòng),這個(gè)抖動(dòng)沒(méi)有什么專業(yè)的名字,我們就稱之為數(shù)碼管抖動(dòng)吧。這種數(shù)碼管抖動(dòng)是什么原因造成的呢?為何在數(shù)據(jù)改變的時(shí)候才抖動(dòng)呢?

          來(lái)分析一下我們的程序,程序在定時(shí)到1秒的時(shí)候,執(zhí)行了“秒數(shù)+1并轉(zhuǎn)換為數(shù)碼管顯示字符”這個(gè)操作,一個(gè)32位整型數(shù)的除法運(yùn)算,實(shí)際上是比較耗費(fèi)時(shí)間的,至于這一段程序究竟耗費(fèi)了多少時(shí)間,大家可以通過(guò)第四章講的調(diào)試方法來(lái)看看這段程序運(yùn)行用了多少時(shí)間。由于每次定時(shí)到1秒的時(shí)候,程序都多運(yùn)行了這么一段,導(dǎo)致了某個(gè)數(shù)碼管的點(diǎn)亮?xí)r間比其他情況下要長(zhǎng)一些,總時(shí)間就變成了1ms+本段程序運(yùn)行時(shí)間,于此同時(shí),其它的數(shù)碼管就熄滅了5ms+本段程序運(yùn)行時(shí)間,如果這段程序運(yùn)行時(shí)間非常短,那么可以忽略不計(jì),但很明顯,現(xiàn)在這段程序運(yùn)行時(shí)間已經(jīng)比較長(zhǎng)了,以致于嚴(yán)重影響到視覺效果了,所以我們要采取另外一種思路去解決這個(gè)問(wèn)題。

          1.5單片機(jī)中斷系統(tǒng)

          1.5.1中斷的產(chǎn)生背景

          請(qǐng)?jiān)O(shè)想這樣一個(gè)場(chǎng)景:此刻我正在廚房用煤氣燒一壺水,而燒開一壺水剛好需要10分鐘,我是一個(gè)主體,燒水是一個(gè)目的,而且我只能時(shí)時(shí)刻刻在這里燒水,因?yàn)橐坏┧_了,溢出來(lái)澆滅煤氣的話,有可能引發(fā)一場(chǎng)災(zāi)難。但就在這個(gè)時(shí)候呢,我又聽到了電視里傳來(lái)《天龍八部》的主題歌,馬上就要開演了,我真想奪門而出,去看我最喜歡的電視劇。然而,聽到這個(gè)水壺發(fā)出的“咕嘟”的聲音,我清楚:除非等水燒開了,否則我是無(wú)法享受我喜歡的電視劇的。

          這里邊主體只有一個(gè)我,而我要做的有兩件事情,一個(gè)是看電視,一個(gè)是燒水,而電視和燒水是兩個(gè)獨(dú)立的客體,它們是同時(shí)進(jìn)行的。其中燒水需要10分鐘,但不需要了解燒水的過(guò)程,只需要得到水燒開的這樣一個(gè)結(jié)果就行了,提下水壺和關(guān)閉煤氣只需要幾秒的時(shí)間而已。所以我們采取的辦法就是:燒水的時(shí)候,定上一個(gè)鬧鐘,定時(shí)10分鐘,然后我就可以安心看電視了。當(dāng)10分鐘時(shí)間到了,鬧鐘響了,此刻水也燒開了,我就過(guò)去把煤氣滅掉,然后繼續(xù)回來(lái)看電視就可以了。

          這個(gè)場(chǎng)景和單片機(jī)有什么關(guān)系呢?

          在單片機(jī)的程序處理過(guò)程中也有很多類似的場(chǎng)景,當(dāng)單片機(jī)正在專心致志的做一件事情(看電視)的時(shí)候,總會(huì)有一件或者多件緊迫或者不緊迫的事情發(fā)生,需要我們?nèi)リP(guān)注,有一些需要我們停下手頭的工作去馬上去處理(比如水開了),只有處理完了,才能回頭繼續(xù)完成剛才的工作(看電視)。這種情況下單片機(jī)的中斷系統(tǒng)就該發(fā)揮它的強(qiáng)大作用了,合理巧妙的利用中斷,不僅可以使我們獲得處理突發(fā)狀況的能力,而且可以使單片機(jī)能夠“同時(shí)”完成多項(xiàng)任務(wù)。

          1.5.2定時(shí)器中斷的應(yīng)用

          在第五章我們學(xué)過(guò)了定時(shí)器,而實(shí)際上定時(shí)器一般用法都是采取中斷方式來(lái)做的,我是故意在第五章用查詢法,就是使用if(TF0==1)這樣的語(yǔ)句先用定時(shí)器,目的是明確告訴同學(xué)們,定時(shí)器和中斷不是一回事,定時(shí)器是單片機(jī)模塊的一個(gè)資源,確確實(shí)實(shí)存在的一個(gè)模塊,而中斷,是單片機(jī)的一種運(yùn)行機(jī)制。尤其是初學(xué)者們,很多人會(huì)誤以為定時(shí)器和中斷是一個(gè)東西,只有定時(shí)器才會(huì)觸發(fā)中斷,但實(shí)際上很多事件都會(huì)觸發(fā)中斷的,除了“燒水”,還有“有人按門鈴”,“來(lái)電話了”等等。

          標(biāo)準(zhǔn)51單片機(jī)中控制中斷的寄存器有兩個(gè),一個(gè)是中斷使能寄存器,另一個(gè)是中斷優(yōu)先級(jí)寄存器,這里先介紹中斷使能寄存器,如表6-1和表6-2所示。隨著一些增強(qiáng)型51單片機(jī)的問(wèn)世,可能會(huì)有增加的寄存器,大家理解了我們這里所講的,其它的通過(guò)自己研讀數(shù)據(jù)手冊(cè)就可以理解明白并且用起來(lái)了。

          本帖子中包含更多資源

          您需要登錄才可以下載或查看,沒(méi)有帳號(hào)?立即注冊(cè)

          x
          我要點(diǎn)贊0

          收藏1舉報(bào)

          這個(gè)家伙好懶,什么都沒(méi)留下回復(fù)編輯
          金沙灘工作室

          • 發(fā)消息
          發(fā)表于 2014-6-9 08:47:22
          2#

          表6-1IE——中斷使能寄存器的位分配(地址0xA8、可位尋址)

          7

          6

          5

          4

          3

          2

          1

          0

          符號(hào)

          EA

          --

          ET2

          ES

          ET1

          EX1

          ET0

          EX0

          復(fù)位值

          0

          --

          0

          0

          0

          0

          0

          0

          表6-2IE——中斷使能寄存器的位描述

          符號(hào)

          描述

          7

          EA

          總中斷使能位,相當(dāng)于總開關(guān)

          6

          --

          --

          5

          ET2

          定時(shí)器2中斷使能

          4

          ES

          串口中斷使能

          3

          ET1

          定時(shí)器1中斷使能

          2

          EX1

          外部中斷1使能

          1

          ET0

          定時(shí)器0中斷使能

          0

          EX0

          外部中斷0使能

          中斷使能寄存器IE的位0~5控制了6個(gè)中斷使能,而第6位沒(méi)有用到,第7位是總開關(guān)。總開關(guān)就相當(dāng)于我們家里或者學(xué)生宿舍里的那個(gè)電源總閘門,而0~5位這6個(gè)位相當(dāng)于每個(gè)分開關(guān)。那么也就是說(shuō),我們只要用到中斷,就要寫EA=1這一句,打開中斷總開關(guān),然后用到哪個(gè)分中斷,再打開相對(duì)應(yīng)的控制位就可以了。

          我們現(xiàn)在就把前面的數(shù)碼管動(dòng)態(tài)顯示的程序改用中斷再實(shí)現(xiàn)出來(lái),同時(shí)數(shù)碼管顯示抖動(dòng)和“鬼影”也一并處理掉了。程序運(yùn)行的流程跟圖6-1所示的流程圖是基本一致的,但因?yàn)榧尤肓酥袛?,所以整個(gè)流程被分成了兩部分,秒計(jì)數(shù)和轉(zhuǎn)換為數(shù)碼管顯示字符的部分還留在主循環(huán)內(nèi),而動(dòng)態(tài)掃描部分則移到了中斷函數(shù)內(nèi),并加入了消隱的處理。下面來(lái)看程序:

          #include

          sbitADDR0=P1^0;

          sbitADDR1=P1^1;

          sbitADDR2=P1^2;

          sbitADDR3=P1^3;

          sbitENLED=P1^4;

          unsignedcharcodeLedChar[]={//數(shù)碼管顯示字符轉(zhuǎn)換表

          0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,

          0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E

          };

          unsignedcharLedBuff[6]={//數(shù)碼管顯示緩沖區(qū),初值0xFF確保啟動(dòng)時(shí)都不亮

          0xFF,0xFF,0xFF,0xFF,0xFF,0xFF

          };

          unsignedchari=0;//動(dòng)態(tài)掃描的索引

          unsignedintcnt=0;//記錄T0中斷次數(shù)

          voidmain()

          {

          unsignedlongsec=0;//記錄經(jīng)過(guò)的秒數(shù)

          EA=1;//使能總中斷

          ENLED=0;//使能U3,選擇控制數(shù)碼管

          ADDR3=1;//因?yàn)樾枰獎(jiǎng)討B(tài)改變ADDR0-2的值,所以不需要再初始化了

          TMOD=0x01;//設(shè)置T0為模式1

          TH0=0xFC;//為T0賦初值0xFC67,定時(shí)1ms

          TL0=0x67;

          ET0=1;//使能T0中斷

          TR0=1;//啟動(dòng)T0

          while(1)

          {

          if(cnt>=1000)//判斷T0溢出是否達(dá)到1000次

          {

          cnt=0;//達(dá)到1000次后計(jì)數(shù)值清零

          sec++;//秒計(jì)數(shù)自加1

          //以下代碼將sec按十進(jìn)制位從低到高依次提取并轉(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í)器0中斷服務(wù)函數(shù)*/

          voidInterruptTimer0()interrupt1

          {

          TH0=0xFC;//重新加載初值

          TL0=0x67;

          cnt++;//中斷次數(shù)計(jì)數(shù)值加1

          //以下代碼完成數(shù)碼管動(dòng)態(tài)掃描刷新

          P0=0xFF;//顯示消隱

          switch(i)

          {

          case0:ADDR2=0;ADDR1=0;ADDR0=0;i++;P0=LedBuff[0];break;

          case1:ADDR2=0;ADDR1=0;ADDR0=1;i++;P0=LedBuff[1];break;

          case2:ADDR2=0;ADDR1=1;ADDR0=0;i++;P0=LedBuff[2];break;

          case3:ADDR2=0;ADDR1=1;ADDR0=1;i++;P0=LedBuff[3];break;

          case4:ADDR2=1;ADDR1=0;ADDR0=0;i++;P0=LedBuff[4];break;

          case5:ADDR2=1;ADDR1=0;ADDR0=1;i=0;P0=LedBuff[5];break;

          default:break;

          }

          }

          大家可以先把程序抄下來(lái),編譯下載到單片機(jī)里運(yùn)行,看看實(shí)際效果。是否可以看到,近乎完美的顯示效果經(jīng)過(guò)我們的努力終于做成功了。下面我們還要再來(lái)解析一下這個(gè)程序。

          在這個(gè)程序中,有兩個(gè)函數(shù),一個(gè)是主函數(shù),一個(gè)是中斷服務(wù)函數(shù)。主函數(shù)main()我們就不用說(shuō)了,重點(diǎn)強(qiáng)調(diào)一下中斷服務(wù)函數(shù),它的書寫格式是固定的,首先中斷函數(shù)前邊void表示函數(shù)返回空,即中斷函數(shù)不返回任何值,函數(shù)名是InterruptTimer0(),這個(gè)函數(shù)名在符合函數(shù)命名規(guī)則的前提下可以隨便取,我們?nèi)∵@個(gè)名字是為了方便區(qū)分和記憶,而后是interrupt這個(gè)關(guān)鍵字,一定不能錯(cuò),這是中斷特有的關(guān)鍵字,另外后邊還有個(gè)數(shù)字1,這個(gè)數(shù)字1怎么來(lái)的呢?我們先來(lái)看表6-3。

          表6-3中斷查詢序列

          中斷

          函數(shù)編號(hào)

          中斷名稱

          中斷

          標(biāo)志位

          中斷

          使能位

          中斷

          向量地址

          默認(rèn)

          優(yōu)先級(jí)

          0

          外部中斷0

          IE0

          EX0

          0x0003

          1(最高)

          1

          T0中斷

          TF0

          ET0

          0x000B

          2

          2

          外部中斷1

          IE1

          EX1

          0x0013

          3

          3

          T1中斷

          TF1

          ET1

          0x001B

          4

          4

          UART中斷

          TI/RI

          ES

          0x0023

          5

          5

          T2中斷

          TF2/EXF2

          ET2

          0x002B

          6

          這個(gè)表格同樣不需要大家記住,需要的時(shí)候過(guò)來(lái)查就可以了。我們現(xiàn)在看第二行的T0中斷,要使能這個(gè)中斷那么就要把它的中斷使能位ET0置1,當(dāng)它的中斷標(biāo)志位TF0變?yōu)?時(shí),就會(huì)觸發(fā)T0中斷了,那么這時(shí)就應(yīng)該來(lái)執(zhí)行中斷函數(shù)了,單片機(jī)又怎樣找到這個(gè)中斷函數(shù)呢?靠的就是中斷向量地址,所以interrupt后面中斷函數(shù)編號(hào)的數(shù)字x就是根據(jù)中斷向量得出的,它的計(jì)算方法是x*8+3=向量地址。當(dāng)然表中都已經(jīng)給算好放在第一欄了,我們可以直接查出來(lái)用就行了。到此為止,中斷函數(shù)的命名規(guī)則我們就都搞清楚了。

          中斷函數(shù)寫好后,每當(dāng)滿足中斷條件而觸發(fā)中斷后,系統(tǒng)就會(huì)自動(dòng)來(lái)調(diào)用中斷函數(shù)。比如我們上面這個(gè)程序,平時(shí)一直在主程序while(1)的循環(huán)中執(zhí)行,假如程序有100行,當(dāng)執(zhí)行到50行時(shí),定時(shí)器溢出了,那么單片機(jī)就會(huì)立刻跑到中斷函數(shù)中執(zhí)行中斷程序,中斷程序執(zhí)行完畢后再自動(dòng)返回到剛才的第50行處繼續(xù)執(zhí)行下面的程序,這樣就保證了動(dòng)態(tài)顯示間隔是固定的1ms,不會(huì)因?yàn)槌绦驁?zhí)行時(shí)間不一致的原因?qū)е聰?shù)碼管顯示的抖動(dòng)了。

          1.1.1中斷的優(yōu)先級(jí)

          中斷優(yōu)先級(jí)的內(nèi)容,大家先通過(guò)我的介紹大概了解一下即可,后邊實(shí)際應(yīng)用的時(shí)候我們?cè)僭敿?xì)理解。

          在講中斷產(chǎn)生背景的時(shí)候,我們僅僅講了看電視和燒水的例子,但是實(shí)際生活當(dāng)中還有更復(fù)雜的,比如我正在看電視,這個(gè)時(shí)候來(lái)電話了,我要進(jìn)入接電話的“中斷”程序當(dāng)中去,就在接電話的同時(shí),聽到了水開的聲音,水開的“中斷”也發(fā)生了,我們就必須要放下手上的電話,先把煤氣關(guān)掉,然后再回來(lái)聽電話,最后聽完了電話再看電視,這里就產(chǎn)生了一個(gè)優(yōu)先級(jí)的問(wèn)題。

          還有一種情況,我們?cè)诳措娨暤臅r(shí)候,這個(gè)時(shí)候聽到水開的聲音,水開的“中斷”發(fā)生了,我們要進(jìn)入關(guān)煤氣的“中斷”程序當(dāng)中,而在關(guān)煤氣的同時(shí),電話聲音響了,而這個(gè)時(shí)候,我們的處理方式是先把煤氣關(guān)閉,再去接聽電話,最后再看電視。

          從這兩個(gè)過(guò)程中,我們可以得到一個(gè)結(jié)論,就是最最緊急的事情,一旦發(fā)生后,我們不管當(dāng)時(shí)處在哪個(gè)“程序”當(dāng)中,我們必須先去處理最最緊急的事情,處理完畢后再去解決其它事情。在我們的單片機(jī)程序當(dāng)中有時(shí)候也是這樣的,有一般緊急的中斷,有特別緊急的中斷,這取決于具體的系統(tǒng)設(shè)計(jì),這就涉及到中斷優(yōu)先級(jí)和中斷嵌套的概念,在本章節(jié)我們先簡(jiǎn)單介紹一下相關(guān)寄存器,不做例程說(shuō)明。

          中斷優(yōu)先級(jí)有兩種,一種是搶占優(yōu)先級(jí),一種是固有優(yōu)先級(jí),先介紹搶占優(yōu)先級(jí)。來(lái)看表6-4和表6-5。

          表6-4IP——中斷優(yōu)先級(jí)寄存器的位分配(地址0xB8、可位尋址)

          7

          6

          5

          4

          3

          2

          1

          0

          符號(hào)

          --

          --

          PT2

          PS

          PT1

          PX1

          PT0

          PX0

          復(fù)位值

          --

          --

          0

          0

          0

          0

          0

          0

          表6-5IP——中斷優(yōu)先級(jí)寄存器的位描述

          符號(hào)

          描述

          7

          --

          保留

          6

          --

          保留

          5

          PT2

          定時(shí)器2中斷優(yōu)先級(jí)控制位

          4

          PS

          串口中斷優(yōu)先級(jí)控制位

          3

          PT1

          定時(shí)器1中斷優(yōu)先級(jí)控制位

          2

          PX1

          外部中斷1中斷優(yōu)先級(jí)控制位

          1

          PT0

          定時(shí)器0中斷優(yōu)先級(jí)控制位

          0

          PX0

          外部中斷0中斷優(yōu)先級(jí)控制位

          IP這個(gè)寄存器的每一位,表示對(duì)應(yīng)中斷的搶占優(yōu)先級(jí),每一位的復(fù)位值都是0,當(dāng)我們把某一位設(shè)置為1的時(shí)候,這一位的優(yōu)先級(jí)就比其它位的優(yōu)先級(jí)高了。比如我們?cè)O(shè)置了PT0位為1后,當(dāng)單片機(jī)在主循環(huán)或者任何其它中斷程序中執(zhí)行時(shí),一旦定時(shí)器T0發(fā)生中斷,作為更高的優(yōu)先級(jí),程序馬上就會(huì)跑到T0的中斷程序中來(lái)執(zhí)行。反過(guò)來(lái),當(dāng)單片機(jī)正在T0中斷程序中執(zhí)行時(shí),如果有其它中斷發(fā)生了,還是會(huì)繼續(xù)執(zhí)行T0中斷程序,直到把T0中的中斷程序執(zhí)行完畢以后,才會(huì)去執(zhí)行其它中斷程序。

          當(dāng)進(jìn)入低優(yōu)先級(jí)中斷中執(zhí)行時(shí),如又發(fā)生了高優(yōu)先級(jí)的中斷,則立刻進(jìn)入高優(yōu)先級(jí)中斷執(zhí)行,處理完高優(yōu)先級(jí)級(jí)中斷后,再返回處理低優(yōu)先級(jí)中斷,這個(gè)過(guò)程就叫做中斷嵌套,也稱為搶占。所以搶占優(yōu)先級(jí)的概念就是,優(yōu)先級(jí)高的中斷可以打斷優(yōu)先級(jí)低的中斷的執(zhí)行,從而形成嵌套。當(dāng)然反過(guò)來(lái),優(yōu)先級(jí)低的中斷是不能打斷優(yōu)先級(jí)高的中斷的。

          那么既然有搶占優(yōu)先級(jí),自然就也有非搶占優(yōu)先級(jí)了,也稱為固有優(yōu)先級(jí)。在表6-3中的最后一列給出的就是固有優(yōu)先級(jí),請(qǐng)注意,在中斷優(yōu)先級(jí)的編號(hào)中,一般都是數(shù)字越小優(yōu)先級(jí)越高。從表中可以看到一共有1~6共6級(jí)的優(yōu)先級(jí),這里的優(yōu)先級(jí)與搶占優(yōu)先級(jí)的一個(gè)不同點(diǎn)就是,它不具有搶占的特性,也就是說(shuō)即使在低優(yōu)先級(jí)中斷執(zhí)行過(guò)程中又發(fā)生了高優(yōu)先級(jí)的中斷,那么這個(gè)高優(yōu)先級(jí)的中斷也只能等到低優(yōu)先級(jí)中斷執(zhí)行完后才能得到響應(yīng)。既然不能搶占,那么這個(gè)優(yōu)先級(jí)有什么用呢?

          答案是多個(gè)中斷同時(shí)存在時(shí)的仲裁。比如說(shuō)有多個(gè)中斷同時(shí)發(fā)生了,當(dāng)然實(shí)際上發(fā)生這種情況的概率很低,但另外一種情況就常見的多了,那就是出于某種原因我們暫時(shí)關(guān)閉了總中斷,即EA=0,執(zhí)行完一段代碼后又重新使能了總中斷,即EA=1,那么在這段時(shí)間里就很可能有多個(gè)中斷都發(fā)生了,但因?yàn)榭傊袛嗍顷P(guān)閉的,所以它們當(dāng)時(shí)都得不到響應(yīng),而當(dāng)總中斷再次使能后,它們就會(huì)在同時(shí)請(qǐng)求響應(yīng)了,很明顯,這時(shí)也必需有個(gè)先后順序才行,這就是非搶占優(yōu)先級(jí)的作用了——如表6-3中,誰(shuí)優(yōu)先級(jí)最高先響應(yīng)誰(shuí),然后按編號(hào)排隊(duì),依次得到響應(yīng)。

          搶占優(yōu)先級(jí)和非搶占優(yōu)先級(jí)的協(xié)同,可以使單片機(jī)中斷系統(tǒng)有條不紊的工作,既不會(huì)無(wú)休止的嵌套,又可以保證必要時(shí)緊急任務(wù)得到優(yōu)先處理。在后續(xù)的學(xué)習(xí)過(guò)程中,中斷系統(tǒng)會(huì)與我們?nèi)缬半S形,處處都有它的身影,隨著學(xué)習(xí)的深入,相信你對(duì)它的理解也會(huì)更加的深入。

          1.1練習(xí)題

          1、掌握C語(yǔ)言數(shù)組的概念、定義和應(yīng)用。

          2、掌握if語(yǔ)句和switch語(yǔ)句的用法及區(qū)別,編程的時(shí)候能夠正確選擇使用哪個(gè)語(yǔ)句。

          3、徹底理解中斷的原理和應(yīng)用方法,關(guān)閉教程自己獨(dú)立把本章節(jié)程序編寫完畢并且下載到實(shí)驗(yàn)板上實(shí)踐。

          4、嘗試修改程序,讓我們的數(shù)碼管只顯示有效位,也就是高位的0不顯示。

          5、嘗試寫一個(gè)從999999開始倒計(jì)時(shí)的程序,并且改用定時(shí)器T1的中斷來(lái)完成,通過(guò)寫這個(gè)程序,熟練掌握定時(shí)器和中斷的應(yīng)用。




          評(píng)論


          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();