組み込み界で定番のマルチタスクOSを移植!!
dsPICマイコン用μITRONの制作(補足記事)
森 久直
Hisanao Mori
dsPICにリアルタイムOSを実装することができたのは,スタック・ポインタが使える ようになったことが大きな要因です.これまでのPICシリーズでは,循環バッファと して動作するハードウェアにリターン・アドレスを格納していました.例えば,8レベ ルのハードウェア・スタックでは,9回目のプッシュによって,1回目にプッシュした 値が上書きされてしまうのです.
それがdsPICでは,スタック・ポインタのおかげで,タスク毎のスタック・エリアをRAM 上に確保できるようになりました.スタック・ポインタは,リアルタイムOSを実装する ためには必要なものです.
製作例に実装したリアルタイムOSは,表1に示したシステム・コールをサポートしています. 2[KB]というRAMの容量を考慮した上で,マルチタスクが実現できるように必要最低 限のものを揃えました.
タスクの切替制御,タスク間の同期,タスク間の条件付き同期,ハードウェア資源 の排他制御,時間管理,等の機能が使用できます.
システム・コールの詳細は,「μITRON3.0標準ハンドブック」をご覧下さい.書籍が 販売されていますが,(社)トロン協会のホームページからもPDF版(日本語版)を 入手することができます.
(社)トロン協会 -> http://www.tron.org/ TOPページのTRON関連資料というリンクをたどっていくとダウンロードできます.
システム・ コール名 |
機能概要/引数 | ||||||||
---|---|---|---|---|---|---|---|---|---|
slp_tsk | 自タスクを待ち状態にする ex)slp_tsk(); |
||||||||
wup_tsk | 指定したタスクの待ち状態を解除する ex)wup_tsk(task2); 引数=task1,task2,task3,... |
||||||||
wai_flg | 待ち解除条件のビット・パターンを設定して待ち状態になる ex)wai_flg(&c,flgid1,0x07,0x01);
| ||||||||
set_flg | 指定したビットをセットする ex)set_flg(flgid1,0x04); 第1引数=flgid1,flgid2(この2つのみです) 第2引数=イベント・フラグ・ビット・パターン |
||||||||
wai_sem | ハードウェア資源の獲得 ex)wai_sem(semid1); 引数=semid1,semid2(この2つのみです) |
||||||||
sig_sem | ハードウェア資源の返却 ex)sig_sem(semid1); 引数=semid1,semid2(この2つのみです) |
||||||||
dly_tsk | 指定した時間だけ待ち状態にする ex)dly_tsk(1000); 引数=16bit長の数値,時間単位はms |
||||||||
get_tim | システム・クロックを取得する ex)get_tim( &gettime ); 引数=timedef.hで定義される構造体型変数のアドレス |
||||||||
set_tim | システム・クロックを設定する ex)set_tim( &testtime ); 引数=timedef.hで定義される構造体型変数のアドレス |
||||||||
dis_dsp | タスク切替を禁止する ex)dis_dsp(); |
||||||||
ena_dsp | タスク切替を許可する ex)ena_dsp(); |
||||||||
loc_cpu | 割込みとタスク切替を禁止する ex)loc_cpu(); |
||||||||
unl_cpu | 割込みとタスク切替を許可する ex)unl_cpu(); |
登録できるタスクは7つまでです.はじめからRAM上にタスクのスタック領域を確保する仕組みにしたことと,RAMの容量による制約です.ただし,スタック領域を動的に確保するように,ソース・コードを変更することにより,登録できるタスク数を増やすことができます.
使用できるイベント・フラグは2つで,8ビット長です.初期のビット・パターンは0にクリアされています.AND待ち,OR待ち,待ち解除時のビット・パターンのクリアの有無,等を設定できます.
なお,待ち行列を生成しないので,同じイベント・フラグを同時に使用することはできません.
使用できるセマフォは2つまでです.計数型セマフォと呼ばれるタイプで,0〜255の範囲をカウントできます.初期のカウントの値は1になっています.ソース・コードrtosconf.hの変更により,初期値の変更ができます.
なお,待ち行列を,8タスク分まで生成できます.
Timer1はリアルタイムOSの時間管理機能に使用しています.したがって,このモジュ ールをアプリケーションの中で使用することはできません.
しかし,残りのUSART,Timer2,Timer3,A/D等の割込みは全て使用できます.
実装したリアルタイムOSの構造は,図1に示すようにOSの起動やシステム・コールなど,主に6つのプログラムで構成されています.これらは,C言語でプログラムを作成しました.
電源がONになってから,スタートアップルーチンを経過して,最初に走るのが「リアルタイムOS起動」です.ここでは主に,スタック・ポインタの設定,タスクの登録,タスク毎のスタック領域の初期化,システム・クロックをカウントするタイマの初期設定と起動,起/動タスクへの分岐などを行います.
一度,起動タスクに分岐した後は,主にタスクや割込みハンドラが走ります.その時のリアルタイムOSは,システム・クロックをタイマ割込みによってカウントしているか,システム・コールの要求に対応した処理を行います.スケジューリングの結果,必要ならばタスク切換えを実行します.
![]() |
図1 リアルタイムOSの構造 |
製作例に実装したリアルタイムOSのタスクには,図2に示すように「実行可能状態(READY)」,「実行状態(RUN)」,「待ち状態(WAIT)」の3つの状態があります.
![]() |
図2 最低限必要なタスクの状態 |
そして,READY状態のタスクはレディ・キューと呼ばれる行列につながれ,優先度ベースのスケジューリングにより管理されます.このレディ・キューは,タスク・コントロール・ブロックTCBの行列とも言えます.これを表したのが図3です.
![]() |
図3 レディ・キューをTCBの行列で形成する |
TCBとは,タスクを管理するために必要な情報をまとめたものです.その情報には,タスクを識別するID,タスクの優先度,タスクの状態などがあります.
レディ・キューは,HEADという変数を開始点にして,TCBの中のNEXTが次TCBの先頭アドレスを指すという構造をしています.HEADはRUN状態のTCBを指します.こうして,複数のTCBは一つの行列を形成します.後は,TCB間の接続の仕方を変えることにより,レディ・キューへの接続と切離しを行います.
レディ・キューに接続するときは,TCBの中の優先度PRIとNEXTを先頭から順番に参照していきます.そして,接続しようとするTCBのPRIが,参照しているTCBのPRIより小さくなったとき,その参照しているTCBの前に接続します.
レディ・キューから切離すときは,切離すTCBを探し出して,そのTCBのNEXTをNULL(値0)にします.そして,切離すTCBの前後にあるTCB同士を接続します.
このように,レディ・キューへのタスクの接続と,レディ・キューからのタスクの切離しによって,スケジューリングが行われます.
ステータス・レジスタSRと,ワーキング・レジスタWの退避と復帰の処理をしなければならないので,インライン・アセンブラを用いました.また,フレーム・ポインタW14とスタック・ポインタW15の退避場所については,プログラムにおける退避と復帰の処理を容易にするためにTCBの中に確保しました.W14とW15の退避先となるアドレスが固定ですので,操作し易くなります.
基本的な処理の流れは,次のようになります.
タスクの切替え処理を終えた後には,リターン命令が実行され,次に実行するタスクに移ります.
タスクごとのスタック・エリア,タスク・コントロール・ブロック(TCB),セマフォ,フラグのRAM上の配置は図4のようになります.
dsPIC30F3013のRAMのうち,ユーザーが使用できるのは,0x0800〜0x0FFF番地です.この中で,タスク毎のスタック・エリアのメモリ容量を200[byte]にし,タスク毎のTCBのメモリ容量を32[byte]にすることで,タスクを7つ使用できるようにしました.
セマフォは6[byte],イベント・フラグは8[byte]のメモリ容量で,それぞれ2つずつ確保しています.
最終的には,リアルタイムOSとタスクのコード・サイズは約8[KB]になりました.ROMは24[KB]ですので,まだ余裕があります.
![]() |
図4 スタック・エリアやTCBをすき間なく配置したときのRAMの状態 |
図をクリックすると拡大表示します |