でんげき☆ Network Service

Raspberry Pi 4 で運用実験中 Connect checker

No.21

Arduino とマトリクスキーボード

202202201020325-admin.jpg
 
以前にどうにかしてたっていうか「Arduino Leonardo (Pro Micro) でマルチメディアキーボードを作ろう!」の後ぐらいにいろいろ調べてたら…なんかマトリクス・キーボードっていうの? 少ないピン数で多くのスイッチを読み取れる方法があるらしい??ってのを見つけて気になってたんで勉強がてらその辺を試してみることにしました

そんなマトリクスってのを日本語的に云うと…碁盤の目みたいなものって感じになるっぽい? それをキーボード的に実装すると…例えば横方向にマイコンから何本かの出力線を這わせておいてそれと何本かの入力線をスイッチを介してあたかも碁盤の目のように配置したもの?ってなるらしい??

202202201020324-admin.jpg 20211113072346-admin.jpg
そんな感じで Arduino VKLSVAN Pro Micro USB ATmega32U4 (※以下 Arduino と略す)を使って実際に作ってみました ちなみに Pro Micro のピン配列図はこんな感じです つーかブレッドボード(?)が狭くて碁盤の目になってなくてアレですよね(汗

202202201020325-admin.png
回路図的なものにするとこんな感じです つーか回路図なんて描いたことないんで正しくない図かもしれませんが…まぁ概念的な絵だと思って見てください(滝汗

202202201020324-admin.png
ここでは Arduino の 6 番ピンと 7 番ピンを出力として使い 14 番ピンと 15 番ピンを入力として使います
そして 6 番ピンのみに信号を出力します その状態で A もしくは B のスイッチが押されればそれぞれ対応した 14 番ピンか 15 番ピンで信号を読み取ることができます それにより A もしくは B のスイッチが押されたかどうかを特定できます
※なんか PULLUP 回路の都合上っていうの?
※普段は HIGH にしてあって…「出力する」としたものを LOW にすることで動作する仕組みです ちょっとややこしいですね(-_-;)

202202201020323-admin.png
引き続き今度は 7 番ピンにのみ信号を出力します その状態で C もしくは D のスイッチが押されればそれぞれ対応した 14 番ピンか 15 番ピンで信号を読み取ることができます それにより C もしくは D のスイッチが押されたかどうかを特定できます
この一連の動作を繰り返すことにより碁盤の目のように配置されたスイッチを特定していく…そんな構造のキーボードをマトリクスキーボードと呼ぶらしいです

それでは実際にスケッチを書いて動作を確認してみましょう

----------

#include "Keyboard.h"

void setup() {
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(14, INPUT_PULLUP);
  pinMode(15, INPUT_PULLUP);

  digitalWrite(6, HIGH);  // 6 番ピンを HIGH にしておく
  digitalWrite(7, HIGH);  // 7 番ピンを HIGH にしておく

  Serial.begin(9600);
  Keyboard.begin();
}

void loop() {
// -------------------------------- //
  digitalWrite(6, LOW); // 6 番ピンを LOW にしてスキャン開始
  if (digitalRead(14) == LOW) { // 14 番ピンは LOW になっている?
    Serial.print("A\n");  // LOW なら A のスイッチが押されている
    Keyboard.print("A\n");
  }
  if (digitalRead(15) == LOW) { // 15 番ピンは LOW になっている?
    Serial.print("B\n");  // LOW なら B のスイッチが押されている
    Keyboard.print("B\n");
  }
  digitalWrite(6, HIGH);  // 6 番ピンを HIGH にしてスキャン終了
// -------------------------------- //
  digitalWrite(7, LOW); // 7 番ピンを LOW にしてスキャン開始
  if (digitalRead(14) == LOW) { // 14 番ピンは LOW になっている?
    Serial.print("C\n");  // LOW なら C のスイッチが押されている
    Keyboard.print("C\n");
  }
  if (digitalRead(15) == LOW) { // 15 番ピンは LOW になっている?
    Serial.print("D\n");  // LOW なら D のスイッチが押されている
    Keyboard.print("D\n");
  }
  digitalWrite(7, HIGH);  // 7 番ピンを HIGH にしてスキャン終了
// -------------------------------- //
  delay(100); // 適当に待って繰り返し
}

----------

202202201020322-admin.png
シリアルモニタとテキストエディタを並べて動作テストしてみましょう スイッチに対応した文字が表示されれば成功です ちなみに今回はシリアルモニタとテキストエディタの両方に出力するようにしてますが…初期のテスト段階ではシリアルモニタだけを使ったほうがいいかもですね

  :

さてそんな感じでマトリクス・キーボードの大まかな動作を理解できた気になったんで…ぼちぼちマルチメディアキーボード的なやつを作るか!って思いたいトコロなんだけどスイッチ類を買い揃えて配線したりとかとかしょーみ面倒くさいなぁと思ってたんですよね そう思いつつ某Aマゾンを眺めてたら気になるものを見つけました

Ren He 5個セット 16キー 4*4 マトリックス フィルム ボタン キーボード マトリックススイッチ キーパッド メンブレン式 マトリックスキーボード Arduinoに対応

かなり怪しげな感じだけど 5 個入りなのに 600 円でお釣りがきちゃうお値段に釣られてポチっちゃいました 初期不良でいくつか動かなかったとしても十分にお値打ちっポイネ!!

202202201020323-admin.jpg
なんか適当なプチプチに包まれた荷姿で届くと思ってたのに…郵便受けをギリ通る厚みのプラケースに入れられて来ましたw やるじゃんww

202202201020321-admin.png
ちなみにデータシートの類は入ってなかったんで自力で配線を解析する必要があったけど…まぁわりと素直な作りだったんで簡単に判明してよかったです そして Arduino と接続したピン番号とかも

20220220102032-admin.jpg 202202201020321-admin.jpg 202202201020322-admin.jpg
実際に接続してみました まぁ毎度のごとく「接続例」って感じなんで…この辺は各人のお好みでどうにかしてもらえばいいかと思います

それでは早速スケッチを書いて動作を確認してみましょう

----------

const byte KEYOT[] = {4, 5, 6, 7};  // 出力ピンの設定
const byte KEYIN[] = {14, 15, 18, 19};  // 入力ピンの設定
const int WAIT = 100; // ちょっと待たせる

//  関数 keyscan() がキーの状態を読み取った値を入れておく配列
byte SW[sizeof(KEYOT)][sizeof(KEYIN)];

const byte ON = LOW;  // LOW と HIGH を…
const byte OF = HIGH; // ON と OF の別名で定義しておく

void setup() {
  for (byte i = 0; i < sizeof(KEYOT); i++) {
    pinMode(KEYOT[i], OUTPUT);  // 出力ピンのモード設定
    digitalWrite(KEYOT[i], HIGH); // 出力ピンを HIGH に設定
  }
  for (byte i = 0; i < sizeof(KEYIN); i++) {
    pinMode(KEYIN[i], INPUT_PULLUP);  // 入力ピンを PULLUP モードに設定
  }
  Serial.begin(9600); // シリアル通信を開始
}

void loop() {
  keyscan();  // キーの状態を読み取る
// -------------------------------- //
  if (SW[0][0] == ON) { Serial.println("1"); }
  if (SW[0][1] == ON) { Serial.println("2"); }
  if (SW[0][2] == ON) { Serial.println("3"); }
  if (SW[0][3] == ON) { Serial.println("A"); }
// -------------------------------- //
  if (SW[1][0] == ON) { Serial.println("4"); }
  if (SW[1][1] == ON) { Serial.println("5"); }
  if (SW[1][2] == ON) { Serial.println("6"); }
  if (SW[1][3] == ON) { Serial.println("B"); }
// -------------------------------- //
  if (SW[2][0] == ON) { Serial.println("7"); }
  if (SW[2][1] == ON) { Serial.println("8"); }
  if (SW[2][2] == ON) { Serial.println("9"); }
  if (SW[2][3] == ON) { Serial.println("C"); }
// -------------------------------- //
  if (SW[3][0] == ON) { Serial.println("*"); }
  if (SW[3][1] == ON) { Serial.println("0"); }
  if (SW[3][2] == ON) { Serial.println("#"); }
  if (SW[3][3] == ON) { Serial.println("D"); }
// -------------------------------- //
  delay(WAIT);
}

void keyscan() {  // キーの状態を読み取る関数
  for (byte i = 0; i < sizeof(KEYOT); i++) {
    digitalWrite(KEYOT[i], LOW);  // 特定の出力ピンを LOW にする
    for (byte j = 0; j < sizeof(KEYIN); j++) {
      if (digitalRead(KEYIN[j]) == ON) {  // 入力ピンの状態を読む
        SW[i][j] = ON;  // キーが押されている
      } else {
        SW[i][j] = OF;  // キーは離されている
      }
    }
    digitalWrite(KEYOT[i], HIGH); // LOW にした出力ピンを HIGH に戻す
  }
}

----------

冒頭にある

const byte KEYOT[] = {4, 5, 6, 7};  // 出力ピンの設定
const byte KEYIN[] = {14, 15, 18, 19};  // 入力ピンの設定
const int WAIT = 100; // ちょっと待たせる

この辺と…あとは loop() 内を書き換えるだけでいいように書いてみました 使用するピン数が増減しても概ね追従できると思います
あーあと setup() で使用したい機能を開始させるような辺りの書き換えも必要ですかね

それと…話がややこしくなるとアレなんで今回はシリアル通信のみで動作チェックしています

キーの状態を読み取る keyscan() 関数ってのがありますが…グローバル変数を使っているので引数も返り値もありません
読み取った値は SW[][] って二次元配列にセットしています

あと PULLUP の特性っていうか普段が HIGH となっていて…スイッチが押されると LOW になるって辺りが何となく直感的にイメージしにくいように思えたんで
const byte ON = LOW;  // LOW と HIGH を…
const byte OF = HIGH; // ON と OF の別名で定義しておく
って感じでスイッチが押されてる ON ってのとスイッチが離されてる OF って感じの別名で定義してあります

  :

そして loop() 内で keyscan() を呼んでキーの状態を読み取っておいて…あとは個々の SW[][] の状態に応じてお好みの文字(列)を出力させるなり何なりをすればいいでしょう
将来的にっていうかもし「1」が押されてたらキーボードとして文字を出力して「#」が押されてたらマウスの右クリックを…って感じで様々な処理を行えるよう少しばかり長ったらしく書いてあります

この辺の「やること」の種類が決まりきっているのであれば

void loop() {
  keyscan();
// -------------------------------- //
  const char* str[] = {
    "1\n", "2\n", "3\n", "A\n",
    "4\n", "5\n", "6\n", "B\n",
    "7\n", "8\n", "9\n", "C\n",
    "*\n", "0\n", "#\n", "D\n",
  };
  for (byte i = 0; i < sizeof(KEYOT); i++) {
    for (byte j = 0; j < sizeof(KEYIN); j++) {
      if (SW[i][j] == ON) {
        Serial.print(str[sizeof(KEYOT) * i + j]);
      }
    }
  }
// -------------------------------- //
  delay(WAIT);
}

こんなふうに for() で回すように書けばもうちょっとスッキリするかもしれんですね これはまぁ「やること」の内容によっていろいろ書き方があるよねって事で参考までに

  :

あーあと後半の中華クソ安 16 キーボードでの実験ではテキストエディタへの文字出力を行っていないんですが…これを実際に行うと不具合が出るものがあります

20220220102032-admin.png
キーボード上の「*」 なんですけどね シリアルモニタではちゃんと「*」が出力されているのに…テキストエディタ上では「(」になってしまいます
※別のスケッチでテストした際の画像です

これは #include "Keyboard.h" した際のキーボードレイアウトが KeyboardLayout_en_US になってて…まぁいわゆる US キーボードってやつですかね これが日本で一般的に使われている JIS キーボードとレイアウト(キーの配置)が違うから起こる現象のようです
アルファベットと数字に関しては問題ないのですが…記号はその多くが US と JIS とで配列が違うんで問題になります

202202201033061-admin.png
先ほどの「*」を例にすると… US キーボードでは「*」が「8」のキーに割り当てられています

20220220103306-admin.png
そのキーコードを JIS キーボードに当てはめると「8」のキーにあるのは「(」って事になるので正しく表示されないのです

困っちゃいますよね…そこそこ数が出回ってる(と思われる) JIS キーボードなんだで Keyboard.begin(KeyboardLayout_ja_JP); くらい通るようにしといてほしいのに!って思うんだけどそうはいきません(-_-;)

どうしたもんかなーって思いつつ調べてたら…何やら Keyboard.h を複製した後に JIS キーボード向けに書き換える手法を見つけました→ Arduino Leonardoで\記号を打つ:メガギガテラス:So-netブログ
実際に試してみたところ…これでバッチリ動作しました! まぁあくまで自己責任で!って感じになるんだけど…自作キーボードでいろいろしたい!って場合にはとても有用な情報だと思うんで参考にしてみてください

  :

キーボード自作って話になると耳にするマトリクスキーボードってやつですね 今までは何となく漠然と聞き流していたんですが…今回はその辺に踏み込んでその原理をほんのり理解できた気になれてよかったです マイコン側のピン数を減らしつつ…より多くのキーを読み取るテクニックは今後も色々と使えそうなんで今後もより掘り下げた勉強をしていきたいなって思いました

あと…マトリクスキーボードの構造的な問題っていうか特定条件の複数キーの同時押しをした際に正常に動きません 今回の中華クソ安 16 キーボードを使った実験を例にとると…縦軸に配されたキー(例えば「1と4」とか「AとBとC」とか)を同時押しすると「どれも押されてない」って判定されます まぁなんか読みたいキーとは別のスイッチを介して HIGH が逆流してくる(?)ことにより誤動作するらしく…そんな逆流を防ぐためにダイオードを入れるって事らしいんですが今回は既成品でその辺をどうにもできかねる感じなんでそのままにしてあります(汗

まぁその辺を踏まえた上で…この中華クソ安 16 キーボードでマルチメディアキーボード的なものを作るとしますかね 程々に硬い板に貼り付けて配線の取りまとめをすればいいだけって感じの見栄えよりお手軽さ!って感じのアレですが(瀧汗

そんなこなんで今回も長々とお疲れさまでした! 安いのに遊び甲斐のある Arduino をもっと色々と使い倒していきタイネ!(>_<)w #Arduino

情報 <7955文字>

DASHBOARD

■複合検索:

  • 投稿者名:
  • 投稿年月:
  • #タグ:
  • カテゴリ:
  • 出力順序:

■ハッシュタグ:

■カテゴリ:

■日付検索:

■機器状態:

Raspberry Pi 4 Status

編集

RSSフィード