[PC-98][Arduino] PS/2キーボードをArduinoでPC-98につなげる

さて、前回はPS/2マウスを98のバスマウスに変換して利用してみました。

次にPS/2キーボードを98につなげて使えるようにしてみました。使う機材は前回と同じ格安のArduino Nano(の互換機)です。

ところで、最近やっとgitHub始めました(冷やし中華的な?)。いままでずっとローカルでやってましてgitは統合開発環境なんかはリポジトリの管理が標準でついているのでなんとなく使っていました。一人でやってるんでリモートリポジトリの重要性もあんま感じなく、gitHubを覚える時間があったらコードを書こうと思っていたのですが、公開するときにやっぱり便利なんで、初心者ですが始めてみました。公開第一号です。でもあんまよくわかってません!

コードを見ながらざくっと読んでいただければ…ざっくり解説を入れていきます。


ArduinoNanoとの接続

98側のコネクタはMiniDin8pin、PS/2キーボードはMiniDin6pinとなります。ピンアサインは下記のとおりです

※画像はyagura様コネクト資料室から転載させていただきました

注意が必要なPinはPS2_CLKで割込み可能なPinしか使えません。Nanoであれば2または3ピンとなります。

あとはGND、5Vを接続すればOKです。電源はPC-98本体から取りますので別途用意する必要はありません。

 

コネクタは上の商品でいいと思います。AliExpressでも取り扱いがありますので好きなのをどうぞ

注意が必要なのがMiniDin8pinのオスオスケーブルで下記の商品

これ、意外に安くていいなと思ったのですが…。

クロスケーブルで結線がクロスされています!!

これに気づかなくて、しばらーーーくハマってました。ストレートケーブルは、なかなかないのでAliExpressで探した方がいいかもしれません。僕は泣きながらクロスケーブルのピンアサインを変えて使っています。


PC-98シリーズのキーボードの仕様

先に説明したMiniDin8ピンで接続します。接続プロトコルはシリアル通信(19200bps,8bit,奇数[odd]パリティ,ストップビット1bit)となっているようです。Nanoと接続するにはハードウエアシリアルかソフトウエアシリアルを使うことになると思います。ハードウエアシリアルはスケッチ書き込み、ログの読み出しで使いたいと思っているのでソフトウエアシリアルを用いることとしました。

しかし!標準のライブラリではパリティのオプションがつけられないとのこと。下のライブラリを利用することで利用可能となることがわかりました

98のキーボードインターフェイスの実装をしなければならないのですが、いかんせん古い情報ですので先人のコードを読んだり、ネットの海を探したりしました。特に

上のサイトのio_kb.txt

上のサイトには特にお世話になりました。

98のキーボードの通信処理は

  • 送信処理(キーコード[何が押されたか離されたか]・問い合わせに対する反応[ACK,NACK])
  • 受信処理(キーボード種類、LEDの状態問い合わせ、キーリピート情報などなど)

があり適切に処理しないと、特にwindows2000などにはキーボードして認識されません。まずは純正キーボードがどのように98本体とやり取りしているかをロジックアナライザやシリアル通信で観察することにしました。

使ったロジアナはWindowsでのセットアップのクセが強すぎる中華格安ロジアナです。ソフトはPulseViewを使っています。


98本体からキーボードへ送られるコマンドの処理

Rは98本体->キーボード、Sはキーボード->98本体を示します。

電源投入時には、R:FC R:9D S:FA R:70 S:FA R:00 R:9F S:FA S:A0 S:80 R:FF となっていました。R:9D S:FA R:70 S:FAはLED(Caps,カナ,Num)の設定、R:9F S:FA S:A0 S:80はキーボードの種類の問い合わせと応答(新キーボードはA0 80を返す)となっています。

また、98本体->キーボードのみのデータとなりますが

  • Win98SE起動時: FF FF 96 96 96 96 96 96 96 9C 51 9C 70
  • Win2Kでは起動時: 96 96 96 FF 9F 96 96 96 9C 70 9D 70 9D 9D 70 9D 70 9C 70 9D 72 9D 72 9D 72 9C 70 9D 72 9D 72 9D 72
  • DOS6起動時: FF

が送られてきていました。起動時と同じように9DはLED設定、9Fはキーボード種類問い合わせです

96は「モード識別で自動変換モードではA0h,86hが返される。通常モードではA0h,85hが返される」とあります。なんのことかよくわかりませんがw、純正キーボードはA0 85を返しているようです。

9C 51 、9C 70はキーリピートの情報でio_kb.txtや先人のコードでは以下のようなデータ構造をしていることなんですが、ちょっとこれはなんか違うんじゃないかと思うわけです。詳しくは下のキーリピート問題に書いてあります。今回、どのみちPS/2キーボードにもキーリピートの設定があって98キーボードに近い設定を強制的にするようにしたのでキーリピート設定が送られてきてもACKを返しとけばいいのです(笑)

とまぁ、こう考えると 9C、9D、9F、96あたりに無難に応答してあげればOKということになります。全コードは最後に掲載しますが下のように処理します。num_fはNumLockのLEDなんですが、この機能自体はスケッチ上で処理するため状態の通知があっても無視しています。というか98にNumLockってありました?w


キーボードから98へ送られるコマンドの処理

キーボードから98へ送られるデータとして、前項の各種問い合わせの応答と、キーコードの送信になります。RDY(送信可能かどうか)、RTY(再送要求があるか)を確認して送るという関数となります。


PS/2側キーボードのデータ送受信

次はPS/2側のデータ送受信です。これはArduinoとグローバルな器材のPS/2キーボードの接続という比較的メジャー命題なので情報も多く、ライブラリでササっとできると思っていました。

が有名どころです。結構機能満載で、スキャンコードをアスキーコードに変換処理などしてくれます。今回はPS/2のスキャンコードの取得だけできればいいのでオーバースペックですねぇ。また、LEDの制御(Caps,NumLock,ScrollLock)も行えるライブラリとなると急激に情報が減ります。

色々探していると

の情報を発見。非常にシンプルで分かりやすいです。しかもLEDの制御も徹底的に解説しており、サンプルコードまであり感謝しかありません。このコードでPS/2キーボードのデータ送受信と制御を行うこととしました。まずは必要な部分を別ファイル(ps2ScanCode.h)にまとめます

このヘッダファイル(実装も含んでますが…)を事前にメインのスケッチでincludeして、以下のコードで初期化を行います。

これでPS/2キーボードのスキャンコード取得とLED制御などのコマンド送信を行うことができます。次はPS/2スキャンコードを98キーボードのスキャンコードに変換する部分です。


スキャンコード変換

まず入力されたPS/2側のスキャンコードの詳細です。

この資料みてみると、ブレイクコード(キーを離したとき場合)は押したキーコードの前にF0コードを付ける。またE0で始まるスキャンコードはE0の後にF0コードを入れればよいことがわかります。が、例外もあります。

Pauseキーはブレイクコードがないですし、シフト・NumLockキー状態に左右されるキーでは押すときのキーコードにF0がある場合があります。

次は98のスキャンコードですが、先人のソースや下のサイト、純正キーボードをたたいてロジアナを観察することで情報を得ました

これらをまとめて、keyconst.hで定義しました。また特殊キーはコード内で処理しています。もっといい処理方法がある気もしますが、とりあえずこれで動いているので暇なとき整理したいと思います。メインはcodeArray、codeArrayE0でPS2->98キーコード変換をします。NumLock解除時にはcodeArrayNotLkを利用します。これでvfキー、テンキーをカーソル替わりに使うことを実現しています。とりあえずコードを追ってもらえればと思います。


キーリピート問題

キーリピートとは、あるキーを押し続けると一定時間後リピートする機能です。このキーリピート機能は98キーボード、PS/2キーボード共にキーボード側で実現しています。ちなみにUSBキーボードはソフトウエア(BIOSなど)やドライバ側で実現しています。98キーボードは先に触れたように9Cコマンドが送られてくるとリピートするまでのディレイとリピート間隔のデータを送ってきます。それに基づいてキーボードはキーリピートを行います。

ロジックアナライザで計測したところ、98純正キーボードのデフォルトではディレイ500msでリピート間隔40msでした。先人のコードをみるとディレイは500msなんですが、間隔が60msとなっていました。何度計測しても40msなので自分のデータを信じていきたいと思います。

次に調べたことが9Cコマンドに送られてくるパラメータの解釈です。

資料には上のように定義されているとのことです。実験として、Win98上でのコマンドプロンプト(DOS窓)を開いてみると9C 51が送られてきます。フォーカスがメモ帳などに切り替わると9C 70が送られてきます。

ちなみにロジックアナライザを見ていると9C 51の時はキーリピートをキーボードが行っています。が、9C 70の後ではドライバ側で行っているようでキーリピートさせてもロジックアナライザでは反応ありません。つまり70はキーリピート無効、51はキーリピート有効であると考察できます。

つまりWin2K、Win98SE起動時では最後の9Cコマンドでは70が送られてくることからキーリピートは無効になっていてドライバ管理ということですね。

そもそも、キーリピート無効という設定が上の資料からは読み取れません。先人のコードを見るとbit0-4が00000bだと無効としてあります。しかし、70h = 01110000bで00000bのデータ部がありません。うーん

ここからは僕の推測ですので話半分で読んでください。

70hの上下4bitを反転させると00000111bなります。こうするとbit0-4が00000bとなりキーリピートは無効となります。

51hの場合は00010101bとなります。リピート間隔はbit0-4が00010bとなりリピートは有効となります。ディレイはbit5-6ですので10bで上の資料の対応表から500msとなり正しくなります。bit0-4のリピート間隔の解釈ですが、これは最高速の間隔(00001b)を20msとすると 20ms×10b(2Dex) = 40msとなり実測値と同じくなります。

この仮説を検証しようにも9Cのデータが70と51しか観測できず推測の域を出ることができません。このへん詳しい人いませんかね?ツイッターで待ってますw

でコードでの実装ですが、いろいろやってみてわかったことは、9C 70が送られて、キーリピート無効でも無視してキーボードでキーリピートをしてても問題なく動くのですw。というわけで常にデフォルトでキーリピートをオンにし続ければいいわけです。

というわけで上記コードをPS/2キーボードに送り98のキーリピートと同一にセットし続けておくことで解決します。

ではなんで、くどくど9Cのデータのことを話したかというと、次に作るUSBマウスキーボード変換器(実はほぼ完成)のコード処理に欠かせない知識となったので忘備録的に書かせてもらいました。みなさんも興味ありますよね?(オイ)


最後に(セットアップメニュー起動とか)

あとの細かい部分はコードをみてほしいのです。自分好みにカスタマイズしてください。もう何を書こうと思ったのかすら忘れてしまってw。

起動時にキーを押すとセットアップメニューを起動できます。いろいろありますがHELPが有名どころですね

この変換器で…セットアップメニュー起動をできるかできないか!どっちなんだいっ?

できません!!

電源投入時に

R:FC R:9D S:FA R:70 S:FA R:00 R:9F S:FA S:A0 S:80 R:FF

というデータが純正キーボードやりとりされているのは上で説明しましたが、最初のFCを受信して次の9Dを受信する間に起動するキーコード(HELPキーなど)を送らないとダメなんですね。

しかし、この変換器は電源が入り、ソフトウエアシリアルの初期化が完了し、最速でキーコードを送ってもR:9F付近が限界でした。というわけで無理なんですw。別電源で予めNanoを起動させとくか、ハードウエアシリアルを使うかすればいいのかもしれません。

複数のハードウエアシリアルがあるATmega2560あたりと思いますが、持ってません!まぁESP32があるんですが、ロジックレベルが違うんだよなぁ・・・

とはいえ、次の予定ではESP32を使ってUSBキーボード・マウスを同時に変換する変換器を作っています。試験段階ですがセットアップメニュー起動はできていますが、癖が強い方法となりそうですw。こうご期待(誰得?)


 

2 thoughts on “[PC-98][Arduino] PS/2キーボードをArduinoでPC-98につなげる

Comments are closed.