目次
前
次
応用例E ソーラーパネル用ロガー
手元に実験評価用のソーラーパネルがあります。
窓から日の光が入っている間、どの程度の発電しているかを
調べるためUSOを利用しました。さらにコントローラに仕立て
直してみました。
測定は、午前7時から午後3時として、瞬間と積算の電力を
表示できるようにします。
利用したマイコン計測ユニットは、以下。
H8/3052FOneに2行x16桁のLCDを接続してあります。
測定に必要な処理をタスクに分割してみました。
- TASK0 全体管理
- TASK1 瞬間電力測定
- TASK2 積算電力計算
- TASK3 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には、次の情報を表示します。
- 動作中か休止中か(run or idle)
- 瞬時電力数値
- 積算電力を6分ごとのバー
積算電力に関しては、次のバー表示としておきます。
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 ;
}
}
目次
前
次