電子工作で使えるジョイスティックを試してみる

コンピュータ、組み込み

ジョイスティックタイプやスライドタイプなど電子工作で使えるいろいろな十字キーを試してみました。
それぞれに形状には個性があり入力信号も様々です。
入力状態をRP2040-Matirixを使って示してみます。

今回紹介するもの

正確な名前がわからないので仮としての名称で紹介します。

5方向スイッチwith基板

特徴

XY方向と中心がボタンになっています。
任意の方向に押し込むことにより、離れているか/押されているかの入力です。
しっかりクリック感あります。

出力2値
入力方向4方向 + 中心

aitendoで購入しました。

外観

購入したモジュールはボタンと基板は「載せて」あるだけの状態なので、半田が必要です。

スライド十字キー(JOYSTICK-02)

製品名や型番が見つからないので説明上このようにしました。

特徴

XY方向のみの入力です。
つまみは倒れこみの角度は無く、任意の方向に平行移動させて入力します。
入力つまみの移動量により出力量が変化するアナログ出力なので、入力量の調整ができます。

出力アナログ
入力方向4方向
外観

前面

外観からLEDの色がわからないので、シールによる識別をしています。

JOYスティック

特徴

寝かせこみタイプの方向入力と中心がタクトボタンになっています。
入力つまみの倒しこみ量により出力量が変化するアナログ出力なので、入力量の調整ができます。

出力方向 アナログ
中心 2値
入力方向4方向 + 中心
外観

前面

使用感

それぞれ特徴のある入力方式なので、シーンに応じて必要な選択ができます。

5方向スイッチwith基板

押しているか離しているかの2値ですが、クリック感がしっかりしているので入力の安心感があります。
押し込み量などで入力量の制御はできませんが、最もオーソドックスな入力方式なので扱いやすいと思います。

スライド十字キー(JOYSTICK-02)

アナログでの入力ができますが、スライドのストローク(移動量)が少ないのと、どのくらいの入力量なのかが感覚で分かりにくいところがあります。
例えばマイコンをマウスHID化したとして、マウスカーソルの移動量を見てから入力量のフィードバックを行う形になるので、使いこなすには少し時間がかかりそうです。

JOYスティック

ゲーム機コントローラなどでもよく見る入力です。
倒しこみ量が多いので、倒しこみ角度で入力の程度を感じやすいです。
ジョグのつまみ自体が入力モジュールとしては比較的大きいので、組み込んで使用する場合は大きさを考慮して設計する必要があります。

以前にタクトボタンを組み合わせてコントローラを作ってみましたが、専用のXY入力を使用することで入力のしやすさが改善できそうです。

準備

使うもの

画像名称、型番用途
5方向スイッチwith基板2値タイプの方向入力モジュール
スライド十字キー(JOYSTICK-02)アナログタイプの
スライド式方向入力モジュール
JOYスティックアナログタイプの
JOGスティック式方向入力モジュール
RP2040-Matrixボタンの入力状態の読み取りと
入力状態の表示を、LEDで示します。
ブレッドボード配線します
ジャンパワイヤ数本配線します

調査

スライド十字キーや、JOYスティックなどのアナログ入力タイプはあらかじめニュートラル位置でのADC読み取り値を把握する必要があります。

ニュートラル位置(未入力状態)はADC分解能の中心程度の値ですが、若干上振れや下振れしています。
そのため中央値を未入力状態にすると、常に触れている側の入力状態になります。

またADCや回路上のノイズが読み取り値に影響し、常に値が若干振幅することがあります。

これらの予定していない入力状態をキャンセルするために「遊び」を設定することで安定した動作をします。

本サンプルでは、WS2812の輝度分解能(8bit : 256)に変換した後中心からp-p 10%程度はどちらでもない未入力状態としています。

動作

5方向スイッチwith基板

説明

入力した方向に対応するLED(WS2812)を発光させます。
キーの中央ボタンは、LEDマトリクスの中心を発光させます。

配線

ブレッドボード上では、画像のように配線します。
上下左右の関係は画像上で示した配置で行います。

RP2040-Matrix配線5方向スイッチwith基板
3V3VCC (中抜きの〇)
GPIO0中心 (塗りつぶしの●)
GPIO1↑ 上
GPIO2← 左
GPIO3↓ 下
GPIO4→ 右
スケッチ
/**********************************************************************
【ライセンスについて】
Copyright(c) 2022 by tamanegi
Released under the MIT license
'http://tamanegi.digick.jp/about-licence/

【マイコン基板】
RP2040-Matrix

【スケッチの説明】
5方向スイッチwith基板の入力状態を読み取り、
RP2040-MatrixのLEDで入力状態を表示します。

【ライブラリ】
Raspberry Pi Pico / RP2040 > Generic RP2040
Adafruit NeoPixel by Adafruit

【準備】
RP2040-Matrix   <-> 5方向スイッチwith基板
3V              <-> VCC (中抜きの○)
GPIO0           <-> 中心 (塗りつぶしの●)
GPIO1           <-> ↑ 上
GPIO2           <-> ← 左
GPIO3           <-> ↓ 下
GPIO4           <-> → 右

【バージョン情報】
2023/12/2 : 新規
**********************************************************************/

#include <Adafruit_NeoPixel.h>

#define DIN_PIN 16           // NeoPixel の出力ピン番号
#define LED_COUNT 25         // LEDの連結数
#define BRIGHT_MAX 16        //最大輝度   (最大  255まで)
Adafruit_NeoPixel pixels(LED_COUNT, DIN_PIN, NEO_GRB + NEO_KHZ800);

#define LED_COUNT 25             //WS2812 の連結数

//ここでの上下左右の概念はブレッドボードで使用することを前提に以下に定義する
//XYボード ブレッドボード横向きに対しXYボードは電源が上側
//RP2040-Matrix USBコネクタが左側
#define PIN_CENTER    (0)
#define PIN_UP        (1)
#define PIN_LEFT      (2)
#define PIN_DOWN      (3)
#define PIN_RIGHT     (4)

#define LEDINDEX_CENTER   (12)
#define LEDINDEX_UP       (14)
#define LEDINDEX_LEFT     (2)
#define LEDINDEX_DOWN     (10)
#define LEDINDEX_RIGHT    (22)

//パレット 0 to 255 の 256諧調
long PaletteR = 0x10;
long PaletteB = 0x10;
long PaletteG = 0x10;
uint32_t Palette;

void setup()
{
  pixels.begin();             //NeoPixel制御開始

  pinMode(PIN_CENTER, INPUT_PULLDOWN);
  pinMode(PIN_UP, INPUT_PULLDOWN);
  pinMode(PIN_LEFT, INPUT_PULLDOWN);
  pinMode(PIN_DOWN, INPUT_PULLDOWN);
  pinMode(PIN_RIGHT, INPUT_PULLDOWN);
  
  Palette = pixels.Color(PaletteR, PaletteG, PaletteB);      //パレットの作成
}

void loop()
{
  //各GPIOの入力状態を読み取り、入力があればLEDの点灯、なければ消灯する
  uint32_t Center = (digitalRead(PIN_CENTER) == HIGH) ? Palette : 0;
  uint32_t Up = (digitalRead(PIN_UP) == HIGH) ? Palette : 0;
  uint32_t Left = (digitalRead(PIN_LEFT) == HIGH) ? Palette : 0;
  uint32_t Down = (digitalRead(PIN_DOWN) == HIGH) ? Palette : 0;
  uint32_t Right = (digitalRead(PIN_RIGHT) == HIGH) ? Palette : 0;

  pixels.setPixelColor(LEDINDEX_CENTER, Center);
  pixels.setPixelColor(LEDINDEX_UP, Up);
  pixels.setPixelColor(LEDINDEX_LEFT, Left);
  pixels.setPixelColor(LEDINDEX_DOWN, Down);
  pixels.setPixelColor(LEDINDEX_RIGHT, Right);

  pixels.show();
}
結果

キーの入力方向のLEDが点灯しました。
クリック感があるので入力している安心感もあります。

動画では、SlideやJoystickに比べてLEDの点灯と消灯が2値で行われていることがわかります。
ゆっくり操作を行うことでSlideタイプやJoystickでは輝度の変化が確認できます。

スライド十字キー(JOYSTICK-02)

説明

入力した方向に対応するLED(WS2812)を発光させます。
スライド量に比例した光量で発光させます。

配線

ブレッドボード上では、画像のように配線します。
上下左右の関係は画像上で示した配置で行います。

注意:JOYStickに比べて、X軸とY軸の関係が違います。

RP2040-Matrix配線スライド十字キー(JOYSTICK-02)
3V3VCC
GNDGND
GPIO26X
GPIO27Y
スケッチ
/**********************************************************************
【ライセンスについて】
Copyright(c) 2022 by tamanegi
Released under the MIT license
'http://tamanegi.digick.jp/about-licence/

【マイコン基板】
RP2040-Matrix

【スケッチの説明】
Slide十字キー(JOYSTICK-02)基板の入力状態を読み取り、
RP2040-MatrixのLEDで入力状態を表示します。

ADCアナログ入力をWS2812の輝度分解能にリスケーリングし、入力量に比例した輝度で表現します。

【ライブラリ】
Raspberry Pi Pico / RP2040 > Generic RP2040
Adafruit NeoPixel by Adafruit

【準備】
RP2040-Matrix   <-> 
3V              <-> VCC
GND             <-> GND
GPIO26          <-> X
GPIO27          <-> Y

【バージョン情報】
2023/12/2 : 新規
**********************************************************************/

#include <Adafruit_NeoPixel.h>

#define DIN_PIN 16           // NeoPixel の出力ピン番号
#define LED_COUNT 25         // LEDの連結数
#define BRIGHT_MAX 16        //最大輝度   (最大  255まで)
Adafruit_NeoPixel pixels(LED_COUNT, DIN_PIN, NEO_GRB + NEO_KHZ800);

#define LED_COUNT 25             //WS2812 の連結数

//ここでの上下左右の概念はブレッドボードで使用することを前提に以下に定義する
//XYボード ブレッドボード横向きに対しXYボードは電源が上側
//RP2040-Matrix USBコネクタが左側
#define PIN_X       (26)
#define PIN_Y       (27)

//点灯WS2812のインデックス定義
#define LEDINDEX_CENTER   (12)
#define LEDINDEX_UP       (14)
#define LEDINDEX_LEFT     (2)
#define LEDINDEX_DOWN     (10)
#define LEDINDEX_RIGHT    (22)

#define WS2812_BRIGHT (255)     //最大輝値

void setup()
{
  pixels.begin();             //NeoPixel制御開始

  pinMode(PIN_X, INPUT);
  pinMode(PIN_Y, INPUT);
  
//  Palette = pixels.Color(PaletteR, PaletteG, PaletteB);      //パレットの作成
  Serial.begin(115200);
}

void loop()
{  
  long LEDINDEX_ON_X = 0;
  long LEDINDEX_OFF_X = 0;
  long LEDINDEX_ON_Y = 0;
  long LEDINDEX_OFF_Y = 0;
  uint32_t Palette_ON_X = 0;
  uint32_t Palette_OFF_X = 0;
  uint32_t Palette_ON_Y = 0;
  uint32_t Palette_OFF_Y = 0;
  
  //ADC分解能(10bit = 1024)の半分の値を引くことでマイナス側はX軸なら右、Y軸なら下、プラス側はX軸なら左、Y軸なら上の判定をする
  long X_AD = analogRead(PIN_X) - 512;
  long Y_AD = analogRead(PIN_Y) - 512;

  //X軸 左へスライドさせると値が大きくなる
  if(X_AD < 0)
  {
    LEDINDEX_ON_X = LEDINDEX_RIGHT;
    LEDINDEX_OFF_X = LEDINDEX_LEFT;
    X_AD *= -1;                       //正数化
  }
  else
  {
    LEDINDEX_ON_X = LEDINDEX_LEFT;
    LEDINDEX_OFF_X = LEDINDEX_RIGHT;
  }

  //Y軸 下へスライドさせると値が大きくなる
  if(Y_AD < 0)
  {
    LEDINDEX_ON_Y = LEDINDEX_DOWN;
    LEDINDEX_OFF_Y = LEDINDEX_UP;
    Y_AD *= -1;                       //正数化
  }
  else
  {
    LEDINDEX_ON_Y = LEDINDEX_UP;
    LEDINDEX_OFF_Y = LEDINDEX_DOWN;
  }


  //ADの読み取り値をWS2812の輝度分解能へスケール変換
  X_AD /= 2;                          //ADCの最大値は1023, 読み取りで半分にしたので511, WS2812に設定する最大は255なのでさらに半分にする。
  Y_AD /= 2;

  //この処理はニュートラルポジションでもどちらかに触れているために、"あそび"(どちらの入力でもない状態)の範囲を設定する。
  //今回は10%(分解能256に対して約10%の25)に設定している。
  if(X_AD < 12) X_AD = 0;
  if(Y_AD < 12) Y_AD = 0;

  Palette_ON_X = pixels.Color(X_AD, X_AD, X_AD);      //パレットの作成
  Palette_ON_Y = pixels.Color(Y_AD, Y_AD, Y_AD);
  pixels.setPixelColor(LEDINDEX_ON_X, Palette_ON_X);
  pixels.setPixelColor(LEDINDEX_ON_Y, Palette_ON_Y);
  pixels.setPixelColor(LEDINDEX_OFF_X, 0);
  pixels.setPixelColor(LEDINDEX_OFF_Y, 0);
  
  delay(10);
  Serial.printf("X, Y : %ld, %ld\r\n", X_AD, Y_AD);
  
  pixels.show();
}
結果

キーの入力方向のLEDが点灯しました。
スライド量に比例して光量が変化します。
スライドのストローク量が小さいこともあり、中間の変化具合はわかりにくいです。

JOYスティック

説明

入力した方向に対応するLED(WS2812)を発光させます。
キーの中央ボタンは、LEDマトリクスの中心を発光させます。
方向の入力は倒しこみ量に比例した光量で発光させます。

配線

ブレッドボード上では、画像のように配線します。
上下左右の関係は画像上で示した配置で行います。

注意:スライド十字キー(JOYSTICK-02)に比べて、X軸とY軸の関係が違います。

RP2040-Matrix配線JOYスティック
3V3VCC
GNDGND
GPIO26VRy
GPIO27VRx
GPIO28SW
スケッチ
/**********************************************************************
【ライセンスについて】
Copyright(c) 2022 by tamanegi
Released under the MIT license
'http://tamanegi.digick.jp/about-licence/

【マイコン基板】
RP2040-Matrix

【スケッチの説明】
Slide十字キー(JOYSTICK-02)基板の入力状態を読み取り、
RP2040-MatrixのLEDで入力状態を表示します。

ADCアナログ入力をWS2812の輝度分解能にリスケーリングし、入力量に比例した輝度で表現します。

【ライブラリ】
Raspberry Pi Pico / RP2040 > Generic RP2040
Adafruit NeoPixel by Adafruit

【準備】
RP2040-Matrix   <-> JOYスティック
3V              <-> VCC
GND             <-> GND
GPIO26          <-> VRy
GPIO27          <-> VRx
GPIO28          <-> SW

【バージョン情報】
2023/12/2 : 新規
**********************************************************************/

#include <Adafruit_NeoPixel.h>

#define DIN_PIN 16           // NeoPixel の出力ピン番号
#define LED_COUNT 25         // LEDの連結数
#define BRIGHT_MAX 16        //最大輝度   (最大  255まで)
Adafruit_NeoPixel pixels(LED_COUNT, DIN_PIN, NEO_GRB + NEO_KHZ800);

#define LED_COUNT 25             //WS2812 の連結数

//ここでの上下左右の概念はブレッドボードで使用することを前提に以下に定義する
//XYボード ブレッドボード横向きに対しXYボードは電源が上側
//RP2040-Matrix USBコネクタが左側
#define PIN_X       (26)
#define PIN_Y       (27)
#define PIN_CENTER  (28)

#define LEDINDEX_CENTER   (12)
#define LEDINDEX_UP       (14)
#define LEDINDEX_LEFT     (2)
#define LEDINDEX_DOWN     (10)
#define LEDINDEX_RIGHT    (22)

#define WS2812_BRIGHT (255)     //最大輝値

void setup()
{
  pixels.begin();             //NeoPixel制御開始

  pinMode(PIN_X, INPUT);
  pinMode(PIN_Y, INPUT);
  pinMode(PIN_CENTER, INPUT_PULLUP);

  Serial.begin(115200);
}

void loop()
{  
  long LEDINDEX_ON_X = 0;
  long LEDINDEX_OFF_X = 0;
  long LEDINDEX_ON_Y = 0;
  long LEDINDEX_OFF_Y = 0;
  uint32_t Palette_ON_X = 0;
  uint32_t Palette_OFF_X = 0;
  uint32_t Palette_ON_Y = 0;
  uint32_t Palette_OFF_Y = 0;

  uint32_t Palette_Center = 0;
  boolean Center = false;

  //ADC分解能(10bit = 1024)の半分の値を引くことでマイナス側はX軸なら右、Y軸なら上、プラス側はX軸なら左、Y軸なら下の判定をする
  long X_AD = analogRead(PIN_X) - 512;
  long Y_AD = analogRead(PIN_Y) - 512;

  //注意 : もともとSLIDEタイプの入力モジュールからのスケッチ修正をしています。
  //X, Y軸の関係が逆になっているので、ブレッドボードで使用できる方向でプログラムを修正しています。

  //X軸 左へスライドさせると値が大きくなる
  if(X_AD < 0)
  {
    LEDINDEX_ON_X = LEDINDEX_RIGHT;
    LEDINDEX_OFF_X = LEDINDEX_LEFT;
    X_AD *= -1;                       //正数化
  }
  else
  {
    LEDINDEX_ON_X = LEDINDEX_LEFT;
    LEDINDEX_OFF_X = LEDINDEX_RIGHT;
  }

  //Y軸 下へスライドさせると値が小さくなる
  if(Y_AD < 0)
  {
    LEDINDEX_ON_Y = LEDINDEX_UP;
    LEDINDEX_OFF_Y = LEDINDEX_DOWN;
    Y_AD *= -1;                       //正数化
  }
  else
  {
    LEDINDEX_ON_Y = LEDINDEX_DOWN;
    LEDINDEX_OFF_Y = LEDINDEX_UP;
  }

  //ADの読み取り値をWS2812の輝度分解能へスケール変換
  X_AD /= 2;                          //ADCの最大値は1023, 読み取りで半分にしたので511, WS2812に設定する最大は255なのでさらに半分にする。
  Y_AD /= 2;

  //この処理はニュートラルポジションでもどちらかに触れているために、"あそび"(どちらの入力でもない状態)の範囲を設定する。
  //今回は10%(分解能256に対して約10%の25)に設定している。
  if(X_AD < 25) X_AD = 0;
  if(Y_AD < 25) Y_AD = 0;

  Palette_ON_X = pixels.Color(X_AD, X_AD, X_AD);      //パレットの作成
  Palette_ON_Y = pixels.Color(Y_AD, Y_AD, Y_AD);
  pixels.setPixelColor(LEDINDEX_ON_X, Palette_ON_X);
  pixels.setPixelColor(LEDINDEX_ON_Y, Palette_ON_Y);
  pixels.setPixelColor(LEDINDEX_OFF_X, 0);
  pixels.setPixelColor(LEDINDEX_OFF_Y, 0);

  //中心ボタンの状態読み取りとLEDの点灯、消灯
  Center = digitalRead(PIN_CENTER);
  if(Center == HIGH)
  {
    Palette_Center = pixels.Color(0, 0, 0);  
  }
  else
  {
    Palette_Center = pixels.Color(WS2812_BRIGHT, WS2812_BRIGHT, WS2812_BRIGHT);  
  }
  pixels.setPixelColor(LEDINDEX_CENTER, Palette_Center);

  pixels.show();

  delay(10);
  Serial.printf("X, Y, Center : %ld, %ld\r\n", X_AD, Y_AD);
}
結果

キーの入力方向のLEDが点灯しました。
ストロークがあるので中間位置の入力もしやすいと感じます。
中央を押しながら方向の入力もできますが、中心を抑え込みながら外側に倒しこみをするので操作は難しいです。

コメント

タイトルとURLをコピーしました