目次

応用例C 定時撮影

 3箇所に、撮影用カメラを置き、撮影指示を与えれば
 撮影が完了するシステムがあるとします。

 このシステムで、撮影用カメラをA、B、Cとして、20秒ごと
 30秒ごと、50秒ごとに撮影指示を与える処理を、USOで実現
 してみます。




 開始と終了のトリガースイッチを入れて
 システムを構成しています。

 システム構成図から、必要となるタスクを考えます。

 4タスクとなったので、タスク0からタスク3とします。


マイクロコンピュータ選定

 電池駆動ができるマイクロコンピュータを想定して、電源電圧が  広い製品がよいので、ATMELのATmega168を利用します。  トリガースイッチを利用するので、マイクロコンピュータには  外部割込みで対応することが望ましいです。ATMELのAVRには  最低ひとつの外部割込みがあるので、それを利用します。  外部割込みは、falling edgeを利用します。  外部割込みが入る度に、内部状態を変更します。

タスク分割

 4タスクに分割したので、タスク0からタスク3にします。  各タスクの動作概要を考えます。  タスク0   内部状態を示す変数performを用意します。   変数performを外部割込みにより、IDLEとRUNを   交互に設定します。   perform=IDLEでは、タスク1〜3をSUSPENDします。   perform=RUNでは、タスク1〜3をREADYにします。  タスク1   外部装置に、トリガーを与えます。   外部装置はカメラAとして、20秒ごとに   トリガーを与えます。  タスク2   外部装置であるカメラBに、トリガーを与えます。   30秒ごとにトリガーを与えます。  タスク3   外部装置であるカメラCに、トリガーを与えます。   50秒ごとにトリガーを与えます。

タスク初期状態設定

 タスク概要から、タスク0はREADY、他のタスクは  SUSPENDにしておけばよいでしょう。  まとめると、以下です。  これらが確定していると、USOのシステムコールで初期  状態を設定します。 sta_tsk(TSK_ID0,TTS_READY); sta_tsk(TSK_ID1,TTS_SUSPEND); sta_tsk(TSK_ID2,TTS_SUSPEND); sta_tsk(TSK_ID3,TTS_SUSPEND);

タスク動作定義

 各タスクの動作を定義します。  タスク0  「トリガー判定による、他タスク起動。」が与えられた  仕様のひとつなので、これを定義します。  シーケンスを、言葉で記述します。 外部割込みのトリガーを判定   トリガーにより、内部状態を変更   内部状態により、他のタスクの状態変更  Cのコードでは、以下のようにします。 if ( eflag == ON ) { eflag = OFF ; perform++ ; perform &= 1 ; if ( perform == RUN ) { rsm_tsk(TSK_ID1); rsm_tsk(TSK_ID2); rsm_tsk(TSK_ID3); } else { sus_tsk(TSK_ID1); sus_tsk(TSK_ID2); sus_tsk(TSK_ID3); } }  外部割込みでスイッチの状態を取得して  トリガー判定するので、周期処理による  タスク処理にします。  10秒ごとの周期タスクにします。 void tsk0_proc(void) { if ( eflag == ON ) { eflag = OFF ; perform++ ; perform &= 1 ; if ( perform == RUN ) { rsm_tsk(TSK_ID1); rsm_tsk(TSK_ID2); rsm_tsk(TSK_ID3); } else { sus_tsk(TSK_ID1); sus_tsk(TSK_ID2); sus_tsk(TSK_ID3); } } /* cycle 10sec = 10 ms * 1000 */ wai_tsk( 1000 ); }  タスク1  「カメラAへのトリガー出力」が与えられた仕様  なので、これを定義します。  マイコンのピンPB0から、トリガーを出力します。 tmp = PORTB ; tmp |= 0x01 ; PORTB = tmp ; tmp &= ~0x01 ; PORTB = tmp ;  周期を20秒とするので、システムコールを使います。 wai_tsk( 2000 );  まとめます。 void tsk1_proc(void) { /* send trigger */ tmp = PORTB ; tmp |= 0x01 ; PORTB = tmp ; tmp &= ~0x01 ; PORTB = tmp ; /* cycle 20sec */ wai_tsk( 2000 ); }  タスク2  「カメラBへのトリガー出力」が与えられた仕様  なので、これを定義します。  トリガー出力をPB1からとします。 tmp = PORTB ; tmp |= 0x02 ; PORTB = tmp ; tmp &= ~0x02 ; PORTB = tmp ;  周期を30秒とします。 void tsk2_proc(void) { /* send trigger */ tmp = PORTB ; tmp |= 0x02 ; PORTB = tmp ; tmp &= ~0x02 ; PORTB = tmp ; /* cycle 30sec */ wai_tsk( 3000 ); }  タスク3  タスク1、2と似た処理なので、トリガー出力と  周期処理を仕様としてまとめます。 void tsk3_proc(void) { /* send trigger */ tmp = PORTB ; tmp |= 0x04 ; PORTB = tmp ; tmp &= ~0x04 ; PORTB = tmp ; /* cycle 50sec */ wai_tsk( 5000 ); }

コンフィグレーション

 各タスクの内容を定義したので、コンフィグレータを利用  して、スケルトンのソースコードを生成します。  スケルトンソースコードの中にある、次の関数にタスク処理  を定義します。  main関数で、各タスクの初期状態を指定します。 cre_tsk(TSK_ID0,tsk0_proc); cre_tsk(TSK_ID1,tsk1_proc); cre_tsk(TSK_ID2,tsk2_proc); cre_tsk(TSK_ID3,tsk3_proc); sta_tsk(TSK_ID0,TTS_READY); sta_tsk(TSK_ID1,TTS_SUSPEND); sta_tsk(TSK_ID2,TTS_SUSPEND); sta_tsk(TSK_ID3,TTS_SUSPEND);  コンフィグレーションした場合、デフォルトで  すべてのタスクをREADYとします。  クロスコンパイラのために、定義ファイルをインクルードして  必要な初期化をします。  main関数の中で、無限ループに入る前に、必要な初期化を  記述すると、次のようになります。(コンパイラにより、  割込み禁止、割込み許可の関数は異なります。) /* initialize system */ user_initialize(); /* initialize monitor */ init_os(); cre_tsk(TSK_ID0,tsk0_proc); cre_tsk(TSK_ID1,tsk1_proc); cre_tsk(TSK_ID2,tsk2_proc); cre_tsk(TSK_ID3,tsk3_proc); sta_tsk(TSK_ID0,TTS_READY); sta_tsk(TSK_ID1,TTS_SUSPEND); sta_tsk(TSK_ID2,TTS_SUSPEND); sta_tsk(TSK_ID3,TTS_SUSPEND); /* enable interrupt */ ei();  関数user_initializeの中に、ポートの入出力関連の初期化  内蔵モジュール初期化、変数の定数設定を含めます。

外部割込み定義

 外部割込みを使うためには、入力ピンとモードを  設定しなければなりません。  入力ピンは、INT0(PD2)を使います。  スイッチ変化は、falling edgeでとらえます。  外部割込み発生は、フラグeflagで通知します。  ATmega168では、初期設定を以下とします。 /* set INT1 interrupt */ EICRA = (1 << ISC01) ; /* enable interrupt (global) */ EIMSK = (1 << INT0) ;  フラグは、割込みハンドラで設定します。 ISR(INT0_vect) { eflag = ON ; }

タイマー割込み定義

 USOのためにタイマーを1個利用します。  マイクロコンピュータとして、ATmega168を利用する  と仮定し、システムクロックを16MHzを分周して100Hz  (=10ms)を生成します。  タイマーのプリスケーラを利用して、8分周します。   16MHz/8=2000000  100Hzを生成するためには、20000分周すれば充分です。  タイマー1のコンペアマッチ割込みを利用するため、2つ  のレジスタに数値を設定します。   OCR1A = 19999 ;   OCR1B = 40000 ;  タイマー1の初期化は、モード指定、カウンタとレジスタ設定  タイマースタートの3処理にまとめます。 /* compare match (1/8) -> 2MHz */ TCCR1B = (1 << WGM12) | (1 << CS11) ; /* clear counter */ TCNT1 = 0 ; /* set initial values */ OCR1A = 1999 ; OCR1B = 4600 ; /* Enable Compare match interruption */ EIMSK = (1 << OCIE1A) ;  このコードは、関数user_initializeに含めます。  割込みが発生した場合は、WAITであるタスクを探して  カウンタを減算します。  カウンタが0になれば、WAITからREADYに変更します。 ISR(TIMER1_COMPA_vect) { UWORD xtmp; UBYTE loop; /* call timer handling */ xtmp = waitq ; for ( loop = 0 ; loop < TSK_ID_MAX ; loop++ ) { if ( xtmp & 1 ) { tcb[loop].wcount-- ; if ( tcb[loop].wcount == 0 ) { rsm_tsk(loop); } } xtmp >>= 1 ; } }

全ソースコード

 実際にクロスコンパイラにかけ、テストしたソース  コードは、以下です。 #include <avr/io.h> #include <avr/interrupt.h> #define OFF 0 #define ON OFF+1 #define NO OFF #define YES OFF+1 typedef unsigned char UBYTE ; typedef unsigned short UWORD ; typedef signed char SBYTE ; typedef signed short SWORD ; typedef struct { void (*tsk)(void); UWORD wcount ; } TCBP ; #define TSK_ID_MAX 4 #define TSK_ID0 0 #define TSK_ID1 1 #define TSK_ID2 2 #define TSK_ID3 3 TCBP tcb[TSK_ID_MAX]; #define TTS_SUSPEND 0 #define TTS_WAIT TTS_SUSPEND+1 #define TTS_READY TTS_SUSPEND+2 volatile UBYTE ready ; volatile UBYTE suspend; volatile UBYTE waitq ; volatile UBYTE run_tsk; /*------------------------*/ /* task function protoype */ /*------------------------*/ void tsk0_proc(void); void tsk1_proc(void); void tsk2_proc(void); void tsk3_proc(void); /*-----------------------*/ /* system call prototype */ /*-----------------------*/ void init_os(void); void cre_tsk(UBYTE tid,void (*tsk)(void)); void sta_tsk(UBYTE tid,UBYTE sta); void rsm_tsk(UBYTE tid); void sus_tsk(UBYTE tid); void slp_tsk(void); void wai_tsk(UWORD x); UBYTE is_tsk_ready(UBYTE tid); void timer_handler(void); /*--------------------------------*/ /* Insert user functions protoype */ /*--------------------------------*/ void user_initialize(void); volatile UBYTE perform ; volatile UBYTE eflag ; #define IDLE 0 #define RUN IDLE+1 #define CAM_A 0 #define CAM_B 1 #define CAM_C 2 #define TIM_0 1000 #define TIM_A 2000 #define TIM_B 3000 #define TIM_C 5000 /*------*/ /* main */ /*------*/ int main(void) { TCBP pcur_tsk ; cli() ; /* initialize monitor */ init_os(); cre_tsk(TSK_ID0,tsk0_proc); cre_tsk(TSK_ID1,tsk1_proc); cre_tsk(TSK_ID2,tsk2_proc); cre_tsk(TSK_ID3,tsk3_proc); sta_tsk(TSK_ID0,TTS_READY); sta_tsk(TSK_ID1,TTS_SUSPEND); sta_tsk(TSK_ID2,TTS_SUSPEND); sta_tsk(TSK_ID3,TTS_SUSPEND); user_initialize(); sei() ; /* loop */ run_tsk = TSK_ID0 ; while ( ON ) { pcur_tsk = tcb[run_tsk] ; if ( is_tsk_ready( run_tsk ) == YES ) { (*(pcur_tsk.tsk))(); } run_tsk++; if ( run_tsk == TSK_ID_MAX ) { run_tsk = TSK_ID0 ; } } /* dummy */ return 0 ; } /*-----------------------*/ /* Insert user functions */ /*-----------------------*/ void user_initialize(void) { /* PORT C */ PORTC = 0b00000000 ; /* 00000000 */ DDRC = 0b11111111 ; /* oooooooo */ /* PORT B */ PORTB = 0b00000000 ; /* 00000000 */ DDRB = 0b11111111 ; /* oooooooo */ /* PORT D */ PORTD = 0b00000100 ; /* 00000100 */ DDRD = 0b11111011 ; /* oooooioo */ /* initialize timer/counter1 */ { /* compare match (1/8) -> 2MHz */ TCCR1B = (1 << WGM12) | (1 << CS11) ; /* clear counter */ TCNT1 = 0 ; /* set initial values */ OCR1A = 1999 ; OCR1B = 4600 ; } /* set TIMER1 interrupt */ TIMSK1 = (1 << OCIE1A) ; /* set INT1 interrupt */ EICRA = (1 << ISC01) ; /* enable interrupt (global) */ EIMSK = (1 << INT0) ; /* clear flag */ eflag = OFF ; /* set initial state */ perform = IDLE ; } /* EXTERNAL INT0 */ ISR(INT0_vect) { eflag = ON ; } /* timer1 interrupt */ ISR(TIMER1_COMPA_vect) { UBYTE xtmp; UBYTE loop; /* call timer handling */ xtmp = waitq ; for ( loop = 0 ; loop < TSK_ID_MAX ; loop++ ) { if ( xtmp & 1 ) { tcb[loop].wcount-- ; if ( tcb[loop].wcount == 0 ) { rsm_tsk(loop); } } xtmp >>= 1 ; } } /*----------------*/ /* task functions */ /*----------------*/ void tsk0_proc(void) { if ( eflag == ON ) { /* clear flag */ eflag = OFF ; /* update state */ perform++ ; perform &= 1 ; /* judge */ if ( perform == RUN ) { rsm_tsk(TSK_ID1); rsm_tsk(TSK_ID2); rsm_tsk(TSK_ID3); } else { sus_tsk(TSK_ID1); sus_tsk(TSK_ID2); sus_tsk(TSK_ID3); } } /* cycle 10sec = 10 ms * 1000 */ wai_tsk( TIM_0 ); } /* camera #A handling */ void tsk1_proc(void) { UBYTE tmph ; UBYTE tmpl ; /* send trigger */ tmph = PORTB ; tmpl = tmpl ; tmph |= (1 << CAM_A) ; PORTB = tmph ; tmpl &= ~(1 << CAM_A) ; PORTB = tmpl ; /* cycle 20sec = 10 ms * 2000 */ wai_tsk( TIM_A ); } /* camera #B handling */ void tsk2_proc(void) { UBYTE tmph ; UBYTE tmpl ; /* send trigger */ tmph = PORTB ; tmpl = tmpl ; tmph |= (1 << CAM_B) ; PORTB = tmph ; tmpl &= ~(1 << CAM_B) ; PORTB = tmpl ; /* cycle 30sec = 10 ms * 3000 */ wai_tsk( TIM_B ); } /* camera #C handling */ void tsk3_proc(void) { UBYTE tmph ; UBYTE tmpl ; /* send trigger */ tmph = PORTB ; tmpl = tmpl ; tmph |= (1 << CAM_C) ; PORTB = tmph ; tmpl &= ~(1 << CAM_C) ; PORTB = tmpl ; /* cycle 50sec = 10 ms * 5000 */ wai_tsk( TIM_C ); } /*------------------*/ /* system call body */ /*------------------*/ void init_os(void) { ready = suspend = waitq = 0 ; } void cre_tsk(UBYTE tid,void (*tsk)(void)) { tcb[tid].tsk = tsk; tcb[tid].wcount = 0; } void sta_tsk(UBYTE tid,UBYTE sta) { UBYTE tmp ; tmp = (1 << tid); if ( sta == TTS_READY ) { ready |= tmp; } if ( sta == TTS_SUSPEND ) { suspend |= tmp; } if ( sta == TTS_WAIT ) { waitq |= tmp; } } void rsm_tsk(UBYTE tid) { UBYTE tmp ; tmp = (1 << tid); ready |= tmp; suspend &= ~tmp; waitq &= ~tmp; } void sus_tsk(UBYTE tid) { UBYTE tmp ; tmp = (1 << tid); ready &= ~tmp; suspend |= tmp; waitq &= ~tmp; } void slp_tsk(void) { sus_tsk(run_tsk); } void wai_tsk(UWORD x) { UBYTE tmp ; tmp = (1 << run_tsk); ready &= ~tmp; suspend &= ~tmp; waitq |= tmp; tcb[run_tsk].wcount = x ; } UBYTE is_tsk_ready(UBYTE tid) { return( (ready >> tid) & 1 ) ; }
目次

inserted by FC2 system