2015年08月25日 | 公開。 |
2015年08月28日 | 必要なArduinoの種類とArduino IDEのバージョンの説明を追加した。 |
この記事で紹介している電卓は、Make:Japanの電卓自作に役立つキーパッドの作り方という記事で、紹介されました。
この記事で紹介している電卓を作るには、Arduino Uno、Arduino Mega 2560、Arduino Leonardo、Arduino Pro 328 5V 16MHz、Seeduino V3.0など、ResKeypadライブラリが対応している8ビットのArduino(および互換機)と、Arduino IDE 1.0.1~1.0.6、1.6.X、または1.7.X(Xは任意の数字)が必要です。
この記事では、最初は電卓に使うキーパッドの仕組みについて説明していますが、とにかく電卓を早く組み立てたいという方は、4ページ目からご覧ください。
どこの家にでもあり、ちょっとがんばれば自作できそうで、実際にはなかなか自作できない電子機器といえば、電卓がその代表例になるでしょう。普段、よく使う機器なので、自作できれば感激が大きいかも知れません。そういうわけで、今回はArduinoを使って電卓を作ってみます。
最近では100円ショップでも普通に電卓を売っていますが、さすがに100円では電卓を自作できません。電卓を自作する意味は、経済的な意味ではなく、身近にある製品の仕組みの理解というところにあります。
さて、電卓を自作するにあたって、問題になりそうな点が2つあります。
プログラムに手間がかかる点は、勉強を兼ねて、がんばってやるしかないのですが、スイッチをたくさん使う点については、ハードウェアの構成上、何らかの工夫をする必要があります。
ここで、必要最低限の機能を持った電卓に、いくつのスイッチ(ボタン)が必要か考えてみましょう。電源ボタンを除いて考えると、電卓に必要なボタンは、図1の様な感じになるのではないでしょうか。
図1の電卓には、19個ものボタンが付いています。市販されている電卓では、ルート計算用のボタンとか、メモリ機能用のボタンとかが付いており、さらに多いボタンが付いています。
ところで、スイッチ(ボタン)の状態をArduinoに読み込ませるには、図2の様に、I/Oピンにスイッチおよびプルアップ抵抗をつないで、リスト1の様にdigitalRead関数で読み出すのが一般的です。
#define IO_PIN 4 // 使用するI/Oピンを指定する
void setup() {
pinMode(IO_PIN,INPUT); // 読み取りに使うI/Oピンを入力にする。INPUTの代わりにINPUT_PULLUPにすると、プルアップ抵抗を省略できる。
}
void loop() {
if(digitalRead(IO_PIN)==LOW) {
// スイッチがONの時の処理
} else {
// スイッチがOFFの時の処理
} // if
}
図2の回路において、ボタンが押されておらず、スイッチがOFFの場合は、I/Oピンがプルアップ抵抗を介して5Vにつながっている状態なので、入力電圧は5Vになります。digitalRead関数で読み取ると、HIGHを返します。
一方で、ボタンが押されており、スイッチがONの場合は、I/OピンがGNDにショートされてしまうので、入力電圧は0Vになります。digitalRead関数で読み取ると、LOWを返します。
リスト1のスケッチは、一部未完成ですが、実際には// スイッチがONのときの処理
と書いてある部分と、// スイッチがOFFの時の処理
と書いてある部分に、それぞれ必要な処理を書いて、スケッチを完成させます。
この様にすれば、スイッチの状態を読み取れはするのですが、スイッチひとつに付きI/Oピンひとつが必要になります。例えばArduino UNOならば、I/Oピンは全部で20あるのですが、図1の電卓の様に、19個のスイッチを使うとなると、それだけで19のI/Oピンを使ってしまい、液晶やLEDなど、数字を表示する装置に割り当てるI/Oピンが不足してしまいます。
もちろん、Arduino MEGAやDUE等のようにI/Oピンが多いArduinoを使えば問題は解決しますが、価格の高いArduinoを使わなければなりません。
市販されている電卓では、図2の様な単純なスイッチ読み取り回路が使われる事はありません。マイコンや電卓専用ICのI/Oピンをたくさん使うためです。必要なI/Oピンが多いと、製造価格上昇の原因になります。
それでは、実際にどのように電卓がスイッチの状態を読み取っているかというと、図3のような、キーマトリックス方式の回路を使う場合が多いのです。
この図は、16個のスイッチを8つのI/Oピンで読み取る例を示しています。この場合は、I/Oピンひとつあたり2つのスイッチの状態が読み出せることになります。(もし19個のスイッチを読み取るなら、I/Oピンは9つ必要。)
IN1~4は、入力用のI/Oピンで、OUT1~4は、出力用のI/Oピンです。OUT1~4の中で、必ずひとつだけL(LOW)を出力し、残りはハイインピーダンス(信号線が電気的にどこにもつながっていない状態)にします。
例えばOUT1をL、OUT2~4をハイインピーダンスにすると、IN1~4に、それぞれSW1、SW5、SW9、SW13の状態が読み出せます。この時、スイッチがONの場合はL、スイッチがOFFの場合はH(HIGH)が、対応する信号線に読み出せます。
OUT2をL、OUT1とOUT3~4をハイインピーダンスにした場合は、IN1~4に、それぞれSW2、SW6、SW10、SW14の状態が読み出せます。
このスイッチの読み取りの方法をArduinoのスケッチにするなら、リスト2の様になります。
#define IN1 0 // 入力用I/Oピンを指定する
#define IN2 1 // 入力用I/Oピンを指定する
#define IN3 2 // 入力用I/Oピンを指定する
#define IN4 3 // 入力用I/Oピンを指定する
#define OUT1 4 // 出力用I/Oピンを指定する
#define OUT2 5 // 出力用I/Oピンを指定する
#define OUT3 6 // 出力用I/Oピンを指定する
#define OUT4 7 // 出力用I/Oピンを指定する
void setup() {
pinMode(IN1 ,INPUT ); // 入力用I/Oピンを入力にする。INPUTの代わりにINPUT_PULLUPにすると、プルアップ抵抗を省略できる。
pinMode(IN2 ,INPUT ); // 入力用I/Oピンを入力にする。INPUTの代わりにINPUT_PULLUPにすると、プルアップ抵抗を省略できる。
pinMode(IN3 ,INPUT ); // 入力用I/Oピンを入力にする。INPUTの代わりにINPUT_PULLUPにすると、プルアップ抵抗を省略できる。
pinMode(IN4 ,INPUT ); // 入力用I/Oピンを入力にする。INPUTの代わりにINPUT_PULLUPにすると、プルアップ抵抗を省略できる。
pinMode(OUT1,OUTPUT); // 出力用I/Oピンをハイインピーダンスにするために、入力にする。
pinMode(OUT2,OUTPUT); // 出力用I/Oピンをハイインピーダンスにするために、入力にする。
pinMode(OUT3,OUTPUT); // 出力用I/Oピンをハイインピーダンスにするために、入力にする。
pinMode(OUT4,OUTPUT); // 出力用I/Oピンをハイインピーダンスにするために、入力にする。
}
void loop() {
// SW1、SW5、SW9、SW13を読むために、OUT1をL、OUT2~4をハイインピーダンスにする。
pinMode(OUT4,INPUT ); // OUT4をハイインピーダンスにする。
pinMode(OUT1,OUTPUT); // OUT1を出力にする。
digitalWrite(OUT1,LOW); // OUT1にLを出力する。
delay(1); // 信号線の状態が安定するのを待つ
// IN1を読み、SW1の状態を調べる
if(digitalRead(IN1)==LOW) {
// SW1がONの時の処理
} // if
// IN2を読み、SW5の状態を調べる
if(digitalRead(IN2)==LOW) {
// SW5がONの時の処理
} // if
// IN3を読み、SW9の状態を調べる
if(digitalRead(IN3)==LOW) {
// SW9がONの時の処理
} // if
// IN4を読み、SW13の状態を調べる
if(digitalRead(IN4)==LOW) {
// SW13がONの時の処理
} // if
// SW2、SW6、SW10、SW14を読むために、OUT2をL、OUT1とOUT3~4をハイインピーダンスにする。
pinMode(OUT1,INPUT ); // OUT1をハイインピーダンスにする。
pinMode(OUT2,OUTPUT); // OUT2を出力にする。
digitalWrite(OUT2,LOW); // OUT2にLを出力する。
delay(1); // 信号線の状態が安定するのを待つ
// IN1を読み、SW2の状態を調べる
if(digitalRead(IN1)==LOW) {
// SW2がONの時の処理
} // if
// IN2を読み、SW6の状態を調べる
if(digitalRead(IN2)==LOW) {
// SW6がONの時の処理
} // if
// IN3を読み、SW10の状態を調べる
if(digitalRead(IN3)==LOW) {
// SW10がONの時の処理
} // if
// IN4を読み、SW14の状態を調べる
if(digitalRead(IN4)==LOW) {
// SW14がONの時の処理
} // if
// SW3、SW7、SW11、SW15を読むために、OUT3をL、OUT1~2とOUT4をハイインピーダンスにする。
pinMode(OUT2,INPUT ); // OUT2をハイインピーダンスにする。
pinMode(OUT3,OUTPUT); // OUT3を出力にする。
digitalWrite(OUT3,LOW); // OUT3にLを出力する。
delay(1); // 信号線の状態が安定するのを待つ
// IN1を読み、SW3の状態を調べる
if(digitalRead(IN1)==LOW) {
// SW3がONの時の処理
} // if
// IN2を読み、SW7の状態を調べる
if(digitalRead(IN2)==LOW) {
// SW7がONの時の処理
} // if
// IN3を読み、SW11の状態を調べる
if(digitalRead(IN3)==LOW) {
// SW11がONの時の処理
} // if
// IN4を読み、SW15の状態を調べる
if(digitalRead(IN4)==LOW) {
// SW15がONの時の処理
} // if
// SW4、SW8、SW12、SW16を読むために、OUT4をL、OUT1~3をハイインピーダンスにする。
pinMode(OUT3,INPUT ); // OUT3をハイインピーダンスにする。
pinMode(OUT4,OUTPUT); // OUT4を出力にする。
digitalWrite(OUT4,LOW); // OUT4にLを出力する。
delay(1); // 信号線の状態が安定するのを待つ
// IN1を読み、SW4の状態を調べる
if(digitalRead(IN1)==LOW) {
// SW4がONの時の処理
} // if
// IN2を読み、SW8の状態を調べる
if(digitalRead(IN2)==LOW) {
// SW8がONの時の処理
} // if
// IN3を読み、SW12の状態を調べる
if(digitalRead(IN3)==LOW) {
// SW12がONの時の処理
} // if
// IN4を読み、SW16の状態を調べる
if(digitalRead(IN4)==LOW) {
// SW16がONの時の処理
} // if
}
この様に、まともにキーマトリックス方式でスイッチ状態を読み取ろうとすると、長いプログラムになってしまいます。そこで、通常はスイッチの読み取り処理を関数の形でまとめてしまいます。Arduinoを使う場合は、Arduino PlaygroundのThe Matrix Keypad how-to(英文)で紹介されているKeypadライブラリを使えば、スイッチを読み取る関数を自作する必要もありません。
このページでは、市販の電卓の場合、キーマトリックス方式によりボタンの状態を読み取る事が多い事を説明しました。次のページでは、キーマトリックス方式よりも少ないI/Oピンでボタンの状態を読み取れる、抵抗分圧方式について説明し、実際に抵抗分圧方式のキーパッドの設計と製作を行います。
商品名 | I/Oピン一つで読める4X5キーパッドキット | |
税抜き小売価格 | 2400円 | |
販売店 | スイッチサイエンス | |
サポートページ | I/Oピン一つで読める4X5キーパッドキットサポートページ |
商品名 | Arduino用ブートローダ/スケッチライタキット | |
税抜き小売価格 | 3000円 | |
販売店 | スイッチサイエンス | |
サポートページ | Arduino用ブートローダ/スケッチライタキットサポートページ |