正確な時間稼ぎをする(周期ハンドラ)
OSなしの タイマーAを使った
タイマーで正確な時間稼ぎをする に当たる処理をしてみます。
正確な時間で処理を実行する方法は μITronではいく通りもありますが、周期ハンドラを使った方法です。
ただ、3664ではタイマーAは、RTOSのシステム時間用に使用されている。
周期ハンドラは 設定した周期で、周期的に実行されるハンドラ そのままです。
ハンドラに登録したfunc関数が 設定の周期で実行される。
この関数内で LEDの点滅処理の切り替え起動させる。
プログラム
.c
/* ------------------------------------------------------------------------ */
/*IOポートでLEDを光らせる
P1-4 と P1-5 が1秒間隔で交互に点灯
起動ごとに点灯LEDが変わるtask1を
周期ハンドラで1秒間隔に起動する
周期ハンドラ func() 1秒間隔
/* ------------------------------------------------------------------------ */
#include "itron.h"
#include "kernel.h"
#include "kernel_id.h"
#include "h83664f.h"
#define SLP_TEST 1
/* メイン関数 */
int main()
{
sta_hos();
return 0;
}
/* 初期化ハンドラ */
void Initialize(VP_INT exinf)
{
/* ポート初期化設定 */
IO.PMR1.BYTE=0x00; /* 全て0にセットし、ポート使用に設定 0000 0000*/
IO.PCR1=0xFF; /* 全て1にセットし、出力に設定 1111 1111 */
IO.PUCR1.BYTE=0x00; /* 全て0にセットし、プルアップなし*/
act_tsk(TSKID_SAMPLE1);
}
unsigned char f=0;
void Task1(VP_INT exinf)
{
for ( ; ; )
{
slp_tsk(); // 自ら起床待ち状態に遷移する
if( f==1 ){
IO.PDR1.BIT.B4 = 1; // P14
IO.PDR1.BIT.B5 = 0;
f=0;
}else{
IO.PDR1.BIT.B4 = 0;
IO.PDR1.BIT.B5 = 1; // P15
f=1;
}
}
}
void func( VP_INT exinf){
iwup_tsk( TSKID_SAMPLE1 ); /* タスクを起床 */
}
.cfg
/* ------------------------------------------------------------------------ */
/* Hyper Operating System V4 サンプルプログラム */
/* コンフィギュレーションファイル */
/* */
/* Copyright (C) 1998-2003 by Project HOS */
/* http://sourceforge.jp/projects/hos/ */
/* ------------------------------------------------------------------------ */
INCLUDE("\"sample.h\"");
INCLUDE("\"ostimer.h\"");
/* HOS 独自の設定 */
HOS_KERNEL_HEAP(0); /* カーネルヒープの設定(省略時 0) */
HOS_TIM_TIC(1024, 125); /* タイムティックの設定(省略時 1/1 ) */
HOS_MAX_TPRI(8); /* 最大優先度(省略時 16) */
HOS_MIN_INTNO(19); /* 使用する割り込み番号の最小値(省略時 0) */
HOS_MAX_INTNO(23); /* 使用する割り込み番号の最大値(省略時 0) */
HOS_MAX_TSKID(8); /* 最大タスクID番号(省略時静的生成に必要なだけ) */
/* OS タイマ */
ATT_INI({TA_HLNG, 0, OsTimer_Initialize});
ATT_ISR({TA_HLNG, 0, 19, OsTimer_TimerHandler});
/* サンプルプログラム */
ATT_INI({TA_HLNG, 0, Initialize});
CRE_TSK(TSKID_SAMPLE1, {TA_HLNG, 1, Task1, 1, 256, NULL});
/* 周期ハンドラ */
CRE_CYC( CYC_ID, {TA_HLNG | TA_STA, 0 ,func, 1000,0} );
.h
#ifndef __PROJECT_HOS__sample_h__
#define __PROJECT_HOS__sample_h__
void Initialize(VP_INT exinf);
void Task1(VP_INT exinf);
void func(VP_INT exinf);
#endif /* __PROJECT_HOS__sample_h__ */
設定時間1000が来るごとにfuncが実行されるような設定にする。
周期ハンドラ設定
/* 周期ハンドラ */ CRE_CYC( CYC_ID, {TA_HLNG | TA_STA, 0 ,func, 1000,0} );
左から CYC_ID がIDとなる名前 、TA_HLNG | TA_STA は必要な設定、 funcが登録関数名、 1000は周期時間、0はTA_STA設定のときは周期ハンドラ生成から最初の起動までの時間 ( 詳細は P256 ITRON仕様書 周期ハンドラなど参照 )
周期ハンドラからよばれるfunc内で、LEDの点滅処理のタスクを起動させる。1秒ごとの周期に設定。
slp_tskでてきます。このサービスコールで、タスクは自分がスリープし、待ち状態(WAITTING)になる。
この処理では slp_tsk を使って実行中のLED点滅タスクを眠らせiwup_tskで起床し再び実行、これを繰り返すことで点滅動作をしています。
slp_tskで眠り wup_tsk 起床
・slp_tsk(スリープ) ←→ wup_tsk (起床)
・待ち状態(WAITTING) 実行状態
wup_tsk はハンドラからの場合はiwup_tskを使うようです。
仕様書にwup、iwup_tskの違いについての説明が見あたらなかったのですが、iwup_tskは非タスクコンテキスト用とのことらしい。
最初に イニシャライズで自動実行されると slp_tsk で眠る。
周期ハンドラで周期的にiwup_tsk が実行されると 起床してLED処理実行後、またスリープ の流れで、無限ループになっています。
実行結果
LEDのタスクは頭から実行されるたびにLEDを切り替えるので、 実行 → スリープ 起床 →実行 と起床して実行するごとにLEDが切り替わる。
これまで同様、1秒ごとの周期で LEDが切り替わります。
slp_tsk で眠らせ wup_tsk での実行は、
スリープ
↓ ↑
起床 実行
この流れを繰り返すループです。
タスクが実行されると、ループに入りそこで、目的処理の 最初か、または最後に 必ずslp_tskが実行される。wup_tsk が実行されるとまたそこから起きて実行、目的処理を
実行したあとはスリープという流れは無限ループでやるのが良いようです。
slp_tskを使っていますが act_tskとext_tskでも同様のことができます
act_tskを使うと 休止→実行 ext_tsk 実行→休止
slp_tskを使ったときと較べても 実行、待ち、休止などの状態の違いが関わってOSの難しさがでてきます。
実際、複数のタスクの休止、実行などにはタスクの優先順位、スケジューリングなどの処理の中身が関わってきますが、複数のタスク処理でないのでここでは簡潔です。
定期的に実行する処理は他にも同様のLEDの動作を割り込みハンドラを使ってもできます。
- twtter
- google+
- hatena