目次
前
次
sdccによるハードウエアテスト
SDCCを利用して、Z84C015のハードウエアをテストします。
Z84C015は、以下の周辺回路が内蔵されています。
CGCは、システム全体のマスタークロックを決め
WDTは、ウォッチドッグタイマーでシステム暴走
を防止するために使います。
PIO→CTC→SIOの順に、動作テストします。
PIOは、スイッチ、LEDボードを利用します。
CTCは、クロックを出力させて、マルチメータで
確認します。
SIOは、PersonalComputer上の端末ソフトを使い
確認します。
PIOテスト
Z80のPIOは、モード0〜モード3までありますが
ビットモードで利用します。
ビットモードでは、nSTB、RDYを気にせず使えます。
モード設定は、制御レジスタにパラメータを設定
しなければならないので、シーケンスを確認します。
- 8ビットの上位7、6ビットに11を、下位4ビットを1111に設定
- 8ビットで入出力設定(1:入力、0;出力)
- 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とした後、各カウンタに
分周比を設定します。
シーケンスは、以下とします。
- 8ビットの制御ワードを設定
- 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 ;
}
}
各チャネルの分周比を、以下とします。
- チャネル0 128
- チャネル1 64
- チャネル2 32
- チャネル3 16
関数を利用し、ループで簡単に初期化します。
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)
目次
前
次