久しぶりの 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 スイッチマトリクス回路
スイッチマトリクスについては、ググるといろいろ出てきますので、参照して下さい。
基本的な考え方は同じなんですが、プルアップするもの、プルダウンするもの、ダイオードのあるもの、ないもの、様々ですね。
今回の回路ではプルアップ方式にしました。内部プルアップでもいけます。
ダイオードは、悩まず入れましょう。
押しボタンを 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 に、押したキーの値を表示させるようにしました。
7 セグメント LED は 4 桁ですが、そのまま利用して、キーに対応した 1 〜 12 の数値を表示させることにします。回路も同じ、スケッチは若干の修正をしていますが、基本的に同じです。
このドライバ IC TM1630 を利用することで、Arduino の I/O ピンは 3 個ですみますし、一度データを送ればずっと表示しつづけてくれますので、Arduino の負担も少なくなります。
スケッチ
スケッチの部分ごとに、毎度の備忘録的解説です。
なお、LED ドライバ IC TM1630 に関する詳細は、過去ブログ記事 ここ、ここ、ここを参照して下さい。
- // 12-Keyboard to 7-Segment Display 2022.05.19 meyon
- // 12-KeyScan Definition
- const byte Row0 = 2;
- const byte Row1 = 3;
- const byte Row2 = 4;
- const byte Row3 = 5;
- const byte Column0 = A3;
- const byte Column1 = A4;
- const byte Column2 = A5;
キー入力に関する I/O ピンの定義です。
D2 〜 D5 にスイッチマトリクスの行ラインを、A3 〜 A5 に列ラインを割り当てます。
- // 7-SD Driver TM1630 Definition
- #define DISPLAY_MODE 0x00
- #define WRITE_REG_AUTO_ADDR 0x40
- #define WRITE_REG_FIXED_ADDR 0x44
- #define READ_KEY_SCAN 0x42
- #define SET_DISPLAY_ADDR 0xC0
- #define DISPLAY_OFF 0x80
- #define DISPLAY_ON 0x88 | 0x84
- #define STB_ENABLE LOW
- #define STB_DISABLE HIGH
- #define DOT_POSITION -1
- const byte dioPin = 6;
- const byte clkPin = 7;
- const byte stbPin = 8;
- const byte numberOfDigits = DISPLAY_MODE & 1 ? 5 : 4;
- byte gridData[numberOfDigits] = {0};
- const byte digit[] = {
- 0b01111110, // 0
- 0b00001100, // 1
- 0b10110110, // 2
- 0b10011110, // 3
- 0b11001100, // 4
- 0b11011010, // 5
- 0b11111010, // 6
- 0b01001110, // 7
- 0b11111110, // 8
- 0b11011110, // 9
- 0b00000000, // 10:blank
- 0b00100000, // 11:dot
- };
- const byte blank = 10;
- const byte dot = 11;
LED ドライバに関する定義です。
制御コマンド、I/O ピン割り当て、7 セグメントの表示データを定義します。
- void setup() {
- // 12-KeyScan Setup
- pinMode(Row0, OUTPUT);
- pinMode(Row1, OUTPUT);
- pinMode(Row2, OUTPUT);
- pinMode(Row3, OUTPUT);
- pinMode(Column0, INPUT);
- pinMode(Column1, INPUT);
- pinMode(Column2, INPUT);
- digitalWrite(Row0, HIGH);
- digitalWrite(Row1, HIGH);
- digitalWrite(Row2, HIGH);
- digitalWrite(Row3, HIGH);
- // 7-SD Driver TM1630 Setup
- pinMode(dioPin, OUTPUT);
- pinMode(clkPin, OUTPUT);
- pinMode(stbPin, OUTPUT);
- // Serial.begin(9600);
- }
各 I/O ピンのモードの設定です。
50 〜 62 行はキー入力に関する設定です。
列 (Column) はインプットモードです。内部プルアップにして、プルアップ抵抗を省略することもできます。行 (Row) はアウトプットモードとし、通常は HIGH ですが、キー検知時に LOW にします。
64 〜 67 行は、LED ドライバへの制御出力です。
69 行は、テスト用のシリアル出力です。必要な時にコメントアウトして下さい。
- // 12-Keyboard Scan
- unsigned int keyscan() {
- unsigned int ks = 0xC000;
- digitalWrite(Row0, LOW);
- ks |= (!digitalRead(Column0)<<0 | !digitalRead(Column1)<<1 | !digitalRead(Column2)<<2);
- digitalWrite(Row0, HIGH);
- digitalWrite(Row1, LOW);
- ks |= (!digitalRead(Column0)<<3 | !digitalRead(Column1)<<4 | !digitalRead(Column2)<<5);
- digitalWrite(Row1, HIGH);
- digitalWrite(Row2, LOW);
- ks |= (!digitalRead(Column0)<<6 | !digitalRead(Column1)<<7 | !digitalRead(Column2)<<8);
- digitalWrite(Row2, HIGH);
- digitalWrite(Row3, LOW);
- ks |= (!digitalRead(Column0)<<9 | !digitalRead(Column1)<<10 | !digitalRead(Column2)<<11);
- digitalWrite(Row3, HIGH);
- return ks;
- }
キーの状態を検出し、ビット列を返す関数です。
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 になります。
- // 7-Segment Display Drive
- void displayNumbers(int n) {
- for (byte i = 0; i < numberOfDigits; i++) {
- int exponentialInDecimal = pow(10, i) + 0.5;
- bool zeroSuppression = (0 != i) && (DOT_POSITION < i) && (exponentialInDecimal > n);
- gridData[i] = zeroSuppression ? blank : n / exponentialInDecimal % 10 ;
- }
- digitalWrite(stbPin, STB_ENABLE);
- shiftOut(dioPin, clkPin, LSBFIRST, DISPLAY_MODE);
- digitalWrite(stbPin, STB_DISABLE);
- digitalWrite(stbPin, STB_ENABLE);
- shiftOut(dioPin, clkPin, LSBFIRST, WRITE_REG_AUTO_ADDR);
- digitalWrite(stbPin, STB_DISABLE);
- digitalWrite(stbPin,STB_ENABLE);
- shiftOut(dioPin, clkPin, LSBFIRST, SET_DISPLAY_ADDR | 0x00);
- for (byte i = 0; i < numberOfDigits; i++) {
- shiftOut(dioPin, clkPin, LSBFIRST, digit[gridData[i]]);
- shiftOut(dioPin, clkPin, LSBFIRST, DOT_POSITION == i ? digit[dot] : 0);
- }
- digitalWrite(stbPin, STB_DISABLE);
- digitalWrite(stbPin, STB_ENABLE);
- shiftOut(dioPin, clkPin, LSBFIRST, DISPLAY_ON);
- digitalWrite(stbPin, STB_DISABLE);
- }
LED ドライバ IC で数値を表示する関数です。引数に表示したい数値を渡します。
コマンドを送るときは、まず STB を有効 (LOW) にし、コマンドを送信したら STB を無効 (HIGH) に戻します。コマンドは、最初に表示モードを、次にデータ設定として書き込みモードと自動インクリメントを指定します。そして、表示レジスタのアドレスに続けて、表示データを送信します。最後に表示をラッチして終わりです。
いちど制御データを送れば、次の制御データがくるまでその値をずっと表示し続けてくれますので、Arduino はその間、他の作業に専念できます。
- void loop() {
- static byte keyData = 1;
- unsigned int keyStatus = keyscan();
- // Serial.println(keyStatus, BIN);
- switch(keyStatus) {
- case 0xC001: keyData = 1; break;
- case 0xC002: keyData = 2; break;
- case 0xC004: keyData = 3; break;
- case 0xC008: keyData = 4; break;
- case 0xC010: keyData = 5; break;
- case 0xC020: keyData = 6; break;
- case 0xC040: keyData = 7; break;
- case 0xC080: keyData = 8; break;
- case 0xC100: keyData = 9; break;
- case 0xC200: keyData = 10; break;
- case 0xC400: keyData = 11; break;
- case 0xC800: keyData = 12;
- }
- displayNumbers(keyData);
- }
今回は、押したキーに対応した数値を 7 セグメント LED に表示し、保持させるようにします。
複数のキーを同時に押したときは、先に押したほうを優先します。
キーの状態は keyStatus に受け取ります。
30 〜 43 行で、keyStatus の値により表示させる数値に分岐させています。複数のキーを同時に押した時の keyStatus の値は規定していませんので、無視されます。
128 行は、テスト用のシリアル出力です。キーの状態がビット列で確認できます。
ブレッドボード
左上が 7 セグメント LED 表示器とドライバ IC TM1630 です。
LED が暗く見えますが、室内では十分な明るさがあります。コマンドでもう少し明るくすることも可能です。
右上は電源回路で、Arduino 用の DC9V と 制御用の DC5V を供給しています。
DC5V は、LED 1 桁に 160mA の電流容量が必要です。ダイナミック点灯なのでこれが最大値になりますが、Arduino から供給することは避けたほうがよいと思います。
中段のブレッドボードに Arduino NANO 。左側の空地は、今後の拡張スペースです。
下のブレッドボードが 3×4 キーボードで、Arduino とは 7 本のラインと 5V の電源ラインでつながっています。Arduino の入力を内部プルアップにすれば、電源ラインとプルアップ抵抗を省略できますね。
ということで、12 キーボードを作り、押したキーの値を 7 セグメント LED に出力、表示する回路とスケッチができました。いろいろと応用できるかなぁと思っています。