2021年10月23日土曜日

RGB変換アダプター

1991年頃のパソピア7画像を発掘。
東芝のRGB TV Premeraと5インチミニフロッピーとスーファミ・・・
まだパソピア7が黄ばんでなくて綺麗。。

TVの背面には、RGB 21pinとDC 10Vのコネクタがある。

パソピア7はデジタル8pinコネクタなので、21pinのTVに繋ぐために変換アダプターPAD-91が必要だった。
ずっとパソピア7の箱に埋もれていたのを最近発掘。

アダプターには、RGB 21pinと8pinの切り替えスイッチと、水平位置の調整つまみがある

分解してみた。
すんごく手間取った。。
デジタル8pinコネクタと基板がケースに装着されたまま直接半田付けされていたのので、ケースから外すのに基板から半田を吸い取ってコネクタとケースを外した。

カラーパレットで問題になる水平ブランキングの問題は、TC4538BPで対応している様子。
水平位置調整つまみで抵抗値を変えてTC4538BPに入れることで、水平位置を調整しているっぽい

この対策方法は凄いんだけど、基板とデジタル8pinコネクタの直接はんだ付けはやめて。。

2021年8月25日水曜日

ゲーセン物語展

先日小樽に行く用事があったので、久々に小樽文学館に行った。
前回行ったのは2012年で「テレビゲームと文学」という企画展。
今回は「小樽・札幌ゲーセン物語展2」という企画展をやってました。
前回のチラシ(左側)を見ると1~3月にもやっていたのね。。

展示筐体は期間によって基板が変わるようで、この時はぷよぷよと出たな!!ツインビー。
椅子がいい!やっぱりこの椅子だよね。
テーブル筐体もいい感じです

壁にはポスターや解説文。当時のゲームサークルの話題など読み応えありました。
システムII、お世話になりました。

ショーケースには雑誌やCD、ビデオなど


Beepもショーケースに入るのか... うちにも1冊ある。。

イラストも素晴らしい。

他にもゲーセンノートやゲーム同人誌の紹介など、狭いエリアに沢山ありました。
今はこの状況なので休館中ですが、落ち着いてきたら是非また再開して欲しいです。

-----
ところで小樽文学館のゲーセン企画展の横で、栗本 薫氏の文庫本が展示してました。
自分は伊集院大介シリーズが大好きで、ずーと読んでました。まだ引出しに全巻あります...

栗本 薫氏は亡くなられてから10年以上経ちますが、単行本未収録を収めた作品「伊集院大介最後の推理」が電子書籍で読めるようになっていたのですね。こんど読んでみよう...

栗本 薫氏(中島 梓さん)がクイズ ヒントでピントの女性キャプテンだったと知ったのは結構最近。。すごい人でした...

2021年7月10日土曜日

ケースも作ってみた

今回、基板は長方形ではなく切り抜き加工してもらった
XPAC2はケース作りが大変だったので、市販のケースに入る大きさにした。
Picoのサイズを考えて、タカチ電機工業のTW2-2-8B(W40xD80xH20)を使うことに。
基板を中に入れてみるとこんな感じ。ぴったり

ケースにケーブル用の穴をあけるのにCNCを使う。
穴をあけるとこんな感じ
MicroUSBコネクタ用の穴は、くり抜きなのでCNCで削れなかった。
リューターで穴をあけて、ヤスリで整えた。

部品を乗せた基板をセットしてみると、ケーブルが太くて蓋が閉まらない。
蓋側も削ってみたが、それでも余裕がない
一番右端に74HC574がついているが、その部品が高めになっている
ちょっと低めの74HC574を使って組み直してみる
部品を変えて作り直した基板。これでICの高さがそろった。

ケーブル抜け防止のために、結束バンドで止めてみた。

基板をケーブルに止めるのに、ネジ2つ必要。
基板の厚さが2mmでネジ穴の深さが4mmだったので、M2 x 6mmのネジを買ってみた。
実際に固定してみるとちょっと長かったので、2mmほどニッパーでカットした。
今度はグラグラしないで、ちゃんと固定できた。
蓋を閉めるとこんな感じ。
せっかく低いICにしたにも関わらず結束バンド厚みの分、蓋がしっかり閉まらない...
ネジと対角側が少し浮いた感じになっているが、使う分には問題なさそうなのでこのままにしておく

PicoのMicroUSBコネクタに変換ケーブルを接続して完成。


今回はPicoを使ったおかけで、コンパクトにまとめることができた。
Pasopia700 USB Keyboard Adaptorはこれで一旦完成。
満足。

2021年6月20日日曜日

基板を作ってみた

Pasopia700用USB Keyboard Adaptorの基板を作ってみる。

今回も以前と同じEAGLE 5を使うが、Raspberry Pi Pico用のライブラリがない。
Picoのマニュアルを見ながらスクリプトファイルを作成し、ライブラリファイルを作る。

ファイル(F) → 開く(O) → ライブラリ(L)... でライブラリウインドウを開く

ファイル(F) → スクリプトファイルを実行(T)... で作成したスクリプトファイルを開く

問題なければ、回路図用と基板用のパッケージが生成される。
ファイル(F) → 名前を付けて保存(A)... でライブラリファイルを保存できる

EAGLE5用に作成したRaspberry Pi Picoのスクリプトとライブラリは、ここに置いておく。
このライブラリを使って書いた回路図はこちら。

基板はこちら

PCB基板の作成は、XPAC2と同じくunicraftさんにお願いした。
基板発注にに必要なガーバーデータをEAGLE5で作成する。
まずはファイル(F) → CAMプロセッサ でCAMウィンドウを表示する

ファイル(F) → 開く(O) → job... で出力データに合ったジョブファイルを選択する

今回は2層基板のガーバーデータ出力に「gerb274x[2L].cam」ジョブ、ドリルデータ出力に「excellon.cam」ジョブを使用。
各ジョブを読み込んだ後に「Process Job」ボタンを押すとファイル出力される。
出力された各ファイルを、unicraftさん指定のファイル名に変更して発注しました。

発注してから1週間ほどで基板が到着。
今回も綺麗に仕上げてもらいました。

装着する部品を並べてみると、XPAC2より少ないです。

組みたててみると、こんな感じ

試作品の基板と並べてみました。

Pasopia700で動作チェック

すると、新しい基板だと時々キー入力されない時がある。
調べてみると、PicoのGP18入力値が不安定になっている。

GP18は、Pasopia700からのCLOCK信号(DINケーブルの11pin)とGP20からの出力信号のAND結果を入力している。
Picoのロジック側で、USBキーボードの変化があった際にGP20を一瞬LowにしてHighに戻しているが、GP18がLowを拾えないタイミングがある様子。
    gpio_put(20, 0);
    gpio_put(20, 1);
この間にウェイトを入れてLowの間隔を長くすると問題は起きなくなった。
更にロジックを見直してみると、GP18はPicoの内蔵プルアップを有効にしていた。
gpio_pull_up(18);  // Pin11: CLK
このプルアップを外しても問題は起きなくなった。
今回の回路では全てのパソピア信号線に対してハード的にプルアップ・プルダウンの回路を組んであるので、Pico内蔵のプルアップ・プルダウンは設定しないようにPico側のプログラムを変更することにします。(GP18のLowウェイトは元に戻します)

原因はちゃんと調べていないけど、試作品と異なりリード線がシンプルになった分、信号がリニアに伝わるようになったからでは?と勝手に思っている。。

2021年6月13日日曜日

USB Host見直し

Raspberry Pi PicoのSDKが更新されていて、USB Hostが新しくなっていたので試してみる

前のブログ画像にもあったのだが、デバッグ画面に時々parse_configuration_descriptorのAssertが出るのが気になっていた。


pico-sdkのtinyUSBが更新されていたので、最新版を取得。
以前、usb hostのサンプルは、

\pico-example\usb\host\host_hid

にあったが、今回はpico-sdkの下に移った。

\pico-sdk\lib\tinyusb\examples\host\cdc_msc_hid\src

またSDK内部も更新されており、以前のコードのままだとビルドも通らなくなっていた。
ソースコードを修正して、もう一度何種類かキーボードを試した結果、
  • Buffalo BKBU-J109LG : OK
  • Buffalo YDKBU02BK : OK
  • iBuffalo SAVIOR BSKBC02 : NG
  • Fujitsu USB Hub付キーボード : NG
  • Dell Keyboard KB212 : OK
  • Sanwa Supply SKB-SL065V : OK
  • ユーエーシー KU-8933 : OK

キーボードの認識結果は前回と同じだったが、Assertは出なくなった。


一度キーボードを抜き差しすると正しく認識しなくなるのは直っていないが、Assertが出なくなったぶん、新しいSDKの方が安心して使えそう。

あとUSBコネクタが断線したので、ケーブルが無いのものに変更。
横出しなのでちょっと扱いにくいが、断線の心配がないから安心...



2021年5月28日金曜日

PIOを見直してみる

前回Picoでパソピア700のUSBキーボードアダプタを作ってみたが、安定しない。

Core1とPIOのFIFOバッファやりとりに速度のばらつきがありそうなので、そこに依存しないPIOの使い方を考えてみる。

まずは回路図の見直し


今回は8bitのデータ保持用に、74LS574を追加。
配線してみると、こんな感じ

PicoとUSBキーボードを接続し、DIN13pinはPasopia700と接続する

Raspberry Pi Picoには、133MHzで動作する2つのコアと、それとは独立に動作するプログラマブルI/O(PIO)を8つ持っている。

今回は1つのコア(Core0)と8つのPIO(PIO0 SM0-3とPIO1 SM0-3)を使って、以下の処理を試してみる
(1) Core0でUSBキーボードで押されたキーの情報を、8つのPIOの各FIFOバッファに格納
(2) PIOでパソピアからの7bitスキャン信号(GPIO10-16)をCLK立ち上がり時に読み込み、FIFOに格納されているビット情報から1bitを決めて出力。8つのPIOがそれぞれのGPIOに出力して、8bitデータをGPIO2-9に出力
(5) 前回保存しておいた7bitスキャン信号は74LS574に保存してあるので、キー入力に変化があった場合、押されたキーの情報を、8つのPIOの各FIFOバッファに格納し、8つのPIOを再呼び出しすることでGPIO2-9の出力データを更新する

PIOの都合もあって、FIFOに渡す32bitのデータ配列は以下の並びに
Bit 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
KeyBlock A&B&C B&C A&C C A&B B A None
KeyScanLine0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3 KeyOff(0), KeyOn(1)

パソピア700から届く7bitのスキャン信号は、3bitのKeyBlockと4bitのKeyScanLine信号。
7bit全ての組み合わせを保存するのに128bitデータ領域が必要だが、FIFO 1つで対応したかったので32bitにキー情報を収める。
KeyBlockは3bit全ての組み合わせを持ち、KeyScanLineはそれぞれのビット毎にKeyOn/Offデータを保持する。どれか1つでもKeyOn(1)があれば、そのGPIOはLow出力する。

例えば以下表Exampleのように、7bitのスキャン信号(0b0110101)が届いた場合、
Pico GPIO GP16 GP15 GP14 GP13 GP12 GP11 GP10
DIN13pin 9pin 8pin 7pin 6pin 5pin 4pin 3pin
Pasopia700 KeyBlock_C KeyBlock_B KeyBlock_A KeyScanLIne_3 KeyScanLIne_2 KeyScanLIne_1 KeyScanLIne_0
Example 0 1 1 0 1 0 1 0b0110101

KeyBlockはAとBが有効なので、FIFOの15~12bitのデータをチェックする
KeyScanLineは0と2が有効なので、FIFOの15bitと13bitの値をチェックし、どちらか1つでも1があれば、GPIOのピンにLow(0)をセットする
パソピア700とKeyScanLineの並びが逆だったり、出力信号のLow/Highが逆だったりするが、PIOの制約があったのでFIFOはこの並び順にした。

出来上がったPIOは以下
.program usbkey
.side_set 1 opt

usbkey_start:
    wait 0 gpio 18 [7]  ; GP18(=Din11pin=CLK)が0になるまで待つ
    wait 1 gpio 18 [1]  ; GP18(=Din11pin=CLK)が1になるまで待つ
    // バッファ(74LC574)がある場合、Pinデータを読み込む前に1clock必要
    in NULL, 32     ; ISRを初期化 →(ISR) |0|...|0|

    ; SideSetピンを1にセットして、TX FIFO(32bitデータ)をOSRに読み出し
    ; noblockオプション: FIFOが空の場合、Scratch XからOSRにコピー
    pull noblock side 1
    mov X, OSR ; 次回FIFOが空だった場合に備えてOSR値をScratch Xにコピー

    ; Pins(GP10-GP16)の7bitデータをInput Shift Register (ISR)にセット
    in pins, 7      ; ISRを7bitシフトした後、pinデータ読み込み
    mov OSR, ISR  ; ISRをOSRにコピー(OSRのShiftCounterもゼロリセット)
    mov ISR, X  ; XレジスタをISRにコピー(ISRのShiftCounterもゼロリセット)

    out Y, 1        ; OSRの左1bitデータをYレジスタにセット
    jmp !Y shift_keyblock_c_end ; Yレジスタ=0(KeyBlock_C=0)ならジャンプ
    in NULL, 16  ; 16bitシフト
shift_keyblock_c_end:
    out Y, 1        ; OSRの左1bitデータをYレジスタにセット
    jmp !Y shift_keyblock_b_end ; Yレジスタ=0(KeyBlock_B=0)ならジャンプ
    in NULL, 8    ; 8bitシフト
shift_keyblock_b_end:
    out Y, 1        ; OSRの左1bitデータをYレジスタにセット
    jmp !Y shift_keyblock_a_end ; Yレジスタ=0(KeyBlock_A=0)ならジャンプ
    in NULL, 4    ; 4bitシフト
shift_keyblock_a_end:

    in ISR, 4
    in NULL, 28  ; ISRの左28bitをゼロ埋め
keyscanline_loop:
    mov Y, OSR  ; YレジスタにOSRの内容をコピー
    jmp !Y keyscanline_end ; Y(OSR)=0(KSL=全てLow)の場合はループ終了
    out Y, 1        ; OSRの左1bitデータをYレジスタに左シフト
    jmp !Y keyscanline_Low ; Y=0(KSLx=0)の場合は次のビット判定にJump

keyscanline_High:
    in ISR, 1      ; ISRのSETビット列を1ビットループ
    jmp keyscanline_loop
keyscanline_Low:
    in NULL, 1    ; ISRのSETビット列を1ビット破棄
    jmp keyscanline_loop
keyscanline_end:
    in NULL, 28  ; 左4bitの値のみ有効にする
    mov Y, ISR   ; YレジスタにISRの内容をコピー
    jmp !Y usbkey_start ; Y=0(SETx全て0)なら、指定Pin=Highのまま戻る
set_pin_low:
    nop side 0 ; Y!=0(SETxいずれか1)なら、指定Pin=LowにしてStartに戻る

% c-sdk {
static inline void usbkey_program_init(PIO pio, uint sm, uint offset, uint pin) {
    pio_gpio_init(pio, pin);
    pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
    pio_gpio_init(pio, 10);
    pio_gpio_init(pio, 11);
    pio_gpio_init(pio, 12);
    pio_gpio_init(pio, 13);
    pio_gpio_init(pio, 14);
    pio_gpio_init(pio, 15);
    pio_gpio_init(pio, 16);
    pio_sm_set_consecutive_pindirs(pio,  // [pio] The PIO instance
      sm,  // [sm] State machine index (0..3) to use
      10,  // [pin_base] the first pin to set a direction for
      7,  // [pin_count] the count of consecutive pins to set the direction for
      false);  // [is_out] the direction to set; true = out, false = in

    pio_sm_config c = usbkey_program_get_default_config(offset);
    sm_config_set_sideset_pins(&c, pin);
    sm_config_set_in_pins(&c,
      10);  // [in_base] 0-31 First pin to set as input
    sm_config_set_in_shift(&c,  // INは右シフト
      true,  // [shift_right] true to shift ISR to right, false to shift ISR to left
      false, // [autopush] whether autopush is enabled
      32);  // [push_threshold]
    sm_config_set_out_shift (&c,  // OUTは左シフト
      false,// [shift_right] true to shift OSR to right, false to shift OSR to left
      false, // [autopull] whether autopull is enabled
      32);  // [pull_threshold]

    pio_sm_init(pio,  // [pio] The PIO instance; either pio0 or pio1
      sm,     // [sm] State machine index (0..3)
      offset, // [initial_pc] the initial program memory offset to run from
      &c);  // [config] the configuration to apply (or NULL to apply defaults)
    pio_sm_set_enabled(pio,  // [pio] The PIO instance; either pio0 or pio1
      sm,     // [sm] State machine index (0..3)
      true);  // [enabled] true to enable the state machine; false to disable
}
%}
PIOの命令数はギリギリ31に収めた。マニュアルには32が上限とあり、確かに32命令でもビルドが通るが、31命令以下にしないとPico実行時にAssertで止まってしまう。

PIOの設定は、C言語側で行う
void init_pio(void) {
    uint offset0 = pio_add_program(pio0, &usbkey_program);
    usbkey_program_init(pio0, 0, offset0, 2);     // GP2
    usbkey_program_init(pio0, 1, offset0, 3);     // GP3
    usbkey_program_init(pio0, 2, offset0, 4);     // GP4
    usbkey_program_init(pio0, 3, offset0, 5);     // GP5

    uint offset1 = pio_add_program(pio1, &usbkey_program);
    usbkey_program_init(pio1, 0, offset1, 6);     // GP6
    usbkey_program_init(pio1, 1, offset1, 7);     // GP7
    usbkey_program_init(pio1, 2, offset1, 8);     // GP8
    usbkey_program_init(pio1, 3, offset1, 9);     // GP9

    pio0->txf[0] = 0;
    pio0->txf[1] = 0;
    pio0->txf[2] = 0;
    pio0->txf[3] = 0;
    pio1->txf[0] = 0;
    pio1->txf[1] = 0;
    pio1->txf[2] = 0;
    pio1->txf[3] = 0;
}
FIFOに登録するキーデータは、定義しておく
typedef struct {
    uint32_t nKeyReturnValue;
    uint8_t keyReturnPin;
    uint8_t modifier;
} PASOPIA700_KEYMAP;

static const PASOPIA700_KEYMAP keyMap[256] = {
    {0x00000000, 0x08, 0x00},   // 00:No Event -> Nop
    {0x00000000, 0x08, 0x00},   // 01:Overrun Error -> Nop
    {0x00000000, 0x08, 0x00},   // 02:POST Fail -> Nop
    {0x00000000, 0x08, 0x00},   // 03:ErrorUndefined -> Nop
    {0x22220000, 0x00, 0x00},   // 04:a A -> A チ
    {0x11110000, 0x03, 0x00},   // 05:b B -> B コ
    {0x11110000, 0x02, 0x00},   // 06:c C -> C ソ
    …
    {0x00000000, 0x08, 0x00}    // FF:RESERVED -> Nop
};
キーが押されたタイミングで、新しいキーデータを各PIOのFIFOに登録しCLK信号をラッチ
static inline void process_kbd_report(hid_keyboard_report_t const *p_new_report) {
    const PASOPIA700_KEYMAP* pKey;
    uint32_t keyReturn[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
    uint8_t modifier = p_new_report->modifier;

    // Key1
    pKey = keyMap + p_new_report->keycode[0];
    keyReturn[pKey->keyReturnPin] |= pKey->nKeyReturnValue;
    modifier |= pKey->modifier;
    …
    // Key6
    pKey = keyMap + p_new_report->keycode[5];
    keyReturn[pKey->keyReturnPin] |= pKey->nKeyReturnValue;
    modifier |= pKey->modifier;
    // Modifier
    if (0 < (modifier & 0b00100010)) {
        keyReturn[1] |= 0x80808080;     // modifier[bit1]=Shift -> KS0[bit1]=SHIFT
    }
    …
    // 各PIOのFIFOにセットする
    pio0->txf[0] = keyReturn[0];  // GP2
    pio0->txf[1] = keyReturn[1];  // GP3
    pio0->txf[2] = keyReturn[2];  // GP4
    pio0->txf[3] = keyReturn[3];  // GP5
    pio1->txf[0] = keyReturn[4];  // GP6
    pio1->txf[1] = keyReturn[5];  // GP7
    pio1->txf[2] = keyReturn[6];  // GP8
    pio1->txf[3] = keyReturn[7];  // GP9

    // GP20を一旦LOWにして、GP18(CLK)に出力する
    gpio_put(20, 0);
    gpio_put(20, 1);
パソピア700から届くCLK(Pin11)とPicoのGP20のどちらかがLowなら、GP18にLowが入るようにAND回路を組んでおく。

実際にPasopia700に接続して、ロジアナで計測してみる

DIR=High(パソピア700から7bitスキャン信号が届く)になった直後、KeyScanLine信号がふらついてる。但しCLK立ち上がり時は安定しているので大丈夫そう。
DIR=Low(Picoから8bit信号線を送る)の後に少しふらついている。
CLK信号の立ち上がりにPIOが動作するようにしているので、これは良くない。。

上の回路図に書いたが安全のため、8bit信号線とCLK、DIR、全てにプルアップ、プルダウンをつけると安定した。
上のキャプチャは、A0~A7がKeyScanLine、B0がCLK、B1がDIR
ふらつきが無くなって、全てのキーが安定して入力されるようになった。

多分こんな変態的なPIOの使い方はあまりないと思うが、なんかやりきった感はある。
満足...