0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PS ポップンコントローラーを読む

0
Posted at

概要

PlayStation向けにKONAMI公式に出されたコントローラー。
ポップンコントローラーはPS1時代のポプコン1、PS2時代のポプコン2とがあり、ポプコン1は使いづらい方。
ポプコン1は
・径XXの平面ボタン。
・ストロークはとても浅い(ほぼない)、戻りもなに浅すぎる。
・かなりうるさい、パコパコパコ...。
とそれなりに難点がある。
ポプコン2ではこれらがある程度改善されているらしい。

ふらっと寄った渋めのブックオフで500円で売られていた。なにものかさっぱりわからなかったが思わずかちた。
今回はこれをTeensyで読む。

PSコントローラーのプロトコル

PS1,2はコントローラーとの通信をSPIで行う。
して、簡単に扱えるので既存のライブラリ(PsxNewLib)なんかがあるのだが、とりわけポプコンはやや特殊仕様となっていて、それでは使えない。

↓ピンアウト参考

特殊仕様...とは言ったが、むしろ逆に近く、DS1,2やその他コントローラー類でよくあるAnalogスティック周りの設定を行わない。その分既存ライブラリでは詰まってしまうらしい。

↓通信仕様参考
https://gamesx.com/controldata/psxcont/psxcont.htm

具体的な通信仕様については上部記事たちにて割愛させてもらう。
DATA線はオープンコレクタなので、3.3v-DATA間に1kΩほどのプルアップを付ける。マイコン内部でも可。
PSコンは3.3v電源/ロジックなので、5vレベルのマイコンを使う場合はレベルシフタが必要となる。

方法

PSコンの端子はオスメス共に端子部分が奥まっており、ピンヘッダでのごり押しは効かなさそうだったためAmazonにてPSコン延長コードを購入。二本880円など。
ぶった切って被膜むいてユニ基に配線。
最安で入手したければ、ジャンク品のマルチタップを分解するのがいいと思うが、面倒くさい。
他に特別用意したものはない。

コード

ではコード。またまたGenerated by Gemini。
Teensy4.0、Joystickモードにて動作確認。
他のボードで使用する際、USB-Joystick周りやピン配置は適宜合わせる必要あり。

popcontes.ino
/*
 * Pop'n Controller (1) to USB Joystick for Teensy
 * 
 * 接続ピン:
 * ATT: 10, CMD: 11, DAT: 12, CLK: 13
 * 
 * 注意: DATAピン(12)は3.3Vへ1kΩでプルアップすること!
 * Arduino IDEの [Tools] > [USB Type] を "Joystick" に設定して書き込んでください。
 */

const int ATT = 10;
const int CMD = 11;
const int DAT = 12;
const int CLK = 13;

void setup() {
  pinMode(ATT, OUTPUT);
  pinMode(CMD, OUTPUT);
  pinMode(CLK, OUTPUT);
  pinMode(DAT, INPUT_PULLUP); // 物理抵抗(1k)推奨だが、保険で内蔵もON
  
  digitalWrite(ATT, HIGH);
  digitalWrite(CLK, HIGH);

  // USBのポーリングレートを上げるための設定 (Teensy 4.xは標準で速い)
  Joystick.useManualSend(true); 
  
  // デバッグ用(完成したらコメントアウト推奨)
  Serial.begin(115200); 
}

// 通信のコア関数
uint8_t psx_transfer(uint8_t command) {
  uint8_t data = 0;
  for (int i = 0; i < 8; i++) {
    digitalWrite(CMD, (command & (1 << i)) ? HIGH : LOW);
    digitalWrite(CLK, LOW);
    delayMicroseconds(20); // ポプコンのチップが反応できる速度に調整
    
    if (digitalRead(DAT)) data |= (1 << i);
    
    digitalWrite(CLK, HIGH);
    delayMicroseconds(20);
  }
  return data;
}

void loop() {
  // 通信開始
  digitalWrite(ATT, LOW);
  delayMicroseconds(10);

  psx_transfer(0x01);          // アドレス
  uint8_t id = psx_transfer(0x42); // データ要求
  psx_transfer(0x00);          // 準備信号 (0x5A)
  uint8_t data1 = psx_transfer(0x00); // 4バイト目 (SELECT, START, 十字)
  uint8_t data2 = psx_transfer(0x00); // 5バイト目 (各種ボタン)

  digitalWrite(ATT, HIGH);

  // コントローラーが認識されている場合のみ処理
  if (id != 0xFF) {
    /* 
     * ポプコン(1)の標準的なボタンマッピング (負論理なので ! で反転)
     * JSの navigator.getGamepads()[0].buttons[n-1] に対応
     */
    Serial.print("Data1(Hex): "); Serial.print(data1, BIN);
    Serial.print(" | Data2(Hex): "); Serial.println(data2, BIN);
    // 左から順番に 1 ~ 9 番に割り当て
    Joystick.button(1, !(data2 & 0x10)); // 1: 白左 
    Joystick.button(2, !(data2 & 0x20)); // 2: 黄左 
    Joystick.button(3, !(data2 & 0x08)); // 3: 緑左 
    Joystick.button(4, !(data2 & 0x40)); // 4: 青左 
    Joystick.button(5, !(data2 & 0x04)); // 5: 赤中央 
    Joystick.button(6, !(data2 & 0x80)); // 6: 青右 
    Joystick.button(7, !(data2 & 0x02)); // 7: 緑右 
    Joystick.button(8, !(data1 & 0x10)); // 8: 黄右 
    Joystick.button(9, !(data2 & 0x01)); // 9: 白右 

    // USBパケットを一斉送信(ジッター防止)
    Joystick.send_now(); 
  }

  // 1ms待機 (1000Hz)
  delay(1); 
}

正味あまりいうことはないのだが、psx_trassfer()内のdelayは決め打ち値なのでもしかしたら漏れているかも?目視では確認できなかった。
見ての通りソフトSPIなので、ハードSPIに比べなんらか遅くなっているはずではあるが、teensyなんかを使っていればこの程度の処理速度で詰まることはないだろう。

動作

(おまけ)ポプコン入力チェック描画

ブラウザ上で表示するやつをつくってつくらせてみた。

popconVisualizer.html
popconVisalizer.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Pop'n Input Viewer</title>
    <style>
        body {
            background-color: #222; /* 暗めの背景で白ボタンを際立たせる */
            color: white;
            font-family: sans-serif;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100vh;
            margin: 0;
        }

        .controller {
            position: relative;
            width: 600px;
            height: 250px;
            background: #444;
            border-radius: 50px;
            padding: 20px;
            box-shadow: 0 10px 0 #000;
        }

        .button {
            position: absolute;
            width: 70px;
            height: 70px;
            border-radius: 50%;
            border: 4px solid #000;
            transition: transform 0.05s;
        }

        /* ボタンごとの色と配置 */
        .btn-white  { background-color: #eee; box-shadow: 0 0 10px rgba(255,255,255,0.2); }
        .btn-yellow { background-color: #ffd700; }
        .btn-green  { background-color: #32cd32; }
        .btn-blue   { background-color: #1e90ff; }
        .btn-red    { background-color: #ff4500; }

        /* 押した時の「発光」エフェクト */
        .pressed {
            transform: scale(0.95);
            filter: brightness(1.5);
            box-shadow: 0 0 30px currentColor;
        }

        /* 2段配置の座標指定(左から順番) */
        #btn0 { left: 30px;  top: 130px; color: #fff; } /* 白左 */
        #btn1 { left: 90px;  top: 40px;  color: #ff0; } /* 黄左 */
        #btn2 { left: 160px; top: 130px; color: #0f0; } /* 緑左 */
        #btn3 { left: 220px; top: 40px;  color: #0cf; } /* 青左 */
        #btn4 { left: 290px; top: 130px; color: #f00; } /* 赤中 */
        #btn5 { left: 360px; top: 40px;  color: #0cf; } /* 青右 */
        #btn6 { left: 420px; top: 130px; color: #0f0; } /* 緑右 */
        #btn7 { left: 490px; top: 40px;  color: #ff0; } /* 黄右 */
        #btn8 { left: 550px; top: 130px; color: #fff; } /* 白右 */

        #status { margin-bottom: 20px; font-size: 1.2rem; }
    </style>
</head>
<body>
    <div id="status">コントローラーのボタンを押してください...</div>
    <div class="controller">
        <div id="btn0" class="button btn-white"></div>
        <div id="btn1" class="button btn-yellow"></div>
        <div id="btn2" class="button btn-green"></div>
        <div id="btn3" class="button btn-blue"></div>
        <div id="btn4" class="button btn-red"></div>
        <div id="btn5" class="button btn-blue"></div>
        <div id="btn6" class="button btn-green"></div>
        <div id="btn7" class="button btn-yellow"></div>
        <div id="btn8" class="button btn-white"></div>
    </div>

<script>
        const statusDiv = document.getElementById('status');
        const btnElements = [];
        for (let i = 0; i < 9; i++) {
            btnElements.push(document.getElementById(`btn${i}`));
        }

        function update() {
            const gamepads = navigator.getGamepads();
            const gp = gamepads[0];

            if (gp) {
                statusDiv.innerText = "Pop'n Controller Connected!";
                
                // TeensyのJoystick.button(1〜9)は gp.buttons[0〜8] に入る
                for (let i = 0; i < 9; i++) {
                    if (gp.buttons[i] && gp.buttons[i].pressed) {
                        btnElements[i].classList.add('pressed');
                    } else {
                        btnElements[i].classList.remove('pressed');
                    }
                }
            } else {
                statusDiv.innerText = "コントローラーを待機中... (ボタンを押してください)";
            }

            requestAnimationFrame(update);
        }

        // ゲームパッド接続イベント
        window.addEventListener("gamepadconnected", (e) => {
            console.log("Gamepad connected:", e.gamepad);
        });

        update();
    </script>
</body>
</html>

描画の様子は上記ツイート。

参考

↑再掲

↑PSコンもろもろに対応したコンバーターをBOOTH販売している方

ログ

2026.04.30 ポプコン衝動買い
2026.05.01 PSコントローラーケーブル購入
2026.05.02 動作確認、記事作成

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?