Arduinoを使った電卓の製作(1)

このページをスマホなどでご覧になる場合は、画面を横長にする方が読みやすくなります。
目次へ  前のページへ (1) (2) (3) (4) (5) (6) (7) 次のページへ
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ページ目からご覧ください。

目次

1. はじめに … 1ページ
2. 電卓に必要なスイッチの数を考える … 1ページ
3. 電卓にはキーマトリックス方式が使われる場合が多い … 1ページ
4. 今回の電卓で使う抵抗分圧方式のキーパッド … 2ページ
5. 抵抗分圧方式のキーパッドの設計 … 2ページ
6. 抵抗分圧方式のキーパッドの製作 … 2ページ
7. ResKeypadライブラリのインストール … 3ページ
8. 製作したキーパッドの動作確認 … 3ページ
9. 電卓の製作 … 4ページ
9-1. 電卓のハードウェア … 4ページ
9-2. 電卓のソフトウェア … 4ページ
10. 製作した電卓の使い方と注意点 … 5ページ
10-1. 製作した電卓で扱える桁数 … 5ページ
10-2. 各ボタンの意味や働き … 5ページ
10-3. 製作した電卓の使い方と履歴表示機能 … 5ページ
10-4. 全桁訂正機能と1桁訂正機能 … 5ページ
10-5. エラーの発生と復帰 … 5ページ
10-6. 計算精度について … 5ページ
11. 電卓スケッチの解説 … 6ページ
11-1. 数値を表わす文字列からfloat型への間の変換 … 6ページ
11-2. float型数値から文字列への変換 … 6ページ
11-3. 画面表示 … 6ページ
11-4. 主なグローバル変数 … 6ページ
11-5. loop関数の動作 … 6ページ
12. 回路のアレンジの例 … 7ページ
13. 最後に … 7ページ

1.はじめに

どこの家にでもあり、ちょっとがんばれば自作できそうで、実際にはなかなか自作できない電子機器といえば、電卓がその代表例になるでしょう。普段、よく使う機器なので、自作できれば感激が大きいかも知れません。そういうわけで、今回はArduinoを使って電卓を作ってみます。

最近では100円ショップでも普通に電卓を売っていますが、さすがに100円では電卓を自作できません。電卓を自作する意味は、経済的な意味ではなく、身近にある製品の仕組みの理解というところにあります。

さて、電卓を自作するにあたって、問題になりそうな点が2つあります。

プログラムに手間がかかる点は、勉強を兼ねて、がんばってやるしかないのですが、スイッチをたくさん使う点については、ハードウェアの構成上、何らかの工夫をする必要があります。

2.電卓に必要なスイッチの数を考える

ここで、必要最低限の機能を持った電卓に、いくつのスイッチ(ボタン)が必要か考えてみましょう。電源ボタンを除いて考えると、電卓に必要なボタンは、図1の様な感じになるのではないでしょうか。

図1、必要最低限の機能の電卓のイメージ
図1、必要最低限の機能の電卓のイメージ

図1の電卓には、19個ものボタンが付いています。市販されている電卓では、ルート計算用のボタンとか、メモリ機能用のボタンとかが付いており、さらに多いボタンが付いています。

ところで、スイッチ(ボタン)の状態をArduinoに読み込ませるには、図2の様に、I/Oピンにスイッチおよびプルアップ抵抗をつないで、リスト1の様にdigitalRead関数で読み出すのが一般的です。

図2、プルアップ抵抗を用いたスイッチの状態の読み取り
図2、プルアップ抵抗を用いたスイッチの状態の読み取り
リスト1、図2の回路のスイッチ読のみ取り用スケッチCOPY
#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を使わなければなりません。

広告

3.電卓にはキーマトリックス方式が使われる場合が多い

市販されている電卓では、図2の様な単純なスイッチ読み取り回路が使われる事はありません。マイコンや電卓専用ICのI/Oピンをたくさん使うためです。必要なI/Oピンが多いと、製造価格上昇の原因になります。

それでは、実際にどのように電卓がスイッチの状態を読み取っているかというと、図3のような、キーマトリックス方式の回路を使う場合が多いのです。

図3、キーマトリックス方式の、スイッチの読み取り回路
図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の様になります。

リスト2、キーマトリックス方式でスイッチの状態を読み取るスケッチCOPY
#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ピンでボタンの状態を読み取れる、抵抗分圧方式について説明し、実際に抵抗分圧方式のキーパッドの設計と製作を行います。

目次へ  前のページへ (1) (2) (3) (4) (5) (6) (7) 次のページへ

このページで使われている用語の解説

関連ページ

関連製品

I/Oピン一つで読める4X5キーパッドキット 商品名 I/Oピン一つで読める4X5キーパッドキット
税抜き小売価格 2400円
販売店 スイッチサイエンス
サポートページ
Arduino用ブートローダ/スケッチライタキット 商品名 Arduino用ブートローダ/スケッチライタキット
税抜き小売価格 3000円
販売店 スイッチサイエンス
サポートページ
Arduino 電子工作
このサイトの記事が本になりました。
書名:Arduino 電子工作
ISBN:978-4-7775-1941-5
工学社の書籍の内容の紹介ページ
本のカバーの写真か書名をクリックすると、Amazonの書籍購入ページに移動します。