目次

応用例A 夜間点滅器

 USOを、夜間点滅器システムに適用してみます。

 システム構成は、下図とします。



 仕様は、以下とします。
  光センサーの出力を入力して、暗いと判断できる
  場合、LEDを点滅します。

  明るいと判断できる場合、LEDを消灯します。

  点滅間隔は、1秒とします。

 システム構成図から、必要となるタスクを考えます。
  1. 光センサーの出力で、明暗を判定し、他のタスクを起動
  2. LEDを1秒ごとに、点灯、消灯
  3. LEDを消灯
 1から3までのタスクとなったので、タスク0からタスク2  として、動作を定義します。

タスク動作定義

 タスク2を定義します。  「LEDを消灯する。」が与えられている仕様なので  LEDを接続しているポートに、消灯の信号を送ります。  LED消灯は、ワンショット処理なので、システムコール  slp_tskを利用して、処理が終わったならばSUSPENDにします。 void tsk2_proc(void) { /* LED消灯 */ /* SUSPENDに状態遷移 */ }  LEDは、LED_PORTに接続されているとして  消灯信号を出力します。 void tsk2_proc(void) { LED_PORT = LED_OFF ; /* SUSPENDに状態遷移 */ }  処理終了で、SUSPEND状態にします。 void tsk2_proc(void) { LED_PORT = LED_OFF ; slp_tsk(); }  まとめます。 void tsk2_proc(void) { LED_PORT = LED_OFF ; slp_tsk(); }  タスク1を定義します。  「LEDを1秒ごとに、点灯、消灯。」が考えた  仕様なので、周期タスクを定義すれば充分です。  1秒の間隔は、システムコールwai_tskを利用して生成します。  タスクが実行されるごとに、LED_OFF、LED_ONの信号を  出力します。ON、OFF信号は、0か1なので、カウンタ  をインクリメントして、LSB(Least Significant Bit)の  値を出力します。 void tsk1_proc(void) { /* LED点滅 */ /* カウンタインクリメント */ /* 時間待ち指定 */ }  タスクの実行回数を計数するカウンタ変数をcountとし  インクリメント処理をコードにします。 void tsk1_proc(void) { /* LED点滅 */ count++; /* 時間待ち指定 */ }  LEDを点滅する処理をコードにします。 void tsk1_proc(void) { LED_PORT = count & 1 ; count++; /* 時間待ち指定 */ }  1秒間隔の周期起動処理をコードにします。 void tsk1_proc(void) { LED_PORT = count & 1 ; count++; wai_tsk( 100 ) ; }  タスク0を定義します。  「光センサーの出力で、明暗を判定し、他のタスクを起動。」が  考えた仕様なので、光センサーの出力を判断しなければなりません。  明暗の論理値が、出力されるとして判断します。  すでに、他のタスクを起動している場合に、再起動するのは  無駄なので、現在と一つ前の状態を比較して、異なっている  ならば動作するように定義します。  また、明暗の判断は、30秒に1度程度で充分なので  周期処理させます。  以上の内容を踏まえ、動作を擬似コーディングします。 void tsk0_proc(void) { /* センサーの状態入力 */ /* 2つの状態を比較し、異なればタスク起動 */ /* 現在の状態を保存 */ /* 時間待ち指定 */ }  現在と一つ前の状態を、cur_state、pre_stateに保存して  いるとします。 void tsk0_proc(void) { /* センサーの状態入力 */ if ( cur_state != pre_state ) { if ( cur_state == NIGHT ) { rsm_tsk(TSK_ID1); } if ( cur_state == DAY ) { sus_tsk(TSK_ID1); rsm_tsk(TSK_ID2); } } /* 現在の状態を保存 */ /* 時間待ち指定 */ }  現在の状態を、保存します。 void tsk0_proc(void) { /* センサーの状態入力 */ if ( cur_state != pre_state ) { if ( cur_state == NIGHT ) { rsm_tsk(TSK_ID1); } if ( cur_state == DAY ) { sus_tsk(TSK_ID1); rsm_tsk(TSK_ID2); } } pre_state = cur_state ; /* 時間待ち指定 */ }  センサー出力を取得します。 void tsk0_proc(void) { cur_state = SENSOR_OUT ; if ( cur_state != pre_state ) { if ( cur_state == NIGHT ) { rsm_tsk(TSK_ID1); } if ( cur_state == DAY ) { sus_tsk(TSK_ID1); rsm_tsk(TSK_ID2); } } pre_state = cur_state ; /* 時間待ち指定 */ }  30秒の時間間隔を設定します。 void tsk0_proc(void) { cur_state = SENSOR_OUT ; if ( cur_state != pre_state ) { if ( cur_state == NIGHT ) { rsm_tsk(TSK_ID1); } if ( cur_state == DAY ) { sus_tsk(TSK_ID1); rsm_tsk(TSK_ID2); } } pre_state = cur_state ; wai_tsk( 3000 ) ; }

変数、定数、フラグ定義

 タスクで利用しているフラグを、タスク関数から拾い出します。  タスク0で利用している変数、定数、フラグを拾います。  変数  cur_state pre_state  定数  DAY NIGHT  フラグ なし  タスク1で利用している変数、定数、フラグを拾います。  変数  count  定数  なし  フラグ なし  タスク2で利用している変数、定数、フラグを拾います。  変数  なし  定数  LED_OFF  フラグ なし  変数を定義します。  typedef unsigned char UBYTE ; UBYTE cur_state; UBYTE pre_state; UBYTE count;  定数を定義します。  #define LED_OFF 1  #define NIGHT 1  #define DAY 0

コンフィグレーション

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

タイマー割込み定義

 USOのためにタイマーを1個利用します。  マイクロコンピュータとして、ATTiny2313を利用する  と仮定し、システムクロックを12MHzを分周して100Hz  (=10ms)を生成します。  タイマーのプリスケーラを利用して、64分周します。   12MHz/64=187500  100Hzを生成するためには、1875分周すれば充分です。   187500/100=1875  タイマー1のコンペアマッチ割込みを利用するため、2つ  のレジスタに数値を設定します。   OCR1A = 1874 ;   OCR1B = 2000 ;  タイマー1の初期化は、モード指定、カウンタとレジスタ設定  タイマースタートの3処理にまとめます。 /* select clock source */ TCCR1B = (1 << WGM12) | (1 << CS11) | (1 << CS10); /* clear timer/counter */ TCNT1 = 0 ; /* set compare values */ OCR1A = 1874 ; OCR1B = 2000 ; /* Enable Compare match interruption */ TIMSK = (1 << OCIE1A) ;  このコードは、関数user_initializeに含めます。  割込みが発生した場合は、WAITであるタスクを探して  カウンタを減算します。  カウンタが0になれば、WAITからREADYに変更します。 SIGNAL(SIG_TIMER1_COMPA) { UWORD xtmp; UBYTE loop; /* clear counter */ TCNT1 = 0 ; /* 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 ; } }

全ソースコード

 実際にクロスコンパイラにかけ、テストしたソース  コードは、以下です。  LEDの点灯、消灯は、コンパイラの仕様にあわせて  変更してあります。 #include <avr/io.h> #include <avr/interrupt.h> #define TSK_ID_MAX 3 #define TSK_ID0 0 #define TSK_ID1 1 #define TSK_ID2 2 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 TTS_SUSPEND 0 #define TTS_WAIT TTS_SUSPEND+1 #define TTS_READY TTS_SUSPEND+2 #define NO 0 #define YES 1 #define OFF 0 #define ON 1 #define DAY 0 #define NIGHT 1 #define SENSOR_OUT (PIND & 1) volatile UBYTE ready ; volatile UBYTE suspend; volatile UBYTE waitq ; volatile UBYTE run_tsk; TCBP tcb[TSK_ID_MAX]; volatile UBYTE pre_state ; volatile UBYTE cur_state ; volatile UBYTE count ; /*------------------------*/ /* task function protoype */ /*------------------------*/ void tsk0_proc(void); void tsk1_proc(void); void tsk2_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); /*------*/ /* 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); sta_tsk(TSK_ID0,TTS_READY); sta_tsk(TSK_ID1,TTS_SUSPEND); sta_tsk(TSK_ID2,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 ; } /*----------------*/ /* task functions */ /*----------------*/ /* judge */ void tsk0_proc(void) { cur_state = SENSOR_OUT ; if ( cur_state != pre_state ) { if ( cur_state == NIGHT ) { rsm_tsk(TSK_ID1); } if ( cur_state == DAY ) { sus_tsk(TSK_ID1); rsm_tsk(TSK_ID2); } } pre_state = cur_state ; wai_tsk( 3000 ) ; } #define LED_BIT 0x40 /* impress LED state */ void tsk1_proc(void) { if ( count & 1 ) { PORTD |= LED_BIT ; } else { PORTD &= ~LED_BIT ; } count++; wai_tsk( 100 ) ; } /* turn off LED */ void tsk2_proc(void) { PORTD &= ~LED_BIT ; slp_tsk(); } /*------------------*/ /* system call body */ /*------------------*/ void init_os(void) { ready = 0 ; suspend = 0 ; 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) & ON ) ; } /*-----------------------*/ /* Insert user functions */ /*-----------------------*/ void user_initialize(void) { /* set I/O initial values */ PORTB = 0b00000010 ; /* oooooooo */ PORTD = 0b11111111 ; /* oooiiiii */ /* set I/O directions */ DDRB = 0b11111111 ; /* oooooooo */ DDRD = 0b11110000 ; /* oooiiiii */ /* set flags */ pre_state = DAY ; cur_state = DAY ; /* initialize timer1 */ { /* select clock source */ TCCR1B = (1 << WGM12) | (1 << CS11) | (1 << CS11); /* clear timer/counter */ TCNT1 = 0 ; /* set compare values */ OCR1A = 1874 ; OCR1B = 2000 ; /* Enable Compare match interruption */ TIMSK = (1 << OCIE1A) ; } } /* timer1 interrupt */ ISR(TIMER1_COMPA_vect) { UBYTE xtmp; UBYTE loop; /* clear counter */ TCNT1 = 0 ; /* 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 ; } }
 PICではスタックがないチップもあるので、USOを導入し難いですが  ディスパッチャとスケジューラをswitch文で実現するという条件を  加えれば可能です。(以下は、mikroCを利用した場合) #define LLED0 PORTA.F0 #define MFOUT PORTA.F2 #define CDS PORTA.F5 #define OFF 0 #define ON OFF+1 #define MASKFF 0xff #define MASK0F 0x0F #define MASK0E 0x0e #define CNTBEGIN 6 #define CNTMAX 10 #define TSK_ID_MAX 4 #define TSK_ID0 0 #define TSK_ID1 1 #define TSK_ID2 2 #define TSK_ID3 3 #define TTS_SUSPEND 0 #define TTS_WAIT TTS_SUSPEND+1 #define TTS_READY TTS_SUSPEND+2 typedef unsigned char UBYTE ; volatile UBYTE wflag ; volatile UBYTE ready ; volatile UBYTE suspend; volatile UBYTE waitq ; volatile UBYTE run_tsk; volatile UBYTE bpat[4] ; volatile UBYTE wcount[4] ; volatile UBYTE xcnt ; volatile UBYTE timcnt ; volatile UBYTE state ; #define DAY 0 #define NIGHT 1 /*------------------------*/ /* 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 sta_tsk(UBYTE tid,UBYTE sta); void rsm_tsk(UBYTE tid); void sus_tsk(UBYTE tid); void slp_tsk(void); void wai_tsk(UBYTE x); UBYTE is_tsk_ready(UBYTE tid); void timer_handler(void); /* interrupt handler */ void interrupt(void) { /* generate 100Hz */ if ( INTCON.T0IF == ON ) { /* clear flag */ INTCON.T0IF = OFF ; /* initialize */ TMR0 = CNTBEGIN ; /* increment */ timcnt++ ; /* judge */ if ( (timcnt & 15) == CNTMAX ) { /* set flag */ wflag = ON ; } } } /* function prototype */ void init_usr(void); void main(void) { /* initialize */ init_usr() ; /* initialize task */ init_os(); /* endless loop */ run_tsk = TSK_ID0 ; while ( ON ) { /* 10ms counter */ if ( wflag == ON ) { wflag = OFF ; timer_handler(); } /* round robbin */ if ( is_tsk_ready( run_tsk ) ) { switch ( run_tsk ) { case TSK_ID0 : tsk0_proc(); break ; case TSK_ID1 : tsk1_proc(); break ; case TSK_ID2 : tsk2_proc(); break ; case TSK_ID3 : tsk3_proc(); break ; default : break ; } } run_tsk++; run_tsk &= 3 ; } } /* define function body */ void init_usr(void) { /* select 4MHz */ OSCCON = (0x0d << 3) | 0x03 ; /* disable A/D converter */ ADCON0.ADON = OFF ; ADCON2 = 0 ; /* disable D/A converter */ DACCON0.DACEN = OFF ; /* disable compare module */ CM1CON0.C1ON = OFF ; CM1CON0.C1OE = OFF ; /* I/O directions */ TRISA = 0x38 ; /* bit0,1,2 as output , others as input */ /* pull-up */ WPUA = 0x30 ; /* initialize Timer 0 */ { /* 4MHz/4 = 1MHz -> 1MHz/4 = 250kHz prescaler = 1:4 */ OPTION_REG = 0x01 ; /* 256 - 250 = 6 */ TMR0 = CNTBEGIN ; /* enable timer0 overflow interrupt */ INTCON.TMR0IE = ON ; } /* enable general interrupt */ INTCON.GIE = ON ; /* initialize variables */ state = NIGHT ; timcnt = 0 ; xcnt = 0 ; /* clear flags */ wflag = OFF ; } /*----------------*/ /* task functions */ /*----------------*/ /* system control */ void tsk0_proc(void) { /* judge */ if ( CDS == OFF ) { state = DAY ; sus_tsk( TSK_ID1 ); rsm_tsk( TSK_ID2 ); } else { if ( state == DAY ) { state = NIGHT ; rsm_tsk( TSK_ID1 ); } } /* 2000ms */ wai_tsk( 200 ) ; } /* LED flashing */ void tsk1_proc(void) { /* impress */ LLED0 = OFF ; if ( xcnt & ON ) { LLED0 = ON ; } /* increment */ xcnt++ ; /* 1000ms */ wai_tsk( 100 ) ; } /* turn off LED */ void tsk2_proc(void) { /* impress */ LLED0 = OFF ; /* exit */ slp_tsk(); } /* get sensor state */ void tsk3_proc(void) { /* monitor */ MFOUT = OFF ; if ( timcnt & ON ) { MFOUT = ON ; } /* 1000ms */ wai_tsk( 100 ) ; } /*------------------*/ /* system call body */ /*------------------*/ void init_os(void) { /* clear RTOS values */ ready = 0 ; suspend = 0 ; waitq = 0 ; /* bit pattern */ *(bpat+0) = 0x01 ; *(bpat+1) = 0x02 ; *(bpat+2) = 0x04 ; *(bpat+3) = 0x08 ; /* TASK create */ cre_tsk(TSK_ID0); cre_tsk(TSK_ID1); cre_tsk(TSK_ID2); cre_tsk(TSK_ID3); /* initialize task state */ sta_tsk(TSK_ID0,TTS_READY); sta_tsk(TSK_ID1,TTS_SUSPEND); sta_tsk(TSK_ID2,TTS_SUSPEND); sta_tsk(TSK_ID3,TTS_READY); } void cre_tsk(UBYTE tid) { *(wcount+tid) = 0 ; } void sta_tsk(UBYTE tid,UBYTE sta) { UBYTE tmp ; /* get bit pattern */ tmp = *(bpat+tid); /* store */ 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 ; UBYTE tmpx ; /* get bit pattern */ tmp = *(bpat+tid); tmpx = tmp ^ MASKFF ; /* bit handling */ ready |= tmp ; suspend &= tmpx; waitq &= tmpx; } void sus_tsk(UBYTE tid) { UBYTE tmp ; UBYTE tmpx ; /* get bit pattern */ tmp = *(bpat+tid); tmpx = tmp ^ MASKFF ; /* bit handling */ ready &= tmpx; suspend |= tmp ; waitq &= tmpx; } void slp_tsk(void) { sus_tsk(run_tsk); } void wai_tsk(UBYTE x) { UBYTE tmp ; UBYTE tmpx ; /* get bit pattern */ tmp = *(bpat+run_tsk); tmpx = tmp ^ MASKFF ; /* bit handling */ ready &= tmpx; suspend &= tmpx; waitq |= tmp ; /* update counter */ *(wcount+run_tsk) = x ; } UBYTE is_tsk_ready(UBYTE tid) { return( ready & *(bpat+tid) ) ; } void timer_handler(void) { UBYTE loop; UBYTE wcnt ; for ( loop = 0 ; loop < TSK_ID_MAX ; loop++ ) { if ( waitq & *(bpat+loop) ) { wcnt = *(wcount+loop); wcnt--; *(wcount+loop) = wcnt ; if ( wcnt == 0 ) { rsm_tsk(loop); } } } }  PICでは、アセンブリ言語レベルでシフト命令がなく  ローテート命令で代用します。  利用タスク数を最大8に限定しておき、シフト命令を  使わないで、該当タスクのREADY、SUSPEND、WAITを  論理演算で判断します。
目次

inserted by FC2 system