目次
前
次
応用例B 充電器
USOを、充電器に適用してみます。
20ピンのAT90S2313を利用します。
システム構成は、下図とします。
仕様は、以下とします。
DIPスイッチに時間を設定して、スタートボタンを押します。
設定された時間の間、SSR(Solid State Relay)に制御信号を
出力します。
設定時間の間、SSRにAC100Vを通電させて、直流電圧を
充電器に与えます。
システム構成から回路図を作成します。
充電は、OPアンプとトランジスタを利用して簡単に構成します。
1kΩに発生する電圧を、マルチメータで見ながら、可変抵抗のトリマー
を回します。
100mAを流したい場合は、100mAx1kΩ=0.1Vになる
ように、可変抵抗のトリマーを回せば充電電流を調節できます。
システム構成から、必要となるタスクを考えます。
- ボタンスイッチ状態を検出して、必要な仕事の割振り
- 時間から分を計算して、カウンタを設定
- 1分ごとの時間経過を管理
- 60秒経過で、フラグをセットと時間経過管理タスクに通知
- 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 ;
タスクの初期状態定義
タスクの関数を定義したので、初期状態を考えます。
- ボタンスイッチ状態を検出して、必要な仕事の割振り タスク0
- 時間から分を計算して、カウンタを設定 タスク1
- 1分ごとの時間経過を管理 タスク2
- 60秒経過で、フラグをセットと時間経過管理タスクに通知 タスク3
- 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の方向をまとめます。
- ビット6:なし :出力
- ビット5:なし :出力
- ビット4:DIPスイッチ:入力
- ビット3:DIPスイッチ:入力
- ビット2:ボタン :入力
- ビット1:DIPスイッチ:入力
- ビット0:DIPスイッチ:入力
この状態は、次のように指定します。
DDRD = 0b11100000 ; /* oooiiiii */
ポートBの方向をまとめます。
- ビット7:なし :出力
- ビット6:なし :出力
- ビット5:なし :出力
- ビット4:なし :出力
- ビット3:なし :出力
- ビット2:なし :出力
- ビット1:LED :出力
- ビット0:SSR制御信号:出力
この状態は、次のように指定します。
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を実現するチップなので、安価に入手できますから。
目次
前
次