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

          新聞中心

          EEPW首頁 > 嵌入式系統(tǒng) > 設計應用 > 單片機學習之十一:中斷方式按鍵

          單片機學習之十一:中斷方式按鍵

          作者: 時間:2016-09-28 來源:電子產品世界 收藏

            1、 實驗現象

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

            最右面的數碼管初始顯示0,每次按下外部按鍵K0執(zhí)行加一,計數到九時重新回零。

            2、 實驗目的

            掌握方式響應按鍵的原理

            掌握服務程序的具體編寫:如何進行鍵的識別,如何去抖動

            3、 實驗任務分析

            上一個試驗我們了解了查詢方式按鍵的原理。當工作于查詢方式的時候,要不間斷的對外部按鍵進行查詢,其間不能干其他的任何工作。如果的工作量較大,這種方式就很難適應。

            為了進一步提高單片機的工作效率,可以采用方式。

            中斷方式的按鍵響應過程如下:單片機處理自己的工作,如果有按鍵按下,向單片機發(fā)出中斷請求,單片機就會停下現在正在處理的工作,轉去執(zhí)行中斷程序,執(zhí)行之后回來繼續(xù)剛才的工作;如果沒有按鍵按下,單片機就作自己的工作,不理睬外部鍵盤。

            怎樣向單片機發(fā)出中斷請求呢?發(fā)出中斷請求的來源叫做中斷源。單片機可提供五個中斷源。其中2個為外部中斷請求INT0和INT1,2個為片內定時/計數器T0和T1的計滿溢出中斷,還有一個是片內串行口中斷請求TI或者RI。在本試驗中,我們用到的是外部中斷INT1。

            還是先看看電路吧。(注意在作中斷試驗之前,我們必須先把JMP4跳線插上,這樣按鍵動作才能發(fā)出中斷請求。)

            

           

            我們在試驗一中曾經給大家介紹過,單片機的P3口是雙功能口,第一種功能可以作通用的I/O口。而在本試驗中,我們要用到P3口的第二功能。大家看P3.2和P3.3兩個引腳,斜線后面標示的就是P3口的第二功能,分別是INT0和INT1,也就是單片機的兩個外部中斷源。

            我們發(fā)現,當任何一個按鍵按下的時候,P3.3引腳,即INT1上就會出現一個低電平,向單片機發(fā)出中斷申請。如果單片機開放了外部中斷,就響應中斷請求,進入中斷服務程序。

            那么單片機是否開放中斷功能是由什么控制的呢?在這里就涉及到另外一個特殊功能寄存器IE啦。還記得單片機內部256字節(jié)的數據RAM嗎?其中高128字節(jié)(80h~ffh)是特殊功能寄存器區(qū)。以前我們介紹過PSW就在這個區(qū)域,同樣IE也在這個區(qū)域,它的名字叫做“中斷允許寄存器”,專門負責程序是否響應中斷的,它的各位功能說明如下:

            EA:CPU中斷允許標志。EA=0,CPU禁止各種中斷;EA=1,CPU開放中斷。但是,每個中斷源的中斷請求是否允許,還需要由各自的允許位決定。所以在本程序中,大家不要忘了首先把EA置1哦!

            ES:串行口允許中斷。ES=1,允許串行口中斷;ES=0,禁止串行口中斷。所以,在這個程序中,這一位和我們沒有關系,不理它就是啦。

            ET1:定時器/計數器T1的溢出中斷允許。ET1=1時,允許T1溢出中斷;ET1=0時,禁止T1溢出中斷。同樣,這一位和我們也沒有關系。

            EX1:外部中斷1(即INT1)允許中斷。當EX1=1時,允許INT1中斷;當EX1=0時,禁止INT1中斷??梢?,這一位的值和我們這個試驗密切相關,所以大家千萬不要忘了把這一位置1。

            下面的ET0和EX0的功能與ET1和EX1類似,是分別控制定時器T0和外部中斷INT0的,這里就不再贅述啦。

            在前面分析時,我們發(fā)現,四個按鍵中任何一個按下,都會發(fā)出中斷請求。那么,如何判斷中斷請求是由那個按鍵發(fā)出的呢?這就是中斷服務程序的工作了。在中斷服務程序中,需要完成如下工作:鍵識別、去抖動、和鍵功能處理。

            那么,鍵識別是如何完成的呢?它的思路是這樣的。

            (1)、先把P1口的狀態(tài)讀入a寄存器,然后把高四位屏蔽掉

            (2)、判斷是否k0按鍵按下,(即判斷a中的數是否00001101),如果是就調用延時程序去抖,否則,表示不是K0按下,退出中斷返回主程序

            (3)、調用延時程序后,重復上面的(1)(2)步。如果確定k0按下,轉中斷服務程序,否則就是抖動,退出中斷返回主程序。

            在這里還需要說明一點:在進入中斷服務程序之后,我們首先應該關閉中斷,因為如果不關閉中斷,就有可能會引起混亂。在中斷服務程序返回主程序之前,再把中斷打開。

            4、實驗程序

            org 0000h

            ljmp start ;(1)為什么這里要放一條跳轉指令呢,我們在后面解釋把

            org 0013h

            ljmp ext1 ;(2)同上

            org 0030h

            start: clr p1.5 ;主程序開始

            mov r7,#0ffh

            setb ea ;cpu開放中斷

            setb ex1 ;允許外部中斷1申請中斷

            setb it1 ;(3)設置外部中斷1觸發(fā)方式為跳變觸發(fā),原因在后面詳細解釋

            lcall play1 ;調用初始狀態(tài)顯示子程序

            ajmp $ ;等待按鍵發(fā)出中斷申請

            ext1: clr ea ;進入中斷,先關閉中斷

            lcall key_reader ;調用鍵識別子程序

            pass: setb ea ;返回主程序之前先開中斷

            reti ;(4)中斷返回指令,

            key_reader: mov a,p1 ;讀入P1口的狀態(tài)

            anl a,#0fh ;屏蔽高四位

            cjne a,#0dh,pass;如果a的內容不是00001101(表示不是k0按下),就退出中斷

            lcall del10ms ;否則,表示k0按下,調用10ms延時去抖

            mov a,p1 ;再次讀入p1口的內容

            anl a,#0fh ;屏蔽高四位

            cjne a,#0dh,pass ;如果a的內容不是00001101,就是抖動,退出中斷

            lcall play2 ;否則,確定按鍵按下,轉數碼顯示程序

            ret

            play1: mov a,#48h ;讓最右面的數碼管顯示0的子程序

            mov p0,a

            mov p2,#01h;

            ret

            play2: inc r7 ;查表求段碼,然后送到P0口的子程序,大家很熟了

            mov a,r7

            mov dptr,#tab

            movc a,@a+dptr

            mov p0,a

            mov p2,#01h

            cjne a,#48h,next;如果沒有顯示到最后一個數字0,就繼續(xù)查表顯示

            mov r7,#0ffh ;否則,重新從表頭開始顯示

            next: ret

            del10ms: mov r5,#10 ;延時10ms子程序

            del1: mov r4,#02h

            del2: mov r3,#0ffh

            del3: djnz r3,del3

            djnz r4,del2

            djnz r5,del1

            ret

            tab: db 0ebh,52h,62h,0e1h,64h,44h,0eah,40h,60h,48h ;存放0~9的段碼表

            end

            相信通過上面的詳細注釋,大家基本上已經看懂這個程序了。下面我就幾個小問題再給大家作一些解釋。

            5、注釋

            1、先看(1)語句,我們發(fā)現程序開始的時候,在地址為0000h的地方,放了一條跳轉指令。主程序是從程序存儲器中0030h的地方開始存放的,為什么要把前面的空間空出來呢?這是因為前面的空間我們是有用處的。

            由于不同類型的中斷,具有不同的中斷入口地址。CPU在響應中斷的時候,會根據中斷源的類別,轉到相應的入口去執(zhí)行中斷服務程序。外部中斷1的入口地址是0013h(其余4個中斷的入口地址大家可以查閱相關資料),那么cpu在響應了外部中斷1后,會自動的到這個地址來,從這個地址開始往下執(zhí)行程序。所以我們要把前面的一段空出來,以免覆蓋這個地址。

            2、再看看(2)語句,它在中斷1的入口處放了一個跳轉指令,所以,cpu在響應外部中斷后,會到這個入口處往下執(zhí)行程序。于是CPU就會從這里轉去中斷服務程序。大家可能會說,為什么要跳轉,直接從這里開始存放中斷服務程序 不就可以了嘛。

            由于我們很難預料中斷服務程序的長度,所以我們就拿不準主程序應該從什么單元開始存放。這樣就容易造成整個程序的結構混亂。

            所以還是采用上面的方法吧,看起來結構清楚規(guī)范,容易理解。

            3、(3)語句的意思是設置外部中斷1為跳變方式觸發(fā)。大家可能覺得,當K0按下的時候,INT1上會出現一個低電平,為什么不能采用電平方式觸發(fā)呢?解釋如下:當我們選外部中斷為電平方式觸發(fā)的時候,當INT1輸入低電平的時候,cpu響應中斷。在中斷返回之前,INT1必須回到高電平,否則就會第二次產生中斷。所以,如果我們按下按鍵的時間較長,就會產生多次的中斷,導致程序混亂。所以我們在這里要采用邊沿方式觸發(fā)。

            順便說說it1,它是外部中斷觸發(fā)方式控制位,為1的時候選擇跳變方式觸發(fā),為0的時候選擇電平方式觸發(fā)。那么它在哪里呢?

            it1是tcon寄存器其中的一位,tcon和我們知道的psw,ie一樣,也是一個特殊功能寄存器。位置當然也在特殊功能寄存器區(qū)啦。

            TCON是定時器/計數器(T0,T1)的控制寄存器,關于它的應用我們以后通過例子詳細說明。它的各位的功能說明如下:

            TF1:定時器/計數器(T1)的溢出中斷標志,當T1產生溢出試,由硬件置1,CPU響應中斷后,由硬件置0;TF0類似。(以后通過試驗詳細說明)

            TR1:置1時啟動定時/計數器,置0時停止定時/計數器;TR0類似。(以后通過試驗詳細說明)

            IE1:外部中斷1請求標志。當它置一的時候,請求中斷。

            IT1:外部中斷1觸發(fā)方式控制位。如果IT1是1,則外部中斷為跳變方式觸發(fā),否則,為電平方式觸發(fā)。選擇何種方式觸發(fā),視程序的具體情況來定。

            IE0:外部中斷0請求標志,含義同IE1。

            IT0:外部中斷0觸發(fā)方式控制位,含義同IT1。

            4、語句(4)是中斷返回指令,功能和子程序返回指令ret相類似,通常被安排在中斷服務程序的最后。CPU執(zhí)行完這條指令后,從原來被中斷處開始,重新執(zhí)行被中斷的程序。

            現在大家對中斷的基本概念和簡單的編程處理方法有所了解了把,下面一個試驗“按鍵控制跑馬燈”,在這個試驗里面,我們要了解關于中斷現場保護的問題。



          關鍵詞: 單片機 中斷

          評論


          相關推薦

          技術專區(qū)

          關閉
          看屁屁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); })();