簡介
RTX51 Tiny是一種實時作業系統(RTOS),可以用它來建立多個任務(函式)同時執行的套用。嵌入式套用系統經常有這種需求。RTOS可以提供調度、維護、同步等功能。
實時作業系統能靈活的調度系統資源,像CPU和存儲器,並且提供任務間的通信。RTX51 Tiny是一個功能強大的RTOS,且易於使用,它用於8051系列的微控制器。
RTX51 Tiny的程式用標準的C語言構造,由Keil C51 C編譯器編譯。用戶可以很容易的定義任務函式,而不需要進行複雜的棧和變數結構配置,只需包含一個指定的頭檔案。
產品規格
參 數 | 范 圍 |
最大任務數 | 16 |
最大活動任務 | 16 |
代碼空間需求 | 900位元組最大 |
數據空間需求 | 7位元組 |
棧空間需求 | 3位元組/任務 |
外部RAM需求 | 0位元組 |
定時器 | 0 |
系統時鐘因子 | 1000~65535 |
中斷等待 | 20個周期或更少 |
上下文切換時間 | 100~700個周期 |
工具需求
以下為使用RTX51 Tiny需要的套用軟體:
C51編譯器
A51宏彙編器
BL51連線器或LX51連線器
RTX51TNY.LIB和RTX51BT.LIB庫檔案必須保存於庫路徑下,通常,該路徑是"KEIL"C51"LIB資料夾。RTX51TNY.H必須保存在包含路徑下,通常是"KEIL"C51"INC資料夾。
目標需求
RTX51 Tiny運行於大多數8051兼容的器件及其變種上。RTX51 Tiny應用程式可以訪問外部數據存儲器,但核心無此需求。
RTX51 Tiny支持Keil C51編譯器全部的存儲模式。存儲模式的選擇只影響應用程式對象的位置,RTX51 Tiny系統變數和應用程式棧空間總是位於8051的內部存儲區(DATA或IDATA區),一般情況下,應用程式應使用小(SMALL)模式。
RTX51 Tiny執行協作式任務切換(每個任務調用一個作業系統例程)
和時間片輪轉任務切換(每個任務在作業系統切換到下一個任務前運行一個固定的時間段),不支持搶先式任務切換以及任務優先權。RTX51 Full支持搶先式任務切換。
中斷
RTX51 Tiny與中斷函式並行運作,中斷服務程式可以通過傳送信號(用isr_send_signal函式)或設定任務的就序標誌(用isr_set_redy函式)與RTX51 Tiny的任務進行通信。
如同在一個標準的,沒有RTX51 Tiny的套用中一樣,中斷例程必須在RTX51Tiny套用中實現並允許,RTX51 Tinyim 沒有中斷服務程式的管理。
RTX51 Tiny使用定時器0、定時器0中斷,和暫存器組1。如果在程式中使用了定時器0,則RTX51 Tiny將不能正常運轉。你可以在RTX51 Tiny定時器0的中斷服務程式後追加自己的定時器0中斷服務程式代碼(參見硬體定時器)。
RTX51 Tiny假設總中斷總是允許(EA=1)。RTX51 Tiny庫例程在需要時改變中斷系統(EA)的狀態,以確保RTX51 Tiny的內部結構不被中斷破壞。當允許或禁止總中斷時,RTX51 Tiny只是簡單的改變EA的狀態,不保存並重裝EA,EA只是簡單的被置位或清除。因此,如果你的程式在調用RTX51例程前某止了中斷,RTX51可能會失去回響。
在程式的臨界區,可能需要在短時間內禁止中斷。但是,在中斷禁止後,不能調用任何RTX51 Tiny的例程。如果程式確實需要禁止中斷,應該持續很短的時間。
再入函式
C51編譯器提供對再入函式的支持,再入函式在再入堆疊中存儲參數和局部變數,從而保護遞歸調用或並行調用。RTX51 Tiny不支持對C51再入棧的任何管理。因此,如果在程式中使用再入函式,必須確保這此函式不調用任何RTX51 Tiny系統函式,且不被循環任務切掉所打斷。
僅用暫存器傳遞參數和保存自動變數的C函式具有內在的再入性,可以無限制的調用RTX51 Tiny。
非可再入C函式不能被超過一個以上的任務或中斷過程調用。非再入C51函式在靜態存儲區段保存參數和自動變數(局部數據),該區域在函式被多個任務同時調用或遞歸調用時可能會被修改。
如果確定多個任務不會遞歸(或同時)調用,則多個任務可以調用非再入函式。通常,這意味著必須禁止循環任務調度,且該非再入函式不能調用任何RTX51 Tiny系統函式。
附註:
l 如果希望在多個任務或中斷中調用再入或非再入函式,應當禁止循
環任務調度。
C庫例程
可再入C51庫函式可在任何任務中無限制的使用。對於非再入的C51庫函式,同樣有非可再入C函式的限制。
多數據指針
Keil C51編譯器允許使用多數據指針(存在於許多8051的派生晶片中), RTX51 Tiny不提供對它們的支持.因此,在RTX51 Tiny的應用程式中應小心使用多數據指針。
從本質上說,必須確保循環任務切換不會在執行改變數據指針選擇器的代碼時發生。
附註:
l 如果要使用多數據指針,應該禁止循環任務切換。
運算單元
Keil C51編譯器允許使用運算單元(存在於許多8051的派生晶片中)。RTX51 Tiny不提供對它們的支持。
因此,在RTX51 Tiny的應用程式中須小心使用運算單元。
從本質上說,必須確保循環任務切換不會在執行用運算單元的代碼時發生。
附註:
l 如果希望使用運算單元,應禁止循環任務切換。
暫存器組
RTX51 Tiny分配所有的任務到暫存器0,因此,所有的函式必須用C51的默認設定進行編譯,REGISTERBANK(0)。
中斷函式可以使用剩餘的暫存器組。然而,RTX51 Tiny需要暫存器組區域中的6個永久性的位元組,用於這些位元組的暫存器組在配置檔案中指定。
實時程式
實時程式必須對實時發生的事件快速回響。事件很少的程式不用實時作業系統也很容易實現。隨著事件的增加,編程的複雜程度和難度也隨之增大,這正是RTOS的用武之地。
單任務程式
嵌入式程式和標準C程式都是從main函式開始執行的,在嵌入式套用中,main通常是一個無限循環,可以認為是一個持續執行的單個任務,例如:
void main (void)
﹛while(1) /*永遠重複*/
﹛
do_something(); /*執行 do_something“任務”*/
﹜
﹜
在這個例子裡,do_something函式可以認為是一個單任務,由於僅有一個任務在執行,所以沒有必要進行多任務處理或使用多任務作業系統。
多任務程式
許多C程式通過在一個循環里調用服務函式(或任務)來實現偽多任務調度。如:
void main(void)
﹛
int counter="0";
while(1) /*一直重複執行*/
﹛
check_serial_io(); /*檢查串列輸入*/
process_serial_cmds() ; /*處理串列輸入*/
check_kbd_io(); /*檢查鍵盤輸入*/
process_kbd_cmds(); /*處理鍵盤輸入*/
adjust|ctrlr_parms(); /*調整控制器*/
counter++; /*增加計數器*/
﹜
﹜
該例中,每個函式執行一個單獨的操作或任務,函式(或任務)按次序依次執行。
當任務越來越多,調度問題就被自然而然的提出來了。例如,如果process_kbd_cmds函式執行時間較長,主循環就可能需要較長的時間才能返回來執行check_sericd_io函式,導致串列數據可能被丟失。當然,可以在主循環中更
頻繁的調用check_serial_io函式以糾正這個問題,但最終這個方法還是會失效
RTX51 Tiny 程式
當使用Rtx51Tiny時,為每個任務建立獨立的任務函式,例如:
void check_serial_io_task(void) _task_ 1
﹛/*該任務檢測串列I/0*/﹜
void process_serial_cmds_task(void) _task_ 2
﹛/*該任務處理串列命令*/﹜
void check_kbd_io_task(void) _task_ 3
﹛/*該任務檢測鍵盤I/O*/﹜
void process_kbd_cmds_task(void) _task_ 4
﹛/*處理鍵盤命令*/﹜
void startup-_task(void) _task_ 0
﹛
os_create_task(1); /*建立串列I/O任務*/
os_create_task(2); /*建立串列命令任務*/
os_create_task(3); /*建立鍵盤I/O任務*/
os_create_task(4); /*建立鍵盤命令任務*/
os_delete_task(0); /*刪除啟動任務*/
﹜
該例中,每個函式定義為一個RTX51 Tiny任務。RTX51 Tiny程式不需要main函式,取而代之,RTX51 Tiny從任務0開始執行。在典型的套用中,任務0簡單的建立所有其他的任務。
原理
RTX51 Tiny 用於管理目標系統的資源,本章討論RTX51 Tiny如何使用這些資源。
定時器滴答中斷
RTX51 Tiny 用標準8051的定時器0(模式1)生產一個周期性的中斷。該中斷就是RTX51 Tiny的定時滴答(Timer Tick)。庫函式中的逾時和時間間隔就是基於該定時滴答來測量的。
默認情況下,RTX51每10000個機器周期產生一個滴答中斷,因此,對於運行於12MHZ的標準8051來說,滴答的周期是0.01秒,也即頻率是100HZ(12MHz/12/10000)。該值可以在CONF_TNY.A51配置檔案中修改。
附註:
l可以在RTX51的定時滴答中斷里追加自己的代碼。參見CONF_TNY.A51 配置檔案。
l關於RTX51 Tiny如何使用中斷可以參考概述中中斷一節的敘述。
任務
RTX51 Tiny本質上是一個任務切換器,建立一個RTX51 Tiny程式,就
是建立一個或多個任務函式的應用程式。下面的信息可以幫助你快速的理解
RTX51 。
l任務用新的關鍵字由C語言定義,該關鍵字是Keic C51 所支持的。
lRTX51 Tiny維護每個任務的正確狀態(運行、就緒、等待、刪除、逾時)。
l某個時刻只有一個任務處於運行態。
l任務可能處於就緒態、等待態、刪除態或逾時態。
l空閒任務(Idle_Task)總是處於就緒態,當定義的所有任務處於阻 塞狀態時,運行該任務。
任務管理
每個RTX51 Tiny 任務總是處於下述狀態中的一種狀態中。
狀 態 | 描 述 |
運行 | 正在運行的任務處於運行態。某個時刻只能有一個任務處於該狀態。 os_running_task_id 函式返回當前正在運行的任務編號。 |
就緒 | 準備運行的任務處於就緒態。一旦運行的任務完成了處理,RTX51 Tiny選擇一個就緒的任務執行。一個任務可以通過用os_set_ready或os_set_ready函式設定就緒標誌來使其立即就緒(即便該任務正在等待逾時或信號)。 |
等待 | 正在等待一個事件的任務處於等待態。一旦事件發生,任務切換到就緒態。Os_wait函式用於將一個任務置為等待態。 |
刪除 | 沒有被啟動或已被刪除的任務處於刪除態。Os-delete-task函式將一個已經啟動(用os_create_task)的任務置為刪除態。 |
逾時 | 被逾時循環中斷的任務處於逾時態,在循環任務程式中,該狀態相當於就緒態。 |
事件
在實時作業系統中,事件可用於控制任務的執行,一個任務可能等待一個事件,也可能向其他任務傳送任務標誌。
os_wait函式可以使一個任務等待一個或多個事件。
l逾時是一個任務可以等待的公共事件。逾時就是一些時鐘滴答數, 當一個任務等待逾時時,其他任務可以執行。一旦到達指定數量的滴答數,任務就可以繼續執行。
l時間間隔(Interval)是一個逾時(Timeout)的變種。時間間隔與超
時類似,不同的是時間間隔是相對於任務上次調用os_wait函式的指定數量的時鐘滴答數。
l信號是任務間通信的方式。一個任務可以等待其他任務給它發信號(用os_send_signal和isr_send_signal函式)。
l每個任務都有一個可被其它任務設定的就緒標誌(用os_set_ready和
isr_set_ready函式)。一個個等待逾時、時間間隔或信號的任務可以通過設定它的就緒標誌來啟動。
lisr_set_ready函式)。一個等待逾時、時間間隔或信號的任務可以通 過設定它的就緒標誌來啟動。
下表是os_wait函式等待的事件:
K_IVL | 等待制定的時間 |
隔K_SIG | 等待一個信號 |
K_TMO | 等待指定的逾時 |
os-wait返回時,返回值表明發生了的事件:
返 回 值 | 意 義 |
RDY_EVENT | 任務的就緒標誌被置位 |
SIG_EVENT | 收到一個信號 |
TMO_EVENT | 逾時完成或時間間隔到達。 |
os_wait可以等待下面的事件組合:
lK_SIG︱K_TMO:任務延遲直到有信號發給它或者指定數量的時鐘滴答
到達。
lK_SIG︱K_IVL:任務延遲直到有信號到來或者指定的時間間隔到達。
附註:
lK_IVL和K_TMO事件不能組合
任務調度程式
任務調度程式給任務分配處理器,RTX51 Tiny調度程式用下列規則確定
哪個任務要被運行:
當前任務被中斷如果:
1、任務調用了os_switch_task且另一個任務正準備運行。
2、任務調用了os_wait且指定的事件沒有發生。
3、任務執行了比輪轉時間片更長的時間。
另一個任務啟動如果:
1、無其它任務運行。
2、要啟動的任務處於就緒態或逾時態。
循環任務切換
RTX51 Tiny可以配置為用循環法進行多任務處理(任務切換)。循環法允許
並行的執行若干任務。任務並非真的同時執行,而是分時間片執行的(CPU時間分
成時間片,RTX51 Tiny給每個任務分配一個時間片)。由於時間片很短(幾毫秒),
看起來好象任務在同時執行。
任務在它的時間片內持續執行(除非任務的時間片用完)。然後,RTX51 Tin
g切換到下一個就緒的任務運運行。時間片的持續時間可以通過RTX51 Ting配置
定義。
下面是一個RTX51 Tiny程式的例子,用循環法多任務處理,程式中的兩個任務
是計數器循環。RTX51 Tiny在啟動時執行函式名為job0的任務0,該函式建立了另
一個任務job1,在job0執行完它的時間片後, RTX51 Tiny切換到job1。在job1執
行完它的時間片後,RTX51 Ting又切換到job0,該過程無限重複。
#include
int counter0;
int counter1;
void job0(void) _task_ 0
﹛
os_create(1); /*標記任務1為就緒*/
while(1)
﹛ /*無限循環*/
counter0++; /*更新記數器*/
}
}
void job1(void) _task_1
﹛
while(1)
﹛ /*無限循環*/
counter++; /*更新記數器*/
}
}
附註:
l可以用os_wait 或os_switch_task讓RTX51 Tiny切換到另一個任務而不是
等待任務的時間片用完。 os_wait函式掛起當前的任務(使之變為等待態)直
到指定的事件發生(接著任務變為就緒態)。在此期間,任意數量的其他任務
可以運行。
協作任務切換
如果禁止了循環任務處理,就必須讓任務以協作的方式運作,在每個任務
里調用os_wait或os_switch_task,以通知RTX51 Tingy切換到另一個任務。
os_wait與os_switch_task的不同是,os_wait是讓任務等待一個事件,而
os_switch_task是立即切換到另一個就緒的任務。
空閒任務
沒有任務準備運行時,RTX51 Ting執行一個空閒任務。空閒任務就是一個
無限循環。如:
SJMP$
有些8051兼容的晶片提供一種降低功耗的空閒模式,該模式停止程式的執
行,直到有中斷產生。在該模式下,所有的外設包括中斷系統仍在運行。
RTX51 Tiny允許在空閒任務中啟動空閒模式(在沒有任務準備執行時)。當
RTX51 Tiny的定時滴答中斷(或其它中斷)產生時,微控制器恢復程式的執行。
空閒任務執行的代碼在CONF_TNY.A51配置檔案中允許和配置。
棧管理
RTX51 Tiny為每個任務在8051的內部RAM區(IDATA)維護一個棧。任務
運行時,將得到可能得到的最大數量的棧空間。任務切換時,先前的任務棧被
壓縮並重置,當前任務的棧被擴展和重置。
下圖表明一個三任務套用的內部存儲器的布局。
圖略…… :-)
?STACK表示棧的起始地址。該例中,位於棧下方的對象包括全局變數、暫存器和位定址存儲器,剩餘的存儲器用於任務棧。存儲器的頂部可在配置中指定
使用RTX51 Tiny
一般地,下面三步是使用RTX51 Tiny要實現的
l 編寫RTX51程式
l 編譯並連線程式
l 測試和調試程式
一、編寫程式
寫RTX51 Tiny程式時,必須用關鍵字對任務進行定義,並使用在RTX51TNY.H中聲明的RTX51 Tiny核心例程。
1、包含檔案
RTX51 Tiny僅需要包含一個檔案:RTX51TNY.H。所有的庫函式和常數都在該頭檔案中定義。你可以在你的源檔案中包含它:
#include<rtx51tny.h>
2、編程原則
以下是建立RTX51 Tiny程式時必須遵守的原則:
①、確保包含了RTX51TNY.H頭檔案。
②、不要建立main函式,RTX51 Tiny有自己的mian函式。
③、程式必須至少包含一個任務函式。
④、中斷必須有效(EA=1),在臨界區如果要禁止中斷時一定要小心。參見概述中的中斷一節。
⑤、程式必須至少調用一個RTX51 Tiny庫函式(象os_wait)。否則,連線起將不包含RTX51 Tiny庫。
⑥、Task 0是程式中首先要執行的函式,必須在任務0中調用os_create_task 函式以運行其餘任務。
⑦、任務函式必須是從不退出或返回的。任務必須用一個while(1)或類似的結構重複。用os_delete_task函式停止運行的任務。
⑧、必須在uvison中指定RTX51 Tiny,或者在連線器命令行中指定。更多技術文檔參見keil軟體知識庫。
3、定義任務
實時或多任務套用是由一個或多個執行具體操作的任務組成的,RTX51 Tiny支持最多16個任務。
任務就是一個簡單的C函式,返回類型為void,參數列表為void,並且用_task_聲明函式屬性。例如:
void func (void)_task_task_id
這裡,func是任務函式的名字,task_id是從0到15的一個任務ID號。
下面的例子定義函式job0編號為0的任務。該任務使一個計數器遞增並不斷重複。
void job0(void)_task_0
{
while(1)
{
Counter0++;
}
}
附註:
l 所有的任務都應該是無限循環,任務一定不能返回。
l 任務不能返回一個函式值,它們的返回類型必須是void。
l 不能對一個任務傳遞參數,任務的形參必須是void。
l 每個任務必須賦予一個唯一的,不重複的ID。
l 為了最小化RTX51 Tiny的存儲器需求,從0開始對任務進行順序編號。
二、編譯和連線
有兩種方法編譯和連線RTX51 Tiny應用程式。
l 用uvison集成開發環境
l 用命令行工具
1、命令行工具
RTX51 Tiny已經完全集成到了C51編譯語言中,這使得生成RTX51 Tiny應
用非常容易。建立RTX51 Tiny程式只需編寫C函式,無需使用彙編。
從命令行編譯RTX51 Tiny程式…
按常規方式調用編譯器,無需特別的編譯指示。例如:
C51 RTXPROG.C DEBUG OBJECTEXTEND
產生的RTXPROG.OBJ檔案中包含C代碼和定義的RTX51 Tiny任務。
從命令行連線RTX51 Tiny程式:
l 在連線器命令行內指定RTX51TNY指示
l 在目標檔案列表中包含RTX_CONF.OBJ檔案(如果改變了配置)
例如:BL51 RTPROG.OBJ, RTX_CONF.OBJ RTX51TNY
RTX51TNY指示命令連線器連線RTPROG.OBJ和TX_CONF.OBJ並且包含RTX51 Tiny庫。這樣就建立了RTX51 Tiny程式。
附註:
l 不要在RTX51 Tiny程式中建立mian函式,只建立任務函式就可以。main函式包含在RTX51 Tiny庫中,它啟動作業系統和任務0。如果在程式中包含了main函式,將產生一個連線錯誤指示有多個main被定義。
l 程式中至少建立一個任務函式。
l 必須至少調用一個RTX51 Tiny函式(象os_wait或os_create_task),這樣,連線器才會包含RTX51 Tiny庫。
2、uvison集成開發環境
用uvison建立RTX51 Tiny程式。
1) 打開目標對話框選項(從project選單選擇Options for Target)。
2) 選擇目標標籤。
3) 從作業系統選項列表選擇RTX51 Tiny。
三、調試
uvison模擬器允許運行和測試RTX51 Tiny應用程式。RTX51 Tiny程式的載入和無RTX51 Tiny程式的載入是一樣的。無需指定特別的命令和選項。
一個核心的對話框顯示RTX51 Tiny核心和程式中任務的所有特徵。從Peripherals選單選擇RTX51 Tiny Tasklist顯示該對話框。
該對話框中:
l TID是在任務定義中指定的任務ID。
l Task Name是任務函式的名字。
l State是任務當前的狀態。
l Wait for Event指出任務正在等待什麼事件。
l Sig顯示任務信號標誌的狀態(1為置位)。
l Timer指示任務距逾時的滴答數,這是一個自由運行的定時器,僅在任務等待逾時和時間間隔時使用。
l Stack指示任務棧的起始地址