ゆかりメモ

いろんなことのメモです。

オリジナルキーボードを作ってみる その7「キーマトリックス」

前回は2キーキーボードまで出来上がったと思います。 今回はキーマトリックスを使用して4キーキーボードまで展開しましょう。

前回の記事はこちら。

eucalyn.hatenadiary.jp

前回の最後でプログラムはこうなってたと思います。

#include "Keyboard.h"

const int keyNum = 2;

const int inputPin[keyNum] = { 3, 4 };
const byte keyMap[keyNum]  = { 0x61, 0x62 };

bool currentState[keyNum];
bool beforeState[keyNum];

int i;

void setup() {
  for( i = 0; i < keyNum; i++){
    pinMode(inputPin[i],INPUT);
    currentState[i] = LOW;
    beforeState[i] = LOW;
  }

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

void loop() {
  for( i = 0; i < keyNum; i++){
    currentState[i] = digitalRead(inputPin[i]);

    if ( currentState[i] != beforeState[i] ){
      Serial.print("key");
      Serial.print(i);
      if ( currentState[i] == HIGH){
        Serial.println(" Push!");
        Keyboard.press( keyMap[i] );
      } else {
        Serial.println(" Release!");
        Keyboard.release( keyMap[i] );
      }
    beforeState[i] = currentState[i];
    }
  }
}

このままではピンの数しかキーを増やせないですが、キーマトリックスを使用すればより多数のキーを認識させることができます。

キーマトリックス

スイッチをマトリックス状に(2次元的に)配線することで、飛躍的に多数のキーを認識できるようになります。
例えば5×5状に配置すれば、10ピンで25キーを認識させることができます。

理解しやすいように、2×2のキーマトリックスで説明しましょう。
ここでは横の繋がりを行、縦の繋がりを列と表現し、スイッチはその組み合わせによって(行,列)と表現することにします。

2×2キーマトリックスの場合、0行・1行と0列・1列の組み合わせにより、(0,0),(0,1),(1,0),(1,1)の4キーを認識させることができます。

今まではスイッチを5Vと接続することでスイッチには常に電圧がかかっている状態でした。
そのためスイッチを押せば対応するピンにHIGHが入力されるので、それを信号として拾っていました。

キーマトリックスはスイッチの入力側にもマイコンの出力ピンを使います。

Arduinoの出力ピンはdigitalWrite(Pin,HIGH);すると5Vに、
digitalWrite(Pin,LOW);すると0Vに接続されるようになっています。

0行ピンのみをHIGHにしているタイミングで入力ピンを読み取れば(0,0),(0,1)の状態を、
1行ピンのみをHIGHにしているタイミングなら、(1,0),(1,1)の状態を読み取ることができる、という仕組みです。

これを高速で繰り返して、1行ごとに読み込んでいくことでマトリックス上の全キーの情報を取得することができます。

2×2キーマトリックスを組んでみる

では早速試しに組んでみましょう。

まずブレッドボード上にこんな風に回路を組みます。

スイッチを行列の組み合わせで管理するために、2次元配列を使うように変更します。

2次元配列だと、a[0][0]やa[1][1]のように行列の組み合わせで変数を管理することができます。

#include "Keyboard.h"

const int rowNum = 2;
const int colNum = 2;

const int rowPin[rowNum] = { 3, 4 };
const int colPin[colNum] = { 5, 6 };
const byte keyMap[rowNum][colNum]  = {
  { 0x61, 0x62 },
  { 0x63, 0x64 }
};

bool currentState[rowNum][colNum];
bool beforeState[rowNum][colNum];

int i,j;

void setup() {

  for( i = 0; i < rowNum; i++){
    pinMode(rowPin[i],OUTPUT);
  }

  for( i = 0; i < colNum; i++){
    pinMode(colPin[i],INPUT);
  }

  for( i = 0; i < rowNum; i++){
    for( j = 0; j < colNum; j++){
      currentState[i][j] = LOW;
      beforeState[i][j] = LOW;
    }
    digitalWrite(rowPin[i],LOW);
  }

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

void loop() {
  for( i = 0; i < rowNum; i++){
    digitalWrite( rowPin[i], HIGH );

    for( j = 0; j < colNum; j++){
      currentState[i][j] = digitalRead(colPin[j]);

      if ( currentState[i][j] != beforeState[i][j] ){

        Serial.print("key(");
        Serial.print(i);
        Serial.print(",");
        Serial.print(j);
        Serial.print(")");

        if ( currentState[i][j] == HIGH){
          Serial.println(" Push!");
          Keyboard.press( keyMap[i][j] );
        } else {
          Serial.println(" Release!");
          Keyboard.release( keyMap[i][j] );
        }
      beforeState[i][j] = currentState[i][j];
      }
    }
    digitalWrite( rowPin[i], LOW );
  }
}

(0,0)=a, (0,1)=b, (1,0)=c, (1,1)=d が入力されますね。
シリアルモニターでもちゃんと監視できてるかと思います。

行ごとにHIGHにして、INPUTピンを順番に読み取っていくイメージですね。

回路の改良

イメージ通り動いているように感じますね。
しかし実はこれだけでは問題があります。

試しに(0,0),(0,1),(1,0)の3キーを同時に押しっぱなしにしてみてください。

イメージ通りならa,b,cだけが入力されるはずですがなぜかdまで入力されると思います。
これはなぜでしょう…?

それは以下のように意図しないルートを通って電気が流れてしまっているためです。

(1,1)は押していないのに、他のスイッチを通ることでRow1をHIGHにしてる時にCol1にHIGHが流れ込んでいます。

これを避けるために、各スイッチに一つずつダイオードを繋ぎます。 ダイオードは電流を片方向にしか通さない性質があるので、ちゃんと全てダイオードを繋いでいれば、意図しないルートは通れないようになるわけです。

ダイオードは黒い筋の通った方に向かって電流が流れるのでその通りに接続します。

全てにダイオードをつけるとこうなります。

(0,0)のダイオードによって逆流が阻止されています。

ブレッドボード上はこんな感じ。

これでちゃんと意図通りの挙動をする4キーキーボードができましたね!

実際にキーボードの制作に入る前にもう一つだけ重要なことがあるので、 次回は6×2キーボードを作ります。

eucalyn.hatenadiary.jp


まとめ記事はこちら。

eucalyn.hatenadiary.jp