目次

応用例B データロガー

 USOを、データロガーに適用してみます。

 データロガーで測定する数値は、気温、湿度、気圧とします。
 求めた数値は、EEPROMに保存し、測定周期は6分とします。

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



 仕様は、以下とします。
  リアルタイムクロックから、時刻を入力し分が00、06、12、18、
  24、30、36、42、48、54であるとき、センサーからデータ入力します。

  数値データを、2バイトの整数に変換し、EEPROMに保存します。


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

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


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

 電池駆動ができるマイクロコンピュータを想定して、電源電圧が  広い製品がよいので、ATMELのAT90S8535を利用します。  A/Dコンバータを利用するので、マイクロコンピュータには  A/Dコンバータ内蔵タイプが望ましいのです。AT90S8535は8  チャネルのA/Dコンバータを内蔵しています。  1時間に10回データを保存するので、1時間で60バイト。  24時間で、1440バイトとなります。  内蔵EEPROMでは、容量が不足するので、外付けとします。  リアルタイムクロックは、IICバス接続のものがあるので、これを  使います。

タスク分割

 5タスクとなったので、タスク0からタスク4にします。  各タスクの動作概要を考えます。  タスク0   リアルタイムクロックから時刻を入力します。   分が0か6の倍数であれば、温度、湿度、気圧の数値   をデータ入力し、計算するタスクを起動します。   データ保存指示を出します。  タスク1   A/Dコンバータを使い、温度センサーから数値を入力します。   数値を計算し、変数thermometerに保存します。   数値を変数に保存したならば、休眠します。  タスク2   A/Dコンバータを使い、湿度センサーから数値を入力します。   数値を計算し、変数humidetyに保存します。   数値を変数に保存したならば、休眠します。  タスク3   A/Dコンバータを使い、気圧センサーから数値を入力します。   数値を計算し、変数pressureに保存します。   数値を変数に保存したならば、休眠します。  タスク4   データ保存指示を受けて、3変数の値をEEPROMに保存します。

タスク初期状態設定

 各タスクの動作概要から、タスク0とタスク4はREADY  にしておけばよいでしょう。  タスク1からタスク3は、タスク0から起動されるので  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); sta_tsk(TSK_ID4,TTS_READY);

タスク動作定義

 各タスクの動作を定義します。  タスク0  「時刻判定による、他タスク起動。」が与えられた  仕様のひとつなので、これを定義します。  シーケンスを、言葉で記述します。 リアルタイムクロックから分を入力   入力した値が6の倍数ならば、他タスク起動  Cのコードでは、以下のようにします。 minute = get_time(MINUTE); if ( (minute % 6) == 0 ) { rsm_tsk(TSK_ID0); rsm_tsk(TSK_ID1); rsm_tsk(TSK_ID2); }  便利な関数get_timeは、実装時に考えます。  もうひとつの機能が必要でした。  「データ保存指示。」があるので、フラグを  利用して、タスク4に指示を出します。 store_flag = ON ;  6分ごとの周期処理なので、システムコールで待ち時間を指定します。 UBYTE store_flag ; void tsk0_proc(void) { UBYTE minute ; minute = get_time(MINUTE); if ( (minute % 6) == 0 ) { rsm_tsk(TSK_ID0); rsm_tsk(TSK_ID1); rsm_tsk(TSK_ID2); store_flag = ON ; } wai_tsk( 36000 ); }  タスク1  「温度センサー処理」が与えられた仕様  なので、これを定義します。  A/Dコンバータで、温度センサーから数値を入力します。 val = get_analog( TH_CHANNEL ) ;  便利な関数get_analogは、実装時に考えます。  数値を計算し、変数thermometerに保存します。 thermometer = calculate_thermometer( val );  関数calculate_thermometerは、実装時に考えます。  数値を変数保存後、休眠すればよいので、システムコールを使います。 slp_tsk();  まとめます。 #define TH_CHANNEL 0 UWORD thermometer ; void tsk1_proc(void) { UWORD val ; val = get_analog( TH_CHANNEL ) ; thermometer = calculate_thermometer( val ); slp_tsk(); }  タスク2  「湿度センサー処理」が与えられた仕様  なので、これを定義します。  A/Dコンバータで、湿度センサーから数値入力後、変数に保存します。 val = get_analog( HU_CHANNEL ) ; humidety = calculate_humidety( val );  関数calculate_humidetyは、実装時に考えます。  数値を変数保存後、休眠するとして、まとめます。 #define HU_CHANNEL 1 UWORD humidety ; void tsk2_proc(void) { UWORD val ; val = get_analog( HU_CHANNEL ) ; humidety = calculate_humidety( val ); slp_tsk(); }  タスク3  「気圧センサー処理」が与えられた仕様  なので、これを定義します。 #define PR_CHANNEL 2 UWORD pressure ; void tsk3_proc(void) { UWORD val ; val = get_analog( PR_CHANNEL ) ; pressure = calculate_pressure( val ); slp_tsk(); }  タスク4  「データ保存」が与えられた仕様  なので、これを定義します。  フラグの状態をみて、3つの数値を保存します。 if ( store_flag == ON ) { store_flag = OFF ; store_eeprom(thermometer、humidety、pressure); } 関数storeは、実装時に定義します。  まとめると、以下です。 void tsk4_proc(void) { if ( store_flag == ON ) { store_flag = OFF ; store_eeprom(thermometer、humidety、pressure); } }

動作再検討

 5タスクに分割して、各タスクを定義しましたが、タスク  1から3は、同じ動作をしているだけです。  実際には、タスクは3つで充分でした。  もう一度、タスクの初期状態とタスク関数を定義します。  USOのシステムコールで初期状態を設定します。 sta_tsk(TSK_ID0,TTS_READY); sta_tsk(TSK_ID1,TTS_SUSPEND); sta_tsk(TSK_ID2,TTS_READY);  タスク0は、次のように定義します。 UBYTE store_flag ; void tsk0_proc(void) { UBYTE minute ; minute = get_time(MINUTE); if ( (minute % 6) == 0 ) { rsm_tsk(TSK_ID0); store_flag = ON ; } wai_tsk( 36000 ); }  タスク1では、3センサーの処理をまとめます。 #define TH_CHANNEL 0 #define HU_CHANNEL 1 #define PR_CHANNEL 2 UWORD thermometer ; UWORD humidety ; UWORD pressure ; void tsk1_proc(void) { UWORD val ; val = get_analog( TH_CHANNEL ) ; thermometer = calculate_thermometer( val ); val = get_analog( HU_CHANNEL ) ; humidety = calculate_humidety( val ); val = get_analog( PR_CHANNEL ) ; pressure = calculate_pressure( val ); slp_tsk(); }  タスク2用の関数は、関数名を変更するだけです。 void tsk2_proc(void) { if ( store_flag == ON ) { store_flag = OFF ; store_eeprom(thermometer、humidety、pressure); } }

コンフィグレーション

 各タスクの内容を定義したので、コンフィグレータを利用  して、スケルトンのソースコードを生成します。  スケルトンソースコードの中にある、次の関数にタスク処理  を定義します。  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_READY);  コンフィグレーションした場合、デフォルトで  すべてのタスクを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_READY); /* enable interrupt */ ei();  関数user_initializeの中に、ポートの入出力関連の初期化  内蔵モジュール初期化、変数の定数設定を含めます。

タイマー割込み定義

 USOのためにタイマーを1個利用します。  マイクロコンピュータとして、AT90S8535を利用する  と仮定し、システムクロックを8MHzを分周して100Hz  (=10ms)を生成します。  タイマーのプリスケーラを利用して、8分周します。   8MHz/8=1000000  100Hzを生成するためには、10000分周すれば充分です。   1000000/100=1875  タイマー1のコンペアマッチ割込みを利用するため、2つ  のレジスタに数値を設定します。   OCR1A = 9999 ;   OCR1B = 10000 ;  タイマー1の初期化は、モード指定、カウンタとレジスタ設定  タイマースタートの3処理にまとめます。 /* select clock source */ TCCR1A = (1 << CS11) ; /* 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/io8535.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 volatile UBYTE ready ; volatile UBYTE suspend; volatile UBYTE waitq ; volatile UBYTE run_tsk; TCBP tcb[TSK_ID_MAX]; /*------------------------*/ /* 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); /*--------------------------------------*/ /* Insert user definition and variables */ /*--------------------------------------*/ #define TH_CHANNEL 0 #define HU_CHANNEL 1 #define PR_CHANNEL 2 UBYTE store_flag ; UWORD thermometer ; UWORD humidety ; UWORD pressure ; /*--------------------------------*/ /* Insert user functions protoype */ /*--------------------------------*/ void user_initialize(void); UBYTE get_time(UBYTE x); UWORD get_analog(UBYTE x) ; UWORD calculate_thermometer(UWORD x) ; UWORD calculate_humidety(UWORD x) ; UWORD calculate_pressure(UWORD x) ; void store_eeprom(UWORD th,UWORD hu,UWORD pre); /*------*/ /* 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_READY); 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 */ /*----------------*/ void tsk0_proc(void) { UBYTE minute ; minute = get_time(MINUTE); if ( (minute % 6) == 0 ) { rsm_tsk(TSK_ID0); store_flag = ON ; } wai_tsk( 36000 ); } void tsk1_proc(void) { UWORD val ; val = get_analog( TH_CHANNEL ) ; thermometer = calculate_thermometer( val ); val = get_analog( HU_CHANNEL ) ; humidety = calculate_humidety( val ); val = get_analog( PR_CHANNEL ) ; pressure = calculate_pressure( val ); slp_tsk(); } void tsk2_proc(void) { if ( store_flag == ON ) { store_flag = OFF ; store_eeprom(thermometer、humidety、pressure); } } /*------------------*/ /* 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 ) ; } /*-----------------------*/ /* 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 flag */ store_flag = OFF ; /* initialize timer1 */ { /* select clock source */ TCCR1A = (1 << CS11) ; /* clear timer/counter */ TCNT1 = 0 ; /* set compare values */ OCR1A = 1874 ; OCR1B = 2000 ; /* Enable Compare match interruption */ TIMSK = (1 << OCIE1A) ; } } /* timer0 interrupt */ SIGNAL(SIG_TIMER1_COMPA) { 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 ; } } UBYTE get_time(UBYTE x) { return 0 ; } UWORD get_analog(UBYTE x) { return 0 ; } UWORD calculate_thermometer(UWORD x) { return 0 ; } UWORD calculate_humidety(UWORD x) { return 0 ; } UWORD calculate_pressure(UWORD x) { return 0 ; } void store_eeprom(UWORD th,UWORD hu,UWORD pre) { ; }
目次

inserted by FC2 system