軟中斷在51單片機中的實現(xiàn)及其應(yīng)用.doc_第1頁
軟中斷在51單片機中的實現(xiàn)及其應(yīng)用.doc_第2頁
軟中斷在51單片機中的實現(xiàn)及其應(yīng)用.doc_第3頁
軟中斷在51單片機中的實現(xiàn)及其應(yīng)用.doc_第4頁
軟中斷在51單片機中的實現(xiàn)及其應(yīng)用.doc_第5頁
已閱讀5頁,還剩13頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

軟中斷在51單片機中的實現(xiàn)及其應(yīng)用彭樹林摘要 本文討論軟中斷在51單片機(兼容)中的實現(xiàn)方法以及軟中斷在51單片機(兼容)系統(tǒng)中的應(yīng)用。詳細(xì)說明了在51單片機實現(xiàn)軟中斷的方法,并就軟中斷的應(yīng)用作了簡明扼要的闡述。關(guān)鍵詞 軟中斷 單片機 51單片機 實時操作系統(tǒng)1、序言現(xiàn)代的單片機應(yīng)用中,某些單片機為了方便操作系統(tǒng)編程,會保留一些特權(quán)指令給RTOS操作系統(tǒng),以便實時控制整個機器;軟件中的一些原子操作不允許中斷破壞,也需要一些特權(quán)指令。軟中斷指令表面上類似于函數(shù)調(diào)用,主要是使單片機進入特權(quán)運行狀態(tài),并在這個狀態(tài)下,操作一些用戶狀態(tài)下不能使用的功能。51兼容單片機沒有特權(quán)功能,也不存在軟中斷指令。但是我們的一些應(yīng)用確實需要軟中斷特權(quán)指令來完成一些特殊的操作。本文所討論軟中斷在51兼容單片機中的實現(xiàn)方法以及軟中斷在51兼容單片機系統(tǒng)中的應(yīng)用。2、軟中斷與硬中斷軟中斷,最早出現(xiàn)在Intel8086處理器中,該處理器的指令系統(tǒng)中,可使用INT指令來申請中斷服務(wù)。在DOS操作系統(tǒng)下,利用INT 21這條指令,應(yīng)用軟件可以申請多達84個DOS系統(tǒng)服務(wù)。在Linux、Windows、SOLAIRS等操作系統(tǒng)中,都有軟中斷在為系統(tǒng)提供服務(wù)。軟中斷和硬中斷區(qū)別不大,軟中斷是程序中用軟件代碼人為地產(chǎn)生中斷,讓CPU去執(zhí)行相應(yīng)的中斷服務(wù)程序。硬中斷由硬件產(chǎn)生并觸發(fā)中斷,讓CPU去執(zhí)行相應(yīng)的中斷服務(wù)程序以響應(yīng)該事件??梢赃@樣理解,軟中斷以軟件中斷指令所攜帶的參數(shù)作為工作的依據(jù),而硬中斷是以計算機系統(tǒng)中硬件所發(fā)生的事件為工作依據(jù)??梢娷浖袛嗟撵`活性非常強,可根據(jù)程序的運行狀態(tài)、硬件狀態(tài)以及任務(wù)之間的消息作為觸發(fā)條件,申請相應(yīng)的軟中斷功能服務(wù)。軟中斷最大的用途在于RTOS提供系統(tǒng)級服務(wù),提高系統(tǒng)的實時性。在沒有RTOS的情況下,我們也可利用軟中斷服務(wù)對敏感代碼和敏感數(shù)據(jù)進行保護。比如互斥訪問共享硬件以及互斥訪問共享數(shù)據(jù)等。3、51單片機(兼容)中斷系統(tǒng)的秘密51單片機(兼容)的中斷系統(tǒng)中,各個中斷標(biāo)志的置位條件和如何清除,都在公開的技術(shù)資料中進行了詳細(xì)的描述。然而,公開的技術(shù)細(xì)節(jié)實際上并沒有將中斷系統(tǒng)的工作原理講述清楚。大家使用的51兼容單片機,其中斷系統(tǒng)的工作細(xì)節(jié)以及工作的結(jié)果與公開的技術(shù)資料有些小小的差異。比如關(guān)于外部中斷標(biāo)志IE0的置位,附1是五個公司對IE0的描述。各個公司開發(fā)的51單片機(兼容),其中斷系統(tǒng)也兼容,僅僅是中斷源的多少,中斷優(yōu)先級的級數(shù)以及中斷向量的多少的不同,附2列舉了六家公司51單片機(兼容)的中斷系統(tǒng)資料。中斷系統(tǒng)的秘密在于:中斷標(biāo)志完全可以通過一定的方式實現(xiàn)軟件置位和軟件清除??煞裢ㄟ^軟件置位和清除中斷標(biāo)志,是在51單片機中實現(xiàn)軟中斷的關(guān)鍵,如果不能用軟件申請中斷,軟中斷的實現(xiàn)就無從談起。硬件置位中斷申請標(biāo)志只是方法之一而不是唯一的方法,CPU完全可以設(shè)置這些標(biāo)志位為“0”或者“1”。這是理解本文的基礎(chǔ),對于有豐富經(jīng)驗的51單片機開發(fā)者,只要知道這個道理就好,基本沒有必要再閱讀后面的部分。本文接下來的部分具體描述如何在51單片機中實現(xiàn)軟中斷,主要針對51單片機開發(fā)經(jīng)驗還不是十分豐富的讀者。IE0和IE1的秘密在于,如果將中斷檢測的方式(IT0、IT1)設(shè)置為邊沿檢測,就可以用軟件來設(shè)置或清除IE0和IE1(清除也可以讓中斷系統(tǒng)在響應(yīng)中斷時自動完成)。TF0、TF1、TF2、TI以及RI是已知的可以軟件操控的中斷申請標(biāo)志。對于各個公司的51單片機(兼容)增強部分的中斷,可以根據(jù)實際應(yīng)用,在關(guān)閉模塊功能的情況下一般都可以自由使用其中斷資源。具體應(yīng)用時,應(yīng)編寫一個測試軟件,測試具體型號的51單片機各個中斷資源是否能夠被利用,避免走彎路。讀者應(yīng)當(dāng)注意到,有些單片機的某些中斷申請標(biāo)志,其清除方法是通過對該標(biāo)志位寫“1”來完成的,這種中斷源就不適合用于軟中斷,比如STC12C5AxxS2單片機的SPI中斷。如果要使用STC12C5AxxS2單片機的SPI中斷作為軟中斷,就必須放棄該模塊占用的IO引腳,以實現(xiàn)軟中斷。例如在40腳DIP封裝的STC12C5AxxS2單片機中,將SPI影射到P4口不會占用外部的引腳,將SPI設(shè)置為MASTER模式,就可以通過對SPDAT寫入0xFF數(shù)據(jù)來是SPI工作,SPI工作完成后會自動設(shè)置中斷申請標(biāo)志,從而完成軟中斷的申請。而在44或48腳封裝的STC12C5AxxS2單片機,用SPI中斷源實現(xiàn)軟中斷就必須考慮放棄SPI占用的引腳(P1口或P4口)。大部分51單片機(兼容)增強部分,在系統(tǒng)的應(yīng)用中不能全部同時使用,因為它們可能共享了IO端口上的引腳。因此,總是能在一個51單片機中找到空閑資源實現(xiàn)軟中斷。4、軟中斷實現(xiàn)的方法只要是代碼兼容的51單片機,就可以利用空閑不用的中斷來實現(xiàn)軟中斷。對于沒有空閑中斷的應(yīng)用系統(tǒng),也可以通過本文描述的方法來實現(xiàn)共享硬件中斷和軟中斷。在51單片機應(yīng)用系統(tǒng)中,實現(xiàn)軟中斷的基本原理是用軟件置位硬件中斷的申請標(biāo)志位,迫使系統(tǒng)承認(rèn)一個“硬件中斷”發(fā)生進而去響應(yīng)這個“硬件中斷事件”。這樣,就實現(xiàn)了用軟件申請實現(xiàn)中斷服務(wù)。具體在一個應(yīng)用系統(tǒng)中實現(xiàn)時,可能還需要作如下的安排:1)將應(yīng)用系統(tǒng)中已經(jīng)使用的全部硬件中斷設(shè)置為高優(yōu)先級;2)讓軟中斷使用的中斷源的優(yōu)先級為最低優(yōu)先級,以確保軟中斷中可以響應(yīng)硬件中斷,實現(xiàn)中斷嵌套。4.1 使用外部中斷使用外部中斷0和1作為軟中斷時,應(yīng)當(dāng)使中斷的觸發(fā)方式設(shè)置為邊沿觸發(fā),并且對應(yīng)的引腳只能進行輸出,不能為輸入,若設(shè)計為輸入時系統(tǒng)不能分辨硬件觸發(fā)和軟件觸發(fā)。即使是對外輸出,也需要限制為軟中斷的服務(wù)來提供輸出,就能避免輸出的變化錯誤地觸發(fā)中斷。經(jīng)過上述的設(shè)置后,就可以利用軟件來控制中斷標(biāo)志IE0或IE1的置位和清除,由于51單片機(兼容)中斷系統(tǒng)能夠在中斷返回時自動清除外部中段標(biāo)志IE0或IE1,因此在結(jié)束軟中斷服務(wù)時可以不清除中斷標(biāo)志。例如,使用外部中斷0來實現(xiàn)軟中斷。首先,開放外部中斷0,使EX0=1;其次,設(shè)置外部中斷0為沿觸發(fā),使IT0=1;接下來用“IE0 = 1”來置位IE0申請軟中斷服務(wù),而使用“IE0 = 0”來清除IE0結(jié)束軟中斷服務(wù)(可以不使用“IE0 = 0”而由中斷系統(tǒng)在中斷服務(wù)中自動清除)。參見例2所示例程。4.2 使用定時器中斷使用定時器0、定時器1和定時器2中斷,需要考慮的是關(guān)閉定時器,以避免定時器溢出產(chǎn)生中斷申請。這時軟件就能完全控制中斷標(biāo)志TF0、TF1或TF2的置位以及清除,以申請軟中斷和結(jié)束軟中斷服務(wù)。應(yīng)當(dāng)注意,51單片機(兼容)的中斷系統(tǒng)在中斷返回時不會自動清除TF0、TF1和TF2,因此,在結(jié)束軟中斷服務(wù)時,一定要清除相應(yīng)得中斷標(biāo)志TF0、TF1或TF2。對于具有定時計數(shù)器2的單片機,如果將其應(yīng)用于串口的波特率發(fā)生器,就是最好的軟中斷資源。因為定時器而一旦被應(yīng)用于串口產(chǎn)生發(fā)送時鐘或者接收時鐘,定時器的溢出就不會設(shè)置TF2(TF2 will not be set when either RCLK = 1 or TCLK = 1.)。例如,使用定時器0來實現(xiàn)軟中斷。首先,關(guān)閉定時器0,使TR0=0;其次,開放定時器0的中斷,使ET0=1;接下來用“TF0 = 1”來置位TF0申請軟中斷服務(wù),而使用“TF0 = 0”來清除TF0結(jié)束軟中斷服務(wù)。參見例2所示例程。4.3 使用其它中斷資源其他的中斷資源,只要沒有和引腳有關(guān)聯(lián),就可以在關(guān)閉模塊功能的條件下直接,用軟件來設(shè)置或清除相應(yīng)模塊的中斷申請標(biāo)志,以申請軟中斷服務(wù)或結(jié)束軟中斷服務(wù)。例如,在51單片機(兼容)應(yīng)用系統(tǒng)中沒有使用串口(對于多串口單片機,是串口0)通訊,就可以使用串口的RI中斷標(biāo)志來實現(xiàn)軟中斷。首先,禁止接收(復(fù)位狀態(tài)就是禁止的),使REN=0;其次,開放串口中斷,使ES=1;接下來,利用“RI = 1”來置位RI申請軟中斷服務(wù),而使用“RI = 0”來清除RI結(jié)束軟中斷服務(wù)。參見例2所示例程。再例如,在STC12C5AxxS2應(yīng)用系統(tǒng)中,不使用ADC模塊,就可以使用ADC模塊的ADC_FLAG中斷標(biāo)志來實現(xiàn)軟中斷。首先,禁止ADC模塊(復(fù)位狀態(tài)就是禁止的),使ADC_POWER=0,關(guān)閉ADC模塊的電源;其次,開放ADC中斷,使EADC=1;接下來用“ADC_CONTR = 0x10”來置位ADC_FLAG申請軟中斷服務(wù),而使用“ADC_CONTR = 0x00”來清除ADC_FLAG結(jié)束軟中斷服務(wù)。參見例2所示例程。4.3 定義軟中斷為了使軟件具有很好的移植性,可以使用宏定義來實現(xiàn)具體應(yīng)用系統(tǒng)的軟中斷。在這個宏定義中,應(yīng)當(dāng)完成參數(shù)的傳遞以及軟中斷的申請。例1定義了一個可以攜帶3個參數(shù)的宏,第一個參數(shù)是申請的功能號,第二個參數(shù)是輸入數(shù)據(jù)的地址,第三個參數(shù)是結(jié)果輸出的地址。利用這三個參數(shù),就可以處理任意類型的數(shù)據(jù)(參考例2所示的例程)。對于簡單的系統(tǒng),如果軟中斷提供的服務(wù)沒有其他的數(shù)據(jù)需要輸入輸出,就可只使用一個參數(shù):服務(wù)功能號。例1:軟中斷的宏定義#define SWI(a,b,c)FunctionNum=a;InputParameter = (unsigned char *)&b;OutputParameter = (unsigned char *)&c;SWI_IRQ();_nop_();在例1所示的宏中,SWI_IRQ()也是一個宏,用來在宏中自動地選擇中斷源,而不需要修改代碼。參照例2,一個SWI_IRQ()定義的例子:/定義軟件中斷使用的中斷號#define SWI_NUM 1/使用外部中斷0作軟中斷#if SWI_NUM = 0#define SWI_IRQ() IE0 = 1#define CLEAR_SWI() /IE0 = 0/使用T0中斷作軟件中斷#elif SWI_NUM = 1#define SWI_IRQ() TF0 = 1#define CLEAR_SWI() TF0 = 0對于復(fù)雜的系統(tǒng),可以動態(tài)指定軟中斷服務(wù)的具體函數(shù)以及數(shù)據(jù)輸入輸出的入口地址。參照例3,定義軟中斷攜帶兩個參數(shù):一個是功能號,一個是函數(shù)入口地址,就可在軟中斷服務(wù)中執(zhí)行動態(tài)的代碼Func_CallBack,以完成不同類型的任務(wù)。#define SWI(a,b)FunctionNum=a;Func_CallBack = (void *)b;SWI_IRQ();_nop_();void SWI_ISR(void) interrupt SWI_NUM/測試傳入的參數(shù)CLEAR_SWI();/清除可能需要清除的標(biāo)志Func_CallBack();/執(zhí)行指定的功能函數(shù)FunctionNum = 0;/清除申請功能號5、軟中斷和硬件中斷共享一個中斷源的實現(xiàn)的方法前面所描述的軟中斷實現(xiàn)方法,是利用系統(tǒng)中不使用的中斷源,將其分配給軟中斷來使用,在大多數(shù)情況下這都是適的方法。然而有的應(yīng)用系統(tǒng)確實已經(jīng)使用的全部的中斷資源,還想要軟中斷功能,這該怎么辦?下面就來討論這個題目:如何共享硬件中斷和軟中斷。解決這個問題的關(guān)鍵在于正確區(qū)分硬件觸發(fā)與軟件觸發(fā)的中斷,既要保證沒有多余的觸發(fā),又要保證不漏掉任何一個觸發(fā)。由于中斷標(biāo)志只有一個,若軟中斷觸發(fā)時硬中斷也觸發(fā),就不能分辨了。僅僅加軟件標(biāo)志通常不能解決這個問題,這時需要尋求其他的實現(xiàn)方法。必要時需要附加硬件來幫助實現(xiàn)軟中斷功能。選擇共享中斷源的依據(jù)是能夠準(zhǔn)確區(qū)分硬件中斷和軟中斷。比較容易成功的方法是通過附加硬件措施來改造兩個外部中斷中的一個。對于用低電平觸發(fā)的外部中斷申請,如果中斷的撤銷是由CPU來完成的,則CPU在中斷服務(wù)程序中就能通過查詢外部中斷線的電平狀態(tài)來判斷是否同時發(fā)生了硬件中斷的申請,這時軟中斷的申請標(biāo)志和硬中斷的申請電平就是兩個相互獨立的標(biāo)志,也就能夠準(zhǔn)確區(qū)分硬件中斷和軟中斷了。具有一般性的做法是將外部中斷0或外部中斷1的中斷申請改為利用一個D觸發(fā)器來進行電平方式的中斷申請,并將中斷申請的撤銷交給CPU來控制,以便系統(tǒng)能夠準(zhǔn)確識別每一個中斷申請。圖1是將邊沿觸發(fā)中斷改為電平觸發(fā)的一種實現(xiàn)方式。圖2是將主動撤銷信號電平申請改為由CPU控制信號撤銷電平申請的一種實現(xiàn)方式。對于其他的應(yīng)用于系統(tǒng)之間通訊的通訊端口,不推薦用于共享方式實現(xiàn)軟中斷。這是因為通訊的數(shù)據(jù)流可能很大,在沒有FIFO緩沖的51兼容單片機中,很有可能造成接收溢出。在51兼容單片機中,串口UART即使在不使用發(fā)送中斷的情況下,也不推薦應(yīng)用于軟中斷服務(wù),因為每一個數(shù)據(jù)發(fā)送完成都會自動置位TI標(biāo)志,會干擾軟中斷的執(zhí)行。D觸發(fā)起(74HC74)中斷申請CLR#CPU(51兼容單片機)SET#QINT0#P1.0圖1 邊沿觸發(fā)中斷申請改為低電平觸發(fā)中斷申請D觸發(fā)起(74HC74)中斷申請CLKCPU(51兼容單片機)SET#QINT0#P1.0D圖2低電平觸發(fā)中斷申請改為CPU撤銷信號的低電平觸發(fā)中斷申請6、軟中斷在實時操作系統(tǒng)環(huán)境下的應(yīng)用在基于51單片機的嵌入式系統(tǒng)中,數(shù)據(jù)采集處理、程序控制和數(shù)據(jù)通訊是三個主要的應(yīng)用方面,以下三小節(jié)分別描述數(shù)據(jù)采集處理任務(wù)、程序控制任務(wù)和數(shù)據(jù)通訊任務(wù)中軟中斷的使用,以幫助讀者加深對軟中斷的理解。6.1軟中斷服務(wù)在數(shù)據(jù)采集處理任務(wù)中的應(yīng)用實時操作系統(tǒng)下,中斷處理占用的時間越短,系統(tǒng)的實時性越好。在一些應(yīng)用中,數(shù)據(jù)處理的實時性要求很高,系統(tǒng)的響應(yīng)時間在0.5毫秒以內(nèi)。如果數(shù)據(jù)處理的時間在最壞情況下超過0.3毫秒,系統(tǒng)的軟件設(shè)計就受到挑戰(zhàn)。如果將數(shù)據(jù)處理全部放入中斷中進行處理,系統(tǒng)中其它任務(wù)的實時性會受到嚴(yán)重影響。解決的辦法通常是以下兩種:1)不使用51單片機,改用其它處理速度更快的處理器(如ARM處理器);2)不使用操作系統(tǒng)。這兩種解決辦法要受到產(chǎn)品成本和開發(fā)效率的約束。軟中斷的應(yīng)用可以成為上述問題的第3種解決方法,并且是最優(yōu)的解決方法。使用一個最低優(yōu)先級的中斷源來實現(xiàn)軟中斷,將數(shù)據(jù)處理放入軟中斷中完成。這樣,軟中斷就可以隨時剝奪任何一個正在運行的任務(wù)而優(yōu)先執(zhí)行,執(zhí)行完成后可以回到正在執(zhí)行的任務(wù),也可以切換到已就緒的更高優(yōu)先級的任務(wù)。如此,系統(tǒng)的實時性就提高了,同時,軟件開發(fā)效率也提高了。具體的辦法是,在數(shù)據(jù)采集中斷中,只完成數(shù)據(jù)的獲取,將數(shù)據(jù)存放到隊列中,然后申請軟中斷服務(wù)后退出中斷服務(wù)程序。只要系統(tǒng)中沒有其它中斷事務(wù)等待處理,軟中斷就可以開始處理,并且在軟中斷處理過程中可以響應(yīng)其它的硬件中斷,從而保證系統(tǒng)的實時性。由于軟中斷總是優(yōu)先于任務(wù)執(zhí)行,所以能夠保證數(shù)據(jù)處理的實時性。當(dāng)隊列中沒有數(shù)據(jù)時,軟中斷退出,執(zhí)行其它任務(wù)。在這樣的機制下,沒有頻繁的上下文切換,因此CPU的使用效率得到提高,以保證整個系統(tǒng)的實時性。以下代碼片斷是一種車輛檢測器的代碼,使用了軟中斷來處理數(shù)據(jù)??梢钥闯?,找外部中斷0中將數(shù)據(jù)放入隊列,然后申請軟中斷。在軟中斷中,只要隊列中還有數(shù)據(jù),就循環(huán)處理直到隊列為空才退出軟中斷。void int0_int(void) interrupt IE0_VECTOR/外部中斷0TR0 = 0;/讀計數(shù)器NewNode = QMalloc();/申請分配內(nèi)存if(NewNode 3) & 0x1F);LoopQNewNode.TimeStamp = 0;/沒有測速,不需要時間戳AddLoopNode(NewNode);SWI_IRQ();/申請軟件中斷,以便處理數(shù)據(jù)LoopSet();/計算并設(shè)置下一個通道LoopProcTimer = 0;/軟中斷服務(wù)函數(shù)void SWI_ISR(void) interrupt SWI_NUM/while(1)ProcNode = GetLoopNode();/從隊列中取待處理的數(shù)據(jù)if(ProcNode = 0xFF) break;/隊列中沒有數(shù)據(jù),退出lopcout = LoopQProcNode.LoopChannel;/取通道號nvlopcout = LoopQProcNode.LoopNv;/取測量數(shù)據(jù)QFree(ProcNode);/釋放內(nèi)存以便循環(huán)使用LoopProc();/通道計算CLEAR_SWI();/清除可能需要清除的標(biāo)志6.2 軟中斷在程序控制任務(wù)中的應(yīng)用在操作系統(tǒng)中的程序控制任務(wù),其運行受系統(tǒng)內(nèi)核支配。如果程序控制任務(wù)在邏輯上有嚴(yán)格的時間安排,很可能被高優(yōu)先級任務(wù)剝奪運行而導(dǎo)致邏輯失敗。這種任務(wù)如果全部安排在中斷中完成處理,會影響系統(tǒng)中其它任務(wù)的實時性。這時比較好的方法就是利用軟中斷來處理時間要求很嚴(yán)格部分。通過軟中斷,申請在指定時刻執(zhí)行指定的代碼。以下的代碼是程序控制任務(wù)計算出來的控制序列,通過申請軟中斷服務(wù),將待執(zhí)行的程序控制交給系統(tǒng),系統(tǒng)在定時器的嚴(yán)格同步下,將非常準(zhǔn)確地完成控制過程。代碼中,35毫秒后,功能1和功能4將“同時”啟動執(zhí)行,實際上功能1先于功能4執(zhí)行,時間上相差大約10uS左右。SWI_REQ(20,func1,data1);/申請20ms后執(zhí)行功能1,其所需的參數(shù)在data1SWI_REQ(25,func2,data2);/申請25ms后執(zhí)行功能2,其所需的參數(shù)在data2SWI_REQ(26,func3,data3);/申請26ms后執(zhí)行功能3,其所需的參數(shù)在data3SWI_REQ(35,func1,data4);/申請35ms后執(zhí)行功能1,其所需的參數(shù)在data4SWI_REQ(35,func4,data5);/申請35ms后執(zhí)行功能4,其所需的參數(shù)在data5SWI_REQ(36,func3,data6);/申請36ms后執(zhí)行功能3,其所需的參數(shù)在data66.3 軟中斷在數(shù)據(jù)通訊任務(wù)中的應(yīng)用在數(shù)據(jù)通訊中,有些通訊協(xié)議對系統(tǒng)的響應(yīng)時間有嚴(yán)格的要求,兩個數(shù)據(jù)包之間的空閑時間有嚴(yán)格的限制,超過一定時間就認(rèn)為超時。這種情況下,數(shù)據(jù)通訊任務(wù)如果全部安排在中斷中完成處理,則會影響系統(tǒng)其它任務(wù)的實時性,如果放在任務(wù)中運行,運行的時間又不可控制。當(dāng)然可以在任務(wù)中關(guān)閉中斷來處理實時性要求較強的代碼,可是這樣做會影響其他部分的實時性。比較好的方法,就是利用軟中斷,將需要提供的實時服務(wù)通過軟中斷申請將目標(biāo)代碼和數(shù)據(jù)在不影響系統(tǒng)實時性的條件下優(yōu)先執(zhí)行。首先,在系統(tǒng)中建立一個軟中斷函數(shù)執(zhí)行的鏈表,將等待執(zhí)行代碼入口地址和數(shù)據(jù)入口地址放在這個鏈表中。一旦內(nèi)核運行完畢,就自動來檢測這個鏈表中是否存在待執(zhí)行的軟中斷代碼。如果有軟中斷代碼,立即按照優(yōu)先順序執(zhí)行。當(dāng)所有的軟中斷代碼執(zhí)行完成后,就檢查是否有高優(yōu)先級的任務(wù)已經(jīng)就緒等待執(zhí)行,若有,進行任務(wù)調(diào)度。當(dāng)然,申請軟中斷服務(wù)本身就可能引發(fā)任務(wù)調(diào)度。這得看軟中斷服務(wù)完成后,是否有更高優(yōu)先級任務(wù)進入就緒態(tài)。比如,有一個任務(wù)在發(fā)送完成一個數(shù)據(jù)包后,有嚴(yán)格的時間要求在20ms后準(zhǔn)確啟動第二個數(shù)據(jù)包的發(fā)送。如果利用OSWait()或OSTimeDly()的方式,則完全不可能在20ms后準(zhǔn)確開始。如果關(guān)閉中斷等待20ms,系統(tǒng)的效率將十分低下。用軟中斷的方法是,向系統(tǒng)申請一個軟中斷服務(wù),將服務(wù)要求的啟動時刻、提供服務(wù)函數(shù)入口地址以及數(shù)據(jù)的存儲地址提供給系統(tǒng),就可以在指定的時刻運行這段代碼,完成數(shù)據(jù)的發(fā)送工作。即使當(dāng)前任務(wù)在申請完軟中斷服務(wù)后被剝奪,也能保證指定的數(shù)據(jù)在20ms后發(fā)送出去。軟中斷函數(shù)執(zhí)行鏈表的結(jié)構(gòu):typedef struct Swi_List _MALLOC_MEM_ * SwiNode;typedef struct Swi_Listunsigned int TimeDelay;/延遲多少時間開始執(zhí)行,等于0表示立即執(zhí)行void (*Func)(unsigned char * pInOut);/執(zhí)行的函數(shù)入口地址unsigned char * pInOut;/執(zhí)行函數(shù)的數(shù)據(jù)輸入輸出地址SwiNode NextNode;/下一個節(jié)點的地址;這樣的偽代碼就是:/準(zhǔn)備第一個數(shù)據(jù)包Send(Packet1);/發(fā)送第一個數(shù)據(jù)包/準(zhǔn)備第二個數(shù)據(jù)包SWI_REQ(20,Send,Pachet2);/申請20ms后發(fā)送第二個數(shù)據(jù)包7、無操作系統(tǒng)環(huán)境下軟中斷的應(yīng)用在沒有操作系統(tǒng)軟件環(huán)境中,如果要執(zhí)行的事務(wù)比較簡單也比較少,用輪詢的方法調(diào)度各個模塊就基本可以滿足。然而當(dāng)事務(wù)比較多或事務(wù)比較復(fù)雜時,實時性的協(xié)調(diào)就很難。在這種情況下,就可以利用軟中斷來簡化各個模塊之間的時間協(xié)調(diào),可大大提高系統(tǒng)的實時性和可協(xié)調(diào)性。在一個沒有操作系統(tǒng)的51單片機應(yīng)用系統(tǒng)中開發(fā)軟件,軟中斷不僅可以用來幫助對臨界段的管理,以實現(xiàn)硬件資源的互斥訪問以及共享數(shù)據(jù)的互斥訪問,還可以實現(xiàn)無阻塞等待。還舉6.3節(jié)的例子,一個模塊在發(fā)送完成一個數(shù)據(jù)包后,有嚴(yán)格的時間要求在20ms后準(zhǔn)確啟動第二個數(shù)據(jù)包的發(fā)送。用軟中斷在系統(tǒng)中申請一個20ms的延時定時器,定時器到時執(zhí)行回調(diào)函數(shù)Send(),完成第二個數(shù)據(jù)包的發(fā)送。這樣的偽代碼就是:unsigned char Packet_Send(void)If(CanSend=0)return 1;/發(fā)送忙,返回1If(IsPendInTimer(Send))return 2;/Send函數(shù)被掛起在定時器的服務(wù)列表中,第二個數(shù)據(jù)包還沒有啟動發(fā)送,返回2/準(zhǔn)備第一個數(shù)據(jù)包Send(Packet1,len1);/發(fā)送第一個數(shù)據(jù)包/準(zhǔn)備第二個數(shù)據(jù)包SWI_REQ(20,Send,Pachet2,len2);/申請20ms后發(fā)送第二個數(shù)據(jù)包return 0;/發(fā)送完成,返回0下面一個例子,揭示軟中斷如何實現(xiàn)硬件資源的互斥訪問。大家都使用過I2C總線器件,比如實時時鐘、溫度傳感器、EEPROM等等。不論單片機自帶的硬件I2C控制器還是用軟件模擬的I2C控制器,我們都必須保證對I2C總線操作的完整性,避免多個模塊之間同時爭用I2C總線。在實時操作系統(tǒng)環(huán)境下,這個很容易實現(xiàn),但在沒有實時操作系統(tǒng)來管理時,就需要設(shè)置信號量來幫助各個模塊之間協(xié)調(diào)使用。我們都知道,I2C總線是低速總線,軟件模擬中線控制器時將耗費很多CPU時間,并且一個模塊必須完成訪問后才能釋放訪問權(quán)限。在沒有實時操作系統(tǒng)的軟件環(huán)境中,CPU將不得不等待。為了提高CPU的利用率,在I2C總線的驅(qū)動程序中使用軟中斷,能夠很好地協(xié)調(diào)各模塊之間使用I2C總線訪問不同的器件。軟中斷服務(wù)執(zhí)行指定的發(fā)送或接收函數(shù),并將結(jié)果復(fù)制到指定的內(nèi)存地址中,然后還可以執(zhí)行指定的回調(diào)函數(shù)(如果有的話)。由于軟中斷優(yōu)于任何模塊獲得CPU運行,故模塊之間提交的軟中斷獨立于模塊代碼而完整運行(硬件中斷不妨礙其正確執(zhí)行)。由于收發(fā)的硬件操作僅在軟中斷中執(zhí)行,自然實現(xiàn)了硬件資源的互斥訪問。同時,軟中斷中對共享的變量操作也具有了原子性。參閱例程4所示代碼片斷。/模塊1:Void M1()SWI(1,&MySwiNode1);/申請軟中斷/退出該模塊,軟件中斷執(zhí)行完成后,實時時鐘的數(shù)據(jù)自動保存在clock中/模塊2:Void M2()(省略)SWI(1,&MySwiNode2);/申請軟中斷/退出該模塊,軟件中斷執(zhí)行完成后,溫度數(shù)據(jù)自動保存在temp中void main(void)/運行模塊1M1();/運行模塊2M2();8、結(jié)語在51單片機中,利用硬件中斷的中斷向量,實現(xiàn)軟中斷是完全可行的,并且使用軟中斷能夠優(yōu)化應(yīng)用軟件的執(zhí)行效率。例2:一個SWI演示例程(STC12C5AxxS2單片機)#include #include sbit LED = P10;/定義軟件中斷使用的中斷號, 可選09#define SWI_NUM 1/使用外部中斷0作軟中斷#if SWI_NUM = 0#define SWI_IRQ() IE0 = 1#define CLEAR_SWI() /IE0 = 0/使用T0中斷作軟件中斷#elif SWI_NUM = 1#define SWI_IRQ() TF0 = 1#define CLEAR_SWI() TF0 = 0/使用外部中斷1作軟中斷#elif SWI_NUM = 2#define SWI_IRQ() IE1 = 1#define CLEAR_SWI() /IE1 = 0/使用T1中斷作軟件中斷#elif SWI_NUM = 3#define SWI_IRQ() TF1 = 1#define CLEAR_SWI() TF1 = 0/使用UART中斷作軟件中斷#elif SWI_NUM = 4 #define SWI_IRQ() RI = 1#define CLEAR_SWI() RI = 0/使用ADC中斷作軟件中斷#elif SWI_NUM = 5 #define SWI_IRQ() ADC_CONTR = 0x10#define CLEAR_SWI() ADC_CONTR = 0x00/使用LVD中斷作軟件中斷#elif SWI_NUM = 6 #define SWI_IRQ() PCON |= 0x20#define CLEAR_SWI() PCON &= 0xDF/使用PCA中斷作軟件中斷#elif SWI_NUM = 7 #define SWI_IRQ() CF = 1#define CLEAR_SWI() CF = 0/使用UART2中斷作軟件中斷#elif SWI_NUM = 8 #define SWI_IRQ() S2CON |= 0x01#define CLEAR_SWI() S2CON &= 0xFE/使用SPI中斷作軟件中斷#elif SWI_NUM = 9 #define SWI_IRQ() SPDAT = 0xFF#define CLEAR_SWI() SPSTAT = 0xC0 #endif#ifdef SWI_NUMunsigned char FunctionNum;/unsigned char * InputParameter;unsigned char * OutputParameter;#define SWI(a,b,c)FunctionNum=a;InputParameter = (unsigned char *)&bOutputParameter = (unsigned char *)&c;SWI_IRQ();_nop_();#endifvoid SwiFunction_Init(void)/將硬件中斷源置于高優(yōu)先級,IP = 0xFF;IP2 = 0xFF;IPH = 0x00;IPH2 = 0x00;/將軟中斷所使用的中斷源置于低優(yōu)先級#if SWI_NUM 8IP &= (0x01SWI_NUM);#elif SWI_NUM 10IP2 &= (0x01(SWI_NUM-8);#endif/使用外部中斷0作軟中斷#if SWI_NUM = 0IT0 =1;/邊沿觸發(fā)EX0 =1;/使用T0中斷作軟件中斷#elif SWI_NUM = 1ET0 =1;/使用外部中斷1作軟中斷#elif SWI_NUM = 2IT1 =1;/邊沿觸發(fā)EX1 =1;/使用T1中斷作軟件中斷#elif SWI_NUM = 3ET1 =1;/使用UART中斷作軟件中斷#elif SWI_NUM = 4ES =1;/使用ADC中斷作軟件中斷#elif SWI_NUM = 5EADC =1;/使用LVD中斷作軟件中斷#elif SWI_NUM = 6ELVD =1;/使用PCA中斷作軟件中斷#elif SWI_NUM = 7CMOD |= 0x01;/ECF=1/使用UART2中斷作軟件中斷#elif SWI_NUM = 8IE2 |=0x01;/ES2=1/使用SPI中斷作軟件中斷#elif SWI_NUM = 9AUXR1 |= 0x20;/SPI切換到P4口SPCTL = 0xD0;/MASTER模式IE2 |=0x02;/ESPI=1 #endifEA = 1;void main(void)unsigned char SwiFun_Num;int DataIn,result,i;P0 = 0xFF;P1 = 0xFF;P2 = 0xFF;P3 = 0xFF;SwiFunction_Init();/初始化軟中斷功能DataIn = 0;result = 0;SwiFun_Num = 0;while(1)SwiFun_Num+;DataIn+;SWI(SwiFun_Num,DataIn,result); /申請軟中斷服務(wù)if(result != (DataIn + SwiFun_Num) for(;) _nop_();/錯,停在這里for(i=0;i32767;i+) _nop_();#ifdef SWI_NUMvoid SWI_ISR(void) interrupt SWI_NUM/軟中斷服務(wù)函數(shù) 提供的功能是輸出=輸入+申請功能號LED = 1;/便于觀察是否進入軟中斷服務(wù)CLEAR_SWI();/清除可能需要清除的標(biāo)志*(int *) OutputParameter = *(int *)InputParameter + FunctionNum;FunctionNum = 0;/清除申請功能號#endif例3:一個具有動態(tài)代碼執(zhí)行能力的SWI演示例程(STC12C5AxxS2單片機)#include #include void Set_LED0(void);void Set_LED1(void);sbit LED = P10;/定義軟件中斷使用的中斷號 省略,同例2#ifdef SWI_NUMunsigned char FunctionNum;void (*Func_CallBack)(void);#define SWI(a,b)FunctionNum=a;Func_CallBack = (void *)b;SWI_IRQ();_nop_();#endifvoid SwiFunction_Init(void)/省略,代碼同例2void Set_LED0(void)LED = 0;void Set_LED1(void)LED = 1;void main(void)unsigned char SwiFun_Num;int i;P0 = 0xFF;P1 = 0xFF;P2 = 0xFF;P3 = 0xFF;SwiFunction_Init();/初始化軟中斷功能SwiFun_Num = 0;while(1)SwiFun_Num+;/申請軟中斷服務(wù)if(LED =1) SWI(SwiFun_Num,Set_LED0);Else SWI(SwiFun_Num,Set_LED1);for(i=0;i32767;i+) _nop_();#ifdef SWI_NUMvoid SWI_ISR(void) interrupt SWI_NUM/軟中斷服務(wù)函數(shù),提供的服務(wù)是執(zhí)行指定的功能函數(shù)CLEAR_SWI();/清除可能需要清除的標(biāo)志Func_CallBack();/執(zhí)行指定的功能函數(shù)FunctionNum = 0;/清除申請功能號#endif例程4:一個SWI演示例程,互斥訪問I2C總線(STC12C5AxxS2單片機)typedef struct Swi_List * SwiNode;typedef struct Swi_Listunsigned int TimeDelay;/延遲多少時間開始執(zhí)行,等于0表示立即執(zhí)行void (*Func)(unsigned char * pInOut);/執(zhí)行的函數(shù)入口地址unsigned char * pInOut;/執(zhí)行函數(shù)的數(shù)據(jù)輸入輸出地址SwiNode NextNode;/下一個節(jié)點的地址;SwiNode SWI_Head_Node;/系統(tǒng)內(nèi)核使用的指針變量#ifdef SWI_NUMunsigned char FunctionNum;SwiNode SwiIRQNode;#define SWI(a,b)FunctionNum = a;SwiIRQNode = b;SWI_IRQ();_nop_();#endifuint8 IICRead(uint8 *Ret,uint8 Addr,uint8 NByte)/硬件上操作總線,完成讀取功能(省略)uint8 IICWrite(uint8 Addr,uint8 *Data,uint8 NByte)/硬件上操作總線,完成寫入功能(省略)void PCF8563Read(uint8 *Data) uint8 NByte; NByte = *Data+; IICWrite(PCF8563Addr,Data,1); IICRead(Data - 1,PCF8563Addr,NByte);void PCF8563Write(uint8 *Data) uint8 NByte; NByte = *Data+; NByte+; IICWrite(PCF8563Addr,Data,NByte);void Get_Temperature(int *RegTemperature)/讀傳感器的溫度,傳感器的地址由*RegTemperature指定uint8 SensorAddr;uint8 TempReg2;SensorAddr = (uint8 *) RegTemperature;/訪問的傳感器地址TempReg0 = SENSOR_REG_TEMPERATURE; 訪問傳感器的溫度值寄存器 IICWrite(WRITE_SENSOR_ADDR0+(SensorAddr&0x07)*2,TempReg,1);/寫一個字節(jié) IICRead(Temp, READ_SENSOR_ADDR0+(SensorAddr&0x07)*2,2);/讀兩個字節(jié)*RegTemperature = (Temp 0TimeDelay = SwiIRQNode-TimeDelay;SWI_Head_Node-Func = SwiIRQNode-Func;SWI_Head_Node-pInOut = SwiIRQNode-pInOut;SWI_Head_Node-NextNode = 0; else(省略)/尋找是否有就緒的軟中斷任務(wù)需要執(zhí)行pNode = SWI_Head_Node;pRightNode = SWI_Head_Node;for(;)if(pNode != 0)if(pNode-TimeDelay)=0)/發(fā)現(xiàn)一個等待執(zhí)行的軟中斷if(pNode = SWI_Head_Node)/是第一個節(jié)點if(pNode-NextNode) != 0)/還有下一個節(jié)點/撤銷節(jié)點SWI_Head_Node = pNode-NextNode;pRightNode = SWI_Head_Node;/執(zhí)行函數(shù)pNode-Func(pNode-pInOut);/釋放內(nèi)存free(pNode);pNode = pRightNode;/指向下一個節(jié)點else/沒有下一個節(jié)點(省略)else/不是第一個節(jié)點(省略)else/移動指針pRightNode = pNode;pNode = pNode-NextNode;else break;FunctionNum = 0;/清除申請功能號CLEAR_SWI();/清除可能需要清除的標(biāo)志/模塊1:Void M1()Swi_List_Struct MySwiNode1;/static uint8 clock8=0,0,0,0,0,0,0,0;/用于讀寫PCF8563實時時鐘/根據(jù)溫度執(zhí)行相應(yīng)的操作(省略)/讀取PCF8563的時間數(shù)據(jù)MySwiNode1.Func = PCF8563Read;/執(zhí)行的函數(shù)是PCF8563ReadMySwiNode1.pInOut = clock;/輸入輸出的參數(shù)clock8clock 0 = 7;/讀7個字節(jié)clock 1 = 2;/從PCF寄存器2開始讀MySwiNode1.TimeDelay = 0;/執(zhí)行功能的延遲時間為0,要求立即開始執(zhí)行SWI(1,&MySwiNode1);/申請軟中斷/退出該模塊,軟件中斷執(zhí)行完成后,實時時鐘的數(shù)據(jù)自動保存在clock中/模塊2:Void M2()Swi_List_Struct MySwiNode2;/static uint8 temp2=0,0;/用于讀寫AD7416溫度傳感器int * T;T = (int *) temp;/指向存儲溫度的內(nèi)存/根據(jù)溫度執(zhí)行相應(yīng)的操作(省略)/讀取AD7416的溫度數(shù)據(jù)MySwiNode2.Func = Get_Temperature;/執(zhí)行的函數(shù)是Get_TemperatureMySwiNode2.pInOut = temp;/輸入輸出的參數(shù)temp4temp0 = 0;/讀地址編號為0個溫度傳感器的溫度MySwiNode2.TimeDelay = 0;/執(zhí)行功能的延遲時間為0,要求立即開始執(zhí)行SWI

溫馨提示

  • 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

提交評論