目次

応用例A 温度値取得

 USOを、温度値を取得するシステムに適用してみます。

 システム構成は、下図とします。



 仕様は、以下とします。
  スイッチを押すと、2つの温度センサーの値を
  読み取り、温度値に変換後EEPROMに保存。

  次にスイッチを押すまで、温度取得は継続。

  温度センサーからのデータ取得間隔は、1分。

  ホストPCからの要求で、保存している温度値を
  ホストPCに転送。

 システム構成図から、必要となるタスクを考えます。
  1. スイッチ状態を検出して、温度取得開始、停止する。
  2. ADCを利用して、温度データを取得
  3. 温度データを温度値に変換し、EEPROMに保存
  4. 温度値をLCDに表示
  5. 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とシリアルポートでやりとりします。  通信条件は、以下とします。  条件を決めたので、内蔵の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 ;  }
目次

inserted by FC2 system