目次

応用例E ソーラーパネル用ロガー

 手元に実験評価用のソーラーパネルがあります。




 窓から日の光が入っている間、どの程度の発電しているかを
 調べるためUSOを利用しました。さらにコントローラに仕立て
 直してみました。

 測定は、午前7時から午後3時として、瞬間と積算の電力を
 表示できるようにします。

 利用したマイコン計測ユニットは、以下。



 H8/3052FOneに2行x16桁のLCDを接続してあります。

 測定に必要な処理をタスクに分割してみました。

 各タスクで何をすべきなのか詳細を詰めます。


全体管理  測定期間は、午前7時から午後3時までの仕様なので  次の3タスクを動かすことにします。  時間を知るには、リアルタイムクロックを使うか  タイマーユニットから情報を貰うとします。  今回は、次のタイマーユニットを利用。  タイマーユニットからの接点信号で、測定時間内か外かを  知るので、シフトレジスタを利用してrising、fallingの  エッジを検出します。  測定継続と測定停止を、状態変数stateで管理すると  タスクは、次のように定義すればよいでしょう。 #define IDLE 0 #define RUN IDLE+1 #define MSB 0x80 UBYTE state ; UBYTE xsft ; UBYTE bflag ; UBYTE eflag ; UBYTE tflag ; void tsk0_proc(void) { /* edge handling */ if ( bflag == ON ) { /* clear event flag */ bflag = OFF ; /* update state */ state++ ; state &= ON ; /* control each task */ tflag = ON ; } if ( eflag == ON ) { /* clear event flag */ eflag = OFF ; /* update state */ state++ ; state &= ON ; /* control each task */ tflag = ON ; } /* update task state */ if ( tflag == ON ) { /* clear flag */ tflag = OFF ; /* RUN */ if ( state == RUN ) { rsm_tsk( TSK_ID1 ); rsm_tsk( TSK_ID2 ); rsm_tsk( TSK_ID3 ); } else { /* IDLE */ if ( state == IDLE ) { sus_tsk( TSK_ID1 ); sus_tsk( TSK_ID2 ); sus_tsk( TSK_ID3 ); } } /* detect edge */ xsft <<= 1 ; xsft &= 7 ; if ( P7DR & MSB ) { xsft |= ON ; } if ( xsft == 3 ) { bflag = ON ; } if ( xsft == 6 ) { eflag = ON ; } /* cyclic delay 1000ms */ wai_tsk( 100 ); } 瞬間電力測定  電力は、電流と電圧の積で求めるので、A/D変換器で  ソーラーパネルの出力電圧を入力します。  電圧がわかれば、電流を1Aとして計算します。  瞬間電力を常に計算するのは無駄が多いので  1000msごとに計測し、メモリに保存します。  周期的測定には、システムコールwai_tskを利用。 UWORD xpower[60] ; UBYTE idx ; UBYTE cflag ; void tsk1_proc(void) { UWORD adv ; UWORD power ; /* get voltage */ adv = get_adv(0) ; /* calculate */ power = adv * MULX ; /* store */ *(xpower+idx) = power ; /* update index */ idx++; if ( idx == 60 ) { idx = 0 ; /* do calculate */ cflag = ON ; } /* cyclic delay 1000ms */ wai_tsk( 100 ); }  A/D変換器から電圧を入力し、電圧から電力を求めて  メモリに保存。積算電力を計算するタスクのために  イベント通知フラグをセットします。  60データを取得すると、1分間の瞬時電力をメモリに  保存したのと同じ。 積算電力計算  瞬時電力は1分間分メモリに保存されているので  バッファに転送し、6分間の積算電力の合計を  求めます。  1分間の経過は、フラグで通知されので  フラグの状態を見て、作動させます。 UWORD xpower[60] ; UWORD bpower[60] ; ULONG ypower[10] ; void tsk2_proc(void) { UBYTE i ; ULONG sum ; /* judge */ if ( cflag == OFF ) return ; /* copy */ for ( i = 0 ; i < 60 ; i++ ) { *(bpower+i) = *(xpower+i); } /* sum */ sum = 0 ; for ( i = 0 ; i < 60 ; i++ ) { sum += *(bpower+i); } /* shift */ for ( i = 0 ; i < 9 ; i++ ) { *(ypower+i) = *(ypower+i+1); } /* store */ *(ypower+9) = sum ; } LCD表示 LCDには、次の情報を表示します。  積算電力に関しては、次のバー表示としておきます。  LCDの独自フォントを使うので、配列にパターンを  生成し、それらを番号をつけて管理します。  パターンを配列で用意。 char xpat0[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} ; char xpat1[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F} ; char xpat2[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x1F} ; char xpat3[] = {0x00,0x00,0x00,0x00,0x00,0x1F,0x1F,0x1F} ; char xpat4[] = {0x00,0x00,0x00,0x00,0x1F,0x1F,0x1F,0x1F} ; char xpat5[] = {0x00,0x00,0x00,0x1F,0x1F,0x1F,0x1F,0x1F} ; char xpat6[] = {0x00,0x00,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F} ; char xpat7[] = {0x00,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F} ;  LCD内部バッファに、パターンを入れておきます。 void set_cg(char xadr,char *ptr) { char loop ; /* set address */ xadr <<= 3 ; /* shift font address */ xadr |= 0x40 ; /* add function command */ /* store bit pattern */ for ( loop = 0 ; loop < 8 ; loop++ ) { /* set address */ put_lcd_function(xadr); /* set data */ put_lcd_data( *ptr ); /* increment address */ xadr++ ; /* next bit pattern */ ptr++ ; } }  これらを初期化時に利用。 set_cg(0,(char *)(xpat0+0)) ; set_cg(1,(char *)(xpat1+0)) ; set_cg(2,(char *)(xpat2+0)) ; set_cg(3,(char *)(xpat3+0)) ; set_cg(4,(char *)(xpat4+0)) ; set_cg(5,(char *)(xpat5+0)) ; set_cg(6,(char *)(xpat6+0)) ; set_cg(7,(char *)(xpat7+0)) ;  キャラクタとして登録されたなら  呼び出すときには、番号の0から  7を与えるだけで済ませられます。  表示内容は、配列ypowerに含まれているので  0から7の値になるように変換後、表示する  となります。  タスク3は、次のように定義するだけ。 void tsk3_proc(void) { char loop ; char msg[9] ; /* set font access pointer */ *(msg+10) = '\0' ; for ( loop = 0 ; loop < 10 ; loop++ ) { *(msg+loop) = (char)(*(ypower+loop) / 100) ; } /* show */ put_lcd_str(1,0,(char *)(msg+0)) ; }  ハイブリッド車の瞬間燃費と時間を区切っての燃費表示が  バーになっていて、意外にわかりやすいと思って、バーの  表示を選択しました。  エコ運転になっていると、「葉っぱ」がついたりで  わかりやすいですが、運転ではないのでバーのみに。
全ソースコード  実際にクロスコンパイラにかけ、試験した  ソースコードは、以下。 #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); void user_initialize(void); void set_cg(char xadr,char *ptr) { char loop ; /* set address */ xadr <<= 3 ; /* shift font address */ xadr |= 0x40 ; /* add function command */ /* store bit pattern */ for ( loop = 0 ; loop < 8 ; loop++ ) { /* set address */ put_lcd_function(xadr); /* set data */ put_lcd_data( *ptr ); /* increment address */ xadr++ ; /* next bit pattern */ ptr++ ; } } void put_lcd_str(UBYTE r,UBYTE c,UBYTE *ptr) { UBYTE adr ; /* check range */ if ( r > 1 ) return ; if ( c > 7 ) return ; /* set address */ adr = 0x00 ; if ( r ) { adr += 0x40 ; } adr += c ; adr |= MASK80 ; put_lcd_function(adr); /* send character */ for ( adr = c ; adr < 16 ; adr++ ) { put_lcd_data(*ptr); ptr++ ; } } void put_lcd_function(UBYTE xdat) { put_lcd_primitive(OFF,xdat); } void put_lcd_data(UBYTE xdat) { put_lcd_primitive(ON,xdat); } void put_lcd_primitive(UBYTE which,UBYTE dat) { put_lcd_p4(which,dat & MASKF0); put_lcd_p4(which,(dat << 4) & MASKF0); } void put_lcd_p4(UBYTE which,UBYTE dat) { /* LCD_E : OFF */ PORTD &= ~(1 << LCD_E) ; /* LCD_RS : ON or OFF */ PORTD &= ~(1 << LCD_RS) ; if ( which ) { PORTD |= (1 << LCD_RS) ;} /* nibble */ { /* clear upper nibble */ PORTD &= MASK0F ; /* impress upper nibble */ PORTD |= (dat & MASKF0) ; /* trigger H */ PORTD |= (1 << LCD_E) ; /* delay */ delay_ms(1); /* trigger L */ PORTD &= ~(1 << LCD_E) ; } } #define IDLE 0 #define RUN IDLE+1 #define MSB 0x80 UBYTE state ; UBYTE xsft ; UBYTE bflag ; UBYTE eflag ; UBYTE tflag ; UWORD xpower[60] ; UWORD bpower[60] ; ULONG ypower[10] ; UBYTE idx ; UBYTE cflag ; char xpat0[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} ; char xpat1[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F} ; char xpat2[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x1F} ; char xpat3[] = {0x00,0x00,0x00,0x00,0x00,0x1F,0x1F,0x1F} ; char xpat4[] = {0x00,0x00,0x00,0x00,0x1F,0x1F,0x1F,0x1F} ; char xpat5[] = {0x00,0x00,0x00,0x1F,0x1F,0x1F,0x1F,0x1F} ; char xpat6[] = {0x00,0x00,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F} ; char xpat7[] = {0x00,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F} ; /*------*/ /* main */ /*------*/ int main(void) { TCBP pcur_tsk ; /* initialize monitor */ init_os(); /* create tasks */ cre_tsk(TASK_ID0,tsk0_proc); cre_tsk(TASK_ID1,tsk1_proc); cre_tsk(TASK_ID2,tsk2_proc); cre_tsk(TASK_ID3,tsk3_proc); /* set states */ sta_tsk(TASK_ID0,TTS_READY); sta_tsk(TASK_ID1,TTS_SUSPEND); sta_tsk(TASK_ID2,TTS_SUSPEND); sta_tsk(TASK_ID3,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 */ set_cg(0,(char *)(xpat0+0)) ; set_cg(1,(char *)(xpat1+0)) ; set_cg(2,(char *)(xpat2+0)) ; set_cg(3,(char *)(xpat3+0)) ; set_cg(4,(char *)(xpat4+0)) ; set_cg(5,(char *)(xpat5+0)) ; set_cg(6,(char *)(xpat6+0)) ; set_cg(7,(char *)(xpat7+0)) ; } /*----------------*/ /* task functions */ /*----------------*/ void tsk0_proc(void) { /* edge handling */ if ( bflag == ON ) { /* clear event flag */ bflag = OFF ; /* update state */ state++ ; state &= ON ; /* control each task */ tflag = ON ; } if ( eflag == ON ) { /* clear event flag */ eflag = OFF ; /* update state */ state++ ; state &= ON ; /* control each task */ tflag = ON ; } /* update task state */ if ( tflag == ON ) { /* clear flag */ tflag = OFF ; /* RUN */ if ( state == RUN ) { rsm_tsk( TSK_ID1 ); rsm_tsk( TSK_ID2 ); rsm_tsk( TSK_ID3 ); } else { /* IDLE */ if ( state == IDLE ) { sus_tsk( TSK_ID1 ); sus_tsk( TSK_ID2 ); sus_tsk( TSK_ID3 ); } } /* detect edge */ xsft <<= 1 ; xsft &= 7 ; if ( P7DR & MSB ) { xsft |= ON ; } if ( xsft == 3 ) { bflag = ON ; } if ( xsft == 6 ) { eflag = ON ; } /* cyclic delay 1000ms */ wai_tsk( 100 ); } void tsk1_proc(void) { UWORD adv ; UWORD power ; /* get voltage */ adv = get_adv(0) ; /* calculate */ power = adv * MULX ; /* store */ *(xpower+idx) = power ; /* update index */ idx++; if ( idx == 60 ) { idx = 0 ; /* do calculate */ cflag = ON ; } /* cyclic delay 1000ms */ wai_tsk( 100 ); } void tsk2_proc(void) { UBYTE i ; ULONG sum ; /* judge */ if ( cflag == OFF ) return ; /* copy */ for ( i = 0 ; i < 60 ; i++ ) { *(bpower+i) = *(xpower+i); } /* sum */ sum = 0 ; for ( i = 0 ; i < 60 ; i++ ) { sum += *(bpower+i); } /* shift */ for ( i = 0 ; i < 9 ; i++ ) { *(ypower+i) = *(ypower+i+1); } /* store */ *(ypower+9) = sum ; } void tsk3_proc(void) { char loop ; char msg[9] ; /* set font access pointer */ *(msg+10) = '\0' ; for ( loop = 0 ; loop < 10 ; loop++ ) { *(msg+loop) = (char)(*(ypower+loop) / 100) ; } /* show */ put_lcd_str(1,0,(char *)(msg+0)) ; } /*------------------*/ /* 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 ; } }
目次

inserted by FC2 system