關 閉

        新聞中心

        EEPW首頁 > 安全與國防 > 一種基于C51的多任務機制及應用

        一種基于C51的多任務機制及應用

        ——
        作者:廈門大學 王輝堂 顏自勇 陳文薌 時間:2007-12-04 來源:電子設計應用 收藏

        摘要:本文介紹了一種在MCS51程序中實現(xiàn)多任務機制的簡單方法,并給出了源代碼和一個應用實例。通過進行實時任務切換,具有結構簡單清晰、代碼量少、不需使用匯編等優(yōu)點。該方法亦可應用于其他系統(tǒng)。

        關鍵詞
             

        引言

            傳統(tǒng)的單片機程序一般采用單任務機制,單任務系統(tǒng)具有簡單直觀、易于控制的優(yōu)點。然而由于程序只能按順序依次執(zhí)行,缺乏靈活性,只能使用函數(shù)實時地處理一些較短的任務,在較復雜的應用中使用極為不便。嵌入式多任務操作系統(tǒng)的出現(xiàn)解決了這個問題。在中,可以同時執(zhí)行多個并行任務,任務之間可以相互跳轉。但是嵌入式操作系統(tǒng)在提供強大功能的同時,也帶來了代碼量大、結構復雜、對硬件要求較高、開發(fā)難度大且成本高等問題。而很多時候只需要實現(xiàn)簡單的多任務操作就可以滿足實際需要,本文設計的這種簡單的多任務機制,在只增加極少量C語言代碼的前提下,不需使用匯編,無需對原本的程序進行大改動,就可以實現(xiàn)多任務操作。

             實時操作系統(tǒng)RTOS的核心是中斷,利用中斷進行任務切換。在大部分RTOS如μC/OS-II中,每個任務都有自己的堆棧,用來保存任務的一些信息,任務之間通過信號量、郵箱、消息隊列等傳遞信息。在很多情況下并不需要這些功能,只需要使單片機在接收到控制信號后,切換到不同的工作狀態(tài),也就是只要進行任務切換,不需要保存任務的相關信息。舍棄這些復雜的功能可以使程序結構變得簡潔易用。

        兩種機制在應用實例中的比較

            下面用一個應用實例來說明本設計的思路。要設計一個智能,它的功能包括:當有人入侵時執(zhí)行報警工作;用戶可以通過鍵盤板進行功能設置;主板能與管理中心進行通訊,當發(fā)生火災、地震等災情時,管理中心能通知用戶。其結構如圖1所示。平時狀態(tài)下,主板的CPU不斷地掃描各個傳感器的狀態(tài)。當檢測到傳感器的異常信號(有人闖入)時,CPU進入入侵報警狀態(tài),執(zhí)行響警鈴、撥打戶主電話、通知管理中心等工作。當發(fā)生火災地震時,管理中心發(fā)送一個串口代碼給主板CPU,使CPU進入災難報警狀態(tài),執(zhí)行響警鈴、語音報警等操作。用戶需要進行功能設置時可以通過鍵盤板使主板CPU進入功能設置狀態(tài)。因此主板的CPU有4種不同的工作狀態(tài)。


         
          圖1  智能結構示意圖

            如果采用單任務機制, 主板的程序流程如圖2所示。在主函數(shù)中循環(huán)檢測傳感器狀態(tài),如有異常則調用報警函數(shù),災難報警和功能設置在串口中斷中完成。這種單任務結構有兩個缺點。首先,在各種非平時狀態(tài)中,程序需要不停地檢測是否收到撤除信號,這個要求在程序代碼量大、執(zhí)行工作較多的情況下很難實現(xiàn)。其次,各狀態(tài)之間的切換十分困難,用C語言寫的程序為求模塊化,一般函數(shù)數(shù)量較多,函數(shù)調用的嵌套層數(shù)也多,要從一個較深的嵌套立刻跳出到主函數(shù),是非常困難的。一般的解決方法或是使用的庫函數(shù)setjmp()和longjmp()實現(xiàn)長跳轉,但是這兩個函數(shù)在中斷函數(shù)內部是無能為力的;再或是在C函數(shù)中嵌入?yún)R編指令。雖然用匯編指令可以實現(xiàn)程序的長距離跳轉,但是這種方法的調試過程十分煩瑣,而且程序的可移植性差。對于習慣用編程而不想用匯編的設計者,該部分程序是一個難題。 {{分頁}}


          圖2  單任務機制程序流程

        實現(xiàn)多任務機制的程序結構

            本文提供了一種方法,可以在完全不使用匯編指令的前提下實現(xiàn)可移植性強的多任務程序,程序流程如圖3所示。
         



          圖3  多任務結構程序流程

            實現(xiàn)這個多任務機制的完整源代碼如下:
        word idata PC_Value, SP_Value;     file://儲存中斷返回點、SP初值的全局變量
        byte idata Ctrl_Code;               file://控制任務切換的全局變量,在中斷函數(shù)里被賦值
        void main()               
        {
         Initial();          file://初始化函數(shù),與程序結構無關
         SP_Value=SP;        file://獲取SP的初始值
            PC_Value=Get_Next_PC();             file://獲取下一條指令的地址
         EA=1;          file://獲取PC、SP初值后再開中斷保證穩(wěn)定性
         if(Ctrl_Code!=0)
            SP=SP_Value;         file://重置堆棧指針,防止堆棧溢出
            switch( Ctrl_Code)        file://任務入口地址,即中斷的返回點 
         {
          case 1:   goto  TASK1;
          case 2:   goto  TASK2;
          case 3:   goto  TASK3;
          default:  break;
         }
        TASK1:  for( ; ; )
                {          file://任務1代碼        }
        TASK2:  for( ; ; )
                {          file://任務2代碼        }
        TASK3:  for( ; ; )
                {          file://任務2代碼        }
        }
        word Get_Next_PC(void)     file://獲取下一條指令的地址
        {
          word address;
          address=*((unsigned char *)SP);    file://PC的高字節(jié)
          address <<= 8;
          address+=*((unsigned char *)(SP-1));  file://PC的低字節(jié)
          return address+4;       file://查看反匯編代碼,計算所得
        }
        void Chuan_Kou_Interrupt(void) interrupt 4 using 0
        {
            byte a1,a2;
         a1=a1*a2;
         *((unsigned char *)(SP-5))=PC_Value>>8;
         *((unsigned char *)(SP-6))=PC_Value & 0x00ff;
         {
             file://接收串口代碼并根據(jù)代碼修改Ctrl_Code的值
          file://其他操作
         }
         }{{分頁}}

        任務調度原理與實現(xiàn)

            程序的整體思路是在主函數(shù)main中依次放置幾個死循環(huán)作為任務框架,即每個任務都是一個死循環(huán),利用中斷進行任務切換。以剛才所說的安防系統(tǒng)為例,由于主板、鍵盤、管理中心之間是通過串口通訊的,因此串口是用來觸發(fā)任務切換的理想中斷源。程序為所有任務設置一個總入口并放在主函數(shù)中,串口中斷每次返回時必須先經(jīng)過這個總入口,在總入口處檢查任務控制變量(全局變量)的值,任務控制變量已在串口中斷中被賦值,其值決定要切換到哪個任務。

          設計中可以把平時狀態(tài)、入侵報警狀態(tài)、危機報警狀態(tài)、功能設置狀態(tài)分別作為任務1、任務2、任務3、任務4。主板CPU平常工作在平時狀態(tài),即任務1;當串口收到管理中心的危機代碼,在串口中斷函數(shù)中令Ctrl_Code = 3,中斷返回后會切換到任務3;同樣,接收到鍵盤的功能設置代碼后,會切換到任務4;由于入侵檢測是由主板CPU自己負責,因此如果檢測到有人入侵需要切換到入侵報警狀態(tài)時,可以借由鍵盤中轉產(chǎn)生串口中斷,即向鍵盤發(fā)送一串口數(shù)據(jù)并要求鍵盤回送。這樣就實現(xiàn)了各個狀態(tài)的切換。

          實現(xiàn)任務調度需要解決3個關鍵問題:

          ① 獲取任務入口點的程序地址。由于使用C語言不能直接獲取和修改程序計數(shù)器PC的值,而在調用函數(shù)時會將PC值入棧,利用這個特點在任務入口處之前調用Get_Next_PC函數(shù)即可從堆棧中獲得入口地址。Get_Next_PC中,SP為堆棧指針,得到的PC值要加4才是任務入口地址,因為查看反匯編窗口可知,將函數(shù)返回值傳給全局變量PC_Value需要兩條2字節(jié)長的mov指令。

            ② 修改中斷返回地址。修改中斷返回地址的操作與獲取PC值類似,都是通過修改堆棧中的內容實現(xiàn)。但是由于編譯器自身的特點,在進入中斷時,編譯器除了把返回地址入棧外,還會計算自身及它所調用的函數(shù)對寄存器ACC、 B、 DPH、 DPL、 PSW、 R0 ~ R7的改變,并將它認為被改變了的寄存器也入棧保護。如果堆棧結構會隨中斷函數(shù)內容改變而變化,就沒辦法計算中斷返回地址堆棧中的位置。解決方法是,在中斷函數(shù)定義時加上關鍵字using 0 告訴編譯器中斷函數(shù)及其調用的函數(shù)將使用寄存器組0,這樣工作寄存器R0~R7將不會被保存。ACC、PSW、DPH、DPL在對PC_Value操作時已經(jīng)用到,在中斷函數(shù)開頭定義兩個變量a1、b1并令它們相乘,使B寄存器也被入棧,這樣堆棧的結構就是固定的了。

           ③防止堆棧溢出。由于在調用函數(shù)時編譯器會將當前地址入棧,返回時再出棧,當任務切換即中斷多次發(fā)生在函數(shù)調用過程中時,堆棧會因為只入不出而最終導致溢出。這是不能容許的。因此,應在主函數(shù)開頭初始化后立刻將SP值保存,再在每次任務切換后都將SP恢復為初值,這可以有效防止堆棧溢出。

        結語

          根據(jù)以上的比較與分析可以看出這種實現(xiàn)多任務機制的方法具有如下優(yōu)點:與采用單任務機制的程序相比,其結構簡單清晰,易于控制;利用中斷和堆棧實現(xiàn)任務切換時的長跳轉,完全不需使用匯編語言,可移植性強;增加的代碼量極小,實時性好,節(jié)省程序開發(fā)時間。

            以上介紹的方法已經(jīng)通過測試并應用于幾個實際項目中,包括智能小區(qū)安防系統(tǒng)、汽車CAN總線控制系統(tǒng)等,取得了良好效果。只要根據(jù)具體的硬件與編譯環(huán)境稍作修改,亦可應用于其他的單片機系統(tǒng)中。

        參考文獻

        1. 張培仁. 基于C語言編程MCS-51單片機原理與應用. 北京:清華大學出版社, 2003.1.
        2. 胡大可等. 基于單片機8051的嵌入式開發(fā)指南. 北京:電子工業(yè)出版社, 2003.1



        評論


        相關推薦

        技術專區(qū)

        關閉
        主站蜘蛛池模板: 长武县| 南昌市| 集贤县| 明星| 永善县| 进贤县| 甘洛县| 伊春市| 合作市| 凤凰县| 定西市| 土默特左旗| 勃利县| 蓝田县| 云龙县| 白山市| 土默特右旗| 临武县| 西华县| 山东省| 诸暨市| 天镇县| 鹿泉市| 施秉县| 遂平县| 建昌县| 陵水| 襄城县| 会同县| 肥乡县| 三穗县| 泉州市| 会理县| 丰镇市| 卫辉市| 镶黄旗| 邵阳市| 洛隆县| 社会| 上思县| 中阳县|