KZ80マイコンを 偽MSX1へ〜PS/2キーボードMSXアダプタ(2)

KZ80_PS2KBD

以前、MSX Hobbyさんのページを参考に、ArduinoMEGAとKZ80-IOB REV2でPS/2キーボードMSXアダプタを試作しました。
KZ80-IOBへの外付けだったのと配線がバラックでつかいずらかったので、プリント基板化してソフトを改良してみました。いつも通りハマりましたw

プリント基板化で大失敗

ユニバーサル基板化するにあたり、PS/2キーボードからのデータをうけたり、8255へスキャンコードを出力するためのマイコンはATmega328Pを使うことにしました。理由は以下の通りです。

  • MSX HobbyさんのArduinoMEGA用プログラムを使って、この基板用のソフトが作れそうだった
  • 先達の方々(きもあんさんロシアの方)もAVRマイコンを採用されていたため動きそう
  • 秋月電子でも手軽に購入でき、DIP28ピンタイプで基板搭載が容易であろうと推測。ArduinoUNOでも搭載されていて情報が豊富

またロシアの方の別の記事でATTiny2313の内蔵CR発振器(8MHz)で稼働させている例もありまして、これだとI/Oピンが2ピン追加で確保できて8bit分の出力もできるぞと思い、採用させていただきました。

KiCADで回路図、プリントパターンを作っていつものJLCPCBさんへ製造依頼。各種データはこちらにあります。

GitHub - kuninet/KZ80-PS2KBD: KZ80シリーズ用 PS/2キーボード MSX互換アダプタボードのプロジェクト
KZ80シリーズ用 PS/2キーボード MSX互換アダプタボードのプロジェクト. Contribute to kuninet/KZ80-PS2KBD development by creating an account on GitHub.

基板が届いて導通チェックをしていたら、大変なことに気づきまして…..

基板の写真でもわかりますが、ロジックIC 74HC00の7番ピン(GND)がVcc(+5V)につながってます。逆に裏面をみてみたところ、14番ピン(Vcc)がGNDに直結。原因は回路図の間違いでした。
ぐぬぬぬ、発注前にプリントパターンを見ていたと思うのですがちょっと気を抜いてました。

この間違いで意気消沈して数ヶ月放置していたのは内緒….
こちらは、いつもやってるICソケット2段重ねとジャンパ線で回避することが可能でした。次回の基板作成(いつだろう…)には絶対修正するところです。

ArduinoMEGA版との違い

気を取り直して、プリント基板化してつくってみたPS/2キーボードMSXアダプタとArduinoMEGA版の違いを書いてみます。

割り込み処理の違い

プリント基板化してATmega328Pを採用したPS/2キーボードMSXアダプタですが、大きな違いは割り込みの受信のしかたです。MSX Hobbyさんのプログラムでは以下のようにArduinoMEGAの豊富な割り込みを使用して作られています。

  1. PS/2キーボードからのデータは、キーボードのCLK信号を外部割り込みピン(INT1)を使用して処理している
  2. MSXはPPI(8255)のCポートの4本の線を0から順にカウントアップしてきますが、そこも外部割り込みピン(18ピン〜21ピン)によって処理している
  attachInterrupt(digitalPinToInterrupt(21), blink, CHANGE);  //  Interruptions PIN 18,19,20 and 21 Arduino MEGA 2560
  attachInterrupt(digitalPinToInterrupt(20), blink, CHANGE);  //  2 - PIN 21 ; 3 - PIN 20
  attachInterrupt(digitalPinToInterrupt(19), blink, CHANGE);  //  4 - PIN 19 ; 5 - PIN 18
  attachInterrupt(digitalPinToInterrupt(18), blink, CHANGE);  //

2.のPPIからの割り込みをうけている部分のソースは上記になります。

私の作ろうとしているPS/2キーボードMSX基板ではArduinoUNOなどでも搭載しているATmega328Pを使おうとしてまして、外部割り込みピンはINT0/INT1の2本です。ただ、その他のピンもグループ単位(3グループ)ですがピン変化割り込みというものがありそちらを使えば割り込み処理ができそう。4本の信号線が変化しますが、処理としてはどれがが変化したら1回割り込み処理できれば良いので都合が良さそうです。

MSXのキースキャン用信号線

MSXのキースキャンですが、以前も書いたとおり以下のようにPPIのBチャンネルの8本の信号線で受信します。MSX側ではPPIのPC4〜7に順にLレベルの信号を出してキーボードマトリクスのキーが押されているとPB0〜PB7のどこかがLレベルへ落ちることでキーが押されていることを認識します。

右側の物理キーボード群をATmega328Pマイコンでソフトウェア・エミュレートするわけなので、8bit分揃った出力ポートがほしいところです。4bitづつ2回出力だとバラつきそう。
ATmega328Pのデータシートを見るとBポートが良さそうなんですが、ArduinoUNOや色々な作例では安定性重視で16MHz水晶振動子が外部クロックとして2ポート使っています。

ATmega328Pは8MHzの内部CR発振器をもってまして、出荷時には1/8内部分周器で1MHzの状態となっています。1MHzだとさすがにちょっと苦しいですが、8MHzあれば行けるかも & I/Oピンを2つゲットだ!ということで8MHzの内部CR発振器による駆動ということにしました。

ArduinoNanoでソフトウェアを試作

スケッチの移植ポイント

上記のように対応の方向性は決めたものの、生AVRマイコンのソフトは作ったことがないのでハマるのは必定。I/Oピンは足りないですが同じCPUを搭載しているArduinoNanoで試作してみることにしました。
なぜArduinoUNOじゃないのか?というとUNO互換機はI2Cの実験用に別の回路を組んでいたので、そちらの回路を外したくなかったのでした。(^-^);;;

ArduinoMEGA版のソースから変更したスケッチ・プログラムは以下のような感じです。

void setup() {
  
  keyboard.begin(KBD_DATA_PIN, KBD_CLK_PIN, 0); //Compile code with Hotbit ABNT2 Key Map
  
  PCMSK1 |= (_BV(0)|_BV(1)|_BV(2)|_BV(3));
  PCICR |= _BV(PCIE1);
  DDRC &= ~(_BV(0)|_BV(1)|_BV(2)|_BV(3));
  DDRB |= (_BV(0)|_BV(1)|_BV(2)|_BV(3));
  DDRD |= (_BV(4)|_BV(5)|_BV(6)|_BV(7));
  PORTC &= ~(_BV(0)|_BV(1)|_BV(2)|_BV(3));
  keyboard.reset();
}

void loop() {
}

ISR(PCINT1_vect)
{
    volatile  uint8_t *portD=&PORTD;
    volatile  uint8_t *portB=&PORTB;

    if ((PINC & 0x0f) >= 9) { 
      delayMicroseconds(1);
      noInterrupts();
       *portB = (*portB & 0xf0) | (0x0f) ;
       *portD = (*portD & 0x0f) | (0xf0) ;
      interrupts();
      
    }else if ( (PINC & 0x0f) == 8) { // If your machine has more one line change this to 9
      delayMicroseconds(1);
          noInterrupts();
          *portB = (*portB & 0xf0) | (keyboard.MSX_KB_Matrix(0) & 0x0f) ;
          *portD = (*portD & 0x0f) | (keyboard.MSX_KB_Matrix(0) & 0xf0) ;
          interrupts();
    } else {
      noInterrupts();
      delayMicroseconds(2);
      *portB = (*portB & 0xf0) | (keyboard.MSX_KB_Matrix((PINC & 0x0f)+1) & 0x0f) ;
      *portD = (*portD & 0x0f) | (keyboard.MSX_KB_Matrix((PINC & 0x0f)+1) & 0xf0) ;
      interrupts();
    }  
}

setup() では、PPI(8255)からのキースキャン信号をATmega328PのポートCで受けるためPC0〜PC3の4本についてピン変化割り込みを設定します。
ピン変化割り込み制御レジスタ PCICR の ピン変化1群割り込み許可ビット PCIE1 を立てるのと、ピン変化割り込み1群マスクレジスタ PCMSK1 の0ビット目〜3ビット目を1にします。
各ポートの方向レジスタは、ポートC DDRC は入力、ポートB DDRB と ポートD DDRD はPPI(8255)への出力に4ビットづつつかうため 該当ポートを1にします。

ポートCからのピン変化割り込みでCallされるのは ISR(PCINT1_vect){ } ルーチンなので、PPI(8255)へ出力するプログラムはそこへ移動します。キーボードマトリクステーブルの出力はピン数の関係で上位ビットと下位ビットへ分けて2回出力します。ソフトウェアなのでちょっと時間差が出てしまうのが難点です。稼働するかどうかの実験であればよいかなという感じ。

無事稼働

上記ソフトウェア対応を実施して実験開始。ブレッドボードへ搭載して配線した際にズレて配線していたりして謎の文字が画面いっぱいになったりしましたが正しい配線にしたところ無事稼働しました。
2回に分けてスキャンデータをPPI側に出力したりしているため、適当なマイクロ秒単位のWAITを入れることで大半のキーが正しく入力できるようになりました。
以前もハマった数字の0〜7が2文字づつ入力される現象はここでも再発。ひとまずピン変化割り込みで稼働するかどうかという実験だったのでそこは無視しました。ArduinoIDEからスケッチとしてArduinoNanoに書き込んだ環境で、今回わたしがプリント基板化したものとは配線も違いますがピン変化割り込みで稼働するというところまでの確認がとれました。

つづいて、今回わたしがプリント基板化したKZ80-PS2KBD上のATmega328Pのプログラム開発にうつります。こちらも色々とハマりましたので、また別記事でまとめたいと思います。

コメント