アフィリエイト広告
アフィリエイト広告

3×4 キーボードを作る

久しぶりの Arduino ネタ (^_^;)

3 列 4 行の 12 キーボードを作って、押したキーの値を出力したいと思います。

12 キーボードの構成

12 個の押しボタンスイッチを並べるわけですが、単純に考えると、12 本の出力ラインと 1 本の共通ラインで 13 本の出力になります。Arduino の I/O ピンも 12 個使うんで、うーん、ないです。

12to4 エンコーダを使えば、4 個の I/O ピンですみます。ダイオードマトリクスで簡単に作れますが、押しボタンを 2 個以上同時に押すと、ダメです。抵抗ラダーでも、同じです。
プライオリティエンコーダを設計してみたら、AND ゲートが 15 個、OR ゲート 18 個、NOT ゲート 9 個必要になった。はぁ、ないです (;´Д`)

フツーはどうしてんの?
ググってみたら、スイッチマトリクスを使うんだとさ。あ、そうか。3×4 スイッチマトリクスなら I/O ピンは 7 個でいけます。これでいきましょ。

3×4 スイッチマトリクス回路

スイッチマトリクスについては、ググるといろいろ出てきますので、参照して下さい。

3×4 スイッチマトリクス回路

基本的な考え方は同じなんですが、プルアップするもの、プルダウンするもの、ダイオードのあるもの、ないもの、様々ですね。

今回の回路ではプルアップ方式にしました。内部プルアップでもいけます。

ダイオードは、悩まず入れましょう。
押しボタンを 2 個以上同時に押しても、すべてを正常に検知できます。

列 (Column) と行 (Row) のどちらを検知側にするかも、考え方次第です。今回は、行側を制御して、列側で検知するようにしました。
押しボタンは、左上から右下へ 1 から 12 に割り当てますので、Row0 を Low にしたときに Column0 〜 Column2 が 1 〜 3 に相当することになり、並びが感覚的にわかりやすくなると思います。

Column0 〜 Column2 は Arduino の I/O ピン A3 〜 A5 に、Row0 〜 Row3 は D2 〜 D5 に接続しています。これもお好みでどうぞ。
D6 〜 D8 は表示用の出力です。後述します。

7 セグメント LED 表示回路

キーボードからの出力を Arduino IDE のシリアルモニタで確認するのは良いのですが、なにかに出力するようにしたほうが、より電子工作っぽいですね。
今回は、以前試したことのある LED ドライバ IC TM1630 を使って、7 セグメント LED に、押したキーの値を表示させるようにしました。

LEDドライバ TM1630 を使ってみる (2) LED表示
秋月電子通商で購入した「7 セグメント用 LED ドライバー IC TM1630」、データシートを一通り確認してみましたので、今回は LED 表示の動作を確認してみたいと思います。 LED 表示回路 4 桁の 7 セグメント LED が部品...

7 セグメント LED は 4 桁ですが、そのまま利用して、キーに対応した 1 〜 12 の数値を表示させることにします。回路も同じ、スケッチは若干の修正をしていますが、基本的に同じです。
このドライバ IC TM1630 を利用することで、Arduino の I/O ピンは 3 個ですみますし、一度データを送ればずっと表示しつづけてくれますので、Arduino の負担も少なくなります。

スケッチ

スケッチの部分ごとに、毎度の備忘録的解説です。
なお、LED ドライバ IC TM1630 に関する詳細は、過去ブログ記事 ここここここを参照して下さい。

  1. // 12-Keyboard to 7-Segment Display 2022.05.19 meyon
  2. // 12-KeyScan Definition
  3. const byte Row0 = 2;
  4. const byte Row1 = 3;
  5. const byte Row2 = 4;
  6. const byte Row3 = 5;
  7. const byte Column0 = A3;
  8. const byte Column1 = A4;
  9. const byte Column2 = A5;

キー入力に関する I/O ピンの定義です。
D2 〜 D5 にスイッチマトリクスの行ラインを、A3 〜 A5 に列ラインを割り当てます。

  1. // 7-SD Driver TM1630 Definition
  2. #define DISPLAY_MODE 0x00
  3. #define WRITE_REG_AUTO_ADDR 0x40
  4. #define WRITE_REG_FIXED_ADDR 0x44
  5. #define READ_KEY_SCAN 0x42
  6. #define SET_DISPLAY_ADDR 0xC0
  7. #define DISPLAY_OFF 0x80
  8. #define DISPLAY_ON 0x88 | 0x84
  9. #define STB_ENABLE LOW
  10. #define STB_DISABLE HIGH
  11. #define DOT_POSITION -1
  12. const byte dioPin = 6;
  13. const byte clkPin = 7;
  14. const byte stbPin = 8;
  15. const byte numberOfDigits = DISPLAY_MODE & 1 ? 5 : 4;
  16. byte gridData[numberOfDigits] = {0};
  17. const byte digit[] = {
  18.   0b01111110, // 0
  19.   0b00001100, // 1
  20.   0b10110110, // 2
  21.   0b10011110, // 3
  22.   0b11001100, // 4
  23.   0b11011010, // 5
  24.   0b11111010, // 6
  25.   0b01001110, // 7
  26.   0b11111110, // 8
  27.   0b11011110, // 9
  28.   0b00000000, // 10:blank
  29.   0b00100000, // 11:dot
  30. };
  31. const byte blank = 10;
  32. const byte dot = 11;

LED ドライバに関する定義です。
制御コマンド、I/O ピン割り当て、7 セグメントの表示データを定義します。

  1. void setup() {
  2. // 12-KeyScan Setup
  3.   pinMode(Row0, OUTPUT);
  4.   pinMode(Row1, OUTPUT);
  5.   pinMode(Row2, OUTPUT);
  6.   pinMode(Row3, OUTPUT);
  7.   pinMode(Column0, INPUT);
  8.   pinMode(Column1, INPUT);
  9.   pinMode(Column2, INPUT);
  10.   digitalWrite(Row0, HIGH);
  11.   digitalWrite(Row1, HIGH);
  12.   digitalWrite(Row2, HIGH);
  13.   digitalWrite(Row3, HIGH);
  14. // 7-SD Driver TM1630 Setup
  15.   pinMode(dioPin, OUTPUT);
  16.   pinMode(clkPin, OUTPUT);
  17.   pinMode(stbPin, OUTPUT);
  18. // Serial.begin(9600);
  19. }

各 I/O ピンのモードの設定です。

50 〜 62 行はキー入力に関する設定です。
列 (Column) はインプットモードです。内部プルアップにして、プルアップ抵抗を省略することもできます。行 (Row) はアウトプットモードとし、通常は HIGH ですが、キー検知時に LOW にします。

64 〜 67 行は、LED ドライバへの制御出力です。

69 行は、テスト用のシリアル出力です。必要な時にコメントアウトして下さい。

  1. // 12-Keyboard Scan
  2. unsigned int keyscan() {
  3.   unsigned int ks = 0xC000;
  4.   digitalWrite(Row0, LOW);
  5.   ks |= (!digitalRead(Column0)<<0 | !digitalRead(Column1)<<1 | !digitalRead(Column2)<<2);
  6.   digitalWrite(Row0, HIGH);
  7.   digitalWrite(Row1, LOW);
  8.   ks |= (!digitalRead(Column0)<<3 | !digitalRead(Column1)<<4 | !digitalRead(Column2)<<5);
  9.   digitalWrite(Row1, HIGH);
  10.   digitalWrite(Row2, LOW);
  11.   ks |= (!digitalRead(Column0)<<6 | !digitalRead(Column1)<<7 | !digitalRead(Column2)<<8);
  12.   digitalWrite(Row2, HIGH);
  13.   digitalWrite(Row3, LOW);
  14.   ks |= (!digitalRead(Column0)<<9 | !digitalRead(Column1)<<10 | !digitalRead(Column2)<<11);
  15.   digitalWrite(Row3, HIGH);
  16.   return ks;
  17. }

キーの状態を検出し、ビット列を返す関数です。
74 行の 0xC000 に、あまり意味はありません。下位 12 ビットにキーの状態が入力されます。上位の 4 ビットはフラグとして 1100 にしてます。単なる趣味です (^_^;)

76 〜 78 行で、まず Row0 を LOW にしてから、Column0 〜 Column2 の状態をビット列 ks にセットします。セットしたら、Row0 を HIGH に戻します。
80 〜 82 行で、Row1 を LOW にして、各列の状態をビット列にセット。以下同様に、各行を順次 LOW にしてキーの状態を検知していきます。

ビット列 ks の下位 12 ビットは、キーが押されたときに 1 になります。複数のキーを同時に押したときも、それぞれのキーに対応したビットが 1 にセットされますので、12 個全部のキーを押すと ks は 0xCFFF になります。

  1. // 7-Segment Display Drive
  2. void displayNumbers(int n) {
  3.   for (byte i = 0; i < numberOfDigits; i++) {
  4.     int exponentialInDecimal = pow(10, i) + 0.5;
  5.     bool zeroSuppression = (0 != i) && (DOT_POSITION < i) && (exponentialInDecimal > n);
  6.     gridData[i] = zeroSuppression ? blank : n / exponentialInDecimal % 10 ;
  7.   }
  8.   digitalWrite(stbPin, STB_ENABLE);
  9.   shiftOut(dioPin, clkPin, LSBFIRST, DISPLAY_MODE);
  10.   digitalWrite(stbPin, STB_DISABLE);
  11.   digitalWrite(stbPin, STB_ENABLE);
  12.   shiftOut(dioPin, clkPin, LSBFIRST, WRITE_REG_AUTO_ADDR);
  13.   digitalWrite(stbPin, STB_DISABLE);
  14.   digitalWrite(stbPin,STB_ENABLE);
  15.   shiftOut(dioPin, clkPin, LSBFIRST, SET_DISPLAY_ADDR | 0x00);
  16.   for (byte i = 0; i < numberOfDigits; i++) {
  17.     shiftOut(dioPin, clkPin, LSBFIRST, digit[gridData[i]]);
  18.     shiftOut(dioPin, clkPin, LSBFIRST, DOT_POSITION == i ? digit[dot] : 0);
  19.   }
  20.   digitalWrite(stbPin, STB_DISABLE);
  21.   digitalWrite(stbPin, STB_ENABLE);
  22.   shiftOut(dioPin, clkPin, LSBFIRST, DISPLAY_ON);
  23.   digitalWrite(stbPin, STB_DISABLE);
  24. }

LED ドライバ IC で数値を表示する関数です。引数に表示したい数値を渡します。

コマンドを送るときは、まず STB を有効 (LOW) にし、コマンドを送信したら STB を無効 (HIGH) に戻します。コマンドは、最初に表示モードを、次にデータ設定として書き込みモードと自動インクリメントを指定します。そして、表示レジスタのアドレスに続けて、表示データを送信します。最後に表示をラッチして終わりです。
いちど制御データを送れば、次の制御データがくるまでその値をずっと表示し続けてくれますので、Arduino はその間、他の作業に専念できます。

  1. void loop() {
  2.   static byte keyData = 1;
  3.   unsigned int keyStatus = keyscan();
  4. // Serial.println(keyStatus, BIN);
  5.   switch(keyStatus) {
  6.     case 0xC001: keyData = 1; break;
  7.     case 0xC002: keyData = 2; break;
  8.     case 0xC004: keyData = 3; break;
  9.     case 0xC008: keyData = 4; break;
  10.     case 0xC010: keyData = 5; break;
  11.     case 0xC020: keyData = 6; break;
  12.     case 0xC040: keyData = 7; break;
  13.     case 0xC080: keyData = 8; break;
  14.     case 0xC100: keyData = 9; break;
  15.     case 0xC200: keyData = 10; break;
  16.     case 0xC400: keyData = 11; break;
  17.     case 0xC800: keyData = 12;
  18.   }
  19.   displayNumbers(keyData);
  20. }

今回は、押したキーに対応した数値を 7 セグメント LED に表示し、保持させるようにします。
複数のキーを同時に押したときは、先に押したほうを優先します。

キーの状態は keyStatus に受け取ります。
30 〜 43 行で、keyStatus の値により表示させる数値に分岐させています。複数のキーを同時に押した時の keyStatus の値は規定していませんので、無視されます。

128 行は、テスト用のシリアル出力です。キーの状態がビット列で確認できます。

ブレッドボード

3×4 キーボードと 7 セグメント LED 表示器

左上が 7 セグメント LED 表示器とドライバ IC TM1630 です。
LED が暗く見えますが、室内では十分な明るさがあります。コマンドでもう少し明るくすることも可能です。

右上は電源回路で、Arduino 用の DC9V と 制御用の DC5V を供給しています。
DC5V は、LED 1 桁に 160mA の電流容量が必要です。ダイナミック点灯なのでこれが最大値になりますが、Arduino から供給することは避けたほうがよいと思います。

中段のブレッドボードに Arduino NANO 。左側の空地は、今後の拡張スペースです。

下のブレッドボードが 3×4 キーボードで、Arduino とは 7 本のラインと 5V の電源ラインでつながっています。Arduino の入力を内部プルアップにすれば、電源ラインとプルアップ抵抗を省略できますね。

ということで、12 キーボードを作り、押したキーの値を 7 セグメント LED に出力、表示する回路とスケッチができました。いろいろと応用できるかなぁと思っています。

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