2016.10.22
前へ
次へ
ホームページトップへ戻る

マイコン独立大作戦
キーボードインターフェースの製作

〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
WindowsパソコンにUSB接続して使う現行方式はそれなりに便利ではありますが、ときとしてWindows
のしがらみから開放されて、小さいながらも独立した一個のパソコンとして機能したいと思うこともあります。
独立大作戦の作戦その1はCRTインターフェースボードの製作です。
そして作戦その2は、やっぱりキーボードインターフェースしかありませんでしょう。
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜

[第12回]


●PICを使います

前回書きましたようにPS/2キーボードを82C55に直接接続する方法は[Ctrl]+[C]などのキーコードの入力に時間がかかってしまうという難点があるため、マイコン独立大作戦のキーボードインターフェースとしては使いません。
もっとも82C55に直結するという方法は簡便ですし、何より間に何もはさまないで信号を直接受信することができる、という利点がありますから、[第5回][第6回]で使った方法は、キーボードからのデータ信号を生の状態で解読しなければならないようなときにはこれからも活用したいと思っています。

さて。
それではマイコン独立大作戦のキーボードインターフェースとして何を使うのか、ということなのですが、ここはやっぱりPICを使う場面だと思います。
PICは16F87を使うことにしました。
こちらがジャノ目基板に組んだPIC16F87を使ったテスト回路の写真です。

といっても、PIC16F87とPS/2ミニDINコネクタと26pinフラットケーブルコネクタを手配線でつないだだけです。
基板の表側から見ただけでは、その3つが見えるだけです。

こちらが基板裏側の写真です。


下が回路図です。

82C55のCポートはPC0〜PC3を出力、PC4〜PC7を入力に設定します。
Bポートはパラレルデータの入力に使います。
Aポートは使いません。

PIC16F87の入出力について簡単に説明します。
PS/2信号のCLKはRB0(INT)で受けます。
前回説明しましたようにPS/2キーボードのCLKパルスは80μsとそれほど速いクロックではありませんから割込みを使わなくても処理できないことはありませんが、プログラムをすっきりまとめるために、ここは割込みを使うことにしました。
ホスト(CPU)側とのインターフェースですが、そこをどうするかかなり悩んであれこれ考えました。
よくよく考えてみましたら、もともとキーボードはCPUとハンドシェークをするような高度なものではなくて、ただのキースイッチに過ぎない、ということに気が付きました。
キーボードの中にIC回路が組み込まれていて、シリアルデータとしてキーコードが出力されているとしても、それは同じことです。
そう考えたらホスト(CPU)側のソフトもPIC側のソフトも簡単なものになってしまいました。

PIC16F87はキーボードからのデータを受信したら、それを解読して、その結果以下の動作をします。

1)キーが押されたら常に最後に押されたキーのスキャンコードをRB7〜RB1、RA0に出力したままにします。
RB0をINT端子としてCLK入力として使うのでRA0で代用します。
2バイトのスキャンコードについては工夫して未使用の8ビットコードを割り当てることにします。
キーが離れたら(1バイトのスキャンコードの場合にはF0+スキャンコードが送られてくる)、もしもそれが最後に押されたキーコードと一致したらRB7〜RA0に00を出力します。
2)もしも押されたキーが[Shift][Ctrl][Alt]だった場合には、各キーに対応してRA1(Shift)、RA2(Ctrl)、RA3(Alt)を1にします(このときはRB7〜RA0にはスキャンコードは出力しません)。
上記いずれかのキーが離れたら、それに対応してRA1〜RA3を0にする。
3)このようにすることで[Shift]〜[Alt]とそのほかのキーが同時に押された場合に、そのことをCPU側は82C55のポートBとポートCを読み込むだけで知ることができます(待っている必要はありません)。

最近はPICのプログラムもCコンパイラで書くのだそうですが、単なるI/O制御程度のものにCコンパイラを使うというのは、いかがなものかと思います。
アセンブラがいいじゃありませんか。
PICのアセンブラはわかりにくいことで定評がありますけれど、慣れてしまえばそれほどのものじゃありません。
なんたってたった35個の命令を覚えるだけでいいのですから、やる気になれば別にどうというほどのことはありませんでしょう(Mid−RangeクラスのPICの場合)。


[出典]Microchip Tecnology Inc. PIC16F87/88 Data Sheet

私はアセンブラのほうがずっとずっと楽だと思いますよ。
ということで、下がとりあえずテストとして作ったPS/2キーボードインターフェースプログラムです。

;;;PIC 16F87  keyboard interface
;
;16/10/4 10/7 10/8 10/22
;internal clock 8mhz
;
        #include <p16f87.inc>        ; processor specific variable definitions  
;
;Program Configuration Register 1
        __CONFIG _CONFIG1,_CP_OFF & _LVP_OFF & _MCLR_ON & _PWRTE_ON & _WDT_ON & _INTRC_CLKOUT
;Program Configuration Register 2
        __CONFIG _CONFIG2,
;
cf=0
zf=2
f=1
w=0
;
bitcntr11 equ 20
bitcntr8 equ 21
keycode equ 22
outdata equ 23
dataokmk equ 24
keyoffmk equ 25
savew equ 26
savests equ 27
;
     org 00
st0
     goto start
;
        org 04
        goto int
;
     org 05
start
     bsf STATUS,5 ;bank 1
        movlw 7c;=8MHz
        movwf OSCCON
     movlw 01;RB0=in(int) CLK
     movwf TRISB
         movlw 10;a4=in DT
        movwf TRISA
        bcf OPTION_REG,6;int edge is falling edge
        bcf STATUS,5 ;bank 0
;
        movlw 0b;=11
        movwf bitcntr11
        movlw 8
        movwf bitcntr8
        clrf keycode
        clrf outdata
        clrf dataokmk
        clrf keyoffmk
        clrf PORTB
        clrf PORTA
        ;int enable
        movlw 0d0
        movwf INTCON
;       
;start
;
loop
        clrwdt
        btfss dataokmk,0
        goto loop
;
;F0(keyoff) ckeck
        movlw 0f0
        subwf keycode,w
        btfsc STATUS,zf
        goto keyoffmkset
;
        btfsc keyoffmk,0;keyoff?
        goto keyoff
;
;if shift,ctrl,alt
        movlw 12;left shift
        subwf keycode,w
        btfsc STATUS,zf
        goto shiftset
        movlw 14;left ctrl
        subwf keycode,w
        btfsc STATUS,zf
        goto ctrlset
        movlw 11;left alt
        subwf keycode,w
        btfsc STATUS,zf
        goto altset
;else 
        movf keycode,w
        call dataout
dataokclr
        clrf dataokmk
        goto loop
;
shiftset
        bsf PORTA,1
        goto dataokclr
ctrlset
        bsf PORTA,2
        goto dataokclr
altset
        bsf PORTA,3
        goto dataokclr
;
keyoffmkset
        bsf keyoffmk,0
        clrf dataokmk
        goto loop
;
keyoff
;shift,ctrl,alt check
        movlw 12;left shift
        subwf keycode,w
        btfsc STATUS,zf
        goto shiftoff
        movlw 14;left ctrl
        subwf keycode,w
        btfsc STATUS,zf
        goto ctrloff
        movlw 11;left alt
        subwf keycode,w
        btfsc STATUS,zf
        goto altoff
;else
        movf keycode,w
        subwf outdata,w
        btfss STATUS,zf;equal?
        goto keyoffmkclr
        movlw 0
        call dataout
keyoffmkclr
        bcf keyoffmk,0
        clrf dataokmk
        goto loop
;
shiftoff
        bcf PORTA,1
        goto keyoffmkclr
ctrloff
        bcf PORTA,2
        goto keyoffmkclr
altoff
        bcf PORTA,3
        goto keyoffmkclr        
;
; subroutine
;
dataout
        movwf outdata
        movwf PORTB
        btfsc outdata,0
        goto set0
        bcf PORTA,0
        goto next
set0
        bsf PORTA,0
next
        clrf dataokmk
        return
;
int
     movwf savew
     swapf STATUS,w
     movwf savests
     bcf STATUS,5;bank 0
        movlw 80
        xorwf PORTA,f;for test
        movlw 0b
        subwf bitcntr11,w
        btfsc STATUS,zf
        goto skipbit
        movf bitcntr8,w
        btfsc STATUS,zf
        goto skipbit
        btfsc PORTA,4;DT
        bsf STATUS,cf
        btfss PORTA,4;DT
        bcf STATUS,cf
        rrf keycode,f
        decf bitcntr8,f
skipbit
        decfsz bitcntr11,f
        goto intend2
        bsf dataokmk,0
        movlw 8
        movwf bitcntr8
        movlw 0b
        movwf bitcntr11
intend2
        bcf INTCON,1
     swapf savests,w
     movwf STATUS
     swapf savew,f
         swapf savew,w
     retfie
;
     end
;

アセンブラのほうが簡単です、と書きましたが、結構長いプログラムになってしまいました。
最初は短かったのですけれど、デバッグをして直していくうちにだんだん長くなってしまいました。
まあ、いつものことなのですけれど。

参考までに、簡単にプログラムの説明をします。

まず最初は割込みルーチンの説明です。
int以下が割込みルーチンです。
PS/2キーボードのCLKの下がりエッジで割込みが発生します。
int以下の最初の3行と終わりのintend2以下はPICの割込み処理のお約束事です。

movlw 80
xorwf PORTA,f
は何をやってるの?ということなのですが、ここではテストのために割込みが正しく行なわれているかRA7出力で確認しています。
動作が確認できればここは不要です。
PICプログラムがうまく動いてくれないときなどに、ちょいとこのような仕掛けをしておくと、プログラムのバグ取りに有効だったりします。

さて。
この割込みルーチンは簡単なものです。
割り込みはCLK毎に下がりエッジで発生します。
2個のビットカウンタbitcntr8とbitcntr11の値によって、スタートビット、パリティビット、ストップビットを判定し、読み飛ばします。
それ以外のクロックのときはDTをkeycodeにシフト入力します。
ストップビットを受信すると、スキャンコードを受信したことをメインプログラムに知らせるために、dataokmkのビット0を1にして処理を終ります。

ここで最初に発生した割り込みがスタートビットではなくて途中のクロックだったらどうなるか?ということなのですが、まずはテストですからそれは考えないことにします。
PICはホスト(CPU)側のプログラムがスタートしたときにリセットがかかって、そこからスタートします(CPU側のプログラムは最初にPC0からL→Hを出力してからスタートします)。
その時点ではPS/2キーボードは押されていないことを前提とします。

ここまでで割込みルーチンの説明は終わりです。
以下メインプログラムの説明に入ります。

メインプログラムはdataokmkのビット0が1になるのをループして待ちます。
テストですからE0,xxの2バイトコードは除外します。
しかし厄介なことにキーが離れたときはF0とスキャンコードの2バイトを判定しなければなりません。
そのために最初にF0かどうかをチェックしてF0ならばkeyoffmkのビット0を1にします(goto keyoffmkset)。

その次の
btfsc keyoffmk,0
のところで今判定したばかりのはずのキーオフのチェックをしていますが、このあたりのところが割込みプログラムに慣れていないとよく理解できないところだと思います。
ここは先にF0を受信した場合のその次のサイクルを意識しています(またはF0が来ないで普通にキーが押されている場合を考えています)。
keyoffmkのビット0が1ならばキーオフの処理をします(goto keyoff)。

keyoffの処理です。
まず[Shift]以下のチェックに入ります。
[Shift][Ctrl][Alt]のコードかどうかをチェックして、一致したら該当する出力を0にします。
それ以外のキーの場合には、これ以前に押されていたキーが離れたのかどうかをチェックします。
今回受信したkeycodeと先に出力したoutdataを比較して一致したら出力データを00にします(movlw 0/call dataoutを実行します)。

キーオフではなかった場合には
;if shift,ctrl,alt
から下の処理をします。
まずは[Shift][Ctrl][Alt]のコードかどうかをチェックして、一致したら該当する出力を1にします。
そうでなかった場合には、受信したキーコードをoutdataに入れるとともにRB7〜RA0に出力します(call dataout)。

これでとりあえずのPS/2キーボードインターフェースとして働いてくれるはずです。
次回はこのプログラムをPIC16F87に書いて、動作させてみることにします。

キーボードインターフェースの製作[第12回]
2016.10.22upload

前へ
次へ
ホームページトップへ戻る