目次

応用例B 充電器

 USOを、充電器に適用してみます。  20ピンのAT90S2313を利用します。  システム構成は、下図とします。  仕様は、以下とします。   DIPスイッチに時間を設定して、スタートボタンを押します。   設定された時間の間、SSR(Solid State Relay)に制御信号を   出力します。  設定時間の間、SSRにAC100Vを通電させて、直流電圧を  充電器に与えます。  システム構成から回路図を作成します。  充電は、OPアンプとトランジスタを利用して簡単に構成します。  1kΩに発生する電圧を、マルチメータで見ながら、可変抵抗のトリマー  を回します。  100mAを流したい場合は、100mAx1kΩ=0.1Vになる  ように、可変抵抗のトリマーを回せば充電電流を調節できます。  システム構成から、必要となるタスクを考えます。
  1. ボタンスイッチ状態を検出して、必要な仕事の割振り
  2. 時間から分を計算して、カウンタを設定
  3. 1分ごとの時間経過を管理
  4. 60秒経過で、フラグをセットと時間経過管理タスクに通知
  5. LEDの点滅、消灯を実現
 1から5までのタスクとなったので、タスク0からタスク4  として、動作を定義します。  コンフィグレータに必要な情報を与え、スケルトンのソース  コードを作成します。

タスク動作定義

 タスク0を定義します。  「ボタンスイッチ状態を検出して、必要な仕事の割振り」が  与えられている仕様なので、スイッチ状態入力と別のタスクを  起動します。 void tsk0_proc(void) { /* フラグにより、ボタンスイッチ状態取得 */ /* フラグのクリア */ /* 他のタスクを起動 */ }  ボタンスイッチ状態がフラグで提示されるとして  フラグをEFLAGとします。EFLAGがOFFならば何もしないで  終了します。もう一段進めた動作を定義します。 void tsk0_proc(void) { /* フラグにより、ボタンスイッチ状態取得 */ if ( EFLAG == OFF ) return ; /* フラグのクリア */ /* 他のタスクを起動 */ }  フラグクリアを記述します。 void tsk0_proc(void) { /* フラグにより、ボタンスイッチ状態取得 */ if ( EFLAG == OFF ) return ; /* フラグのクリア */ EFLAG = OFF ; /* 他のタスクを起動 */ }  他のタスクは、時間設定、時間管理、60秒経過の3つなので  定義されているとして、システムコールを利用して  SUSPENDからREADYに状態変更します。 void tsk0_proc(void) { /* フラグにより、ボタンスイッチ状態取得 */ if ( EFLAG == OFF ) return ; /* フラグのクリア */ EFLAG = OFF ; /* 他のタスクを起動 */ rsm_tsk( TSK_ID1 ); rsm_tsk( TSK_ID2 ); rsm_tsk( TSK_ID3 ); }  タイマー動作中であることがわかるように、LEDを  点滅させた方がよいので、LEDの点滅と消灯をフラグ  で変更できるようにします。  フラグLFLAGをONにすることで、点滅させられるとして  フラグ設定を加えます。 void tsk0_proc(void) { /* フラグにより、ボタンスイッチ状態取得 */ if ( EFLAG == OFF ) return ; /* フラグのクリア */ EFLAG = OFF ; /* 他のタスクを起動 */ rsm_tsk( TSK_ID1 ); rsm_tsk( TSK_ID2 ); rsm_tsk( TSK_ID3 ); LFLAG = ON ; }  まとめると、次のようになります。  void tsk0_proc(void)  {   /* judge */   if ( EFLAG == OFF ) return ;   /* handling */   EFLAG = OFF ;   rsm_tsk( TSK_ID1 ) ; /* set time counter */   rsm_tsk( TSK_ID2 ) ; /* enable time manager */   rsm_tsk( TSK_ID3 ) ; /* enable 1 minute interval */   LFLAG = ON ; /* enable LED flashing */  }  タスク1を定義します。  「時間から分を計算して、カウンタを設定」が考えた  仕様なので、DIPスイッチの状態を入力し、60倍した  値を計算します。この値をグローバル変数に保存します。  言葉で、内容を記述します。 void tsk1_proc(void) { /* データ取得 */ /* 60倍を計算し保存 */ /* 自分自身をSUSPENDに状態変更 */ }  データ取得は、DIPスイッチがどう接続されているかで  記述が変わります。ここでは、ポートDの5、4、1、0  ビット目に8、4、2、1の重みが付いた仕様で接続されたと  します。 void tsk1_proc(void) { UBYTE tmp ; UBYTE dat ; /* データ取得 */ tmp = PIND & 0x1f ; dat = (tmp >> 1) & 0x0c ; dat |= (tmp & 0x03); /* 60倍を計算し保存 */ /* 自分自身をSUSPENDに状態変更 */ }  0を設定したときは、16と読み変えます。 void tsk1_proc(void) { UBYTE tmp ; UBYTE dat ; /* データ取得 */ tmp = PIND & 0x1f ; dat = (tmp >> 1) & 0x0c ; dat |= (tmp & 0x03); if ( dat == 0 ) { dat = 16 ; } /* 60倍を計算し保存 */ /* 自分自身をSUSPENDに状態変更 */ }  グローバル変数time_countに60倍した値を設定します。 void tsk1_proc(void) { UBYTE tmp ; UBYTE dat ; /* データ取得 */ tmp = PIND & 0x1f ; dat = (tmp >> 1) & 0x0c ; dat |= (tmp & 0x03); if ( dat == 0 ) { dat = 16 ; } /* 60倍を計算し保存 */ time_count = 60 * dat ; /* 自分自身をSUSPENDに状態変更 */ }  システムコールで、自分自身をSUSPENDに状態変更します。 void tsk1_proc(void) { UBYTE tmp ; UBYTE dat ; /* データ取得 */ tmp = PIND & 0x1f ; dat = (tmp >> 1) & 0x0c ; dat |= (tmp & 0x03); if ( dat == 0 ) { dat = 16 ; } /* 60倍を計算し保存 */ time_count = 60 * dat ; /* 自分自身をSUSPENDに状態変更 */ slp_tsk() ; }  60秒経過を管理するタスクで利用するカウンタ変数も、この  タスクで初期設定しておきます。 void tsk1_proc(void) { UBYTE tmp ; UBYTE dat ; /* データ取得 */ tmp = PIND & 0x1f ; dat = (tmp >> 1) & 0x0c ; dat |= (tmp & 0x03); if ( dat == 0 ) { dat = 16 ; } /* 60倍を計算し保存 */ time_count = 60 * dat ; /* 60秒経過管理タスクのカウンタ初期化 */ count = 0 ; /* 自分自身をSUSPENDに状態変更 */ slp_tsk() ; }  まとめると、以下となります。 void tsk1_proc(void) { UBYTE tmp ; UBYTE dat ; /* get data */ tmp = PIND & 0x1f ; dat = (tmp >> 1) & 0x0c ; dat |= (tmp & 0x03); if ( dat == 0 ) { dat = 16 ; } /* calculate timer counter */ time_count = 60 * dat ; /* clear 1 minute counter */ count = 0 ; /* sleep */ slp_tsk() ; }  タスク2を定義します。  「1分ごとの時間経過を管理」が考えた仕様です。  もう少し具体的に実現内容を考えます。  SSRに制御信号を出力する、1分経過を監視する2つの動作を  同時に処理します。1分経過は、他のタスクからフラグ通知  で検出するとして、そのときはカウント値を1減らします。  カウント値が0のときに、何か処理をする仕様にします。  言葉で内容を記述します。 void tsk2_proc(void) { /* SSRにONの制御信号出力 */ /* フラグをみて、カウント値を1減らす */ /* カウント値による処理記述 */ }  SSRにONの制御信号を出力する動作を記述します。 void tsk2_proc(void) { /* SSRにONの制御信号出力 */ POWER_ON ; /* フラグをみて、カウント値を1減らす */ /* カウント値による処理記述 */ }  フラグによる動作を付加します。 void tsk2_proc(void) { /* SSRにONの制御信号出力 */ POWER_ON ; /* フラグをみて、カウント値を1減らす */ if ( CFLAG == ON ) { CFLAG = OFF ; time_count-- ; } /* カウント値による処理記述 */ }  カウント値が正のときと、0のときでは動作が異なる  ので、場合分けを記述します。 void tsk2_proc(void) { /* SSRにONの制御信号出力 */ POWER_ON ; /* フラグをみて、カウント値を1減らす */ if ( CFLAG == ON ) { CFLAG = OFF ; time_count-- ; } /* カウント値による処理記述 */ if ( time_count > 0 ) { } else { } }  カウント値が正では、10秒に一度の動作で充分なので  周期的に動作させます。システムコールを利用し10秒  の時間間隔を与えます。 void tsk2_proc(void) { /* SSRにONの制御信号出力 */ POWER_ON ; /* フラグをみて、カウント値を1減らす */ if ( CFLAG == ON ) { CFLAG = OFF ; time_count-- ; } /* カウント値による処理記述 */ if ( time_count > 0 ) { /* set time interval = 10s */ wai_tsk( 1000 ); } else { } }  カウント値が0では、SSRにOFFの制御信号を出力します。  60秒経過のタスクを使うことがなくなるので、そのタスク  を停止します。また、LEDの点滅も必要ないので、消灯  させます。さらに、時間管理が不要になるので、システム  コールで、自分自身をSUSPENDにします。 void tsk2_proc(void) { /* SSRにONの制御信号出力 */ POWER_ON ; /* フラグをみて、カウント値を1減らす */ if ( CFLAG == ON ) { CFLAG = OFF ; time_count-- ; } /* カウント値による処理記述 */ if ( time_count > 0 ) { /* set time interval = 10s */ wai_tsk( 1000 ); } else { /* power control */ POWER_OFF ; /* task handling */ sus_tsk( TSK_ID3 ) ; /* stop 1 minute interval */ LFLAG = OFF ; /* turn off LED */ slp_tsk() ; } }  まとめると、以下となります。 void tsk2_proc(void) { /* power control */ POWER_ON ; /* counter decrement */ if ( CFLAG == ON ) { CFLAG = OFF ; time_count-- ; } /* judge */ if ( time_count > 0 ) { /* set time interval = 10s */ wai_tsk( 1000 ); } else { /* power control */ POWER_OFF ; /* task handling */ sus_tsk( TSK_ID3 ) ; /* stop 1 minute interval */ LFLAG = OFF ; /* turn off LED */ slp_tsk() ; } }  タスク3を定義します。  「60秒経過で、フラグをセットと時間経過管理タスクに通知」が考えた仕様なので、  グローバル変数countの値を1増やすこととフラグ設定が主な仕事になります。  周期は、1秒として動作させます。  言葉で、内容を定義します。 void tsk3_proc(void) { /* カウンタを1増やす */ /* カウンタが60になったときのフラグ処理 */ /* 時間待ち指定 */ }  カウンタは、変数countで扱うので、+1を記述します。 void tsk3_proc(void) { /* カウンタを1増やす */ count++ ; /* カウンタが60になったときのフラグ処理 */ /* 時間待ち指定 */ }  変数countが、60になったときの処理を加えます。 void tsk3_proc(void) { /* カウンタを1増やす */ count++ ; /* カウンタが60になったときのフラグ処理 */ if ( count == 60 ) { count = 0 ; CFLAG = ON ; } /* 時間待ち指定 */ }  システムコールで、周期を再設定します。 void tsk3_proc(void) { /* カウンタを1増やす */ count++ ; /* カウンタが60になったときのフラグ処理 */ if ( count == 60 ) { count = 0 ; CFLAG = ON ; } /* 時間待ち指定 */ wai_tsk( 100 ); }  まとめると、以下となります。 void tsk3_proc(void) { /* increment */ count++ ; /* judge */ if ( count == 60 ) { count = 0 ; CFLAG = ON ; } /* set time interval = 1000ms */ wai_tsk( 100 ); }  タスク4を定義します。  「LEDの点滅、消灯を実現」が考えた仕様なので、  2動作の切り替えと周期処理が必要になります。  消灯は、LEDを接続しているビットにHを出力します。  点滅は、HとLを交互に出力します。周期を1秒にして、  HとLの出力を切り替えると、点滅が実現できます。  点滅は、カウンタを1秒周期で+1して実現します。  カウンタのLSB(Least Significant Bit)が、0、1を  交互に繰り返すので、1のとき点灯の値を出力します。  言葉で内容を記述します。 void tsk4_proc(void) { /* カウンタを1増やす */ /* LED消灯 */ /* カウンタのLSBが1で、点滅指定ならばLED点灯 */ /* 時間待ち指定 */ }  利用するカウンタ変数をled_countとして、+1を加えます。 void tsk4_proc(void) { /* カウンタを1増やす */ led_count++ ; /* LED消灯 */ /* カウンタのLSBが1で、点滅指定ならばLED点灯 */ /* 時間待ち指定 */ }  LED消灯がLED_OFFとして、定義されているとして記述します。 void tsk4_proc(void) { /* カウンタを1増やす */ led_count++ ; /* LED消灯 */ LED_OFF ; /* カウンタのLSBが1で、点滅指定ならばLED点灯 */ /* 時間待ち指定 */ }  1秒周期は、システムコールを利用します。 void tsk4_proc(void) { /* カウンタを1増やす */ led_count++ ; /* LED消灯 */ LED_OFF ; /* カウンタのLSBが1で、点滅指定ならばLED点灯 */ /* 時間待ち指定 */ wai_tsk( 100 ); }  点灯は、変数led_countとフラグLFLAGで記述します。 void tsk4_proc(void) { /* カウンタを1増やす */ led_count++ ; /* LED消灯 */ LED_OFF ; /* カウンタのLSBが1で、点滅指定ならばLED点灯 */ if ( (LFLAG & led_count & 1) ) { LED_ON ; } /* 時間待ち指定 */ wai_tsk( 100 ); }  まとめると、以下となります。 void tsk4_proc(void) { /* increment */ led_count++ ; /* set LED state */ LED_OFF ; if ( (LFLAG & led_count & 1) ) { LED_ON ; } /* set time interval = 1000ms */ wai_tsk( 100 ); }

変数、フラグ定義

 タスクで利用しているフラグを、タスク関数から拾い出します。  タスク0で利用している変数、フラグを拾います。  フラグ EFLAG LFLAG  タスク1で利用している変数、フラグを拾います。  変数  time_count count  フラグ なし  タスク2で利用している変数、フラグを拾います。  変数  time_count  フラグ CFLAG LFLAG  タスク3で利用している変数、フラグを拾います。  変数  count  フラグ CFLAG  タスク4で利用している変数、フラグを拾います。  変数  led_count  フラグ LFLAG  フラグは、1ビットのデータ処理に利用するので、新たにデータ型  を定義して利用します。 typedef union { struct { unsigned B7:1; unsigned B6:1; unsigned B5:1; unsigned B4:1; unsigned B3:1; unsigned B2:1; unsigned B1:1; unsigned B0:1; } BIT ; unsigned char DR ; } FLAGSP ; FLAGSP x_flags ; #define EFLAG x_flags.BIT.B0 #define CFLAG x_flags.BIT.B1 #define LFLAG x_flags.BIT.B2  変数を定義します。  typedef unsigned char UBYTE ;  typedef unsigned short UWORD ; UWORD time_count ; UBYTE led_count ; UBYTE count ;

タスクの初期状態定義

 タスクの関数を定義したので、初期状態を考えます。
  1. ボタンスイッチ状態を検出して、必要な仕事の割振り   タスク0
  2. 時間から分を計算して、カウンタを設定         タスク1
  3. 1分ごとの時間経過を管理               タスク2
  4. 60秒経過で、フラグをセットと時間経過管理タスクに通知 タスク3
  5. LEDの点滅、消灯を実現               タスク4
 タスク0は、ボタンスイッチの状態を監視しているので、常に動くように  しなければなりません。従って、READYとします。  タスク1、タスク2、タスク3は、タスク0から起動されるので、SUSUPEND  とします。  タスク4は、LEDの点滅を制御しますが、フラグで動作指定されるので  READYとします。  システムコールを利用して、各タスクの状態を決定します。 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);

外部割込み定義

 ボタンスイッチ状態を検出するには、外部割込みを利用するのが簡単です。  外部割込みが用意されているマイコンでは、ピンに余裕があれば、なるべく  使うようにした方が、ファームウエアが単純になります。  AT90S2313には、外部割込みが2つあります。そのうちINT0を利用します。  割込みを利用する場合には、割込み指定と割込み処理の2つに分けて設定  しなければなりません。  割込みを指定します。  外部割込みを使うため、レジスタGIMSKにINT0を使うための設定をします。  GIMSK = (1 << INT0) ;  外部割込みは、エッジかレベルのどちらかを利用するのかを指定します。  今回は、立ち上がりエッジを利用しました。  MCUCR = (1 << ISC01) ;  割込み処理を記述します。  外部割込みが発生したときは、フラグEFLAGで通知します。 ISR(INT0_vect) { /* set flag */ EFLAG = ON ; }

タイマー割込み定義

 USOを利用するためには、周期指定のシステムコールwai_tskを利用できる  ようにするため、10msごとの割込みを生成しなければなりません。  タイマー割込みは、システムクロックと分周の関係がマイコン固有なので  データシートを熟読して、使い方を見極める必要があります。  システムクロック8MHzで、タイマー0を利用することにしました。  システムクロックを分周するプリスケーラがあるので、8ビットカウンタとの  兼ね合いで、動作を決定しなければなりません。  システムクロック8MHzを、8分周、64分周すると、周期は1us、8usに  なります。  タイマー0は、OVERFLOWを利用することにします。  250分周するとして、8ビットカウンタに6を設定します。  6から256までカウントするとして、オーバーフローで割込みが発生します。  250分周すると、周期は1us、8usは、250us、2msになります。  10msの割込みは、2msの割込みを5回カウントすれば実現できます。  そのためのカウンタ変数として、os_cntを利用します。  割込みを指定します。  プリスケーラで、64分周を設定します。 TCCR0 = 0x02 ;  カウンタの初期値を、6にします。 TCNT0 = 6 ;  オーバーフローで利用すると指定します。 TIMSK |= (1 << TOIE0) ;  割込み処理を記述します。  タイマー割込みは、2msごとに発生するので、5回カウントして  関数timer_handlerを呼び出します。 ISR(TIMER0_OVF0_vect) { /* clear timer/counter */ TCNT0 = 6 ; /* increment */ os_cnt++ ; /* judge */ if ( os_cnt == 5 ) { os_cnt = 0 ; /* update task state */ timer_handler() ; } }

I/O設定

 マイコンのピンは、方向と初期値を設定する必要があります。  回路図から、ポートB、Dの初期値と方向を設定します。  ポートDの方向をまとめます。  この状態は、次のように指定します。 DDRD = 0b11100000 ; /* oooiiiii */  ポートBの方向をまとめます。  この状態は、次のように指定します。 DDRB = 0b11111111 ; /* oooooooo */  ポートDの値をまとめます。 PORTD = 0b01011111 ; /* oooiiiii */  ポートBの値をまとめます。 PORTB = 0b00000010 ; /* oooooooo */  全体をまとめると、以下になります。 /* set I/O initial values */ PORTB = 0b00000010 ; /* oooooooo */ PORTD = 0b01011111 ; /* oooiiiii */ /* set I/O directions */ DDRB = 0b11111111 ; /* oooooooo */ DDRD = 0b11100000 ; /* oooiiiii */

全ソースコード

 これまでの内容を、ひとつのソースコードにまとめます。 #include <avr/io.h> #include <avr/interrupt.h> #define TSK_ID_MAX 5 #define TSK_ID0 0 #define TSK_ID1 1 #define TSK_ID2 2 #define TSK_ID3 3 #define TSK_ID4 4 typedef unsigned char UBYTE ; typedef unsigned short UWORD ; typedef signed char SBYTE ; typedef signed short SWORD ; typedef union { struct { unsigned char B0:1; unsigned char B1:1; unsigned char B2:1; unsigned char B3:1; unsigned char B4:1; unsigned char B5:1; unsigned char B6:1; unsigned char B7:1; } BIT ; unsigned char DR ; } FLAGSP ; volatile FLAGSP x_flags ; #define EFLAG x_flags.BIT.B0 #define CFLAG x_flags.BIT.B1 #define LFLAG x_flags.BIT.B2 #define POWER_ON (PORTB |= 0x01) #define POWER_OFF (PORTB &= 0xfe) #define LED_ON (PORTB &= 0xfd) #define LED_OFF (PORTB |= 0x02) 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 ITCNT0 6 UBYTE ready ; UBYTE suspend; UBYTE vldtsk ; UBYTE run_tsk; TCBP tcb[TSK_ID_MAX]; UWORD time_count ; UBYTE led_count ; UBYTE count ; UBYTE os_cnt ; /*------------------------*/ /* task function protoype */ /*------------------------*/ void tsk0_proc(void); void tsk1_proc(void); void tsk2_proc(void); void tsk3_proc(void); void tsk4_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 ; /* disable interrupt */ cli() ; /* initialize monitor */ init_os(); /* store task ID to table */ cre_tsk(TSK_ID0,tsk0_proc); cre_tsk(TSK_ID1,tsk1_proc); cre_tsk(TSK_ID2,tsk2_proc); cre_tsk(TSK_ID3,tsk3_proc); cre_tsk(TSK_ID4,tsk4_proc); /* 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_SUSPEND); sta_tsk(TSK_ID4,TTS_READY); /* initialize */ user_initialize(); /* enable interrupt */ 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) { /* judge */ if ( EFLAG == OFF ) return ; /* handling */ EFLAG = OFF ; rsm_tsk( TSK_ID1 ) ; /* set time counter */ rsm_tsk( TSK_ID2 ) ; /* enable time manager */ rsm_tsk( TSK_ID3 ) ; /* enable 1 minute interval */ LFLAG = ON ; /* enable LED flashing */ } /* set time counter */ void tsk1_proc(void) { UBYTE tmp ; UBYTE dat ; /* get data */ tmp = PIND & 0x1f ; dat = (tmp >> 1) & 0x0c ; dat |= (tmp & 0x03); if ( dat == 0 ) { dat = 16 ; } /* calculate timer counter */ time_count = 60 * dat ; /* clear 1 minute counter */ count = 0 ; /* sleep */ slp_tsk() ; } /* time handling */ void tsk2_proc(void) { /* power control */ POWER_ON ; /* counter decrement */ if ( CFLAG == ON ) { CFLAG = OFF ; time_count-- ; } /* judge */ if ( time_count > 0 ) { /* set time interval = 10s */ wai_tsk( 1000 ); } else { /* power control */ POWER_OFF ; /* task handling */ sus_tsk( TSK_ID3 ) ; /* stop 1 minute interval */ LFLAG = OFF ; /* turn off LED */ slp_tsk() ; } } /* 1 minute interval */ void tsk3_proc(void) { /* increment */ count++ ; /* judge */ if ( count == 60 ) { count = 0 ; CFLAG = ON ; } /* set time interval = 1000ms */ wai_tsk( 100 ); } /* LED flashing */ void tsk4_proc(void) { /* increment */ led_count++ ; /* set LED state */ LED_OFF ; if ( (LFLAG & led_count & 1) ) { LED_ON ; } /* set time interval = 1000ms */ wai_tsk( 100 ); } /*------------------*/ /* system call body */ /*------------------*/ void init_os(void) { ready = vldtsk = 0 ; } void cre_tsk(UBYTE tid,void (*tsk)(void)) { vldtsk |= (1 << tid) ; tcb[tid].tsk = tsk; tcb[tid].wcount = 0; } void sta_tsk(UBYTE tid,UBYTE sta) { if ( sta == TTS_READY ) { ready |= (1 << tid); suspend &= ~(1 << tid); } if ( sta == TTS_WAIT ) { ready &= ~(1 << tid); suspend &= ~(1 << tid); } if ( sta == TTS_SUSPEND ) { ready &= ~(1 << tid); suspend |= (1 << tid); } } void rsm_tsk(UBYTE tid) { ready |= (1 << tid); suspend &= ~(1 << tid); } void sus_tsk(UBYTE tid) { ready &= ~(1 << tid); suspend |= (1 << tid); } void slp_tsk(void) { sus_tsk(run_tsk); } void wai_tsk(UWORD x) { ready &= ~(1 << run_tsk); suspend &= ~(1 << run_tsk); tcb[run_tsk].wcount = x ; } UBYTE is_tsk_ready(UBYTE tid) { return( (ready >> tid) & 1 ) ; } /*-----------------------------*/ /* timer handler */ /* call from timer interrupt */ /*-----------------------------*/ void timer_handler(void) { UBYTE tmp; UBYTE i ; tmp = (ready ^ vldtsk) ^ suspend ; for ( i = 0 ; i < TSK_ID_MAX ; i++ ) { if ( tmp & 1 ) { tcb[i].wcount-- ; if ( tcb[i].wcount == 0 ) { rsm_tsk(i); } } tmp >>= 1 ; } } /*-----------------------*/ /* Insert user functions */ /*-----------------------*/ void user_initialize(void) { /* set I/O initial values */ PORTB = 0b00000010 ; /* oooooooo */ PORTD = 0b01011111 ; /* oooiiiii */ /* set I/O directions */ DDRB = 0b11111111 ; /* oooooooo */ DDRD = 0b11100000 ; /* oooiiiii */ /* set flags */ x_flags.DR = 0 ; /* initialize external0 interrupt */ GIMSK = (1 << INT0) ; MCUCR = (1 << ISC01) ; /* initialize timer0 */ TCCR0 = 0x02 ; /* clear timer/counter */ TCNT0 = ITCNT0 ; /* Enable overflow interrupt */ TIMSK |= (1 << TOIE0) ; /* clear 10ms counter */ os_cnt = 0 ; /* set initial values */ time_count = 0 ; led_count = 0 ; count = 0 ; } /* external interrupt */ ISR(INT0_vect) { /* set flag */ EFLAG = ON ; } /* timer0 interrupt */ ISR(TIMER0_OVF0_vect) { /* clear timer/counter */ TCNT0 = ITCNT0 ; /* increment */ os_cnt++ ; /* judge */ if ( os_cnt == 5 ) { os_cnt = 0 ; /* update task state */ timer_handler() ; } }

Makefile定義

 AT90Sに代表されるAVRは、GCCでコンパイル、リンクします。  コンパイル、リンクには、Makefileが必要になります。  Makefileの内容は、以下です。 # MCU name MCU = at90s2313 # Processor frequency. F_CPU = 8000000 # Output format. (can be srec, ihex, binary) FORMAT = ihex # Target file name (without extension). TARGET = usotimer # Object files directory OBJDIR = . # List C source files here. (C dependencies are automatically generated.) SRC = $(TARGET).c # Optimization level, can be [0, 1, 2, 3, s]. OPT = 0 # Debugging format. DEBUG = dwarf-2 # Compiler flag to set the C Standard level. CSTANDARD = -std=c89 # Place -D or -U options here for C sources CDEFS = -DF_CPU=$(F_CPU)UL #---------------- Compiler Options C ---------------- # -g*: generate debugging information # -O*: optimization level # -f...: tuning, see GCC manual and avr-libc documentation # -Wall...: warning level # -Wa,...: tell GCC to pass this to the assembler. # -adhlns...: create assembler listing CFLAGS = -g$(DEBUG) CFLAGS += $(CDEFS) CFLAGS += -O$(OPT) CFLAGS += -funsigned-char CFLAGS += -funsigned-bitfields CFLAGS += -fpack-struct CFLAGS += -fshort-enums CFLAGS += -Wall CFLAGS += -Wstrict-prototypes #CFLAGS += -mshort-calls #CFLAGS += -fno-unit-at-a-time #CFLAGS += -Wundef #CFLAGS += -Wunreachable-code #CFLAGS += -Wsign-compare CFLAGS += -Wa,-adhlns=$(<:%.c=$(OBJDIR)/%.lst) CFLAGS += $(patsubst %,-I%,$(EXTRAINCDIRS)) CFLAGS += $(CSTANDARD) #---------------- Library Options ---------------- # Minimalistic printf version PRINTF_LIB_MIN = -Wl,-u,vfprintf -lprintf_min # Floating point printf version (requires MATH_LIB = -lm below) PRINTF_LIB_FLOAT = -Wl,-u,vfprintf -lprintf_flt # If this is left blank, then it will use the Standard printf version. PRINTF_LIB = # Minimalistic scanf version SCANF_LIB_MIN = -Wl,-u,vfscanf -lscanf_min # Floating point + %[ scanf version (requires MATH_LIB = -lm below) SCANF_LIB_FLOAT = -Wl,-u,vfscanf -lscanf_flt # If this is left blank, then it will use the Standard scanf version. SCANF_LIB = MATH_LIB = -lm # List any extra directories to look for libraries here. EXTRALIBDIRS = #---------------- External Memory Options ---------------- # 64 KB of external RAM, starting after internal RAM (ATmega128!), # used for variables (.data/.bss) and heap (malloc()). #EXTMEMOPTS = -Wl,-Tdata=0x801100,--defsym=__heap_end=0x80ffff # 64 KB of external RAM, starting after internal RAM (ATmega128!), # only used for heap (malloc()). #EXTMEMOPTS = -Wl,--section-start,.data=0x801100,--defsym=__heap_end=0x80ffff EXTMEMOPTS = #---------------- Linker Options ---------------- # -Wl,...: tell GCC to pass this to linker. # -Map: create map file # --cref: add cross reference to map file LDFLAGS = -Wl,-Map=$(TARGET).map,--cref LDFLAGS += $(EXTMEMOPTS) LDFLAGS += $(patsubst %,-L%,$(EXTRALIBDIRS)) LDFLAGS += $(PRINTF_LIB) $(SCANF_LIB) $(MATH_LIB) #LDFLAGS += -T linker_script.x #---------------- Debugging Options ---------------- # For simulavr only - target MCU frequency. DEBUG_MFREQ = $(F_CPU) # Set the DEBUG_UI to either gdb or insight. # DEBUG_UI = gdb DEBUG_UI = insight # Set the debugging back-end to either avarice, simulavr. DEBUG_BACKEND = avarice #DEBUG_BACKEND = simulavr # GDB Init Filename. GDBINIT_FILE = __avr_gdbinit # When using avarice settings for the JTAG JTAG_DEV = /dev/com1 # Debugging port used to communicate between GDB / avarice / simulavr. DEBUG_PORT = 4242 # Debugging host used to communicate between GDB / avarice / simulavr, normally # just set to localhost unless doing some sort of crazy debugging when # avarice is running on a different computer. DEBUG_HOST = localhost #============================================================================ # Define programs and commands. SHELL = sh CC = avr-gcc OBJCOPY = avr-objcopy OBJDUMP = avr-objdump SIZE = avr-size AR = avr-ar rcs NM = avr-nm AVRDUDE = avrdude REMOVE = rm -f REMOVEDIR = rm -rf COPY = cp WINSHELL = cmd # Define Messages # English MSG_ERRORS_NONE = Errors: none MSG_BEGIN = -------- begin -------- MSG_END = -------- end -------- MSG_SIZE_BEFORE = Size before: MSG_SIZE_AFTER = Size after: MSG_COFF = Converting to AVR COFF: MSG_EXTENDED_COFF = Converting to AVR Extended COFF: MSG_FLASH = Creating load file for Flash: MSG_EEPROM = Creating load file for EEPROM: MSG_EXTENDED_LISTING = Creating Extended Listing: MSG_SYMBOL_TABLE = Creating Symbol Table: MSG_LINKING = Linking: MSG_COMPILING = Compiling C: MSG_COMPILING_CPP = Compiling C++: MSG_ASSEMBLING = Assembling: MSG_CLEANING = Cleaning project: MSG_CREATING_LIBRARY = Creating library: # Define all object files. OBJ = $(SRC:%.c=$(OBJDIR)/%.o) $(CPPSRC:%.cpp=$(OBJDIR)/%.o) $(ASRC:%.S=$(OBJDIR)/%.o) # Define all listing files. LST = $(SRC:%.c=$(OBJDIR)/%.lst) $(CPPSRC:%.cpp=$(OBJDIR)/%.lst) $(ASRC:%.S=$(OBJDIR)/%.lst) # Compiler flags to generate dependency files. GENDEPFLAGS = -MMD -MP -MF .dep/$(@F).d # Combine all necessary flags and optional flags. # Add target processor to flags. ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) $(GENDEPFLAGS) # Default target. all: begin gccversion sizebefore build sizeafter end # Change the build target to build a HEX file or a library. build: elf hex eep lss sym #build: lib elf: $(TARGET).elf hex: $(TARGET).hex eep: $(TARGET).eep lss: $(TARGET).lss sym: $(TARGET).sym LIBNAME=lib$(TARGET).a lib: $(LIBNAME) # Eye candy. # AVR Studio 3.x does not check make's exit code but relies on # the following magic strings to be generated by the compile job. #begin: # @echo # @echo $(MSG_BEGIN) #end: # @echo $(MSG_END) # @echo # Display size of file. HEXSIZE = $(SIZE) --target=$(FORMAT) $(TARGET).hex ELFSIZE = $(SIZE) --mcu=$(MCU) --format=avr $(TARGET).elf sizebefore: @if test -f $(TARGET).elf; then echo; echo $(MSG_SIZE_BEFORE); $(ELFSIZE); \ 2>/dev/null; echo; fi sizeafter: @if test -f $(TARGET).elf; then echo; echo $(MSG_SIZE_AFTER); $(ELFSIZE); \ 2>/dev/null; echo; fi # Display compiler version information. gccversion : @$(CC) --version # Generate avr-gdb config/init file which does the following: # define the reset signal, load the target file, connect to target, and set # a breakpoint at main(). gdb-config: @$(REMOVE) $(GDBINIT_FILE) @echo define reset >> $(GDBINIT_FILE) @echo SIGNAL SIGHUP >> $(GDBINIT_FILE) @echo end >> $(GDBINIT_FILE) @echo file $(TARGET).elf >> $(GDBINIT_FILE) @echo target remote $(DEBUG_HOST):$(DEBUG_PORT) >> $(GDBINIT_FILE) ifeq ($(DEBUG_BACKEND),simulavr) @echo load >> $(GDBINIT_FILE) endif @echo break main >> $(GDBINIT_FILE) debug: gdb-config $(TARGET).elf ifeq ($(DEBUG_BACKEND), avarice) @echo Starting AVaRICE - Press enter when "waiting to connect" message displays. @$(WINSHELL) /c start avarice --jtag $(JTAG_DEV) --erase --program --file \ $(TARGET).elf $(DEBUG_HOST):$(DEBUG_PORT) @$(WINSHELL) /c pause else @$(WINSHELL) /c start simulavr --gdbserver --device $(MCU) --clock-freq \ $(DEBUG_MFREQ) --port $(DEBUG_PORT) endif @$(WINSHELL) /c start avr-$(DEBUG_UI) --command=$(GDBINIT_FILE) # Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB. COFFCONVERT = $(OBJCOPY) --debugging COFFCONVERT += --change-section-address .data-0x800000 COFFCONVERT += --change-section-address .bss-0x800000 COFFCONVERT += --change-section-address .noinit-0x800000 COFFCONVERT += --change-section-address .eeprom-0x810000 coff: $(TARGET).elf @echo @echo $(MSG_COFF) $(TARGET).cof $(COFFCONVERT) -O coff-avr $< $(TARGET).cof extcoff: $(TARGET).elf @echo @echo $(MSG_EXTENDED_COFF) $(TARGET).cof $(COFFCONVERT) -O coff-ext-avr $< $(TARGET).cof # Create final output files (.hex, .eep) from ELF output file. %.hex: %.elf @echo @echo $(MSG_FLASH) $@ $(OBJCOPY) -O $(FORMAT) -R .eeprom -R .fuse -R .lock $< $@ %.eep: %.elf @echo @echo $(MSG_EEPROM) $@ -$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" \ --change-section-lma .eeprom=0 --no-change-warnings -O $(FORMAT) $< $@ || exit 0 # Create extended listing file from ELF output file. %.lss: %.elf @echo @echo $(MSG_EXTENDED_LISTING) $@ $(OBJDUMP) -h -S -z $< > $@ # Create a symbol table from ELF output file. %.sym: %.elf @echo @echo $(MSG_SYMBOL_TABLE) $@ $(NM) -n $< > $@ # Link: create ELF output file from object files. .SECONDARY : $(TARGET).elf .PRECIOUS : $(OBJ) %.elf: $(OBJ) @echo @echo $(MSG_LINKING) $@ $(CC) $(ALL_CFLAGS) $^ --output $@ $(LDFLAGS) # Compile: create object files from C source files. $(OBJDIR)/%.o : %.c @echo @echo $(MSG_COMPILING) $< $(CC) -c $(ALL_CFLAGS) $< -o $@ # Compile: create assembler files from C source files. %.s : %.c $(CC) -S $(ALL_CFLAGS) $< -o $@ # Create preprocessed source for use in sending a bug report. %.i : %.c $(CC) -E -mmcu=$(MCU) -I. $(CFLAGS) $< -o $@ # Target: clean project. clean: begin clean_list end clean_list : @echo @echo $(MSG_CLEANING) $(REMOVE) $(TARGET).hex $(REMOVE) $(TARGET).eep $(REMOVE) $(TARGET).cof $(REMOVE) $(TARGET).elf $(REMOVE) $(TARGET).map $(REMOVE) $(TARGET).sym $(REMOVE) $(TARGET).lss $(REMOVE) $(SRC:%.c=$(OBJDIR)/%.o) $(REMOVE) $(SRC:%.c=$(OBJDIR)/%.lst) $(REMOVE) $(SRC:.c=.s) $(REMOVE) $(SRC:.c=.d) $(REMOVE) $(SRC:.c=.i) $(REMOVEDIR) .dep # Create object files directory $(shell mkdir $(OBJDIR) 2>/dev/null) # Include the dependency files. -include $(shell mkdir .dep 2>/dev/null) $(wildcard .dep/*) # Listing of phony targets. .PHONY : all begin finish end sizebefore sizeafter gccversion \ build elf hex eep lss sym coff extcoff \ clean clean_list program debug gdb-config

拡張

 この説明では、1チャネルの処理に限定しています。  20ピンのAT90S2313は、ピンに余裕があるので、2チャネル、3チャネルに  拡張ができます。  時間の入力には、どのチャネルを使うのかとSTARTボタンではなくENTERボタン  を用意すると対応できます。  SSRの制御は、チャネル分だけ用意します。時間カウントは、ひとつのタスクに  して、どのチャネルがタイムアップしたかをフラグで判定します。  市販のタイマーは、3チャネルが限度なので、4チャネル以上のタイマーが  必要な場合は、USOを利用して定義するとあまりコストをかけず実現できます。  4チャネル以上のタイマーを実現したい場合、ATmega168を使うとよいでしょう。  Arduinoを実現するチップなので、安価に入手できますから。
目次

inserted by FC2 system