目次
前
次
応用例A 温度値取得
USOを、温度値を取得するシステムに適用してみます。
システム構成は、下図とします。
仕様は、以下とします。
スイッチを押すと、2つの温度センサーの値を
読み取り、温度値に変換後EEPROMに保存。
次にスイッチを押すまで、温度取得は継続。
温度センサーからのデータ取得間隔は、1分。
ホストPCからの要求で、保存している温度値を
ホストPCに転送。
システム構成図から、必要となるタスクを考えます。
- スイッチ状態を検出して、温度取得開始、停止する。
- ADCを利用して、温度データを取得
- 温度データを温度値に変換し、EEPROMに保存
- 温度値をLCDに表示
- PCからの指示で、全温度値を転送
1から5までのタスクとなったので、タスク0からタスク4
として、動作を定義します。
タスク動作定義
タスク0を定義します。
「スイッチ状態を検出して、温度取得開始、停止する。」が
与えられている仕様なので、スイッチ状態入力と別のタスクの
起動と停止ができればよいでしょう。
void tsk0_proc(void)
{
/* スイッチ状態取得 */
/* フラグにより、タスクを起動 */
/* フラグにより、タスクを停止 */
}
スイッチの状態を、SW_STATEで判断できると仮定して
もう一段進めた動作を定義します。
void tsk0_proc(void)
{
/* スイッチ状態取得 */
if ( SW_STATE == ON ) {
/* フラグにより、タスクを起動 */
/* フラグにより、タスクを停止 */
}
}
フラグDFLAGがあるとして、動作を加えます。
void tsk0_proc(void)
{
/* スイッチ状態取得 */
if ( SW_STATE == ON ) {
/* フラグにより、タスクを起動 */
if ( DFLAG == OFF ) {
rsm_tsk( TSK_ID1 );
}
/* フラグにより、タスクを停止 */
if ( DFLAG == ON ) {
sus_tsk( TSK_ID1 );
}
}
}
スイッチ状態SW_STATE、フラグDFLAGは、後で定義します。
タスク1を定義します。
「ADCを利用して、温度データを取得。」が考えた
仕様なので、ADC制御と温度データをグローバル変数に
保存します。
さらに、1分ごとに温度値を変換するので、一度
起動したなら、周期的に動かします。
void tsk1_proc(void)
{
/* データ取得 */
/* 温度値変換タスク起動 */
/* 時間待ち指定 */
}
ADCを動かす処理は、関数get_adcが担当すると仮定します。
UWORD raw_dat[2] ;
void tsk1_proc(void)
{
/* データ取得 */
raw_dat[0] = get_adc(0) ;
raw_dat[1] = get_adc(1) ;
/* 温度値変換タスク起動 */
/* 時間待ち指定 */
}
温度値変換するタスクは、タスク2として起動します。
UWORD raw_dat[2] ;
void tsk1_proc(void)
{
/* データ取得 */
raw_dat[0] = get_adc(0) ;
raw_dat[1] = get_adc(1) ;
/* 温度値変換タスク起動 */
rsm_tsk( TSK_ID2 );
/* 時間待ち指定 */
}
時間待ちは1分なので、システムコールを利用して
待ち状態を作ります。
UWORD raw_dat[2] ;
void tsk1_proc(void)
{
/* データ取得 */
raw_dat[0] = get_adc(0) ;
raw_dat[1] = get_adc(1) ;
/* 温度値変換タスク起動 */
rsm_tsk( TSK_ID2 );
/* 時間待ち指定 */
wai_tsk( 6000 );
}
ADCからのデータ取得関数get_adcは、後で定義します。
タスク2を定義します。
「温度データを温度値に変換し、EEPROMに保存。」が考えた
仕様なので、グローバル変数に保存されているデータを温度値に変換する
処理、データをEEPROMに保存する処理を定義すればよいでしょう。
void tsk2_proc(void)
{
/* 温度変換 */
/* 温度値をEEPROMに保存 */
/* 自身を休眠させる */
}
温度値に変換する処理は、関数convertcが担当すると仮定します。
SWORD val[2] ;
void tsk2_proc(void)
{
/* 温度変換 */
val[0] = convertc(raw_dat[0]);
val[1] = convertc(raw_dat[1]);
/* 温度値をEEPROMに保存 */
/* 自身を休眠させる */
}
EEPROMに保存する処理は、関数store_eepromが
担当すると仮定します。
保存場所を指定する必要があるので、アドレスepaが
あると仮定します。
SWORD val[2] ;
void tsk2_proc(void)
{
/* 温度変換 */
val[0] = convertc(raw_dat[0]);
val[1] = convertc(raw_dat[1]);
/* 温度値をEEPROMに保存 */
store_eeprom( epa , val[0] ) ; epa += 2 ;
store_eeprom( epa , val[1] ) ; epa += 2 ;
/* 自身を休眠させる */
}
タスク2は、タスク1が周期的に起動するので、
EEPROMへの保存が終われば、休眠して次に
起動するまで待ちます。
システムコールを利用します。
SWORD val[2] ;
void tsk2_proc(void)
{
/* 温度変換 */
val[0] = convertc(raw_dat[0]);
val[1] = convertc(raw_dat[1]);
/* 温度値をEEPROMに保存 */
store_eeprom( epa , val[0] ) ; epa += 2 ;
store_eeprom( epa , val[1] ) ; epa += 2 ;
/* 自身を休眠させる */
slp_tsk();
}
データ変換convertc、データ保存store_eeprom、変数epaは
後で定義します。
タスク3を定義します。
「温度値をLCDに表示。」が考えた仕様なので、
グローバル変数valに保存されているデータを表示します。
温度は、1分ごとに更新されるので、周期的に動作すれば
充分でしょう。
void tsk3_proc(void)
{
/* チャネル0の温度値を文字列に変換 */
/* 0行目に、チャネル0の温度値表示 */
/* チャネル1の温度値を文字列に変換 */
/* 1行目に、チャネル1の温度値表示 */
/* 時間待ち指定 */
}
温度値を文字列に変換する関数cstrがあると仮定します。
void tsk3_proc(void)
{
SBYTE stmp[10];
/* チャネル0の温度値を文字列に変換 */
cstr( val[0] , stmp );
/* 0行目に、チャネル0の温度値表示 */
/* チャネル1の温度値を文字列に変換 */
cstr( val[1] , stmp );
/* 1行目に、チャネル1の温度値表示 */
/* 時間待ち指定 */
}
行を指定して、文字列にLCDに表示する関数put_lcd_str
があると仮定します。
void tsk3_proc(void)
{
SBYTE stmp[10];
/* チャネル0の温度値を文字列に変換 */
cstr( val[0] , stmp );
/* 0行目に、チャネル0の温度値表示 */
put_lcd_str( 0 , stmp );
/* チャネル1の温度値を文字列に変換 */
cstr( val[1] , stmp );
/* 1行目に、チャネル1の温度値表示 */
put_lcd_str( 1 , stmp );
/* 時間待ち指定 */
}
システムコールで、1分間の時間待ちを作ります。
void tsk3_proc(void)
{
SBYTE stmp[10];
/* チャネル0の温度値を文字列に変換 */
cstr( val[0] , stmp );
/* 0行目に、チャネル0の温度値表示 */
put_lcd_str( 0 , stmp );
/* チャネル1の温度値を文字列に変換 */
cstr( val[1] , stmp );
/* 1行目に、チャネル1の温度値表示 */
put_lcd_str( 1 , stmp );
/* 時間待ち指定 */
wai_tsk( 6000 );
}
2つの関数cstr、put_lcd_strは、後で定義します。
タスク4を定義します。
「PCからの指示で、全温度値を転送。」が考えた仕様なので、
EEPROMに保存されているデータを転送します。
void tsk4_proc(void)
{
/* フラグ判定 */
/* データ転送 */
}
PCからの指示を、フラグSFLAGで検知するとします。
void tsk4_proc(void)
{
/* フラグ判定 */
if ( SFLAG == ON ) {
SFLAG = OFF ;
/* データ転送 */
}
}
温度値を2バイトで、2チャネル分で4バイトとします。
1分ごとに4バイト、60分で240バイト。
12時間分で2880バイト分とします。
void tsk4_proc(void)
{
UWORD adr ;
UWORD dat ;
UBYTE i ;
UBYTE j ;
/* フラグ判定 */
if ( SFLAG == ON ) {
SFLAG = OFF ;
/* データ転送 */
adr = 0 ;
for ( i = 0 ; i < 12 ; i++ ) {
for ( j = 0 ; j < 240 ; j++ ) {
/* channel #0 */
dat = load_eeprom( adr ) ;
put_signed_digits(dat) ;
adr += 2 ;
/* channel #1 */
dat = load_eeprom( adr ) ;
put_signed_digits(dat) ;
adr += 2 ;
}
}
}
}
EEPROMからのデータ取得関数load_eeprom、シリアル転送put_signed_digitsは
後で定義します。
全タスクを定義したので、コンフィグレータでスケルトン
ファイルを作成します。
スケルトンファイルに、必要な内容を付け加えていきます。
変数、フラグ定義
タスクで利用しているフラグを、タスク関数から拾い出します。
タスク0で利用している変数、フラグを拾います。
変数 SW_STATE
フラグ DFLAG
タスク1で利用している変数、フラグを拾います。
変数 raw_dat[2]
フラグ なし
タスク2で利用している変数、フラグを拾います。
変数 val[2] epa
フラグ なし
タスク3で利用している変数、フラグを拾います。
変数 なし
フラグ なし
タスク4で利用している変数、フラグを拾います。
変数 SFLAG
フラグ なし
フラグは、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 DFLAG x_flags.BIT.B0
#define SFLAG x_flags.BIT.B1
変数を定義します。
typedef unsigned char UBYTE ;
typedef unsigned short UWORD ;
UBYTE SW_STATE;
UWORD raw_dat[2];
SWORD val[2];
UWORD epa;
タスク1内の関数定義
タスクで利用している関数を、タスク関数から拾い出します。
タスク1で利用している関数を拾います。
get_adc
タスク関数内部で利用する関数は、ハードウエアと密接に関連する
ようになります。どんなハードウエアを利用するかで、千差万別に
なりますが、自分の中で定番のハードウエアを使って構成すれば、
早く確実に定義できます。
温度センサーにS8100を、マイコンにH8/3048Fを使うとします。
H8/3048Fは、10ビットのA/Dコンバータを内蔵しています。
8チャネルのA/Dコンバータのうち、0チャネル、1チャネルを
利用するとして、チャネルを、関数の入力パラメータとします。
UWORD get_adc(UBYTE x)
{
UBYTE cmd ;
UWORD result ;
/* initialize */
result = 0 ;
/* select channel number */
if ( x > 1 ) return result ;
AD.CSR.BIT.CH = x & 7 ;
/* start conversion */
AD.CSR.BIT.ADST = ON ;
/* wait */
while ( AD.CSR.BIT.ADF == OFF ) ;
/* clear flag */
AD.CSR.BIT.ADF = OFF ;
/* get data */
result = AD.DRA ;
if ( x == 1 ) { result = AD.DRB ; }
/* shift */
result >>= 6 ;
result &= 0x03ff ;
return result ;
}
内蔵でも外付けでも、A/Dコンバータには初期化が必要です。
A/Dコンバータの初期化関数も定義します。
void initialize_ad(void)
{
/*
7 bit ADF -> 0 (A/D convert finish)
6 bit ADIE -> 0 (not use interruption)
5 bit ADST -> 0 (A/D convert start)
4 bit SCAN -> 0 (Select single)
3 bit CKS -> 0 (Select clock 134 state)
2 bit CH2 -> 0 (Use channel 0)
1 bit CH1 -> 0
0 bit CH0 -> 0
*/
AD.CSR.BIT.ADF = OFF ;
AD.CSR.BIT.ADIE = OFF ;
AD.CSR.BIT.ADST = OFF ;
AD.CSR.BIT.SCAN = OFF ;
AD.CSR.BIT.CKS = OFF ;
}
タスク2内の関数定義
タスク2で利用している関数を拾います。
convertc
store_eeprom
関数convertcは、A/Dコンバータから入力した数値を
温度値に変換します。
(変換式は実測で求めたので、デバイスにより異なります。)
SWORD convertc(UWORD x)
{
SWORD result ;
result = 10 * (368 - x) ;
result /= 17 ;
result *= 100 ;
return result ;
}
関数store_eepromは、アドレスを指定して外付けEEPROMに
データを保存します。
入力パラメータは、アドレスとデータとします。
1バイトを扱う関数write_epaがあるとして、次のように定義します。
void store_eeprom(UWORD adr,UWORD x)
{
write_epa( adr , x / 256 );
write_epa( adr+1 , x % 256 );
}
IICバスのEEPROMを使うとして関数write_epaは、次のように
定義できます。
void write_epa(UWORD adr,UBYTE dat)
{
/* start condtion */
put_iic_start();
/* send device ID */
put_iic_data( 0xa2 );
/* set upper address */
put_iic_data( (UBYTE)((adr >> 8) & MASKFF) ) ;
/* set lower address */
put_iic_data( (UBYTE)(adr & MASKFF) ) ;
/* transfer 1 byte */
put_iic_data(dat);
/* set stop condition */
put_iic_stop();
/* 10ms delay */
mwait_by(10);
}
IICバスのアクセスには、StartとStopの2つのコンディションが
必要になるので、まず、この2つを定義します。
void put_iic_start(void)
{
/* both high level */
SCL = ON ; SDA = ON ; iic_wait();
/* 0 -> SDA */
SDA = OFF ; iic_wait();
/* 0 -> SCL */
SCL = OFF ;
}
void put_iic_stop(void)
{
/* set level */
SCL = ON ; SDA = OFF ; iic_wait();
/* both level change */
SDA = ON ; iic_wait();
}
関数put_iic_startとput_iic_stopで、時間待ちをする関数を
利用しているので、iic_waitを定義します。
#define IIC_CNT 32
void iic_wait(void)
{
UBYTE i ;
/* wait */
for ( i = 0 ; i < IIC_CNT ; i++ ) ;
}
EEPROMにビットごとに、データを出力する関数を定義します。
#define MASK80 0x80
UBYTE put_iic_data(UBYTE x)
{
UBYTE i,tmp ;
/* load raw data */
tmp = x ;
/* transfer data with write code */
for ( i = 0 ; i < 8 ; i++ ) {
/* send bit datum */
SDA = OFF ;
if ( tmp & MASK80 ) { SDA = ON ; }
iic_wait();
/* 1 -> SCL */
SCL = ON ;
/* shift */
tmp <<= 1 ;
/* 0 -> SCL */
SCL = OFF ;
/* wait */
iic_wait();
}
/* change inupt */
PBDDR &= ~MASK80 ;
/* 1 -> SCL */
SCL = ON ; iic_wait();
/* get acknowledge */
tmp = SDA ;
/* 0 -> SCL */
SCL = OFF ; iic_wait();
/* change output */
PBDDR |= MASK80 ;
return tmp ;
}
EEPROMにデータを1バイトライトするには10msの
時間待ちが必要です。時間待ち用の関数を定義します。
void mwait_by(UWORD x)
{
volatile UWORD i ;
for ( i = 0 ; i < x ; i++ ) { mwait(); }
}
mwait_byは、1msの時間待ちであるmwaitを利用して
いるので、この関数を定義します。
void mwait(void)
{
/* set time counter */
wcnt = 10 ;
/* enable timer processing */
WFLAG = ON ;
/* wait */
while ( ON ) { if ( WFLAG == OFF ) break ; }
}
フラグWFLAGを利用するので、フラグを定義します。
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 DFLAG x_flags.BIT.B0
#define SFLAG x_flags.BIT.B1
#define WFLAG x_flags.BIT.B2
さらに、カウンタ用変数wcntを定義します。
UBYTE wcnt ;
タスク3内の関数定義
タスク3で利用している関数を拾います。
cstr
put_lcd_str
関数cstrは、数値を文字列に変換します。
SBYTE asc_hex_code[] = "0123456789ABCDEF";
void cstr(UWORD x,SBYTE *ptr)
{
UWORD num ;
/* check */
*(ptr+0) = ' ';
if ( x & 0x8000 ) {
*(ptr+0) = '-';
num = x ^ 0xffff ;
num++ ;
}
/* separate */
*(ptr+1) = asc_hex_code[num / 10000] ; num %= 10000 ;
*(ptr+2) = asc_hex_code[num / 1000 ] ; num %= 1000 ;
*(ptr+3) = asc_hex_code[num / 100 ] ; num %= 100 ;
*(ptr+4) = asc_hex_code[num / 10 ] ;
*(ptr+5) = '.' ;
*(ptr+6) = asc_hex_code[num % 10 ] ;
}
関数put_lcd_strは、LCDに文字を表示します。
行番号と文字列の先頭アドレスを入力パラメータとします。
void put_lcd_str(UBYTE r,SBYTE *ptr)
{
UBYTE adr ;
/* check range */
if ( r > 1 ) return ;
/* set address */
switch ( r ) {
case 1 : adr = 0x40 ; break ;
case 2 : adr = 0x14 ; break ;
case 3 : adr = 0x54 ; break ;
default : adr = 0x00 ; break ;
}
adr |= MASK80 ;
put_lcd_primitive(OFF,adr);
while ( *ptr != 0 ) {
put_lcd_primitive(ON,(UBYTE)*ptr);
ptr++ ;
}
}
関数put_lcd_primitiveは、LCDにデータを転送します。
行番号と文字列の先頭アドレスを入力パラメータとします。
void put_lcd_primitive(UBYTE which,UBYTE x)
{
UBYTE tmp ;
tmp = x ;
LCD_E = OFF ;
LCD_RS = which ;
/* impress upper nibble */
PBDR = ((PBDR & MASKF0) | ((tmp >> 4) & MASK0F)) ;
LCD_E = ON ;
tmp &= MASK0F ;
LCD_E = OFF ;
/* impress lower nibble */
PBDR = ((PBDR & MASKF0) | tmp) ;
LCD_E = ON ;
LCD_E = OFF ;
LCD_RS = OFF ;
}
LCDを利用するためには、初期化が必要です。
初期化関数を定義します。
void init_lcd(void)
{
/* initialize hardware */
mwait_by(20) ; /* 20ms */
put_lcd_init(0x30);
mwait_by(5) ; /* 5ms */
put_lcd_init(0x30);
mwait_by(1); /* 1ms */
put_lcd_init(0x30);
mwait_by(1); /* 1ms */
put_lcd_init(0x20);
mwait_by(1); /* 1ms */
/* set function */
put_lcd_primitive(OFF,0x28);
/*
Function
001 DL N F * *
DL(Data Length) = 0 (4bits)
N(Row) = 1 (2 row)
F(Font) = 0 (5x7)
001 0 1 0 * *
*/
put_lcd_primitive(OFF,0x08);
/*
Display off
0000 1 D C B
D(Display) = 0 (OFF)
C(Cursor) = 0 (OFF)
B(Blink) = 0 (OFF)
0000 1 0 0 0
*/
put_lcd_primitive(OFF,0x01);
/*
Clear
0000 0001
*/
mwait_by(2); /* 2ms */
put_lcd_primitive(OFF,0x06);
/*
Entry Mode
0000 01 I/D S
I/D(Increment/Decrement) = 1 (Increment)
S(Shift) = 0 (No shift)
0000 01 1 0
*/
mwait_by(2); /* 2ms */
put_lcd_primitive(OFF,0x0c);
/*
Display on
0000 1 D C B
D(Display) = 1 (ON)
C(Cursor) = 0 (OFF)
B(Blink) = 0 (OFF)
0000 1 1 0 0
*/
put_lcd_primitive(OFF,0x02);
/*
Cursor Home
0000 001*
*/
/* place 0 row 0 column */
put_lcd_primitive(OFF,0x80);
}
内部で利用している関数put_lcd_initを定義します。
void put_lcd_init(UBYTE x)
{
LCD_E = OFF ; LCD_RS = OFF ;
/* impress upper nibble */
PBDR = (PBDR & MASKF0) | ((x >> 4) & MASK0F) ;
LCD_E = ON ;
x >>= 4 ; /* dummy wait */
LCD_E = OFF ;
}
LCDの表示を初期化する関数も定義します。
void init_display(void)
{
put_lcd_str(0," "); /* channel 0 */
put_lcd_str(1," "); /* channel 1 */
}
タスク4内の関数定義
タスク4で利用している関数を拾います。
load_eeprom
put_signed_digits
関数load_eepromは、アドレスを指定してEEPROMから2バイトの
データを取り出します。
UWORD load_eeprom(UWORD x)
{
UBYTE dh,dl;
dh = read_epa( x ) ;
dl = read_epa( x+1 ) ;
return( dh *256 + dl );
}
関数read_epaは、アドレスを指定してEEPROMから1バイトの
データを取り出します。1バイト取得の関数を定義します。
UBYTE read_epa(UWORD adr)
{
UBYTE result ;
/* start condtion */
put_iic_start();
/* send device ID */
put_iic_data( 0xa2 );
/* set upper address */
put_iic_data( (UBYTE)((adr >> 8) & MASKFF) ) ;
/* set lower address */
put_iic_data( (UBYTE)(adr & MASKFF) ) ;
/* set start condition */
put_iic_start();
/* set initial condition */
put_iic_data( 0xa3 );
/* get 1 byte */
result = get_iic_data(ON) ;
/* set stop condition */
put_iic_stop();
return result;
}
内部で、関数get_iic_dataを使っているので、定義します。
UBYTE get_iic_data(UBYTE x)
{
UBYTE i,result ;
/* change inupt */
PBDDR &= ~MASK80 ;
/* default */
result = 0 ;
for ( i = 0 ; i < 8 ; i++ ) {
/* shift */
result <<= 1 ;
/* 1 -> SCL */
SCL = ON ; iic_wait();
/* get bit datum */
result |= SDA ; iic_wait();
/* 0 -> SCL */
SCL = OFF ; iic_wait();
}
/* change output */
PBDDR |= MASK80 ;
if ( x > 0 ) { SDA = ON ; }
else { SDA = OFF ; }
iic_wait();
/* 1 -> SCL */
SCL = ON ; iic_wait();
/* 0 -> SCL */
SCL = OFF ; iic_wait();
return result ;
}
関数put_signed_digitsは、符号付き整数をシリアルポートに
転送します。
SBYTE asc_hex_code[] = "0123456789ABCDEF";
void put_signed_digits(UWORD x)
{
SBYTE stmp[7] ;
/* 符号処理 */
*(stmp+0) = ' ' ;
if ( x & MSB_BIT ) {
*(stmp+0) = '-' ;
x ^= MASKFFFF ;
x++ ;
}
/* 1けたごとに分離 */
*(stmp+1) = x / 10000 ; x %= 10000 ;
*(stmp+2) = x / 1000 ; x %= 1000 ;
*(stmp+3) = x / 100 ; x %= 100 ;
*(stmp+4) = x / 10 ;
*(stmp+5) = x % 10 ;
/* 変換 */
*(stmp+1) = asc_hex_code[*(stmp+1)] ;
*(stmp+2) = asc_hex_code[*(stmp+2)] ;
*(stmp+3) = asc_hex_code[*(stmp+3)] ;
*(stmp+4) = asc_hex_code[*(stmp+4)] ;
*(stmp+5) = '.'
*(stmp+6) = asc_hex_code[*(stmp+5)] ;
/* 端末表示 */
for ( i = 0 ; i < 7 ; i++ ) {
put_txd( *(stmp+i) );
}
/* ディリミタ */
put_txd( '\r' );
}
シリアルポートへ1文字出力する関数を定義します。
void put_txd(SBYTE x)
{
/* wait data transfer */
while ( SCI1.SSR.BIT.TDRE == OFF ) ;
/* put */
SCI1.TDR = x ;
SCI1.SSR.BIT.TDRE = OFF ;
}
時間関係割込み定義
USOのためにタイマーを1個利用します。
H8/3048Fは、クロック14.7456MHzで利用します。
内蔵のITU0を、USOのために利用します。
タイマーITU0の初期化が必要なので、次のように定義します。
/* クロック14.7456MHz時 */
#ifdef SYS_CLOCK_14_7456
typedef enum {
br2400 = 191,
br4800 = 95,
br9600 = 47,
br19200 = 23,
br31250 = 14,
br38400 = 11,
br57600 = 7
} TBaudRate;
#define ITU0_AREG 14745
#define ITU1_AREG 1475
#endif
void init_timer0(void)
{
/* stop timer */
ITU.TSTR.BIT.STR0 = OFF ;
/* TOER : Timer Output Enable Register
7 **** -> 0
6 **** -> 0
5 EXB4 -> 0
4 EXA4 -> 0
3 EB3 -> 0
2 EB4 -> 0
1 EA4 -> 0
0 EA3 -> 0
*/
ITU.TOER.BYTE = 0 ;
/* TIOR : Timer I/O Control Register
7 **** -> 0
6 IOB2 -> 0 GRB is not output compare match register
5 IOB1 -> 0
4 IOB0 -> 0
3 **** -> 0
2 IOA2 -> 0 GRA is not output compare match register
1 IOA1 -> 0
0 IOA0 -> 0
*/
ITU0.TIOR.BYTE = 0 ;
/* TCR : Timer Control Register
7 **** -> 0
6 CCLR1 -> 0 clear TCNT if GRA = TCNT
5 CCLR0 -> 1
4 CKEG1 -> 0 rising edge
3 CKEG0 -> 0
2 TPSC2 -> 0 φ利用
1 TPSC1 -> 0
0 TPSC0 -> 0
*/
ITU0.TCR.BYTE = 0x20 ;
/* TIER : Timer Interrupt Enable Register
7 **** -> 0
6 *** -> 0
5 *** -> 0
4 *** -> 0
3 *** -> 0
2 OVIE -> 0
1 IMIEB -> 0
0 IMIEA -> 1 select compare match interrupt
*/
ITU0.TIER.BIT.IMIEA = ON ;
/* reference */
ITU0.GRA = ITU0_AREG ;
ITU0.GRB = MASKFFFF ;
/* counter */
ITU0.TCNT = 0 ;
/* start timer */
ITU.TSTR.BIT.STR0 = ON ;
}
ITUは、コンペアマッチ割込みを利用します。
1msごとの割込みになるので、カウンタを利用して
ハンドラtimer_handlerをコールします。
#pragma interrupt int_imia0
void int_imia0(void)
{
UBYTE dummy ;
/* clear flag */
dummy = ITU0.TSR.BIT.IMFA ;
ITU0.TSR.BIT.IMFA = OFF ;
os_cnt-- ;
if ( os_cnt > 0 ) return ;
os_cnt = 10 ;
timer_handler();
}
内蔵のITU1を、時間待ちのために利用します。
タイマーITU1の初期化が必要なので、次のように定義します。
void init_timer1(void)
{
/* stop timer */
ITU.TSTR.BIT.STR1 = OFF ;
/* TCR : Timer Control Register
7 **** -> 0
6 CCLR1 -> 0 clear TCNT if GRA = TCNT
5 CCLR0 -> 1
4 CKEG1 -> 0 rising edge
3 CKEG0 -> 0
2 TPSC2 -> 0 φ利用
1 TPSC1 -> 0
0 TPSC0 -> 0
*/
ITU1.TCR.BYTE = 0x20 ;
/* TIER : Timer Interrupt Enable Register
7 **** -> 0
6 *** -> 0
5 *** -> 0
4 *** -> 0
3 *** -> 0
2 OVIE -> 0
1 IMIEB -> 0
0 IMIEA -> 1 select compare match interrupt
*/
ITU1.TIER.BIT.IMIEA = ON ;
/* reference */
ITU1.GRA = ITU1_AREG ;
ITU1.GRB = MASKFFFF ;
/* counter */
ITU1.TCNT = 0 ;
/* start timer */
ITU.TSTR.BIT.STR1 = ON ;
}
ITUは、コンペアマッチ割込みを利用します。
内部でステートマシンを構成し、1msの時間待ちを生成します。
#pragma interrupt int_imia1
void int_imia1(void)
{
UBYTE dummy ;
/* clear flag */
dummy = ITU1.TSR.BIT.IMFA ;
ITU1.TSR.BIT.IMFA = OFF ;
/*----------------------*/
/* 1 ms timer primitive */
/*----------------------*/
switch ( wstate ) {
case 0 :
wstate = 0 ;
if ( WFLAG == ON ) { wstate = 1 ; }
break ;
case 1 :
wcnt-- ;
if ( wcnt == 0 ) { wstate = 2 ; WFLAG = OFF ; }
break ;
default :
wstate = 0 ;
break ;
}
}
スイッチの状態を見て、変数SW_STATEの状態を決める処理を
ITU1の割込みの中に入れます。
UBYTE bcnt ;
UBYTE sft_sw ;
#pragma interrupt int_imia1
void int_imia1(void)
{
UBYTE dummy ;
/* clear flag */
dummy = ITU1.TSR.BIT.IMFA ;
ITU1.TSR.BIT.IMFA = OFF ;
/*----------------------*/
/* 1 ms timer primitive */
/*----------------------*/
switch ( wstate ) {
case 0 :
wstate = 0 ;
if ( WFLAG == ON ) { wstate = 1 ; }
break ;
case 1 :
wcnt-- ;
if ( wcnt == 0 ) { wstate = 2 ; WFLAG = OFF ; }
break ;
default :
wstate = 0 ;
break ;
}
/*----------*/
/* debounce */
/*----------*/
bcnt++ ;
if ( bcnt == 0 ) {
sft_sw <<= 1 ;
sft_sw |= SW_IN ;
if ( sft_sw == 0xf0 ) { SW_STATE = SW_STATE ^ 0x01 ; }
}
}
シリアル処理関係割込み定義
PCとシリアルポートでやりとりします。
通信条件は、以下とします。
- データ転送速度 38400bps
- データ長 8ビット
- ストップビット 1ビット
- パリティ なし
- フロー制御 なし
条件を決めたので、内蔵のSCI1を利用するとして初期化します。
void init_SCI1(TBaudRate x)
{
volatile UWORD i;
/* SCR : Serial Control Register
7 bit TIE -> 0 Transmit Interrupt Enable(disable)
6 bit RIE -> 0 Receive Interrupt Enable(disable)
5 bit TE -> 0 Transmit Enable(disable)
4 bit RE -> 0 Receive Enable(disable)
3 bit MPIE -> 0 Multi Processor Interrupt Enable(disable)
2 bit TEIE -> 0 Transmit End Interrupt Enable(disable)
1 bit CKE1 -> 0 Clock Source (Use Internal Baud Rate Generator)
0 bit CKE0 -> 0
*/
SCI1.SCR.BYTE = 0 ;
/* SMR : Serial Mode Register
7 bit C/nA -> 0 Communication Mode(Asynchronous)
6 bit CHR -> 0 data Charactor (8 bits)
5 bit PE -> 0 Parity Enable(disable)
4 bit O/nE -> 0 Parity Mode(even)
3 bit STOP -> 0 Stop Bit(1 bit)
2 bit MP -> 0 Multi Processor(disable)
1 bit CKS1 -> 0 Clock Source ( φ )
0 bit CKS0 -> 0
*/
SCI1.SMR.BYTE = 0 ;
/* data transfer speed */
SCI1.BRR = x ;
/* wait 1 frame */
for (i = 0; i < 3000 ; i++) ;
/* enable Transmmit and Receive with interrupt */
SCI1.SCR.BYTE = 0x70 ;
}
PCからのコマンドは、受信割込みで、バッファに格納します。
#pragma interrupt int_rxi1
UBYTE sbuf[8] ;
UBYTE sindex ;
void int_rxi1(void)
{
volatile unsigned char ch,dummy ;
/* clear flag */
dummy = SCI1.SSR.BYTE ;
SCI1.SSR.BIT.RDRF = OFF ;
/* get a character */
ch = SCI1.RDR ;
/* store */
*(sbuf+sindex) = ch ;
sindex++ ;
/* check */
if ( ch == '\r' ) {
*(sbuf+sindex) = 0 ;
sindex = 0 ;
if ( *(sbuf+0) == 'g' ) {
SFLAG = ON ;
}
}
}
受信エラーが発生した場合は、すべて無視します。
受信エラーが発生しると、フラグがセットされたままになるため
全エラーフラグをリセットします。
#pragma interrupt int_eri1
void int_eri1(void)
{
/* SSR : Serial State Register
7 bit TDRE -> 0 Transmit Data Buffer Empty
6 bit RDRF -> 1 Receive Data Buffer Full
5 bit ORER -> 1 Overrun Error
4 bit FER -> 1 Framming Error
3 bit PER -> 0 Parity Error
2 bit TEND -> 0 Transmit End
1 bit MPB -> 0 Multi Processor Receive Bit
0 bit MPBT -> 0 Multi Processor Bit Transfer
*/
SCI1.SSR.BYTE &= ~0x30 ;
}
main関数定義
タスクの初期状態とハードウエア初期化が必要なので、以下の
シーケンスとします。
void main(void)
{
TCBP pcur_tsk ;
di();
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);
cre_tsk(TSK_ID4,tsk4_proc);
sta_tsk(TSK_ID0,TTS_READY);
sta_tsk(TSK_ID1,TTS_SUSPEND);
sta_tsk(TSK_ID2,TTS_SUSPEND);
sta_tsk(TSK_ID3,TTS_READY);
sta_tsk(TSK_ID4,TTS_READY);
ei();
init_lcd();
init_display();
/* loop */
run_tsk = TSK_ID0 ;
while ( 1 ) {
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 ;
}
}
}
I/Oのビット定義と初期化が必要なので、マクロ定義と
関数を以下のようにします。
#define P4DDR P4.DDR
#define P4DR P4.DR.BYTE
#define P6DDR P6.DDR
#define P6DR P6.DR.BYTE
#define P7DR P7.DR.BYTE
#define PADDR PA.DDR
#define PADR PA.DR.BYTE
#define PBDDR PB.DDR
#define PBDR PB.DR.BYTE
#define SCL PB.DR.BIT.B6
#define SDA PB.DR.BIT.B7
#define LCD_E PB.DR.BIT.B5
#define LCD_RS PB.DR.BIT.B4
#define SW_IN P7.DR.BIT.B7
void user_initialize(void)
{
/* PORT B */
PBDR = 0x00 ;
PBDDR = MASKFF ; /* all outputs */
initialize_ad(); /*initialize internal ADC */
init_timer0() ; /* initialize ITU0 */
init_timer1() ; /* initialize ITU1 */
init_sci1( br38400 );
os_cnt = 10 ;
wstate = 0 ;
bcnt = 0 ;
sft_sw = 0xff ;
SW_STATE = OFF ;
}
目次
前
次