今回は、ATtiny404と16キー4×4マトリックスキーパッドを使って、押したキーをAQM1602へ表示する簡単なプログラムを作ってみました。
使用したキーパッドは8ピン構成の4×4タイプで、行と列をスキャンすることでどのキーが押されたかを判定します。
キーが押されると、その文字をAQM1602へ表示するだけのシンプルな内容ですが、キーパッド入力の基本的な考え方がよく分かる内容になっています。
またATtiny404は14ピンで扱いやすく、秋月電子で100円程度と非常に安価です。
さらに4KBのプログラムメモリーを搭載しているため、標準I2CライブラリやAQM1602ライブラリも比較的余裕を持って使用できます。
今回のようなキーパッド入力は、
- 初期設定画面
- メニュー操作
- パスコード入力
- 周波数設定
- タイマー設定
などにも応用できるので、電子工作ではかなり便利な入力方法になります。
今回使用したもの
- ATtiny404
- AQM1602 LCD
- 16キー 4×4 マトリックスキーパッド
- ジャンパーワイヤー
- ブレッドボード
キーパッドについて
今回使用した4×4キーパッドは、内部的には「行」と「列」が接続された構造になっています。
例えば「1」キーを押すと、
- 1行目
- 1列目
が導通する仕組みです。
そのためマイコン側では、
- 行を順番にLOWにする
- 列入力を確認する
- LOWになった場所を調べる
ことで、どのキーが押されたかを判定できます。
4×4キーパッドは8ピン必要
4×4キーパッドは、
- 行4本
- 列4本
合計8本のGPIOが必要になります。
そのため小ピン数マイコンでは少し厳しくなりますが、ATtiny404は14ピン版なのでちょうど扱いやすいサイズです。
さらにATtiny404は価格も安く、性能も十分あるため、キーパッド制御にはかなり向いているマイコンだと思います。
また、4×3タイプに変更すれば、
- 行4本
- 列3本
となるため、7ピン構成に減らすことも可能で、残りPA6 PA7 をUARTなどに使うこともでき、いろいろ面白いことができるかもしれません。
数字入力だけなら4×3タイプでも十分実用的です。
回路図

I2CとAQM1602ライブラリについて
今回使用しているSoftI2C_VPORTとAQM1602_SoftI2Cについては、以前の記事で詳しく紹介しています。
#include <SoftI2C_VPORT.h>
#include <AQM1602_SoftI2C.h>
SoftI2C_VPORT i2c(1, 2);こちらでは、
- ソフトウェアI2C
- 軽量AQM1602制御
- Wireを使わない構成
について説明していますので、先に見ると理解しやすいと思います。
使いかたはこちらから確認してください
配列でキーマップを定義する
キーの対応表は配列で作っています。
char keyMap[4][4] = {
{ '1', '2', '3','A'},
{ '4', '5', '6','B'},
{ '7', '8', '9','C'},
{ '*', '0', '#','D'}
};例えば、
- 1行目 + 1列目 → ‘1’
- 2行目 + 3列目 → ‘6’
というように、押された場所と文字を対応させています。
この方法は非常に分かりやすく、初心者にもおすすめです。
行と列をスキャンしてキーを判定する
キー判定は scanKey() 関数で行っています。
処理の流れ
1. 全ての行をHIGHにする
for (uint8_t i = 0; i < 4; i++) {
digitalWrite(rowPins[i], HIGH);
}列入力を確認する
if (digitalRead(colPins[col]) == LOW) {
return keyMap[row][col];
}キーが押されていると、LOWになった行と列が接続されるため、対応する文字を取得できます。
1文字を「文字列」に変換してLCDへ渡す
今回かなり重要なのがこの部分です。
char buf1[2];
buf1[0] = key;
buf1[1] = '\0';C/C++では、文字列の最後に '\0' (ヌル文字)が必要になります。
例えば、
'A'これは「1文字」です。
しかしLCDの print() は、
buf1[0] = key;
buf1[1] = '\0';とすることで、
- 1文字
- 終端文字
を持った「文字列」に変換しています。
これによって、
lcd.print(buf1);のようにAQM1602へ渡すことができます。
ここは初心者の方がかなり混乱しやすい部分なので、ぜひ覚えておきたいポイントです。
「押した瞬間だけ」を判定するのが重要
今回のプログラムでは、この部分も非常に重要です。
if (key != 0 && lastKey == 0)これは、
- 今は押されている
- 前回は押されていなかった
という意味になります。
つまり「押された瞬間だけ」を検出しています。
もし単純に、
if (key != 0)だけにしてしまうと、loop()が高速で回っているため、
111111111111のように同じ文字が大量に表示されてしまいます。
そのため、
lastKeyを使って前回状態を記録し、「押した瞬間だけ」を判定しています。
これはスイッチ入力全般で非常によく使うテクニックです。
完成プログラム
#include <SoftI2C_VPORT.h>
#include <AQM1602_SoftI2C.h>
SoftI2C_VPORT i2c(1, 2);
AQM1602 lcd(i2c);
const uint8_t rowPins[4] = {
PIN_PB0,
PIN_PB1,
PIN_PB2,
PIN_PB3
};
const uint8_t colPins[4] = {
PIN_PA3,
PIN_PA4,
PIN_PA5,
PIN_PA6
};
char keyMap[4][4] = {
{ '1', '2', '3','A'},
{ '4', '5', '6','B'},
{ '7', '8', '9','C'},
{ '*', '0', '#','D'}
};
char scanKey() {
for (uint8_t row = 0; row < 4; row++) {
for (uint8_t i = 0; i < 4; i++) {
digitalWrite(rowPins[i], HIGH);
}
digitalWrite(rowPins[row], LOW);
for (uint8_t col = 0; col < 4; col++) {
if (digitalRead(colPins[col]) == LOW) {
return keyMap[row][col];
}
}
}
return 0;
}
void setup() {
lcd.begin(0x73, 0x52);
lcd.print("keypad");
for (uint8_t i = 0; i < 4; i++) {
pinMode(rowPins[i], OUTPUT);
digitalWrite(rowPins[i], HIGH);
}
for (uint8_t i = 0; i < 4; i++) {
pinMode(colPins[i], INPUT_PULLUP);
}
}
char lastKey = 0;
void loop() {
char key = scanKey();
char buf1[2];
buf1[0] = key;
buf1[1] = '\0';
if (key != 0 && lastKey == 0) {
lcd.setCursor(8, 0);
lcd.print("KEY = ");
lcd.print(buf1);
}
lastKey = key;
delay(40);
}まとめ
今回ATtiny404と4×4マトリックスキーパッドを使って、押したキーをAQM1602へ表示してみまた。
キーパッド入力は一見難しそうに見えますが、
- 行をLOWにする
- 列を読む
- 対応表から文字を取得する
という流れを理解すると意外とシンプルです。
また、
- 「押した瞬間だけ」を検出する方法
- 1文字を文字列へ変換する方法
- マトリックススキャンの基本
など、組み込みプログラムでは重要な考え方も学べます。
この仕組みを応用すると、
- メニュー画面
- パスワード入力
- 数値設定
- 各種パラメータ変更
なども簡単に作れるようになりますので、試してみてください。


