目次

応用例D ファンコントローラ

 天井付近に熱気が停滞するので、デスクトップタイプ
 のPersonalComputerの空冷ファン利用で、対流を促す
 ことにしました。

 仕様は、以下。

 仕様から、タスクを考えます。



 回転と停止のトリガースイッチを加えて
 システムを構成。

 図から、必要となるタスクを考えます。

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


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

 電池駆動できるマイクロコンピュータを想定し、電源電圧が  広い製品がよいので、ATMELのATtiny2313を利用。  トリガースイッチを利用するので、タイマー割込み  を使い、チャタリングを除去します。  トリガースイッチで、内部の状態をIDLE、RUNと切り替えます。  外部割込みを使う方法もありますが、周期タスクの中で  シフトレジスタを使えば、チャタリング除去は可能とし  ひとつのタスクで、チャタリング除去とシステムの状態  変更させます。  ハードウエア仕様は、以下。

タスク分割

 4タスクに分割したので、タスク0からタスク3にします。  各タスクの動作概要を考えます。  タスク0   内部状態を示す変数stateを用意。   変数stateをトリガースイッチで、IDLEとRUNを入れ替え   state=IDLEでは、タスク1をREADYに設定   state=RUNでは、タスク3をREADYに設定  タスク1   ファンドライブ回路に、Hを出力   5秒経過後、タスク2をREADYに設定   その後、自分自身はSUSPENDに設定  タスク2   ファンドライブ回路に、H、Lを出力   HとLの比率を75:25となるように設定  タスク3   タスク0からREADYにされたなら、タスク2を   SUSPENDにし、ファンドライブ回路にLを出力   その後、自分自身はSUSPENDに設定

タスク初期状態設定

 タスク概要から、タスク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のコードでは、以下。 /* shift */ sft_reg <<= 1 ; sft_reg &= 0x07 ; /* get data */ if ( get_pstate() & 0x01 ) { sft_reg |= ON ; } /* judge */ if ( sft_reg == 3 ) { if ( state == IDLE ) { state = RUN ; rsm_tsk( XSTART ); } else { state = IDLE ; rsm_tsk( XSTOP ); } }  外部割込みでスイッチの状態を取得して  トリガー判定するので、周期処理による  タスク処理にします。  タスクIDを使うと、わかりにくいので  より判別しやすい文字列でタスクを指定  しています。  スイッチの状態は、関数get_pstate()で  取得できると仮定して、コード記述します。  チャタリング除去をすればよいので、20ms  ごとの周期タスクにします。 void tsk0_proc(void) { /* shift */ sft_reg <<= 1 ; sft_reg &= 0x07 ; /* get data */ if ( get_pstate() & 0x01 ) { sft_reg |= ON ; } /* judge */ if ( sft_reg == 3 ) { if ( state == IDLE ) { state = RUN ; rsm_tsk( XSTART ); } else { state = IDLE ; rsm_tsk( XSTOP ); } } /* cyclic 20ms */ wai_tsk( 2 ); }  タスク1  「開始から5秒間はフルパワーで回転」が与えられた仕様  なので、これを定義します。  マイコンのピンPB0から、Hを出力します。 put_fan( ON ) ;  ラッパー関数を用意済みとして  呼出すだけにしておきます。  5秒間は、周期処理として、5秒経過後  SUSPENDとしたいので、カウンタを用意し  周期処理とSUSPENDの判定をします。 if ( count == 5 ) { count = 0 ; rsm_tsk( XPWM ); slp_tsk(); } else { wai_tsk( 100 ); }  まとめます。 void tsk1_proc(void) { /* rotate */ put_fan( ON ) ; /* LED */ put_led( ON ) ; /* increment */ count++ ; /* judge */ if ( count == 5 ) { count = 0 ; rsm_tsk( XPWM ); slp_tsk(); } else { wai_tsk( 100 ); } }  カウンタをひとつ用意して、タスクXPWMをREADYに  して自身はSUSPENDになるのか、周期処理をするか  を判定するのがポイントです。  タスク2  「75%DUTYで回転」が与えられた仕様  なので、これを定義します。  ドライブ回路にH、Lを異なる時間で出力する  ために、システムコールwai_tskに与える数値を  カウンタにより変えます。 if ( dcount & 1 ) { put_fan( ON ) ; wai_tsk( 75 ); } else { put_fan( OFF ) ; wai_tsk( 25 ); }  カウンタの変化を付加しておきます。 void tsk2_proc(void) { /* increment */ dcount++ ; /* judge */ if ( dcount & 1 ) { put_fan( ON ) ; wai_tsk( 75 ); } else { put_fan( OFF ) ; wai_tsk( 25 ); } }  タスク3  タスク2をSUSPENDにすればよいので  その処理が終わったなら、自分自身を  SUSPENDにします。 void tsk3_proc(void) { /* stop */ sus_tsk( XPWM ); put_fan( OFF ); put_led( OFF ); /* exit */ slp_tsk(); }

コンフィグレーション

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

タイマー割込み定義

 USOのためにタイマーを1個利用します。  マイクロコンピュータとして、ATtiny2313を利用する  ので、システムクロックを4MHzを分周して1kHz  (=1ms)を生成します。  タイマーのプリスケーラを使わないでおきます。   4MHz/1=4000000  100Hzにするには、10回1msの割込みが発生した  とします。  タイマー1のコンペアマッチ割込みを利用するため、2つ  のレジスタに数値を設定します。 OCR1A = 3999 ; OCR1B = 4500 ;  タイマー1の初期化は、モード指定、カウンタとレジスタ設定  タイマースタートの3処理にまとめます。 /* select CTC , (presclae 1/) 4MHz */ TCCR1B = (1 << WGM12) | (1 << CS10) ; /* clear timer/counter */ TCNT1 = 0 ; OCR1A = 3999 ; OCR1B = 4500 ; /* Enable Compare match interruption */ TIMSK = (1 << OCIE1A) ;  このコードは、関数user_initializeに含めます。  割込みが発生した場合は、WAITであるタスクを探して  カウンタを減算します。  カウンタが0になれば、WAITからREADYに変更します。  トリガーフラグを利用して、10msごとに関数timer_handler  を動かして対応します。 if ( tflag ) { /* clear flag */ tflag = OFF ; /* call */ timer_handler(); }

全ソースコード

 実際にクロスコンパイラにかけ、テストしたソース  コードは、以下です。 #include <avr/io.h> #include <avr/interrupt.h> #define TSK_ID_MAX 4 #define TSK_ID0 0 #define TSK_ID1 1 #define TSK_ID2 2 #define TSK_ID3 3 typedef unsigned char UBYTE ; typedef unsigned short UWORD ; typedef unsigned long ULONG ; 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 volatile UWORD ready ; volatile UWORD suspend; volatile UWORD waitq ; volatile UBYTE run_tsk; volatile UBYTE tflag ; TCBP tcb[TSK_ID_MAX]; volatile ULONG timcnt ; /*------------------------*/ /* 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); #define XTRG TSK_ID0 #define XSTART TSK_ID1 #define XPWM TSK_ID2 #define XSTOP TSK_ID3 #define IDLE 0 #define RUN IDLE+1 #define MASKFF 0xFF volatile UBYTE sft_reg ; volatile UBYTE state ; volatile UBYTE count ; volatile UBYTE dcount ; #define FAN_BIT 1 #define LED_BIT 2 UBYTE get_pstate(void); void put_fan(UBYTE x); void put_led(UBYTE x); /*------*/ /* main */ /*------*/ int main(void) { TCBP pcur_tsk ; /* initialize monitor */ init_os(); /* create tasks */ cre_tsk(XTRG ,tsk0_proc); cre_tsk(XSTART,tsk1_proc); cre_tsk(XPWM ,tsk2_proc); cre_tsk(XSTOP ,tsk3_proc); /* set states */ sta_tsk(XTRG ,TTS_READY); sta_tsk(XSTART,TTS_SUSPEND); sta_tsk(XPWM ,TTS_SUSPEND); sta_tsk(XSTOP ,TTS_SUSPEND); /* */ user_initialize(); /* enable interrupt */ sei() ; /* endless loop */ run_tsk = XTRG ; while ( ON ) { /* RTOS handling */ 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 = XTRG ; } /* 10ms delay */ if ( tflag ) { /* clear flag */ tflag = OFF ; /* call */ timer_handler(); } } /* dummy */ return 0 ; } /*-----------------------*/ /* Insert user functions */ /*-----------------------*/ void user_initialize(void) { /* PORT B */ PORTB = 0b00000001 ; /* 00000000 */ DDRB = 0b11111110 ; /* oooooooi */ /* PORT D */ PORTD = 0b00000000 ; /* 00000000 */ DDRD = 0b11111111 ; /* oooooooo */ /* initialize timer1 */ { /* select CTC , (presclae 1/) 4MHz */ TCCR1B = (1 << WGM12) | (1 << CS10) ; /* clear timer/counter */ TCNT1 = 0 ; OCR1A = 3999 ; OCR1B = 4500 ; /* Enable Compare match interruption */ TIMSK = (1 << OCIE1A) ; } /* initialize registers */ sft_reg = 0 ; state = IDLE ; count = 0 ; dcount = 0 ; /* initialize I/O */ put_fan( OFF ) ; put_led( OFF ) ; } /*----------------*/ /* task functions */ /*----------------*/ void tsk0_proc(void) { /* shift */ sft_reg <<= 1 ; sft_reg &= 0x07 ; /* get data */ if ( get_pstate() & 0x01 ) { sft_reg |= ON ; } /* judge */ if ( sft_reg == 3 ) { if ( state == IDLE ) { state = RUN ; rsm_tsk( XSTART ); } else { state = IDLE ; rsm_tsk( XSTOP ); } } /* cyclic 20ms */ wai_tsk( 2 ); } /* XSTART */ void tsk1_proc(void) { /* rotate */ put_fan( ON ) ; /* LED */ put_led( ON ) ; /* increment */ count++ ; /* judge */ if ( count == 5 ) { count = 0 ; rsm_tsk( XPWM ); slp_tsk(); } else { wai_tsk( 100 ); } } /* XPWM */ void tsk2_proc(void) { /* increment */ dcount++ ; /* judge */ if ( dcount & 1 ) { put_fan( ON ) ; wai_tsk( 75 ); } else { put_fan( OFF ) ; wai_tsk( 25 ); } } /* XSTOP */ void tsk3_proc(void) { /* stop */ sus_tsk( XPWM ); put_fan( OFF ); put_led( OFF ); /* exit */ slp_tsk(); } /*------------------*/ /* system call body */ /*------------------*/ void init_os(void) { ready = 0 ; suspend = 0 ; waitq = 0 ; tflag = OFF ; } void cre_tsk(UBYTE tid,void (*tsk)(void)) { tcb[tid].tsk = tsk; tcb[tid].wcount = 0; } void sta_tsk(UBYTE tid,UBYTE sta) { UWORD 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) { UWORD tmp ; tmp = (1 << tid); ready |= tmp; suspend &= ~tmp; waitq &= ~tmp; } void sus_tsk(UBYTE tid) { UWORD tmp ; tmp = (1 << tid); ready &= ~tmp; suspend |= tmp; waitq &= ~tmp; } void slp_tsk(void) { sus_tsk(run_tsk); } void wai_tsk(UWORD x) { UWORD 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 ) ; } void timer_handler(void) { UBYTE loop ; UBYTE xtmp ; /* check */ xtmp = waitq ; for ( loop = 0 ; loop < TSK_ID_MAX ; loop++ ) { if ( xtmp & ON ) { tcb[loop].wcount-- ; if ( tcb[loop].wcount == 0 ) { rsm_tsk(loop); } } xtmp >>= 1 ; } } /* TIMER1 interrupt */ ISR(TIMER1_COMPA_vect) { /* increment */ timcnt++ ; /* judge */ if ( (timcnt & 0xf) == 10 ) { tflag = ON ; } } UBYTE get_pstate(void) { UBYTE result ; result = PINB ^ MASKFF ; return result ; } void put_fan(UBYTE x) { if ( x ) { PORTB |= (1 << FAN_BIT) ; } else { PORTB &= ~(1 << FAN_BIT) ; } } void put_led(UBYTE x) { if ( x ) { PORTB &= ~(1 << LED_BIT) ; } else { PORTB |= (1 << LED_BIT) ; } }

ハードウエア

 インタフェース回路は、小さな基板にまとめました。  回路は、単純です。  LED、トランジスタ、抵抗、ダイオードがあるだけです。  利用するマイコン基板は、テスト用に作ったボードです。  ファンは、PC空冷用の大きめのものを使います。
目次

inserted by FC2 system