<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>

新聞中心

EEPW首頁(yè) > 嵌入式系統 > 設計應用 > μC/OS-II的多任務(wù)信息流與CAN總線(xiàn)驅動(dòng)

μC/OS-II的多任務(wù)信息流與CAN總線(xiàn)驅動(dòng)

作者: 時(shí)間:2004-12-10 來(lái)源:網(wǎng)絡(luò ) 收藏
摘要:闡述μ關(guān)鍵技術(shù)與中斷處理的一般方法和PC體系中斷的基本概念;以為例,詳細分析在x86實(shí)模式下基于μ的實(shí)現過(guò)程。

關(guān)鍵詞:μ RTOS嵌入式系統 設備 中斷處理程序(ISR) 進(jìn)程調度

μC/OS-II是美國人Jean Labrosse編寫(xiě)的一個(gè)免費的、源碼公開(kāi)的嵌入式實(shí)時(shí)內核。對于開(kāi)發(fā)計算機嵌入式應用產(chǎn)品的技術(shù)人員來(lái)說(shuō)是一個(gè)實(shí)用價(jià)值很高的實(shí)時(shí)嵌入式操作系統ERTOS(Embedded Real Time Operation System)。

  要開(kāi)發(fā)出完善的ERTOS,就要在多的調度和對I/O設備操作的穩定性、協(xié)調性方面做出大量的工作,這也是我在開(kāi)發(fā)ERTOS過(guò)程中深深體會(huì )到的重點(diǎn)所在。希望本文能對開(kāi)發(fā)ERTOS的技術(shù)人員在多和I/O方面有所啟迪。

1 多任務(wù)關(guān)鍵技術(shù)

  在討論多任務(wù)信息流之前,先討論一下多任務(wù)的工作狀態(tài)。在μC/OS中,每個(gè)任務(wù)都是無(wú)限循環(huán)的,每個(gè)任務(wù)都處在以下五種狀態(tài)之一:休眠態(tài)、就緒態(tài)、運行態(tài)、掛起態(tài)和中斷態(tài),如圖1所示。

  在多任務(wù)的調度和驅動(dòng)程序的編寫(xiě)過(guò)程中,必然要涉及到公用代碼段和共享存儲區的保護問(wèn)題。即使是原有的C函數,可重用性方面在沒(méi)有得到理論和實(shí)踐的驗證情況下也需要對其進(jìn)行保護。這樣就需要合理的算法對公用代碼段、共享存儲區進(jìn)行保護,避免操作系統在運行過(guò)程中產(chǎn)生重用性問(wèn)題而導致運行結果不可預測。

  系統在開(kāi)發(fā)過(guò)程中,既要考慮到減少系統的復雜程度,也要兼顧其穩定性與運行效率的要求。這就需要我們對各種算法進(jìn)行合理的選擇:在穩定性可以保障的情況下,選擇相對簡(jiǎn)單,占用CPU時(shí)間少的算法;在穩定性不能保障的情況下,考慮選擇周全的算法。只有這樣才能使操作系統在一定的配置環(huán)境下達到最高的運行效率。

  接下來(lái)分別用void CanSendMessageProcess(void *data)、void CanSendMessage(void *data)、void CanReceiveMessageProcess(void *data)和void CanReceiveMessage(void *data)這四個(gè)任務(wù)來(lái)描述在采用消息隊列、郵箱和信號量通信機制時(shí)的信息流的傳遞過(guò)程。

 ?。?)消息隊列通信機制

  消息隊列在初始化的時(shí)候,建立一個(gè)指定空間大小的數組,這個(gè)數組在使用的時(shí)候取得了環(huán)形緩沖區的概念。這個(gè)數組在運行期間不會(huì )被消除,這樣就避免了重復建立數組的時(shí)候內存空間的泄漏問(wèn)題。當一個(gè)任務(wù)向消息隊列發(fā)送一個(gè)信息的時(shí)候,相應的指針加1(OSQIn+1),隊列滿(mǎn)時(shí)(OSQEntries = OSQSize),OSQIn則與OSQOut指向同一單元。如果在OSQIn指向的單元內插入新的指向消息的指針,就構成FIFO(First-In-First-Out)隊列。相反,如果在OSQOut指向單元的下一個(gè)單元插入新的指針,就構成LIFO隊列(Last-In-First-Out)。在本實(shí)例中,我們定義FIFO隊列。消息指針總是從OSQOut指向的單元取出。OSQStart和OSQEnd定義了消息指針數組的頭和尾,以便在OSQIn和OSQOut到達隊列的邊緣時(shí),進(jìn)行邊界檢查和必要的指針調整,實(shí)現其循環(huán)功能。

  消息隊列數據結構如下:

typedef struct os_q {

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

void *OSQStart; /*指向消息隊列的指針數組的起始地址的指針*/

void *OSQEnd; /* 指向消息隊列結束單元的下一個(gè)地址的指針*/

void *OSQIn; /* 指向消息隊列中插入下一條信息位置的指針*/

void *OSQOut; /* 指向消息隊列中下一個(gè)取出消息位置的指針*/

INT16U OSQSize; /* 消息隊列中總的單元數*/

INT16U OSQEntries; /*消息隊列中總的消息數量*/

} OS_Q;

圖2為消息隊列信息流的演示說(shuō)明。

 ?、?CanSendMessageProcess任務(wù)完成信息的計算工作以后,將要發(fā)送的信息送進(jìn)消息隊列1。

 ?、?CanSendMessage任務(wù)負責取得消息隊列1里面的信息。

 ?、?通過(guò)I/O端口將數據發(fā)送到總線(xiàn)上去。如果消息隊列中沒(méi)有信息,則該任務(wù)由運行狀態(tài)進(jìn)入等待狀態(tài),直到從消息隊列中接收到信息為止。

 ?、?CanReceiveMessage任務(wù)負責讀取總線(xiàn)上面的信息。

 ?、?CanReceiveMessage任務(wù)將讀取到的信息送入消息隊列2。

 ?、?CanReceiveMessageProcess任務(wù)是從消息隊列2中取出信息開(kāi)始計算工作,如果消息隊列為空的話(huà),該任務(wù)進(jìn)入等待狀態(tài)。

  消息隊列適用于一對一、一對多、多對多和多對一的關(guān)系。也就是說(shuō),消息隊列可以作為一塊共享的公共區域,為實(shí)施互斥,任務(wù)間需要同步;為了合作,進(jìn)程間需要交換信息,這樣也就實(shí)現了同步和通信。

 ?。?)郵箱通信機制

  郵箱的概念和管道(管線(xiàn))有相似的定義,一個(gè)任務(wù)或者中斷服務(wù)子程序向另一個(gè)任務(wù)發(fā)送一個(gè)指針型的變量,該指針指向一個(gè)包含了特定“消息”的數據結構。在源端的任務(wù)只能向郵箱寫(xiě),在目的端的任務(wù)只能從郵箱讀。郵箱傳輸流數據,即連續的字節串或流。因此,訪(fǎng)問(wèn)一個(gè)郵箱就像是訪(fǎng)問(wèn)一個(gè)順序文件。郵箱可以用來(lái)通知一個(gè)事件的發(fā)生(發(fā)送一條信息),也可以用來(lái)共享某些資源,這樣郵箱就被當成一個(gè)二值信號量。

  圖3為郵箱信息流的演示說(shuō)明。

 ?、?CanSendMessageProcess任務(wù)將計算好的數據發(fā)送給CanSendMessage任務(wù),然后進(jìn)入就緒態(tài)等待應答信號。CanSendMessage在接收的同時(shí)發(fā)送應答握手信號給CanSendMessageProcess,確認信息接收完畢。

 ?、贑anSendMessage任務(wù)將CanSend MessageProcess任務(wù)發(fā)送來(lái)的信息發(fā)送到CAN總線(xiàn),發(fā)送結束后進(jìn)入就緒態(tài)等待下一次傳輸工作。

 ?、?CanReceiveMessage任務(wù)接收來(lái)自總線(xiàn)的信息流,將接收到的信息發(fā)送到Can  ReceiveMessageProcess任務(wù),進(jìn)入就緒態(tài)等待應答信號。

 ?、?CanReceiveMessageProcess任務(wù)收到信息后發(fā)送應答握手信號。

 ?。?)信號量通信機制

  信號量(semaphore)是一種約定機制:兩個(gè)或多個(gè)任務(wù)通過(guò)簡(jiǎn)單的信號進(jìn)行合作,一個(gè)任務(wù)可以被迫在某一位置停止,直到它接收到一個(gè)特定的信號。在多任務(wù)內核中普遍將信號量用于:

  ◇ 標志某事件的發(fā)生;

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

  ◇ 使兩個(gè)任務(wù)的行為同步。

  信號量主要實(shí)施三種操作:

  ◇ 一個(gè)信號量可以初始化為非負數;

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

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

  為了滿(mǎn)足信息傳遞過(guò)程中實(shí)時(shí)高效的原則,在消息隊列中部分地引入信號量的概念。也就是CanSendMessageProcess任務(wù),把若干個(gè)字節的信息一次性地發(fā)送到消息隊列,令信號量加1并由運行態(tài)進(jìn)入等待掛起狀態(tài)。在CanSendMessage任務(wù)獲得信號量后進(jìn)入就緒態(tài),等待CPU的使用權進(jìn)入運行態(tài)。進(jìn)入運行態(tài)后,該任務(wù)使信號量減1并從消息隊列中取出信息后通過(guò)I/O端口發(fā)送到CAN總線(xiàn)。CanReceiveMessage任務(wù)和CanReceive MessageProcess任務(wù)執行與上面相反的操作。這個(gè)實(shí)例說(shuō)明了信號量用于標志某事件的發(fā)生。(見(jiàn)圖2。)

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

  μC/OS-II中,中斷服務(wù)程序一般用匯編語(yǔ)言來(lái)寫(xiě)。以下是中斷服務(wù)程序的示意代碼。

  用戶(hù)中斷服務(wù)程序:

  保存全部CPU寄存器;

  調用OSIntEnter或OSIntNesting直接加1;

  執行用戶(hù)代碼做中斷服務(wù);

  調用OSIntExit;

  恢復所有CPU寄存器;

  執行中斷返回指令;

  這里μC/OS-II提供了兩個(gè)ISR與內核的接口函數:OSIntEnter和OSIntExit。OSIntEnter通知μC/OS-II內核,中斷服務(wù)程序開(kāi)始運行了。實(shí)際上,此函數做的工作是把一個(gè)全局變量OSIntNesting加1。在x86等有累加指令的CPU中,可以用指令代替OSIntEnter:

INC BYTE PTR OSIntNesting

此中斷嵌套計數器可以確保所有中斷處理完成后再作任務(wù)調度。另一個(gè)接口函數OSIntExit則通知內核,中斷服務(wù)已結束。根據相應情況,返回被中斷點(diǎn)(可能是一個(gè)任務(wù)或者被嵌套的中斷服務(wù)程序)或由內核作任務(wù)調度。

  用戶(hù)編寫(xiě)的ISR必須被安裝到某一位置,以便中斷發(fā)生后,CPU根據相應的中斷號運行準確的服務(wù)程序。許多實(shí)時(shí)操作系統都提供了安裝、卸載中斷服務(wù)程序的API接口函數,有些成熟的RTOS甚至對中斷控制器的管理都有相應的API函數。但 μC/OS-II內核沒(méi)有提供類(lèi)似的接口函數,需要用戶(hù)在對應的CPU移植中自己實(shí)現。這些接口函數與具體的硬件環(huán)境有關(guān),接下來(lái)PC體系下的中斷處理對此有詳細的說(shuō)明。

3 PC體系下的中斷

  X86系列的處理器可支持256個(gè)中斷,并用向量表的方法來(lái)關(guān)聯(lián)每個(gè)中斷和相應ISR的位置。在實(shí)模式下,中斷向量表(IVT)存于內存的低端1K。每個(gè)向量表條目占4字節,保存一個(gè)ISR的段地址和偏移信息。PC系統使用兩個(gè)級聯(lián)的可編程中斷控制器82C59A。一個(gè)82C59A能連接8個(gè)硬件中斷,編號為IRQ0~IRQ7。 PC總共可管理15個(gè)外部中斷源,PC的中斷控制器如圖4所示。(關(guān)于82C59A的詳細使用可參見(jiàn)有關(guān)資料。)

  在μC/OS下,CAN總線(xiàn)I/O端口中斷向量設置偽代碼:

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

  保存原有的中斷向量

  保存掩碼寄存器的值

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

  關(guān)閉CPU中斷

  設置新的中斷向量

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

  開(kāi)CPU中斷

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

}

4 信號量與緩沖隊列支持下的CAN總線(xiàn)驅動(dòng)

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

  下面介紹信號量配合下的環(huán)形緩沖隊列與中斷處理程序之間的關(guān)系問(wèn)題,這也是設備驅動(dòng)部分的核心內容。

  ERTOS的驅動(dòng)程序與其它操作系統有所不同。比如Windows、Unix、Solaris、Linux等操作系統弱化了設備的概念,用戶(hù)進(jìn)程對設備的使用可以通過(guò)文件系統來(lái)完成。然而,在μC /OS-II上開(kāi)發(fā)CAN總線(xiàn)驅動(dòng)程序沒(méi)有那么嚴格,只要滿(mǎn)足設備在連續的CPU時(shí)間上使用時(shí)不發(fā)生時(shí)間重疊就可以了。

  串行設備或者其它字符型設備都存在外設處理速度和CPU速度不匹配的問(wèn)題,所以需要建立相應的緩沖區。向CAN口發(fā)送數據時(shí),只要把數據寫(xiě)到緩沖區,然后由SJA1000控制器逐個(gè)取出往外發(fā)。從CAN口接收數據時(shí),往往等收到若干個(gè)字節后才需要CPU進(jìn)行處理,所以這些預收的數據可以先存于緩沖區。緩沖區可以設置收到若干個(gè)字節后再中斷CPU,這樣避免了因為CPU的頻繁中斷而降低系統的實(shí)時(shí)性。

  在對緩沖區讀寫(xiě)的過(guò)程中,經(jīng)常會(huì )遇到想發(fā)送數據時(shí),發(fā)送緩沖已滿(mǎn);想去讀時(shí),接收緩沖卻是空的。對于用戶(hù)程序端,可以采用查詢(xún)工作方式,即放棄無(wú)法讀寫(xiě)的操作,然后再頻繁地去嘗試這個(gè)操作直到成功,這樣程序效率顯然降低。如果引入讀、寫(xiě)兩個(gè)信號量分別對緩沖區兩端的操作進(jìn)行同步,問(wèn)題將迎刃而解。用戶(hù)任務(wù)想寫(xiě)但緩沖區滿(mǎn)時(shí),在信號量上睡眠,讓CPU運行別的任務(wù),待ISR從緩沖區讀走數據后喚醒此睡眠的任務(wù);類(lèi)似地,用戶(hù)任務(wù)想讀但緩沖區空時(shí),也可以在信號量上睡眠,待外部設備有數據來(lái)了再喚醒。由于
μC/OS-II的信號量提供了超時(shí)等待機制,CAN口當然也具有超時(shí)讀寫(xiě)能力。

  帶緩沖和信號量的CAN口接收和發(fā)送部分見(jiàn)本刊網(wǎng)絡(luò )補充版(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控制器端口發(fā)送數據*/

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的定制寄存器的值 */

接收和發(fā)送數據緩沖區數據結構定義:

typedef struct {

INT16U RingBufRxCtr; /* 接收緩沖中字符數目 */

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

INT8U RingBufRxInPtr; /* 接收緩沖中下一字符的寫(xiě)入位置 */

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

INT8U RingBufRx[CAN_RX_BUF_SIZE]; /* 接收環(huán)形緩沖區*/

INT16U RingBufTxCtr;

/* 發(fā)送緩沖中字符數目 */

OS_EVENT *RingBufTxSem; /* 發(fā)送信號量 */

INT8U *RingBufTxInPtr;

/* 發(fā)送緩沖中下一字符的寫(xiě)入位置 */

INT8U *RingBufTxOutPtr;

/* 發(fā)送緩沖中下一待讀出字符的位置 */

INT8U RingBufTx[CAN_TX_BUF_SIZE]; /* 發(fā)送環(huán)形緩沖區*/

} CAN_RING_BUF;

結 語(yǔ)

  本文是在嵌入式計算機技術(shù)領(lǐng)域的應用背景下提出的,整個(gè)工程開(kāi)發(fā)結束以后,系統正常運作時(shí)間超過(guò)27天。希望本文的提出對開(kāi)發(fā)嵌入式操作系統的技術(shù)人員能有所幫助,同時(shí)也希望同一領(lǐng)域的開(kāi)發(fā)人員共同探討、共同發(fā)展。



評論


相關(guān)推薦

技術(shù)專(zhuān)區

關(guān)閉
国产精品自在自线亚洲|国产精品无圣光一区二区|国产日产欧洲无码视频|久久久一本精品99久久K精品66|欧美人与动牲交片免费播放
<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>