Countdown Timer

カウントダウンタイマー

単純なタイマーを自力で考えてみた。
スタートボタンやアラームはなく、初期値から1秒ずつ減っていくだけ。
Arduinoボードには何もつながずに、シリアルモニタで数字を確認する。

・99分99秒が最大値
・1、3、4桁目は、0から9まで表示
・2桁目(10秒)は、0から5まで表示
・カウントをスタートすると、数字が1ずつ減る
・0の次は9(または5)に戻り、直後に左の桁から1を引く
・00:00になったら、LEDを点灯させる

//初期値を0111(1分11秒)にする
int digit1 = 0;
int digit2 = 1;
int digit3 = 1;
int digit4 = 1; void setup(){
Serial. begin(9600);
pinMode(13,OUTPUT);
} void loop(){
if( (digit1 == 0) && (digit2 == 0) && (digit3 == 0) && (digit4 == 0) ){ //0000なら
digitalWrite(13,HIGH);//内臓LEDを点灯
delay(10000);//10秒後
digitalWrite(13,LOW);//内臓LEDを消灯
while(1){}//loopを終了
}

else{//0000でないなら

if( (digit1 == 0) && (digit2 == 0) && (digit3 == 0) ){--digit4;}//000xなら、-1
else if(digit4 == 0){digit4 = 9;} //000x以外で、1の桁が0になったら9にする
else{--digit4;} //1の桁が0でないなら、-1

if(digit4 == 9){//一桁(秒)が9になったら
if( (digit1 == 0) && (digit2 == 0) && (digit3 == 0) ){ //000xなら、0を固定
digit1 = 0;
digit2 = 0;
digit3 = 0;
}
else if( (digit1 == 0) && (digit2 == 0) ){--digit3;}//00xxなら、十桁を-1
else if(digit3 == 0){digit3 = 5;}//00xx以外で、十桁が0になったら5にする
else{--digit3;}//十桁が0でないなら、十桁を-1
}

if( (digit3 == 5) && (digit4 == 9) ){//xx59(59秒)になったら
if( (digit1 == 0) && (digit2 == 0) ){ //0059なら、0を固定
digit1 = 0;
digit2 = 0;
}
else if(digit1 == 0){--digit2;}//0059以外で、0x59なら、百桁を1マイナス
else if(digit2 == 0){digit2 = 9;}//0059以外で、百桁が0になったら9に
else{--digit2;}//それ以外、百桁を1マイナス
}

if( (digit2 == 9) && (digit3 == 5) && (digit4 == 9) ){//x959の場合
if(digit1 != 0){--digit1;}//千桁が0でなければ、千桁を1マイナス
}
Serial.print(digit1);
Serial.print(digit2);
Serial.print(digit3);
Serial.println(digit4);//printlnは改行を加える
delay(1000);
}
}

4-digit LED

4桁のLEDを使って、タイマーを作りたい。
まずは4桁LEDの使い方を確認した。

 

1.4桁LEDの接続

4桁LEDは上下に6つずつのピンが付いている。
下段の左から右に1-6、上段の右から左に7-12と番号が付いている。

f:id:spinspine:20170131211205p:plain


12、9、8、6番は4桁それぞれのスイッチになっている。

f:id:spinspine:20170131211324p:plain

番号に対応したセグメントが光る。

f:id:spinspine:20170131211347p:plain

一桁の時はカソードピンがあって、GNDに繋いだのだが、4桁にはそれがない。
12,9,8,6の桁表示用のピンをカソードとして使うってこと?

5461AS(4桁LED)のデータシート
http://www.topliteusa.com/uploadfile/2014/0825/A-5461AS.pdf

5161AS(1桁LED)のデータシート
http://www.dipmicro.com/?datasheet=TOS-5161AS.pdf

7セグメントLEDにはコモンカソードのものとコモンアノードのものがあり、多くは品番に「C」とか「A」とか入ってて見分けられるらしいが、上の二つは「A」があるけどカソードということでいいらしい。
http://thomas.bibby.ie/using-the-kyx-5461as-4-digit-7-segment-led-display-with-arduino/

2.電流の計算

接続の仕方をネットで検索すると、そのまま接続するとボードが壊れてしまうので、トランジスタを入れた方がいいらしい。しかし自分にはこの話がよく分からなかったので整理したい。

4桁LEDの順電流はセグメントあたり30mAとある。
(1桁の7セグメントLEDは各セグメントあたり20mAだった)

Arduino UNOは1ピンあたり20mAしか流せない。
スペック通りに考えれば、合計で30×8セグメント=240mAの電流を供給する必要があり、力不足になってしまう。
しかしI/Oピンを各セグメントに接続すると、20×8ピン=160mAはあるので、まあ大丈夫な気がする。暗くなるだけじゃないの。

一方で、Arduinoボードに規定以上の電流を流すと壊れてしまう。
アナログ/デジタルピンなどのI/Oピンの許容は最大40mA、VCCとGNDピンは最大200mAとなっている。
http://playground.arduino.cc/Main/ArduinoPinCurrentLimitations

なのでそもそも、240mAどころか160mAをそのままピンにつないでもArduinoボードが壊れてしまう可能性がある。
カソードになるI/Oピンに流れる電流を40mAに抑えるには、40÷8セグメント=5mAになるように抵抗を入れる必要があり、5V÷0.005A=1000Ωを各出力ピンに挟めばいい。

こうすればトランジスタは必要ないが、電流量は小さいのでLEDはさらに暗くなる。
LEDをもっと明るくする場合は抵抗を低くしてピンあたりの電流を増やす必要があり、するとピンの許容を超える電流を流すことになるので、それを保護するためにトランジスタを入れるということらしい。

f:id:spinspine:20170208213043p:plain

ダイナミック点灯とは、複数のLEDを同時に光らせるのではなく、高速に切り替えることによって同時に光っているように見せる方法だ。

よって一瞬で考えれば一桁ずつしか光らないので、四桁の場合でも一桁分(40mA)だけ供給すればいい(たぶん)。

 

3.実際に点灯させてみる

よく分からないのはカソードの扱いだが、とりあえずArduino UNOの1-8のI/OピンをセグメントA-Gに出力、9-12のI/Oピンを桁表示(12,9,8,6)の端子に接続して出力(!)する下記のコードを実行。※テストのため、セグメントはBとCだけ

youtu.be

const int digit_1 = 9;//9番ピンを1桁目に
const int digit_2 = 10;//10番ピンを2桁目に
const int digit_3 = 11;//11番ピンを3桁目に
const int digit_4 = 12;//12番ピンを4桁目に
const int LED_B = 2;//2番ピンをセグメントBに
const int LED_C = 3;//3番ピンをセグメントCに
void setup(){ //全てのピンを出力に設定 pinMode(digit_1, OUTPUT);
pinMode(digit_2, OUTPUT);
pinMode(digit_3, OUTPUT);
pinMode(digit_4, OUTPUT);
pinMode(LED_B, OUTPUT);
pinMode(LED_C, OUTPUT);
} void loop(){
digitalWrite(digit_1, HIGH);//LEDをオフにする
digitalWrite(digit_2, HIGH);
digitalWrite(digit_3, HIGH);
digitalWrite(digit_4, HIGH);
delay(1000);
digitalWrite(digit_1, LOW);//千の桁に1を表示
digitalWrite(LED_B, HIGH);
digitalWrite(LED_C, HIGH);
delay(1000);
digitalWrite(digit_2, LOW);//百の桁に1を表示
digitalWrite(LED_B, HIGH);
digitalWrite(LED_C, HIGH);
delay(1000);
digitalWrite(digit_3, LOW);//十の桁に1を表示
digitalWrite(LED_B, HIGH);
digitalWrite(LED_C, HIGH);
delay(1000);
digitalWrite(digit_4, LOW);//一の桁に1を表示
digitalWrite(LED_B, HIGH);
digitalWrite(LED_C, HIGH);
delay(1000);
}

つまりArduinoのI/Oピンは5Vを出力するだけでなく、オンオフ切替可能なGNDにもなるということか。二つのI/Oピンを繋いで、どちらも「HIGH」(あるいはどちらも「LOW」)の時は双方の電圧に差が生じないので電流は流れず、どちらかが「LOW」(あるいは「HIGH」)になれば流れるようになる。

ということは、上のコードの「LOW」と「HIGH」を全て逆にしても電流は流れるはずだが、LEDはダイオードなので一方向にしか電流を流さない。

Power Supply for Arduino

Arduinoの電源はUSBから供給される。


パソコンと離して使う場合は、ACアダプターを使うこともできる。
・出力電圧が9Vか12V
・定格電流が250mA以上
・センタープラス

http://playground.arduino.cc/Learning/WhatAdapter

 

または下記のようなアダプターを使って、9Vの電池を使うことも出来る。

http://playground.arduino.cc/Learning/9VBatteryAdapter

Voltage, Current, Resistance

電圧と電流と抵抗の関係は三角形で表せるので、V=IRという式だけ覚えておけば、他の式も思い出しやすい。
V = I × R
I = V / R
R = V / I

f:id:spinspine:20170201091244p:plain

Ohm's law - Wikipedia

 

直列回路の場合は抵抗がいくつあっても、単純に足して計算すれば電流がわかり、どこでも同じ量の電流が流れている。


並列回路の場合は分岐したところで電流を分け合うが、抵抗が大きい路に流れる電流は小さくなる。逆にいえば、分岐した路には等しく電圧がかかっていることになる。

f:id:spinspine:20170201095143g:plain

キルヒホッフの法則 ■わかりやすい高校物理の部屋■

電圧は高い方から低い方へ向かって電流が流れる。
よく分からないのが、直列回路で複数の抵抗を繋いだ場合、抵抗によって電圧が上下に変化すると思うのだが、それでもいいのだろうか?電流は高い方から低い方へ流れるだけじゃないの?

Push Button Counter

ボタンを押すごとに、数値が1つ増えていくカウンター。
1.変数を使って、現在の値をArduinoに記憶しておく
2.その値をLEDに出力する

1.
まずはLEDを接続しないで、ボタンを押した回数をArduinoに記憶させる。
シリアルモニターに値を表示させて、記述が正しいかチェックする。

const int button = 12; //12ピンにボタンを接続
int val = 0;
int old_val = 0;
int state = 0;

void setup(){
pinMode(button, INPUT);
Serial. begin(9600);
}

void loop(){
val = digitalRead(button);
if( (val == HIGH) && (old_val == LOW) ) { //old_valがLOWの時に、ボタン押したら、stateに1を加える。HIGHの時はボタンを押しても実行されない。これによりstateに連続して書き込まれるのを防ぐ。
if(state == 9){state = 0;} else{state++;} //9まで進んだら0に戻る
Serial.println(state);
delay(300); //ボタンのバウンジング防止
}
old_val = val; //スイッチが押されると、valはhighになり、上のif文は実行される。その直後old_valもhighになり、if文の実行が止まる。ボタンを離すとLOWに戻る。
}

 

 

2.
カウント値をLEDに表示させるために、数値を定義する。

void display_0(){         //display_0()で0を定義
digitalWrite(2, HIGH);
digitalWrite(3, HIGH);
digitalWrite(4, HIGH);
digitalWrite(5, HIGH);
digitalWrite(6, HIGH);
digitalWrite(7, HIGH);
digitalWrite(8, LOW);
}
void display_1(){         //display_1()で1を定義
digitalWrite(2, LOW);
digitalWrite(3, HIGH);
digitalWrite(4, HIGH);
digitalWrite(5, LOW);
digitalWrite(6, LOW);
digitalWrite(7, LOW);
digitalWrite(8, LOW);
}

void setup(){
for(int = i=1, i<=8, i++){        //for文を使って、1-8のピンをまとめて指定
pinMode(i, OUTPUT);
}

void loop(){    //stateが*なら、display_*を表示
if(state == 0){
display_0();}
else if(state == 1){
display_1();}
}

array

arrayはデータ型の一つで、変数を集めて配列を作成します。
変数は { } に , で区切って配列します。[ ]は配列の数を初期化します。

int myArray[10]={9,3,2,4,3,2,7,8,9,11};


インデックス([0]からはじまる整数、[9]なら配列の10番目を指す)で、変数を呼び出すことが出来る。

     // myArray[9]    contains 11
     // myArray[10]   is invalid and contains random information (other memory address)  

 

 

forループと組み合わせて使われることが多い。https://www.arduino.cc/en/Tutorial/Arrays

int timer = 100; // The higher the number, the slower the timing. 
int ledPins[] = { 2, 7, 4, 6, 5, 3
}; // an array of pin numbers to which LEDs are attached
int pinCount = 6; // the number of pins (i.e. the length of the array)

void setup() {
// the array elements are numbered from 0 to (pinCount - 1).
// use a for loop to initialize each pin as an output:
for (int thisPin = 0; thisPin < pinCount; thisPin++) {
pinMode(ledPins[thisPin], OUTPUT); } }

void loop() {
// loop from the lowest pin to the highest:
for (int thisPin = 0; thisPin < pinCount; thisPin++) {
// turn the pin on:
digitalWrite(ledPins[thisPin], HIGH); delay(timer); // turn the pin off: digitalWrite(ledPins[thisPin], LOW); } // loop from the highest pin to the lowest:
for (int thisPin = pinCount - 1; thisPin >= 0; thisPin--) {
// turn the pin on: digitalWrite(ledPins[thisPin], HIGH); delay(timer); // turn the pin off: digitalWrite(ledPins[thisPin], LOW); } }

switch case

switch caseはswith()の式がcaseの定数と合致した場合に実行される。どのcaseとも一致しない場合は、defaultが実行される。
if elseと似ているが、ifの場合はboolean型で、比較演算子を使った式の結果をTRUEかFALSEで返すのに対し、switch caseは定数と一致した場合のみ実行される。

Example

switch (var) {
    case 1:
      //do something when var equals 1
      break;
    case 2:
      //do something when var equals 2
      break;
    default:
      // if nothing else matches, do the default
      // default is optional
    break;
  }

チュートリアル1 https://www.arduino.cc/en/Tutorial/SwitchCase

チュートリアル2 https://www.arduino.cc/en/Tutorial/SwitchCase2