目次
前
次
応用例C 定時撮影
3箇所に、撮影用カメラを置き、撮影指示を与えれば
撮影が完了するシステムがあるとします。
このシステムで、撮影用カメラをA、B、Cとして、20秒ごと
30秒ごと、50秒ごとに撮影指示を与える処理を、USOで実現
してみます。
開始と終了のトリガースイッチを入れて
システムを構成しています。
システム構成図から、必要となるタスクを考えます。
- 開始と終了のトリガースイッチ処理
- カメラAに撮影指示
- カメラBに撮影指示
- カメラCに撮影指示
4タスクとなったので、タスク0からタスク3とします。
マイクロコンピュータ選定
電池駆動ができるマイクロコンピュータを想定して、電源電圧が
広い製品がよいので、ATMELのATmega168を利用します。
トリガースイッチを利用するので、マイクロコンピュータには
外部割込みで対応することが望ましいです。ATMELのAVRには
最低ひとつの外部割込みがあるので、それを利用します。
外部割込みは、falling edgeを利用します。
外部割込みが入る度に、内部状態を変更します。
タスク分割
4タスクに分割したので、タスク0からタスク3にします。
- タスク0 スイッチ変化による、他タスク起動
- タスク1 カメラAに撮影指示
- タスク2 カメラBに撮影指示
- タスク3 カメラCに撮影指示
各タスクの動作概要を考えます。
タスク0
内部状態を示す変数performを用意します。
変数performを外部割込みにより、IDLEとRUNを
交互に設定します。
perform=IDLEでは、タスク1〜3をSUSPENDします。
perform=RUNでは、タスク1〜3をREADYにします。
タスク1
外部装置に、トリガーを与えます。
外部装置はカメラAとして、20秒ごとに
トリガーを与えます。
タスク2
外部装置であるカメラBに、トリガーを与えます。
30秒ごとにトリガーを与えます。
タスク3
外部装置であるカメラCに、トリガーを与えます。
50秒ごとにトリガーを与えます。
タスク初期状態設定
タスク概要から、タスク0はREADY、他のタスクは
SUSPENDにしておけばよいでしょう。
まとめると、以下です。
- タスク0 READY
- タスク1 SUSPEND
- タスク2 SUSPEND
- タスク3 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);
タスク動作定義
各タスクの動作を定義します。
タスク0
「トリガー判定による、他タスク起動。」が与えられた
仕様のひとつなので、これを定義します。
シーケンスを、言葉で記述します。
外部割込みのトリガーを判定
トリガーにより、内部状態を変更
内部状態により、他のタスクの状態変更
Cのコードでは、以下のようにします。
if ( eflag == ON ) {
eflag = OFF ;
perform++ ;
perform &= 1 ;
if ( perform == RUN ) {
rsm_tsk(TSK_ID1);
rsm_tsk(TSK_ID2);
rsm_tsk(TSK_ID3);
} else {
sus_tsk(TSK_ID1);
sus_tsk(TSK_ID2);
sus_tsk(TSK_ID3);
}
}
外部割込みでスイッチの状態を取得して
トリガー判定するので、周期処理による
タスク処理にします。
10秒ごとの周期タスクにします。
void tsk0_proc(void)
{
if ( eflag == ON ) {
eflag = OFF ;
perform++ ;
perform &= 1 ;
if ( perform == RUN ) {
rsm_tsk(TSK_ID1);
rsm_tsk(TSK_ID2);
rsm_tsk(TSK_ID3);
} else {
sus_tsk(TSK_ID1);
sus_tsk(TSK_ID2);
sus_tsk(TSK_ID3);
}
}
/* cycle 10sec = 10 ms * 1000 */
wai_tsk( 1000 );
}
タスク1
「カメラAへのトリガー出力」が与えられた仕様
なので、これを定義します。
マイコンのピンPB0から、トリガーを出力します。
tmp = PORTB ;
tmp |= 0x01 ;
PORTB = tmp ;
tmp &= ~0x01 ;
PORTB = tmp ;
周期を20秒とするので、システムコールを使います。
wai_tsk( 2000 );
まとめます。
void tsk1_proc(void)
{
/* send trigger */
tmp = PORTB ;
tmp |= 0x01 ;
PORTB = tmp ;
tmp &= ~0x01 ;
PORTB = tmp ;
/* cycle 20sec */
wai_tsk( 2000 );
}
タスク2
「カメラBへのトリガー出力」が与えられた仕様
なので、これを定義します。
トリガー出力をPB1からとします。
tmp = PORTB ;
tmp |= 0x02 ;
PORTB = tmp ;
tmp &= ~0x02 ;
PORTB = tmp ;
周期を30秒とします。
void tsk2_proc(void)
{
/* send trigger */
tmp = PORTB ;
tmp |= 0x02 ;
PORTB = tmp ;
tmp &= ~0x02 ;
PORTB = tmp ;
/* cycle 30sec */
wai_tsk( 3000 );
}
タスク3
タスク1、2と似た処理なので、トリガー出力と
周期処理を仕様としてまとめます。
void tsk3_proc(void)
{
/* send trigger */
tmp = PORTB ;
tmp |= 0x04 ;
PORTB = tmp ;
tmp &= ~0x04 ;
PORTB = tmp ;
/* cycle 50sec */
wai_tsk( 5000 );
}
コンフィグレーション
各タスクの内容を定義したので、コンフィグレータを利用
して、スケルトンのソースコードを生成します。
スケルトンソースコードの中にある、次の関数にタスク処理
を定義します。
- tsk0_proc
- tsk1_proc
- tsk2_proc
- tsk3_proc
main関数で、各タスクの初期状態を指定します。
cre_tsk(TSK_ID0,tsk0_proc);
cre_tsk(TSK_ID1,tsk1_proc);
cre_tsk(TSK_ID2,tsk2_proc);
cre_tsk(TSK_ID3,tsk3_proc);
sta_tsk(TSK_ID0,TTS_READY);
sta_tsk(TSK_ID1,TTS_SUSPEND);
sta_tsk(TSK_ID2,TTS_SUSPEND);
sta_tsk(TSK_ID3,TTS_SUSPEND);
コンフィグレーションした場合、デフォルトで
すべてのタスクをREADYとします。
クロスコンパイラのために、定義ファイルをインクルードして
必要な初期化をします。
main関数の中で、無限ループに入る前に、必要な初期化を
記述すると、次のようになります。(コンパイラにより、
割込み禁止、割込み許可の関数は異なります。)
/* 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);
cre_tsk(TSK_ID3,tsk3_proc);
sta_tsk(TSK_ID0,TTS_READY);
sta_tsk(TSK_ID1,TTS_SUSPEND);
sta_tsk(TSK_ID2,TTS_SUSPEND);
sta_tsk(TSK_ID3,TTS_SUSPEND);
/* enable interrupt */
ei();
関数user_initializeの中に、ポートの入出力関連の初期化
内蔵モジュール初期化、変数の定数設定を含めます。
外部割込み定義
外部割込みを使うためには、入力ピンとモードを
設定しなければなりません。
入力ピンは、INT0(PD2)を使います。
スイッチ変化は、falling edgeでとらえます。
外部割込み発生は、フラグeflagで通知します。
ATmega168では、初期設定を以下とします。
/* set INT1 interrupt */
EICRA = (1 << ISC01) ;
/* enable interrupt (global) */
EIMSK = (1 << INT0) ;
フラグは、割込みハンドラで設定します。
ISR(INT0_vect)
{
eflag = ON ;
}
タイマー割込み定義
USOのためにタイマーを1個利用します。
マイクロコンピュータとして、ATmega168を利用する
と仮定し、システムクロックを16MHzを分周して100Hz
(=10ms)を生成します。
タイマーのプリスケーラを利用して、8分周します。
16MHz/8=2000000
100Hzを生成するためには、20000分周すれば充分です。
タイマー1のコンペアマッチ割込みを利用するため、2つ
のレジスタに数値を設定します。
OCR1A = 19999 ;
OCR1B = 40000 ;
タイマー1の初期化は、モード指定、カウンタとレジスタ設定
タイマースタートの3処理にまとめます。
/* compare match (1/8) -> 2MHz */
TCCR1B = (1 << WGM12) | (1 << CS11) ;
/* clear counter */
TCNT1 = 0 ;
/* set initial values */
OCR1A = 1999 ;
OCR1B = 4600 ;
/* Enable Compare match interruption */
EIMSK = (1 << OCIE1A) ;
このコードは、関数user_initializeに含めます。
割込みが発生した場合は、WAITであるタスクを探して
カウンタを減算します。
カウンタが0になれば、WAITからREADYに変更します。
ISR(TIMER1_COMPA_vect)
{
UWORD xtmp;
UBYTE loop;
/* 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 ;
}
}
全ソースコード
実際にクロスコンパイラにかけ、テストしたソース
コードは、以下です。
#include <avr/io.h>
#include <avr/interrupt.h>
#define OFF 0
#define ON OFF+1
#define NO OFF
#define YES OFF+1
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 TSK_ID_MAX 4
#define TSK_ID0 0
#define TSK_ID1 1
#define TSK_ID2 2
#define TSK_ID3 3
TCBP tcb[TSK_ID_MAX];
#define TTS_SUSPEND 0
#define TTS_WAIT TTS_SUSPEND+1
#define TTS_READY TTS_SUSPEND+2
volatile UBYTE ready ;
volatile UBYTE suspend;
volatile UBYTE waitq ;
volatile UBYTE run_tsk;
/*------------------------*/
/* 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);
/*--------------------------------*/
/* Insert user functions protoype */
/*--------------------------------*/
void user_initialize(void);
volatile UBYTE perform ;
volatile UBYTE eflag ;
#define IDLE 0
#define RUN IDLE+1
#define CAM_A 0
#define CAM_B 1
#define CAM_C 2
#define TIM_0 1000
#define TIM_A 2000
#define TIM_B 3000
#define TIM_C 5000
/*------*/
/* 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);
cre_tsk(TSK_ID3,tsk3_proc);
sta_tsk(TSK_ID0,TTS_READY);
sta_tsk(TSK_ID1,TTS_SUSPEND);
sta_tsk(TSK_ID2,TTS_SUSPEND);
sta_tsk(TSK_ID3,TTS_SUSPEND);
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 ;
}
/*-----------------------*/
/* Insert user functions */
/*-----------------------*/
void user_initialize(void)
{
/* PORT C */
PORTC = 0b00000000 ; /* 00000000 */
DDRC = 0b11111111 ; /* oooooooo */
/* PORT B */
PORTB = 0b00000000 ; /* 00000000 */
DDRB = 0b11111111 ; /* oooooooo */
/* PORT D */
PORTD = 0b00000100 ; /* 00000100 */
DDRD = 0b11111011 ; /* oooooioo */
/* initialize timer/counter1 */
{
/* compare match (1/8) -> 2MHz */
TCCR1B = (1 << WGM12) | (1 << CS11) ;
/* clear counter */
TCNT1 = 0 ;
/* set initial values */
OCR1A = 1999 ;
OCR1B = 4600 ;
}
/* set TIMER1 interrupt */
TIMSK1 = (1 << OCIE1A) ;
/* set INT1 interrupt */
EICRA = (1 << ISC01) ;
/* enable interrupt (global) */
EIMSK = (1 << INT0) ;
/* clear flag */
eflag = OFF ;
/* set initial state */
perform = IDLE ;
}
/* EXTERNAL INT0 */
ISR(INT0_vect)
{
eflag = ON ;
}
/* timer1 interrupt */
ISR(TIMER1_COMPA_vect)
{
UBYTE xtmp;
UBYTE loop;
/* 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 ;
}
}
/*----------------*/
/* task functions */
/*----------------*/
void tsk0_proc(void)
{
if ( eflag == ON ) {
/* clear flag */
eflag = OFF ;
/* update state */
perform++ ;
perform &= 1 ;
/* judge */
if ( perform == RUN ) {
rsm_tsk(TSK_ID1);
rsm_tsk(TSK_ID2);
rsm_tsk(TSK_ID3);
} else {
sus_tsk(TSK_ID1);
sus_tsk(TSK_ID2);
sus_tsk(TSK_ID3);
}
}
/* cycle 10sec = 10 ms * 1000 */
wai_tsk( TIM_0 );
}
/* camera #A handling */
void tsk1_proc(void)
{
UBYTE tmph ;
UBYTE tmpl ;
/* send trigger */
tmph = PORTB ;
tmpl = tmpl ;
tmph |= (1 << CAM_A) ;
PORTB = tmph ;
tmpl &= ~(1 << CAM_A) ;
PORTB = tmpl ;
/* cycle 20sec = 10 ms * 2000 */
wai_tsk( TIM_A );
}
/* camera #B handling */
void tsk2_proc(void)
{
UBYTE tmph ;
UBYTE tmpl ;
/* send trigger */
tmph = PORTB ;
tmpl = tmpl ;
tmph |= (1 << CAM_B) ;
PORTB = tmph ;
tmpl &= ~(1 << CAM_B) ;
PORTB = tmpl ;
/* cycle 30sec = 10 ms * 3000 */
wai_tsk( TIM_B );
}
/* camera #C handling */
void tsk3_proc(void)
{
UBYTE tmph ;
UBYTE tmpl ;
/* send trigger */
tmph = PORTB ;
tmpl = tmpl ;
tmph |= (1 << CAM_C) ;
PORTB = tmph ;
tmpl &= ~(1 << CAM_C) ;
PORTB = tmpl ;
/* cycle 50sec = 10 ms * 5000 */
wai_tsk( TIM_C );
}
/*------------------*/
/* 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 ) ;
}
目次
前
次