μC/OS-II的多任務資訊流與CAN匯流排驅動

來源:互聯網
上載者:User

μC/OS-II的多任務資訊流與CAN匯流排驅動

摘要:闡述μC/OS-II多任務資訊流關鍵技術與中斷處理的一般方法和PC體系中斷的基本概念;以CAN匯流排為例,詳細分析在x86實模式下基於μC/OS-II的CAN匯流排驅動的實現過程。

    關鍵詞:μC/OS-II RTOS嵌入式系統 裝置驅動 中斷處理常式(ISR) 進程調度

    μC/OS-II是美國人Jean Labrosse編寫的一個免費的、源碼公開的嵌入式即時核心。對於開發電腦嵌入式應用產品的技術人員來說是一個實用價值很高的即時嵌入式作業系統ERTOS(Embedded Real Time Operation System)。

  要開發出完善的ERTOS,就要在多任務的調度和對I/O裝置操作的穩定性、協調性方面做出大量的工作,這也是我在開發ERTOS過程中深深體會到的重點所在。希望本文能對開發ERTOS的技術人員在多任務資訊流和I/O驅動方面有所啟迪。

1 多任務資訊流關鍵技術

  在討論多任務資訊流之前,先討論一下多任務的工作狀態。在μC/OS中,每個任務都是無限迴圈的,每個任務都處在以下五種狀態之一:休眠態、就緒態、運行態、掛起態和中斷態,1所示。

  

在多任務的調度和驅動程式的編寫過程中,必然要涉及到公用程式碼片段和共用儲存區的保護問題。即使是原有的C函數,可重用性方面在沒有得到理論和實踐的驗證情況下也需要對其進行保護。這樣就需要合理的演算法對公用程式碼片段、共用儲存區進行保護,避免作業系統在運行過程中產生重用性問題而導致運行結果不可預測。

  系統在開發過程中,既要考慮到減少系統的複雜程度,也要兼顧其穩定性與運行效率的要求。這就需要我們對各種演算法進行合理的選擇:在穩定性可以保障的情況下,選擇相對簡單,佔用CPU時間少的演算法;在穩定性不能保障的情況下,考慮選擇周全的演算法。只有這樣才能使作業系統在一定的配置環境下達到最高的運行效率。

  接下來分別用void CanSendMessageProcess(void *data)、void CanSendMessage(void *data)、void CanReceiveMessageProcess(void *data)和void CanReceiveMessage(void *data)這四個任務來描述在採用訊息佇列、郵箱和訊號量通訊機制時的資訊流的傳遞過程。

  (1)訊息佇列通訊機制

  訊息佇列在初始化的時候,建立一個指定空間大小的數組,這個數組在使用的時候取得了環形緩衝區的概念。這個數組在運行期間不會被消除,這樣就避免了重複建立數組的時候記憶體空間的泄漏問題。當一個任務向訊息佇列發送一個資訊的時候,相應的指標加1(OSQIn+1),隊列滿時(OSQEntries = OSQSize),OSQIn則與OSQOut指向同一單元。如果在OSQIn指向的單元插入入新的指向訊息的指標,就構成FIFO(First-In- First-Out)隊列。相反,如果在OSQOut指向單元的下一個單元插入新的指標,就構成LIFO隊列(Last-In-First-Out)。在本執行個體中,我們定義FIFO隊列。訊息指標總是從OSQOut指向的單元取出。OSQStart和OSQEnd定義了訊息指標數組的頭和尾,以便在 OSQIn和OSQOut到達隊列的邊緣時,進行邊界檢查和必要的指標調整,實現其迴圈功能。

  訊息佇列資料結構如下:

typedef struct os_q {

struct os_q *OSQPtr; /* 在空閑隊列控制塊中連結所有的隊列控制塊*/

void *OSQStart; /*指向訊息佇列的指標數組的起始地址的指標*/

void *OSQEnd; /* 指向訊息佇列結束單元的下一個地址的指標*/

void *OSQIn; /* 指向訊息佇列中插入下一條資訊位置的指標*/

void *OSQOut; /* 指向訊息佇列中下一個取出訊息位置的指標*/

INT16U OSQSize; /* 訊息佇列中總的單元數*/

INT16U OSQEntries; /*訊息佇列中總的訊息數量*/

} OS_Q;

圖2為訊息佇列資訊流的示範說明。

  ① CanSendMessageProcess任務完成資訊的計算工作以後,將要發送的資訊送進訊息佇列1。

  ② CanSendMessage任務負責取得訊息佇列1裡面的資訊。

  ③ 通過CAN匯流排I/O連接埠將資料發送到匯流排上去。如果訊息佇列中沒有資訊,則該任務由運行狀態進入等待狀態,直到從訊息佇列中接收到資訊為止。

  ④ CanReceiveMessage任務負責讀取匯流排上面的資訊。

  ⑤ CanReceiveMessage任務將讀取到的資訊送入訊息佇列2。

  ⑥ CanReceiveMessageProcess任務是從訊息佇列2中取出資訊開始計算工作,如果訊息佇列為空白的話,該任務進入等待狀態。

  訊息佇列適用於一對一、一對多、多對多和多對一的關係。也就是說,訊息佇列可以作為一塊共用的公用地區,為實施互斥,任務間需要同步;為了合作,進程間需要交換資訊,這樣也就實現了同步和通訊。

  (2)郵箱通訊機制

  郵箱的概念和管道(管線)有相似的定義,一個任務或者中斷服務子程式向另一個任務發送一個指標型的變數,該指標指向一個包含了特定“訊息”的資料結構。在源端的任務只能向郵箱寫,在目的端的任務只能從郵箱讀。郵箱傳輸串流資料,即連續的位元組串或流。因此,訪問一個郵箱就像是訪問一個循序檔。郵箱可以用來通知一個事件的發生(發送一條資訊),也可以用來共用某些資源,這樣郵箱就被當成一個二值訊號量。

  圖3為郵箱資訊流的示範說明。

  ① CanSendMessageProcess任務將計算好的資料發送給CanSendMessage任務,然後進入就緒態等待應答訊號。CanSendMessage在接收的同時發送應答握手訊號給CanSendMessageProcess,確認資訊接收完畢。

  ②CanSendMessage任務將CanSend MessageProcess任務發送來的資訊發送到CAN匯流排,發送結束後進入就緒態等待下一次傳輸工作。

  ③ CanReceiveMessage任務接收來自匯流排的資訊流,將接收到的資訊發送到Can  ReceiveMessageProcess任務,進入就緒態等待應答訊號。

  ④ CanReceiveMessageProcess任務收到資訊後發送應答握手訊號。

  (3)訊號量通訊機制

  訊號量(semaphore)是一種約定機制:兩個或多個任務通過簡單的訊號進行合作,一個任務可以被迫在某一位置停止,直到它接收到一個特定的訊號。在多任務核心中普遍將訊號量用於:

  ◇ 標誌某事件的發生;

  ◇ 控制共用資源的使用權(滿足互斥條件);

  ◇ 使兩個任務的行為同步。

  訊號量主要實施三種操作:

  ◇ 一個訊號量可以初始化為非負數;

  ◇ 等待(wait)操作使訊號量減1。如果值變成負數,則執行等待的任務被阻塞。

  ◇ 得到CPU使用權的任務singal操作使訊號量加1。如果值不是正數,則被等待操作阻塞的任務被解除阻塞。

  為了滿足資訊傳遞過程中即時高效的原則,在訊息佇列中部分地引入訊號量的概念。也就是CanSendMessageProcess任務,把若干個位元組的資訊一次性地發送到訊息佇列,令訊號量加1並由運行態進入等待掛起狀態。在 CanSendMessage任務獲得訊號量後進入就緒態,等待CPU的使用權進入運行態。進入運行態後,該任務使訊號量減1並從訊息佇列中取出資訊後通過I/O連接埠發送到CAN匯流排。CanReceiveMessage任務和CanReceive MessageProcess任務執行與上面相反的操作。這個執行個體說明了訊號量用於標誌某事件的發生。(見圖2。)

2 μC/OS-II的中斷處理

  μC/OS-II中,中斷服務程式一般用組合語言來寫。以下是中斷服務程式的示意代碼。

  使用者中斷服務程式: 

  儲存全部CPU寄存器;

  調用OSIntEnter或OSIntNesting直接加1;

  執行使用者代碼做中斷服務;

  調用OSIntExit;

  恢複所有CPU寄存器;

  執行中斷返回指令;

  這裡μC/OS-II提供了兩個ISR與核心的介面函數:OSIntEnter和OSIntExit。OSIntEnter通知μC/OS-II核心,中斷服務程式開始運行了。實際上,此函數做的工作是把一個全域變數OSIntNesting加1。在x86等有累加指令的CPU中,可以用指令代替OSIntEnter:

    INC BYTE PTR OSIntNesting

    此中斷嵌套計數器可以確保所有中斷處理完成後再作任務調度。另一個介面函數OSIntExit則通知核心,中斷服務已結束。根據相應情況,返回被中斷點(可能是一個任務或者被嵌套的中斷服務程式)或由核心作任務調度。

  使用者編寫的ISR必須被安裝到某一位置,以便中斷髮生後,CPU根據相應的中斷號運行準確的服務程式。許多即時作業系統都提供了安裝、卸載中斷服務程式的API介面函數,有些成熟的RTOS甚至對中斷控制器的管理都有相應的API函數。但 μC/OS-II核心沒有提供類似的介面函數,需要使用者在對應的CPU移植中自己實現。這些介面函數與具體的硬體環境有關,接下來PC體系下的中斷處理對此有詳細的說明。

3 PC體系下的中斷

  X86系列的處理器可支援256個中斷,並用向量表的方法來關聯每個中斷和相應 ISR的位置。在實模式下,中斷向量表(IVT)存於記憶體的低端1K。每個向量表條目佔4位元組,儲存一個ISR的段地址和位移資訊。PC系統使用兩個級聯的可程式化插斷控制器82C59A。一個82C59A能串連8個硬體中斷,編號為IRQ0~IRQ7。 PC總共可管理15個外部中斷源,PC的中斷控制器4所示。(關於82C59A的詳細使用可參見有關資料。)

  在μC/OS下,CAN匯流排I/O連接埠中斷向量設定虛擬碼:

    void CanInitHW(UI segment,BYTE Irq0,BYTE Irq1){

  儲存原有的中斷向量

  儲存掩碼寄存器的值

  使82C59A的掩碼寄存器(0x21)各位置1,關閉中斷輸入

  關閉CPU中斷

  設定新的中斷向量

  正在服務的中斷禁止再次響應服務(假定當前服務中斷是IRQ5)

  開CPU中斷

  清除82C59A的掩碼寄存器(0X21、0XA1)各位,開啟中斷輸入

    }

4 訊號量與緩衝隊列支援下的CAN匯流排驅動

  前面介紹了μC/OS-II核心下多任務調度的關鍵技術、中斷與PC體系下中斷的一般方法。又以82C59A的中斷5(IRQ5)、0x0D中斷向量為例,介紹了中斷服務子程式的重新分配和響應SJA1000控制器收發的中斷服務子程式。

  下面介紹訊號量配合下的環形緩衝隊列與中斷處理常式之間的關係問題,這也是裝置驅動部分的核心內容。

  ERTOS的驅動程式與其它作業系統有所不同。比如Windows、Unix、Solaris、Linux等作業系統弱化了裝置的概念,使用者進程對裝置的使用可以通過檔案系統來完成。然而,在μC /OS-II上開發CAN匯流排驅動程式沒有那麼嚴格,只要滿足裝置在連續的CPU時間上使用時不發生時間重疊就可以了。

  串列裝置或者其它字元型裝置都存在外設處理速度和CPU速度不匹配的問題,所以需要建立相應的緩衝區。向CAN口發送資料時,只要把資料寫到緩衝區,然後由SJA1000控制器逐個取出往外發。從CAN口接收資料時,往往等收到若干個位元組後才需要CPU進行處理,所以這些預收的資料可以先存於緩衝區。緩衝區可以設定收到若干個位元組後再中斷CPU,這樣避免了因為CPU的頻繁中斷而降低系統的即時性。

  在對緩衝區讀寫的過程中,經常會遇到想發送資料時,發送緩衝已滿;想去讀時,接收緩衝卻是空的。對於使用者程式端,可以採用查詢工作方式,即放棄無法讀寫的操作,然後再頻繁地去嘗試這個操作直到成功,這樣程式效率顯然降低。如果引入讀、寫兩個訊號量分別對緩衝區兩端的操作進行同步,問題將迎刃而解。使用者任務想寫但緩衝區滿時,在訊號量上睡眠,讓CPU運行別的任務,待ISR從緩衝區讀走資料後喚醒此睡眠的任務;類似地,使用者任務想讀但緩衝區空時,也可以在訊號量上睡眠,待外部裝置有資料來了再喚醒。由於μC/OS-II的訊號量提供了逾時等待機制,CAN口當然也具有逾時讀寫能力。

  帶緩衝和訊號量的CAN口接收和發送部分見本刊網路補充版(http://www.dpj.com.cn)。

  介面函數總結如下。

void CanInitHW(UI segment,BYTE irq0,BYTE IRQ1)

/*設定SJA1000控制器連接埠中斷向量*/

int canReleaseHW() /* 清除SJA1000控制器連接埠中斷向量*/

int canSendMsg( CANBYTE port, MSG_STRUCT msg)

/* 向定製SJA1000控制器連接埠發送資料*/

int canReceiveMsg( CANBYTE port, MSG_STRUCT msg_ptr)

/*從定製SJA1000控制器連接埠接收資料

int canConfig( CANBYTE port, CAN_STRUCT can)

/*初始化和配置SJA1000控制器 */

int canNormalRun( CANBYTE port )

/*設定SJA1000正常(Normal)運行模式 */

int canReset( CANBYTE port )

/* SJA1000控制器連接埠重新設定,緩衝區置位0xff*/

CANBYTE can0r( CANBYTE addr)

/*讀取SJA1000控制器連接埠0的定製寄存器的值 */

CANBYTE can1r( CANBYTE addr)

/*讀取SJA1000控制器連接埠1的定製寄存器的值 */

接收和發送資料緩衝區資料結構定義:

typedef struct {

INT16U RingBufRxCtr; /* 接收緩衝中字元數目 */

OS_EVENT RingBufRxSem; /* 接收訊號量 */

INT8U RingBufRxInPtr; /* 接收緩衝中下一字元的寫入位置 */

INT8U RingBufRxOutPtr; /* 接收緩衝中下一待讀出字元的位置 */

INT8U RingBufRx[CAN_RX_BUF_SIZE]; /* 接收環形緩衝區*/

INT16U RingBufTxCtr;

/* 發送緩衝中字元數目 */

OS_EVENT *RingBufTxSem; /* 發送訊號量 */

INT8U *RingBufTxInPtr;

/* 發送緩衝中下一字元的寫入位置 */

INT8U *RingBufTxOutPtr;

/* 發送緩衝中下一待讀出字元的位置 */

INT8U RingBufTx[CAN_TX_BUF_SIZE]; /* 發送環形緩衝區*/

} CAN_RING_BUF;

結 語

  本文是在嵌入式電腦技術領域的應用背景下提出的,整個工程開發結束以後,系統正常運作時間超過27天。希望本文的提出對開發嵌入式作業系統的技術人員能有所協助,同時也希望同一領域的開發人員共同探討、共同發展。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.