目次
前
次
応用例B データロガー
USOを、データロガーに適用してみます。
データロガーで測定する数値は、気温、湿度、気圧とします。
求めた数値は、EEPROMに保存し、測定周期は6分とします。
システム構成は、下図とします。
仕様は、以下とします。
リアルタイムクロックから、時刻を入力し分が00、06、12、18、
24、30、36、42、48、54であるとき、センサーからデータ入力します。
数値データを、2バイトの整数に変換し、EEPROMに保存します。
システム構成図から、必要となるタスクを考えます。
- リアルタイムクロックの時刻を判定し、他のタスク起動
- 温度センサーからA/Dコンバータ経由で数値入力
- 湿度センサーからA/Dコンバータ経由で数値入力
- 気圧センサーからA/Dコンバータ経由で数値入力
- 気温、湿度、気圧を、EEPROMに保存
5タスクとなったので、タスク0からタスク4とします。
マイクロコンピュータ選定
電池駆動ができるマイクロコンピュータを想定して、電源電圧が
広い製品がよいので、ATMELのAT90S8535を利用します。
A/Dコンバータを利用するので、マイクロコンピュータには
A/Dコンバータ内蔵タイプが望ましいのです。AT90S8535は8
チャネルのA/Dコンバータを内蔵しています。
1時間に10回データを保存するので、1時間で60バイト。
24時間で、1440バイトとなります。
内蔵EEPROMでは、容量が不足するので、外付けとします。
リアルタイムクロックは、IICバス接続のものがあるので、これを
使います。
タスク分割
5タスクとなったので、タスク0からタスク4にします。
- タスク0 時刻判定による、他タスク起動
- タスク1 温度センサー処理
- タスク2 湿度センサー処理
- タスク3 気圧センサー処理
- タスク4 データ保存
各タスクの動作概要を考えます。
タスク0
リアルタイムクロックから時刻を入力します。
分が0か6の倍数であれば、温度、湿度、気圧の数値
をデータ入力し、計算するタスクを起動します。
データ保存指示を出します。
タスク1
A/Dコンバータを使い、温度センサーから数値を入力します。
数値を計算し、変数thermometerに保存します。
数値を変数に保存したならば、休眠します。
タスク2
A/Dコンバータを使い、湿度センサーから数値を入力します。
数値を計算し、変数humidetyに保存します。
数値を変数に保存したならば、休眠します。
タスク3
A/Dコンバータを使い、気圧センサーから数値を入力します。
数値を計算し、変数pressureに保存します。
数値を変数に保存したならば、休眠します。
タスク4
データ保存指示を受けて、3変数の値をEEPROMに保存します。
タスク初期状態設定
各タスクの動作概要から、タスク0とタスク4はREADY
にしておけばよいでしょう。
タスク1からタスク3は、タスク0から起動されるので
SUSPENDにしておけば充分です。
まとめると、以下です。
- タスク0 READY
- タスク1 SUSPEND
- タスク2 SUSPEND
- タスク3 SUSPEND
- タスク4 READY
これらが確定していると、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);
sta_tsk(TSK_ID4,TTS_READY);
タスク動作定義
各タスクの動作を定義します。
タスク0
「時刻判定による、他タスク起動。」が与えられた
仕様のひとつなので、これを定義します。
シーケンスを、言葉で記述します。
リアルタイムクロックから分を入力
入力した値が6の倍数ならば、他タスク起動
Cのコードでは、以下のようにします。
minute = get_time(MINUTE);
if ( (minute % 6) == 0 ) {
rsm_tsk(TSK_ID0);
rsm_tsk(TSK_ID1);
rsm_tsk(TSK_ID2);
}
便利な関数get_timeは、実装時に考えます。
もうひとつの機能が必要でした。
「データ保存指示。」があるので、フラグを
利用して、タスク4に指示を出します。
store_flag = ON ;
6分ごとの周期処理なので、システムコールで待ち時間を指定します。
UBYTE store_flag ;
void tsk0_proc(void)
{
UBYTE minute ;
minute = get_time(MINUTE);
if ( (minute % 6) == 0 ) {
rsm_tsk(TSK_ID0);
rsm_tsk(TSK_ID1);
rsm_tsk(TSK_ID2);
store_flag = ON ;
}
wai_tsk( 36000 );
}
タスク1
「温度センサー処理」が与えられた仕様
なので、これを定義します。
A/Dコンバータで、温度センサーから数値を入力します。
val = get_analog( TH_CHANNEL ) ;
便利な関数get_analogは、実装時に考えます。
数値を計算し、変数thermometerに保存します。
thermometer = calculate_thermometer( val );
関数calculate_thermometerは、実装時に考えます。
数値を変数保存後、休眠すればよいので、システムコールを使います。
slp_tsk();
まとめます。
#define TH_CHANNEL 0
UWORD thermometer ;
void tsk1_proc(void)
{
UWORD val ;
val = get_analog( TH_CHANNEL ) ;
thermometer = calculate_thermometer( val );
slp_tsk();
}
タスク2
「湿度センサー処理」が与えられた仕様
なので、これを定義します。
A/Dコンバータで、湿度センサーから数値入力後、変数に保存します。
val = get_analog( HU_CHANNEL ) ;
humidety = calculate_humidety( val );
関数calculate_humidetyは、実装時に考えます。
数値を変数保存後、休眠するとして、まとめます。
#define HU_CHANNEL 1
UWORD humidety ;
void tsk2_proc(void)
{
UWORD val ;
val = get_analog( HU_CHANNEL ) ;
humidety = calculate_humidety( val );
slp_tsk();
}
タスク3
「気圧センサー処理」が与えられた仕様
なので、これを定義します。
#define PR_CHANNEL 2
UWORD pressure ;
void tsk3_proc(void)
{
UWORD val ;
val = get_analog( PR_CHANNEL ) ;
pressure = calculate_pressure( val );
slp_tsk();
}
タスク4
「データ保存」が与えられた仕様
なので、これを定義します。
フラグの状態をみて、3つの数値を保存します。
if ( store_flag == ON ) {
store_flag = OFF ;
store_eeprom(thermometer、humidety、pressure);
}
関数storeは、実装時に定義します。
まとめると、以下です。
void tsk4_proc(void)
{
if ( store_flag == ON ) {
store_flag = OFF ;
store_eeprom(thermometer、humidety、pressure);
}
}
動作再検討
5タスクに分割して、各タスクを定義しましたが、タスク
1から3は、同じ動作をしているだけです。
実際には、タスクは3つで充分でした。
もう一度、タスクの初期状態とタスク関数を定義します。
- タスク0 READY
- タスク1 SUSPEND
- タスク2 READY
USOのシステムコールで初期状態を設定します。
sta_tsk(TSK_ID0,TTS_READY);
sta_tsk(TSK_ID1,TTS_SUSPEND);
sta_tsk(TSK_ID2,TTS_READY);
タスク0は、次のように定義します。
UBYTE store_flag ;
void tsk0_proc(void)
{
UBYTE minute ;
minute = get_time(MINUTE);
if ( (minute % 6) == 0 ) {
rsm_tsk(TSK_ID0);
store_flag = ON ;
}
wai_tsk( 36000 );
}
タスク1では、3センサーの処理をまとめます。
#define TH_CHANNEL 0
#define HU_CHANNEL 1
#define PR_CHANNEL 2
UWORD thermometer ;
UWORD humidety ;
UWORD pressure ;
void tsk1_proc(void)
{
UWORD val ;
val = get_analog( TH_CHANNEL ) ;
thermometer = calculate_thermometer( val );
val = get_analog( HU_CHANNEL ) ;
humidety = calculate_humidety( val );
val = get_analog( PR_CHANNEL ) ;
pressure = calculate_pressure( val );
slp_tsk();
}
タスク2用の関数は、関数名を変更するだけです。
void tsk2_proc(void)
{
if ( store_flag == ON ) {
store_flag = OFF ;
store_eeprom(thermometer、humidety、pressure);
}
}
コンフィグレーション
各タスクの内容を定義したので、コンフィグレータを利用
して、スケルトンのソースコードを生成します。
スケルトンソースコードの中にある、次の関数にタスク処理
を定義します。
- tsk0_proc
- tsk1_proc
- tsk2_proc
main関数で、各タスクの初期状態を指定します。
cre_tsk(TSK_ID0,tsk0_proc);
cre_tsk(TSK_ID1,tsk1_proc);
cre_tsk(TSK_ID2,tsk2_proc);
sta_tsk(TSK_ID0,TTS_READY);
sta_tsk(TSK_ID1,TTS_SUSPEND);
sta_tsk(TSK_ID2,TTS_READY);
コンフィグレーションした場合、デフォルトで
すべてのタスクをREADYとします。
クロスコンパイラのために、定義ファイルをインクルードして
必要な初期化をします。
main関数の中で、無限ループに入る前に、必要な初期化を
記述すると、次のようになります。(コンパイラにより、
割込み禁止、割込み許可の関数は異なります。)
/* disable interrupt */
di();
/* 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);
sta_tsk(TSK_ID0,TTS_READY);
sta_tsk(TSK_ID1,TTS_SUSPEND);
sta_tsk(TSK_ID2,TTS_READY);
/* enable interrupt */
ei();
関数user_initializeの中に、ポートの入出力関連の初期化
内蔵モジュール初期化、変数の定数設定を含めます。
タイマー割込み定義
USOのためにタイマーを1個利用します。
マイクロコンピュータとして、AT90S8535を利用する
と仮定し、システムクロックを8MHzを分周して100Hz
(=10ms)を生成します。
タイマーのプリスケーラを利用して、8分周します。
8MHz/8=1000000
100Hzを生成するためには、10000分周すれば充分です。
1000000/100=1875
タイマー1のコンペアマッチ割込みを利用するため、2つ
のレジスタに数値を設定します。
OCR1A = 9999 ;
OCR1B = 10000 ;
タイマー1の初期化は、モード指定、カウンタとレジスタ設定
タイマースタートの3処理にまとめます。
/* select clock source */
TCCR1A = (1 << CS11) ;
/* clear timer/counter */
TCNT1 = 0 ;
/* set compare values */
OCR1A = 1874 ;
OCR1B = 2000 ;
/* Enable Compare match interruption */
TIMSK = (1 << OCIE1A) ;
このコードは、関数user_initializeに含めます。
割込みが発生した場合は、WAITであるタスクを探して
カウンタを減算します。
カウンタが0になれば、WAITからREADYに変更します。
SIGNAL(SIG_TIMER1_COMPA)
{
UWORD xtmp;
UBYTE loop;
/* clear counter */
TCNT1 = 0 ;
/* 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 ;
}
}
全ソースコード
実際にクロスコンパイラにかけ、テストしたソース
コードは、以下です。
LEDの点灯、消灯は、コンパイラの仕様にあわせて
変更してあります。
#include <avr/io.h>
#include <avr/io8535.h>
#include <avr/interrupt.h>
#define TSK_ID_MAX 3
#define TSK_ID0 0
#define TSK_ID1 1
#define TSK_ID2 2
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 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 UBYTE ready ;
volatile UBYTE suspend;
volatile UBYTE waitq ;
volatile UBYTE run_tsk;
TCBP tcb[TSK_ID_MAX];
/*------------------------*/
/* task function protoype */
/*------------------------*/
void tsk0_proc(void);
void tsk1_proc(void);
void tsk2_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);
/*--------------------------------------*/
/* Insert user definition and variables */
/*--------------------------------------*/
#define TH_CHANNEL 0
#define HU_CHANNEL 1
#define PR_CHANNEL 2
UBYTE store_flag ;
UWORD thermometer ;
UWORD humidety ;
UWORD pressure ;
/*--------------------------------*/
/* Insert user functions protoype */
/*--------------------------------*/
void user_initialize(void);
UBYTE get_time(UBYTE x);
UWORD get_analog(UBYTE x) ;
UWORD calculate_thermometer(UWORD x) ;
UWORD calculate_humidety(UWORD x) ;
UWORD calculate_pressure(UWORD x) ;
void store_eeprom(UWORD th,UWORD hu,UWORD pre);
/*------*/
/* 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);
sta_tsk(TSK_ID0,TTS_READY);
sta_tsk(TSK_ID1,TTS_SUSPEND);
sta_tsk(TSK_ID2,TTS_READY);
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 ;
}
/*----------------*/
/* task functions */
/*----------------*/
void tsk0_proc(void)
{
UBYTE minute ;
minute = get_time(MINUTE);
if ( (minute % 6) == 0 ) {
rsm_tsk(TSK_ID0);
store_flag = ON ;
}
wai_tsk( 36000 );
}
void tsk1_proc(void)
{
UWORD val ;
val = get_analog( TH_CHANNEL ) ;
thermometer = calculate_thermometer( val );
val = get_analog( HU_CHANNEL ) ;
humidety = calculate_humidety( val );
val = get_analog( PR_CHANNEL ) ;
pressure = calculate_pressure( val );
slp_tsk();
}
void tsk2_proc(void)
{
if ( store_flag == ON ) {
store_flag = OFF ;
store_eeprom(thermometer、humidety、pressure);
}
}
/*------------------*/
/* 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 ) ;
}
/*-----------------------*/
/* Insert user functions */
/*-----------------------*/
void user_initialize(void)
{
/* set I/O initial values */
PORTB = 0b00000010 ; /* oooooooo */
PORTD = 0b11111111 ; /* oooiiiii */
/* set I/O directions */
DDRB = 0b11111111 ; /* oooooooo */
DDRD = 0b11110000 ; /* oooiiiii */
/* set flag */
store_flag = OFF ;
/* initialize timer1 */
{
/* select clock source */
TCCR1A = (1 << CS11) ;
/* clear timer/counter */
TCNT1 = 0 ;
/* set compare values */
OCR1A = 1874 ;
OCR1B = 2000 ;
/* Enable Compare match interruption */
TIMSK = (1 << OCIE1A) ;
}
}
/* timer0 interrupt */
SIGNAL(SIG_TIMER1_COMPA)
{
UBYTE xtmp;
UBYTE loop;
/* clear counter */
TCNT1 = 0 ;
/* 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 ;
}
}
UBYTE get_time(UBYTE x)
{
return 0 ;
}
UWORD get_analog(UBYTE x)
{
return 0 ;
}
UWORD calculate_thermometer(UWORD x)
{
return 0 ;
}
UWORD calculate_humidety(UWORD x)
{
return 0 ;
}
UWORD calculate_pressure(UWORD x)
{
return 0 ;
}
void store_eeprom(UWORD th,UWORD hu,UWORD pre)
{
;
}
目次
前
次