目次
前
次
応用例D ファンコントローラ
天井付近に熱気が停滞するので、デスクトップタイプ
のPersonalComputerの空冷ファン利用で、対流を促す
ことにしました。
仕様は、以下。
- ひとつのスイッチで、回転と停止を切り替え
- ファン回転中、LEDを点灯してモニタ
- スイッチONから5秒間、フルパワーで回転
- スイッチONから5秒間経過後、75%のDUTY比で回転
- マイコンにATMELのAVR利用
仕様から、タスクを考えます。
回転と停止のトリガースイッチを加えて
システムを構成。
図から、必要となるタスクを考えます。
- 回転と停止のトリガースイッチ処理
- 5秒間のフルパワー回転
- 75%DUTY比で回転
- 停止
4タスクとなったので、タスク0からタスク3で対応します。
マイクロコンピュータ選定
電池駆動できるマイクロコンピュータを想定し、電源電圧が
広い製品がよいので、ATMELのATtiny2313を利用。
トリガースイッチを利用するので、タイマー割込み
を使い、チャタリングを除去します。
トリガースイッチで、内部の状態をIDLE、RUNと切り替えます。
外部割込みを使う方法もありますが、周期タスクの中で
シフトレジスタを使えば、チャタリング除去は可能とし
ひとつのタスクで、チャタリング除去とシステムの状態
変更させます。
ハードウエア仕様は、以下。
タスク分割
4タスクに分割したので、タスク0からタスク3にします。
- タスク0 スイッチ変化による、システム状態変更
- タスク1 5秒間フルパワー回転
- タスク2 75%DUTY比で回転
- タスク3 中断
各タスクの動作概要を考えます。
タスク0
内部状態を示す変数stateを用意。
変数stateをトリガースイッチで、IDLEとRUNを入れ替え
state=IDLEでは、タスク1をREADYに設定
state=RUNでは、タスク3をREADYに設定
タスク1
ファンドライブ回路に、Hを出力
5秒経過後、タスク2をREADYに設定
その後、自分自身はSUSPENDに設定
タスク2
ファンドライブ回路に、H、Lを出力
HとLの比率を75:25となるように設定
タスク3
タスク0からREADYにされたなら、タスク2を
SUSPENDにし、ファンドライブ回路にLを出力
その後、自分自身はSUSPENDに設定
タスク初期状態設定
タスク概要から、タスク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のコードでは、以下。
/* shift */
sft_reg <<= 1 ;
sft_reg &= 0x07 ;
/* get data */
if ( get_pstate() & 0x01 ) { sft_reg |= ON ; }
/* judge */
if ( sft_reg == 3 ) {
if ( state == IDLE ) {
state = RUN ;
rsm_tsk( XSTART );
} else {
state = IDLE ;
rsm_tsk( XSTOP );
}
}
外部割込みでスイッチの状態を取得して
トリガー判定するので、周期処理による
タスク処理にします。
タスクIDを使うと、わかりにくいので
より判別しやすい文字列でタスクを指定
しています。
スイッチの状態は、関数get_pstate()で
取得できると仮定して、コード記述します。
チャタリング除去をすればよいので、20ms
ごとの周期タスクにします。
void tsk0_proc(void)
{
/* shift */
sft_reg <<= 1 ;
sft_reg &= 0x07 ;
/* get data */
if ( get_pstate() & 0x01 ) { sft_reg |= ON ; }
/* judge */
if ( sft_reg == 3 ) {
if ( state == IDLE ) {
state = RUN ;
rsm_tsk( XSTART );
} else {
state = IDLE ;
rsm_tsk( XSTOP );
}
}
/* cyclic 20ms */
wai_tsk( 2 );
}
タスク1
「開始から5秒間はフルパワーで回転」が与えられた仕様
なので、これを定義します。
マイコンのピンPB0から、Hを出力します。
put_fan( ON ) ;
ラッパー関数を用意済みとして
呼出すだけにしておきます。
5秒間は、周期処理として、5秒経過後
SUSPENDとしたいので、カウンタを用意し
周期処理とSUSPENDの判定をします。
if ( count == 5 ) {
count = 0 ;
rsm_tsk( XPWM );
slp_tsk();
} else {
wai_tsk( 100 );
}
まとめます。
void tsk1_proc(void)
{
/* rotate */
put_fan( ON ) ;
/* LED */
put_led( ON ) ;
/* increment */
count++ ;
/* judge */
if ( count == 5 ) {
count = 0 ;
rsm_tsk( XPWM );
slp_tsk();
} else {
wai_tsk( 100 );
}
}
カウンタをひとつ用意して、タスクXPWMをREADYに
して自身はSUSPENDになるのか、周期処理をするか
を判定するのがポイントです。
タスク2
「75%DUTYで回転」が与えられた仕様
なので、これを定義します。
ドライブ回路にH、Lを異なる時間で出力する
ために、システムコールwai_tskに与える数値を
カウンタにより変えます。
if ( dcount & 1 ) {
put_fan( ON ) ;
wai_tsk( 75 );
} else {
put_fan( OFF ) ;
wai_tsk( 25 );
}
カウンタの変化を付加しておきます。
void tsk2_proc(void)
{
/* increment */
dcount++ ;
/* judge */
if ( dcount & 1 ) {
put_fan( ON ) ;
wai_tsk( 75 );
} else {
put_fan( OFF ) ;
wai_tsk( 25 );
}
}
タスク3
タスク2をSUSPENDにすればよいので
その処理が終わったなら、自分自身を
SUSPENDにします。
void tsk3_proc(void)
{
/* stop */
sus_tsk( XPWM );
put_fan( OFF );
put_led( OFF );
/* exit */
slp_tsk();
}
コンフィグレーション
各タスクの内容を定義したので、コンフィグレータを利用
して、スケルトンのソースコードを生成します。
スケルトンソースコードの中にある、次の関数にタスク処理を定義。
- tsk0_proc
- tsk1_proc
- tsk2_proc
- tsk3_proc
main関数で、各タスクの初期状態を指定。
タスクIDは、よりわかりやすい文字列で表現します。
/* create tasks */
cre_tsk(XTRG ,tsk0_proc);
cre_tsk(XSTART,tsk1_proc);
cre_tsk(XPWM ,tsk2_proc);
cre_tsk(XSTOP ,tsk3_proc);
/* set states */
sta_tsk(XTRG ,TTS_READY);
sta_tsk(XSTART,TTS_SUSPEND);
sta_tsk(XPWM ,TTS_SUSPEND);
sta_tsk(XSTOP ,TTS_SUSPEND);
コンフィグレーションした場合、デフォルトで
すべてのタスクをSUSPENDとします。
クロスコンパイラのために、定義ファイルをインクルードして
必要な初期化をします。
main関数の中で、無限ループに入る前に、必要な初期化を
記述すると、次のようになります。(コンパイラにより、
割込み禁止、割込み許可の関数は異なります。)
/* initialize system */
user_initialize();
/* initialize monitor */
init_os();
/* create tasks */
cre_tsk(XTRG ,tsk0_proc);
cre_tsk(XSTART,tsk1_proc);
cre_tsk(XPWM ,tsk2_proc);
cre_tsk(XSTOP ,tsk3_proc);
/* set states */
sta_tsk(XTRG ,TTS_READY);
sta_tsk(XSTART,TTS_SUSPEND);
sta_tsk(XPWM ,TTS_SUSPEND);
sta_tsk(XSTOP ,TTS_SUSPEND);
/* enable interrupt */
ei();
関数user_initializeの中に、ポートの入出力関連の初期化
内蔵モジュール初期化、変数の定数設定を含めます。
タイマー割込み定義
USOのためにタイマーを1個利用します。
マイクロコンピュータとして、ATtiny2313を利用する
ので、システムクロックを4MHzを分周して1kHz
(=1ms)を生成します。
タイマーのプリスケーラを使わないでおきます。
4MHz/1=4000000
100Hzにするには、10回1msの割込みが発生した
とします。
タイマー1のコンペアマッチ割込みを利用するため、2つ
のレジスタに数値を設定します。
OCR1A = 3999 ;
OCR1B = 4500 ;
タイマー1の初期化は、モード指定、カウンタとレジスタ設定
タイマースタートの3処理にまとめます。
/* 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) ;
このコードは、関数user_initializeに含めます。
割込みが発生した場合は、WAITであるタスクを探して
カウンタを減算します。
カウンタが0になれば、WAITからREADYに変更します。
トリガーフラグを利用して、10msごとに関数timer_handler
を動かして対応します。
if ( tflag ) {
/* clear flag */
tflag = OFF ;
/* call */
timer_handler();
}
全ソースコード
実際にクロスコンパイラにかけ、テストしたソース
コードは、以下です。
#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);
/*--------------------------------*/
/* Insert user functions protoype */
/*--------------------------------*/
void user_initialize(void);
#define XTRG TSK_ID0
#define XSTART TSK_ID1
#define XPWM TSK_ID2
#define XSTOP TSK_ID3
#define IDLE 0
#define RUN IDLE+1
#define MASKFF 0xFF
volatile UBYTE sft_reg ;
volatile UBYTE state ;
volatile UBYTE count ;
volatile UBYTE dcount ;
#define FAN_BIT 1
#define LED_BIT 2
UBYTE get_pstate(void);
void put_fan(UBYTE x);
void put_led(UBYTE x);
/*------*/
/* main */
/*------*/
int main(void)
{
TCBP pcur_tsk ;
/* initialize monitor */
init_os();
/* create tasks */
cre_tsk(XTRG ,tsk0_proc);
cre_tsk(XSTART,tsk1_proc);
cre_tsk(XPWM ,tsk2_proc);
cre_tsk(XSTOP ,tsk3_proc);
/* set states */
sta_tsk(XTRG ,TTS_READY);
sta_tsk(XSTART,TTS_SUSPEND);
sta_tsk(XPWM ,TTS_SUSPEND);
sta_tsk(XSTOP ,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 */
sft_reg = 0 ;
state = IDLE ;
count = 0 ;
dcount = 0 ;
/* initialize I/O */
put_fan( OFF ) ;
put_led( OFF ) ;
}
/*----------------*/
/* task functions */
/*----------------*/
void tsk0_proc(void)
{
/* shift */
sft_reg <<= 1 ;
sft_reg &= 0x07 ;
/* get data */
if ( get_pstate() & 0x01 ) { sft_reg |= ON ; }
/* judge */
if ( sft_reg == 3 ) {
if ( state == IDLE ) {
state = RUN ;
rsm_tsk( XSTART );
} else {
state = IDLE ;
rsm_tsk( XSTOP );
}
}
/* cyclic 20ms */
wai_tsk( 2 );
}
/* XSTART */
void tsk1_proc(void)
{
/* rotate */
put_fan( ON ) ;
/* LED */
put_led( ON ) ;
/* increment */
count++ ;
/* judge */
if ( count == 5 ) {
count = 0 ;
rsm_tsk( XPWM );
slp_tsk();
} else {
wai_tsk( 100 );
}
}
/* XPWM */
void tsk2_proc(void)
{
/* increment */
dcount++ ;
/* judge */
if ( dcount & 1 ) {
put_fan( ON ) ;
wai_tsk( 75 );
} else {
put_fan( OFF ) ;
wai_tsk( 25 );
}
}
/* XSTOP */
void tsk3_proc(void)
{
/* stop */
sus_tsk( XPWM );
put_fan( OFF );
put_led( OFF );
/* exit */
slp_tsk();
}
/*------------------*/
/* 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 ;
}
}
UBYTE get_pstate(void)
{
UBYTE result ;
result = PINB ^ MASKFF ;
return result ;
}
void put_fan(UBYTE x)
{
if ( x ) { PORTB |= (1 << FAN_BIT) ; }
else { PORTB &= ~(1 << FAN_BIT) ; }
}
void put_led(UBYTE x)
{
if ( x ) { PORTB &= ~(1 << LED_BIT) ; }
else { PORTB |= (1 << LED_BIT) ; }
}
ハードウエア
インタフェース回路は、小さな基板にまとめました。
回路は、単純です。
LED、トランジスタ、抵抗、ダイオードがあるだけです。
利用するマイコン基板は、テスト用に作ったボードです。
ファンは、PC空冷用の大きめのものを使います。
目次
前
次