目次

sdccによるハードウエアテスト

 SDCCを利用して、Z84C015のハードウエアをテストします。

 Z84C015は、以下の周辺回路が内蔵されています。

 CGCは、システム全体のマスタークロックを決め
 WDTは、ウォッチドッグタイマーでシステム暴走
 を防止するために使います。

 PIO→CTC→SIOの順に、動作テストします。

 PIOは、スイッチ、LEDボードを利用します。



 CTCは、クロックを出力させて、マルチメータで
 確認します。



 SIOは、PersonalComputer上の端末ソフトを使い
 確認します。




PIOテスト

 Z80のPIOは、モード0〜モード3までありますが  ビットモードで利用します。  ビットモードでは、nSTB、RDYを気にせず使えます。  モード設定は、制御レジスタにパラメータを設定  しなければならないので、シーケンスを確認します。
  1. 8ビットの上位7、6ビットに11を、下位4ビットを1111に設定
  2. 8ビットで入出力設定(1:入力、0;出力)
  3. 8ビットで割込みなしに設定
 入出力だけを指定するように、関数を定義します。 void set_mode(UBYTE which,UBYTE x) { UBYTE msg[3] ; UBYTE loop ; /* set control word */ *(msg+0) = 0xcf ; *(msg+1) = x ; *(msg+2) = 0x07 ; /* send control word */ for ( loop = 0 ; loop < 3 ; loop++ ) { if ( which ) { PIO_BC = *(msg+loop) ; } else { PIO_AC = *(msg+loop) ; } } }  PIOはA、Bの2チャネルがあるので、どちらかを  選び、入出力設定ができるようにしました。  スイッチ状態を入力し、反転後、LEDに反映させます。  スイッチは、負論理で動かすとして反転します。  PIOのデータレジスタを、入出力する関数を定義し  入力、出力をひとつの関数で対応します。 void put_data(UBYTE which,UBYTE x) { /* impress */ if ( which ) { PIO_BD = x ; } else { PIO_AD = x ; } } UBYTE get_data(UBYTE which) { UBYTE result ; /* get */ result = PIO_AD ; if ( which ) { result = PIO_BD ; } return result ; }  定義した関数を利用して、まとめます。 /* define my system I/O area */ __sfr __at 0x10 CTC_C0 ; __sfr __at 0x11 CTC_C1 ; __sfr __at 0x12 CTC_C2 ; __sfr __at 0x13 CTC_C3 ; __sfr __at 0x18 SIOAD ; __sfr __at 0x19 SIOAC ; __sfr __at 0x1a SIOBD ; __sfr __at 0x1b SIOBC ; __sfr __at 0x1C PIO_AD ; __sfr __at 0x1D PIO_AC ; __sfr __at 0x1E PIO_BD ; __sfr __at 0x1F PIO_BC ; /* define data types */ typedef unsigned char UBYTE ; typedef unsigned short UWORD ; typedef signed char SBYTE ; typedef signed short SWORD ; /* define constant values */ #define MASKFF 0xff #define OFF 0 #define ON OFF+1 #define PORTA 0 #define PORTB 1 /* function prototype */ void set_mode(UBYTE which,UBYTE x); void put_data(UBYTE which,UBYTE x); UBYTE get_data(UBYTE which); void main(void) { UBYTE tmp ; /* initialize PIO */ set_mode( PORTA , 0x00 ) ; /* all output */ set_mode( PORTB , 0xff ) ; /* all input */ /* endless loop */ while ( ON ) { /* get data */ tmp = get_data( PORTB ) ; /* inverse */ tmp ^= MASKFF ; /* put data */ put_data( PORTA , tmp ); } } void set_mode(UBYTE which,UBYTE x) { UBYTE msg[3] ; UBYTE loop ; /* set control word */ *(msg+0) = 0xcf ; *(msg+1) = x ; *(msg+2) = 0x07 ; /* send control word */ for ( loop = 0 ; loop < 3 ; loop++ ) { if ( which ) { PIO_BC = *(msg+loop) ; } else { PIO_AC = *(msg+loop) ; } } } void put_data(UBYTE which,UBYTE x) { /* impress */ if ( which ) { PIO_BD = x ; } else { PIO_AD = x ; } } UBYTE get_data(UBYTE which) { UBYTE result ; /* get */ result = PIO_AD ; if ( which ) { result = PIO_BD ; } return result ; }  I/Oレジスタ定義は、2014年に改版された仕様で記述しました。

CTCテスト

 CTCは、4チャネルがあるので、タイマーモードに  設定し、クロックを出力します。  全チャネルをタイマーモードにし、プリスケーラ  でソースクロックを1/16とした後、各カウンタに  分周比を設定します。  シーケンスは、以下とします。
  1. 8ビットの制御ワードを設定
  2. 8ビットの分周比を設定
 制御ワードは確定しているので、チャネル番号と  分周比をパラメータとして関数に与えます。 void set_mode(UBYTE which,UBYTE x) { switch ( which ) { case 1 : CTC_C1 = 0x27 ; CTC_C1 = x ; break ; case 2 : CTC_C2 = 0x27 ; CTC_C2 = x ; break ; case 3 : CTC_C3 = 0x27 ; CTC_C3 = x ; break ; default : CTC_C0 = 0x27 ; CTC_C0 = x ; break ; } }  各チャネルの分周比を、以下とします。  関数を利用し、ループで簡単に初期化します。 void init_ctc(void) { UBYTE loop ; UBYTE tmp ; tmp = 0x80 ; for ( i = 0 ; i < 4 ; i++ ) { set_mode(i,tmp); tmp >>= 1 ; } }  まとめます。 /* define my system I/O area */ __sfr __at 0x10 CTC_C0 ; __sfr __at 0x11 CTC_C1 ; __sfr __at 0x12 CTC_C2 ; __sfr __at 0x13 CTC_C3 ; __sfr __at 0x18 SIOAD ; __sfr __at 0x19 SIOAC ; __sfr __at 0x1a SIOBD ; __sfr __at 0x1b SIOBC ; __sfr __at 0x1C PIO_AD ; __sfr __at 0x1D PIO_AC ; __sfr __at 0x1E PIO_BD ; __sfr __at 0x1F PIO_BC ; /* define data types */ typedef unsigned char UBYTE ; typedef unsigned short UWORD ; typedef signed char SBYTE ; typedef signed short SWORD ; /* define constant values */ #define MASKFF 0xff #define OFF 0 #define ON OFF+1 #define PORTA 0 #define PORTB 1 /* function prototype */ void set_mode(UBYTE which,UBYTE x); void init_ctc(void); void main(void) { /* initialize CTC */ init_ctc(); /* endless loop */ while ( ON ) { /* */ } } void set_mode(UBYTE which,UBYTE x) { switch ( which ) { case 1 : CTC_C1 = 0x27 ; CTC_C1 = x ; break ; case 2 : CTC_C2 = 0x27 ; CTC_C2 = x ; break ; case 3 : CTC_C3 = 0x27 ; CTC_C3 = x ; break ; default : CTC_C0 = 0x27 ; CTC_C0 = x ; break ; } } void init_ctc(void) { UBYTE loop ; UBYTE tmp ; tmp = 0x80 ; for ( i = 0 ; i < 4 ; i++ ) { set_mode(i,tmp); tmp >>= 1 ; } }  タイマーを利用しているので、割込みで何かやらせて  みたくなります。  PIOにLEDとスイッチがあるので、方向指示器を実現  してみます。スイッチには、チャタリングがついて  いるので、これを除去するために、シフトレジスタ  を使います。  チャタリングは10msほど継続するので、30ms周期で  PIOのBポートに接続されているスイッチの状態を  読取り、シフトレジスタに入れていきます。  スイッチは、負論理で利用します。負論理なので  スイッチの立上りを判定するには、論理反転して  おきます。  LEDは、正論理で利用します。正論理なので1を  与えると点灯します。  ハードウエア内容が確定したので、実現コードを  考えていきます。  タイマー初期化   利用しているシステムクロックを分周して   10msの周期を作ります。   使っている基板のシステムクロックが16MHzなので   16分周すると1MHzになります。1MHzを250分周して   4kHzを生成します。   16分周は、プリスケーラ設定で対応し、250分周は   タイマーコンスタントレジスタに250を設定して   実現できます。   ここまでは、CTC内部のレジスタへのパラメータ設定   にて対応できます。4kHzは、1/4msなので10msを生成   するには、40をカウントする変数を用意します。   CTCは、モード2割込みに対応しているので、割込み   発生時に、分岐するアドレスをCTC内部レジスタに   設定します。   CTCのチャネル3をタイマーモードで利用、プリスケーラ1/16   カウンタレジスタに250を設定、モード2割込みを使うときの   制御コードは、次のように設定すれば充分です。 /* set interrupt vector */ CTC_C0 = 0x08 ; /* set control word */ CTC_C3 = 0x80 ; /* set time constant */ CTC_C3 = 250 ;   モード2割込みでのエントリーアドレスは、0x0006の   中に入るようにしておきます。  タイマー割込み通知   タイマー割込みが発生する都度、フラグで通知します。   フラグtflagを定義し、割込みが発生すると1をセットし   main関数の中にある処理ブロックに通知します。   通知を受けたブロックは、0をセットし、必要な仕事を   片付けます。   プログラムにすると、単純。 UBYTE tflag ; UBYTE tcount ; if ( tflag == ON ) { /* clear flag */ tflag = OFF ; /* increment coounter */ tcount++ ; /* judge */ if ( tcount == 40 ) { /* clear counter */ tcount = 0 ; /* handling */ } }   変数tcountは、1/4msから10msを生成するために使います。  チャタリング除去と動作指定   10msごとの処理は、スイッチ状態を読み込んで   シフトレジスタに値を入れるだけです。   シフトレジスタ用変数を3つ、ポートの状態を   入れる変数を1つ用意し、対応します。 #define OFF 0 #define ON OFF+1 #define IDLE 0 #define RIGHT 1 #define LEFT 2 UBYTE sft_right ; UBYTE sft_left ; UBYTE sft_stop ; UBYTE sw ; UBYTE mode ; /* handling */ sw = get_data(PORTB) ^ 0xff ; /* shift */ sft_right <<= 1 ; sft_left <<= 1 ; sft_stop <<= 1 ; /* masking */ sft_right &= 0x03 ; sft_left &= 0x03 ; sft_stop &= 0x03 ; /* generate code */ if ( sw & 0x01 ) { sft_right |= ON ; } if ( sw & 0x80 ) { sft_left |= ON ; } if ( sw & 0x10 ) { sft_stop |= ON ; } if ( sw & 0x08 ) { sft_stop |= ON ; } /* judge */ if ( sft_stop == 0x01 ) { mode = IDLE ; } else { if ( sft_right == 0x01 ) { mode = RIGHT ; } if ( sft_left == 0x01 ) { mode = LEFT ; } }  方向指示の点灯処理   8個のLEDを左あるいは右から順番に点灯して   いけばよいことから、点灯パターンを配列に   格納して対応します。   変数modeで、動作を指定されているのでパターンを   取り出してポートに出力します。 UBYTE ledv ; UBYTE rightv ; UBYTE leftv ; /* turn off LEDs */ ledv = 0x00 ; /* judge */ if ( mode == RIGHT ) { ledv = rightv ; } if ( mode == LEFT ) { ledv = leftv ; } /* impress */ put_data( PORTA , ledv );   このままでは、毎回同じ値の出力ですが、次の   ように変数を用意して、値を取出し、変化させ   ます。 UBYTE loop ; UBYTE led_pat[9] = {0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80}; rightv = led_pat[loop] ; leftv = led_pat[8-loop] ;   変数loopを、タイマー割込みで変えていけば   LEDの点灯パターンが変わります。   1/4ms周期で割込みフラグが変化するので、1000msを   作りたいなら、変数を用意し、増やして4000になった   なら、変数loopを+1すればよいでしょう。 UWORD ledcnt ; if ( tflag == ON ) { /* clear flag */ tflag = OFF ; /* increment */ ledcnt++ ; /* judge */ if ( ledcnt == 4000 ) { ledcnt = 0 ; /* update loop */ loop++ ; loop %= 9 ; /* impress */ rightv = *(led_pat+loop) ; leftv = *(led_pat+8-loop) ; ledv = 0x00 ; if ( mode == RIGHT ) { ledv = rightv ; } if ( mode == LEFT ) { ledv = leftv ; } put_data( PORTA , ledv ); } }   タイマー割込みでフラグをひとつ使い、カウンタを   2つ利用して、動作をまとめます。 UBYTE sftflag ; UBYTE ledflag ; if ( tflag == ON ) { /* clear flag */ tflag = OFF ; /* increment coounter */ tcount++ ; ledcnt++ ; /* judge */ if ( tcount == 40 ) { /* clear counter */ tcount = 0 ; /* set flag */ sftflag = ON ; } if ( ledcnt == 4000 ) { /* clear counter */ ledcnt = 0 ; /* set flag */ ledflag = ON ; } } if ( sftflag == ON ) { /* clear flag */ sftflag = OFF ; /* handling */ sw = get_data(PORTB) ^ 0xff ; /* shift */ sft_right <<= 1 ; sft_left <<= 1 ; sft_stop <<= 1 ; /* masking */ sft_right &= 0x03 ; sft_left &= 0x03 ; sft_stop &= 0x03 ; /* generate code */ if ( sw & 0x01 ) { sft_right |= ON ; } if ( sw & 0x80 ) { sft_left |= ON ; } if ( sw & 0x10 ) { sft_stop |= ON ; } if ( sw & 0x08 ) { sft_stop |= ON ; } /* judge */ if ( sft_stop == 0x01 ) { mode = IDLE ; } else { if ( sft_right == 0x01 ) { mode = RIGHT ; } if ( sft_left == 0x01 ) { mode = LEFT ; } } } if ( ledflag == ON ) { /* clear flag */ ledflag = OFF ; /* update loop */ loop++ ; loop %= 9 ; /* impress */ rightv = *(led_pat+loop) ; leftv = *(led_pat+8-loop) ; ledv = 0x00 ; if ( mode == RIGHT ) { ledv = rightv ; } if ( mode == LEFT ) { ledv = leftv ; } put_data( PORTA , ledv ); } (under construction)

SIOテスト

(under construction)
目次

inserted by FC2 system