目次
前
次
ライントレーサ処理
専門学校で講師をしているとき、知人からZ80を
利用したライントレーサがあるので、動かして
欲しいと依頼がありました。
現在は、販売されていませんが、Z80を使った
組込みシステムが全盛の頃で、相撲ロボットに
このハードウエアを参考にしたものが多数あり
ました。
今、手元にはTAMIYAのTwin Gear Boxを利用した
メカがあります。これに、Z80のコアをもつUECを
載せて、動かしてみます。
手元にあるUECとして、SRAMが32kバイトの製品で
外国人の研修で利用していました。
ライントレーサを実現するには、以下のI/Oが必要です。
それぞれのI/Oは、どう利用するのかを簡単に見てみます。
PWM波形生成器
DCモータは、PWM波形のDUTY比を可変して
回転を制御します。DCモータの平均電力を
パルスの幅で制御するので、よく使われて
います。
PWM波形のDUTY比を制御するには、タイマー
割込みを使います。
パラレル入力
パラレル入力は、ラインセンサーからの入力と
スイッチ類の入力に使います。
ラインセンサーは、フォトインタラプタを使い
コース上に密着させる基板で実現します。
(写真で、シャーシ前にあるのがラインセンサー)
スイッチは、スタート、ストップの他、リセット等
の緊急停止に使うため必要になります。
パラレル出力
パラレル出力は、ラインセンサーのデータモニタや
内部状態表示に使います。情報量が多い状態表示に
する場合、LCDを使うこともあります。
利用するZ80基板には、タイマーカウンタ、パラレル入出力は
用意されていますが、パラレル入出力は2ポートだけです。
そこで、I/Oアドレスにデジタルパラレル出力として74LS374を
接続します。
Z80CPUチップ上に用意された、既存周辺I/Oのアドレスと
バッティングしないように、20hから2バイトを使います。
アドレスデコーダを含めた回路は、次のようにします。
I/Oに対する書き込みは、nIOWRが担当しているので
アドレスをA0〜A7から生成し、出来上がった負論理
のアクセス信号との論理積をとります。
A6、A7は、ダイオードによるAND回路を使いました。
ラインセンサー、スイッチ類の入力は、PIOを使います。
I/Oは、次のように割り当てます。
- 1Ch ラインセンサー入力
- 1Eh スイッチ入力類
- 20h モータ出力
- 21h 表示器出力
表示器には、2行x16桁のLCDを使います。
I/Oの仕様を決めたので、I/O操作のコードを作成します。
ラインセンサー入力
ラインセンサーは、デジタルで8ビットの
データを出力する専用品を使います。
単純なデジタルデータ入力なので、PIOAを
モード1で使います。
入力設定は、以下。
PIOAC EQU 01dh
ld c,PIOAC
ld a,1fh
out (c),a
センサーデータは、1バイトの変数領域を
宣言して、そこに値を転送します。
PIOAD EQU 01ch
GET_SENSOR:
push af
push bc
;
ld c,PIOAD
in a,(c)
;
ld (SENSOR),a
;
pop bc
pop af
ret
SENSOR: defs 1
スイッチ入力
スイッチは、start、stop、resetの3ビットにします。
3つのスイッチだけでなく、後で拡張できるように
8ビットのスイッチを扱えるようにします。
PIOBにスイッチを接続するとして、PIOBを入力に
設定します。これはモード1を使うことになります。
PIOBC EQU 01fh
ld c,PIOBC
ld a,1fh
out (c),a
スイッチには、チャタリングがつきものなので
チャタリングを除去することを考えます。
シフトレジスタを用意し、タイマー割込みで10ms周期で
各ビットの値を入力して、シフトレジスタのLSBに格納
していきます。タイマー割込みで、起動されるハンドラ
を記述します。
PIOBD EQU 01eh
GETSW:
push af
push bc
push hl
;
ld c,PIOBD
in a,(c)
ld b,a
; shift
ld hl,swstart
ld a,(hl)
sla a
and 07h
ld (hl),a
;
ld hl,swstop
ld a,(hl)
sla a
and 07h
ld (hl),a
;
ld hl,swreset
ld a,(hl)
sla a
and 07h
ld (hl),a
; store bit datum
GETSW0:
ld a,b
;
bit 0,a
jr z,GETSW1
GETSW01:
ld hl,swstart
inc (hl)
GETSW1:
bit 1,a
jr z,GETSW2
GETSW11:
ld hl,swstop
inc (hl)
GETSW2:
bit 2,a
jr z,GETSW3
GETSW21:
ld hl,swreset
inc (hl)
; judge
GETSW3:
ld a,(swstart)
cp 3
jr z,GETSW4
cp 1
jr z,GETSW4
jr GETSW5
GETSW4:
ld a,1
ld (startflag),a
jr GETSWEND
GETSW5:
ld a,(swstop)
cp 3
jr z,GETSW6
cp 1
jr z,GETSW6
jr GETSW7
GETSW6:
ld a,1
ld (stopflag),a
jr GETSWEND
GETSW7:
ld a,(swreset)
cp 3
jr z,GETSW8
cp 1
jr z,GETSW8
jr GETSWEND
GETSW8:
ld a,1
ld (resetflag),a
;
GETSWEND:
;
pop hl
pop bc
pop af
;
reti
swstart: defs 1
swstop: defs 1
swreset: defs 1
startflag: defs 1
stopflag: defs 1
resetflag: defs 1
該当のシフトレジスタが、2進で011か001で
あれば、フラグを設定します。
タイマー割込みハンドラとしたいので
retiを利用して、通常処理に戻ります。
モータ出力
PWM波形を出力するのは、非常に単純です。
0〜99の状態の中で、1が連続して何度出力
されるかを確定します。
Cで擬似コーディングすると、次のようになります。
portx = 0 ;
/* generate */
if ( pcnt < ldutyx ) { portx |= 2 ; }
if ( pcnt < rdutyx ) { portx |= 1 ; }
/* impress */
XPORT = portx ;
/* update counter */
pcnt++ ;
/* judge */
if ( pcnt == 100 ) {
pcnt = 0 ;
ldutyx = lduty ;
rdutyx = rduty ;
}
変数pcntをカウンタに使い、ldutyx、rdutyxの値と
比較後、出力値を確定してportxに格納します。
portxの値を、I/Oに転送すると、パルスが出力されます。
変数pcntの値を、タイマー割込みで更新していくと
DUTY比に相当するパルスが出てきます。
Cのコードを、アセンブリ言語で記述し直します。
MOTOR EQU 20h
PWMGEN:
push af
push de
push hl
PWMGEN0:
ld hl,portx
ld d,h
ld e,l
xor a
ld (hl),a
;
ld a,(pcnt)
ld hl,ldutyx
cp (hl)
jr nc,PWMGEN1
ld h,d
ld l,e
set 0,(hl)
ld hl,rdutyx
cp (hl)
jr nc,PWMGEN1
ld h,d
ld l,e
set 1,(hl)
PWMGEN1:
ld hl,portx
ld a,(hl)
out (MOTOR),a
PWMGEN2:
ld a,(pcnt)
inc a
cp 100
jr nz,PWMGEN3
ld a,(lduty)
ld (ldutyx),a
ld a,(rduty)
ld (rdutyx),a
xor a
PWMGEN3:
ld (pcnt),a
PWMGENE:
;
pop hl
pop de
pop af
;
reti
pcnt: defs 1
lduty: defs 1
ldutyx: defs 1
rduty: defs 1
rdutyx: defs 1
portx: defs 1
DUTY比を変更する場合、lduty、rdutyの値を
使い、ldutyx、rdutyxの値設定は、割込み
ハンドラに任せます。出力されるPWM波形が
乱れないようにするためです。
表示器出力
表示器には、1桁の7セグメントLEDを利用します。
7セグメントLEDが表示できるのは、0〜9の数字
だけなので、数値に状態を割当てます。
数値と状態の対応は、次のようにします。
- 0 スタートトリガー待ち
- 1 NORMAL_RUN
- 2 CRANK_RUN
- 3 ROTATE
- 4 LANE_RUN
- 5 CHANGE
- 6 BLIND
- 7 others
パラレルI/Oには、8ビットレジスタを接続している
ので表示したい数値をZ80のAレジスタに入れ、サブ
ルーチンを呼ぶと、表示されるようにします。
SEGLED EQU 21h
SHOWS:
push af
push de
push hl
SHOWS0:
; calculate location
ld hl,SHOWT
ld d,0
ld e,a
add hl,de
SHOWS1:
; get bit pattern
ld a,(hl)
out (SEGLED),a
SHOWSE:
;
pop hl
pop de
pop af
;
ret
SHOWT: db 0c0h ; 0
db 0f9h ; 1
db 0a4h ; 2
db 0b0h ; 3
db 099h ; 4
db 092h ; 5
db 082h ; 6
db 0d8h ; 7
db 080h ; 8
db 090h ; 9
センサー情報のモニタには、8個のLEDを
接続して対応します。
この基板用のサブルーチンを定義します。
MONS:
push af
; inverse
cpl
; show
out (SEGLED),a
;
pop af
;
ret
センサーの8ビット情報をZ80のAレジスタに
入れて、サブルーチンをコール。
下準備ができたので、動かすための構成を考えます。
ライントレーサには、スイッチとセンサーが接続される
ので、2種類のイベントにより各サブルーチンが呼出さ
れる構成でファームウエアを記述。
RTOS(Real Time Operating System)を利用して
mainでは、タスクのディスパッチ、スケジューリング
ウエイトカウンタを制御することに。
int main(void)
{
TCBP pcur_tsk ;
/* initialize */
init_usr() ;
/* initialize task */
init_os();
/* endless loop */
run_tsk = TSK_ID0 ;
while ( ON ) {
/* RTOS handling */
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 ; }
/* 10ms counter */
if ( wflag == ON ) {
wflag = OFF ;
timer_handler();
}
}
/* dummy */
return 0;
}
RTOSを導入すると、路面状況に対応した走行は
ひとつのタスク処理の中に埋め込むことが可能
になります。
今回、8タスクに処理を振り分けました。
- task0 全体統括
- task1 NORMAL_RUN
- task2 CRANK_RUN
- task3 ROTATE
- task4 LANE_RUN
- task5 CHANGE
- task6 BLIND
- task7 センサー処理
(under construction)
目次
前
次