モールス信号シールドでC言語プログラミング入門 (2)

第2回は「変数」と「繰り返し処理」を扱います。第1回ではLチカ(LEDの点灯)のコントロールを通じて「定数」を学びました。定数を定義した時の初期値を調節することで、LEDの点灯をゆっくりにしたり、フラッシュライトのようにしたりすることができることを確認しました。今回は、このパターンのバリエーションを増やしてみます。そして、最後に音を出すことを試します。

前回の振り返りから

まず、前回のスケッチを振り返ってみましょう。こうすれば、200ms間隔で点滅を繰り返します。これは、loop()関数は何度でも呼び出されるからでした。

#define LED_PIN 13
const int duration_on_off = 200;
void setup() {
  pinMode(LED_PIN, OUTPUT); // LEDが繋がっている13番ピンを出力モードに設定
}
void loop() {
  digitalWrite(LED_PIN, HIGH); // 13番ピンを5VにしてLEDを点灯
  delay(duration_on_off); // 点灯を保持
  digitalWrite(LED_PIN, LOW); // 13番ピンを0VにしてLEDを消灯
  delay(duration_on_off); // 消灯を保持
}

点灯にリズムを付けてみる

それでは、短い点灯と長い点灯を繰り返すにはどうしたらいいでしょうか。スケッチを少し修正して実行してみましょう。スケッチをArduino Unoへの書き込む方法は第1回の記事を参照してください。実行すると点灯パターンがアルファベットの’A’のモールス符号のようにトツー、トツーと変化するようになったはずです。

#define LED_PIN 13
int duration_on_off = 200;
void setup() {
  pinMode(LED_PIN, OUTPUT); // LEDが繋がっている13番ピンを出力モードに設定
}
void loop() {
  duration_on_off = 200;
  digitalWrite(LED_PIN, HIGH); // 13番ピンを5VにしてLEDを点灯
  delay(duration_on_off); // 点灯を保持
  digitalWrite(LED_PIN, LOW); // 13番ピンを0VにしてLEDを消灯
  delay(duration_on_off); // 消灯を保持
  duration_on_off = 1000;
  digitalWrite(LED_PIN, HIGH); // 13番ピンを5VにしてLEDを点灯
  delay(duration_on_off); // 点灯を保持
  digitalWrite(LED_PIN, LOW); // 13番ピンを0VにしてLEDを消灯
  delay(duration_on_off); // 消灯を保持
}

2行目の文から「const」を取って、「int duration_on_off = 200;」としました。こうすると「duration_on_off」は整数(int)型の変数として扱われ、その後、任意の整数を代入することができます。9行目、14行目でそれぞれ200と1000を代入することで、短い点滅と長い点滅を実現しています。

もし、constを付けたままにしてコンパイルするとスケッチの9行目で下記のエラーが出るはずです。

assignment of read-only variable ‘duration_on_off’

constが付いていると「duration_on_off」は整数(int)型の定数として扱われ、最初に200で初期化されていますので、その後、別の値を代入することはできません。

繰り返し処理を施してみる

続いて、LEDの短い点滅を5回ずつ繰り返すにはどうしたらいいでしょうか。5回の点滅ごとに小休止を入れるためには、5回の点滅を1つのブロックとして扱ってやれば良さそうです。1つのブロックが終了するたびに小休止を入れます。そのスケッチは下記のようになります。

#define LED_PIN 13
int duration_on_off = 200; // 点滅の時間(ms)
int duration_interval = 1000; // 小休止の時間(ms)
void setup() {
  pinMode(LED_PIN, OUTPUT); // LEDが繋がっている13番ピンを出力モードに設定
}
void loop() {
  for (int i = 0; i < 5; i++) {
    digitalWrite(LED_PIN, HIGH); // 13番ピンを5VにしてLEDを点灯
    delay(duration_on_off); // 点灯を保持
    digitalWrite(LED_PIN, LOW); // 13番ピンを0VにしてLEDを消灯
    delay(duration_on_off); // 消灯を保持
  }
  delay(duration_interval); // 小休止
}

「duration_interval」は小休止の時間(ms)を保持しておくための整数(int)型の変数です。この変数はloop()関数の中で使います。

loop()関数の中に「for (int i = 0; i < 5; i++) {…}」というブロックがあります。ここが、5回の点滅を実行する部分です。これは「for文」といって繰り返し処理をします。for文には3つのパラメータがあります。それぞれ、初期化式、条件式、変化式と呼ばれます。

初期化式は、for文を開始する際に最初に1度だけ実行されます。ここでは、整数(int)型の変数iを0で初期化しています。

条件式は、for文でいつまで繰り返しを継続するのかを制御するために使われ、この条件が成り立つ間だけ処理を繰り返します。ここでは、変数iが5未満であれば繰り返し条件が成り立つので、処理を継続します。

変化式は、for文を繰り返すするごとに実行されます。ここでは、iの値を1だけ加算しています。「i++」という書き方は良く使われるのですが、「i = i + 1」を省略したものでどちらも同じ意味です。

そして、for文のブロックの後に「delay(duration_interval);」とすることで、5回の点滅の後に小休止(1000ms)していたというわけです。

同じ方法でブザーを鳴らせる?

ここまで、LEDを点灯させてきましたが、ブザーを鳴らすにはどうすれば良いでしょうか。シールドの回路図を見ると9番ピンにブザーが接続されていることが分かります。

#define BZ_PIN 9 // ブザーは9番ピンに接続されている
int duration_on_off = 2; // 発振の時間(ms)
int duration_interval = 1000; // 小休止の時間(ms)
void setup() {
  pinMode(BZ_PIN, OUTPUT); // ブザーが繋がっている9番ピンを出力モードに設定
}
void loop() {
  for (int i = 0; i < 100; i++) {
    digitalWrite(BZ_PIN, HIGH); // 9番ピンを5Vにする
    delay(duration_on_off); // オンを保持
    digitalWrite(BZ_PIN, LOW); // 9番ピンを0Vにする
    delay(duration_on_off); // オフを保持
  }
  delay(duration_interval); // 小休止
}

音が鳴りましたね。しかし、鳴るには鳴りましたが、変数「duration_on_off」の値を1にしたところで500Hzの高さの音までしか扱えません。もっと細かくオンオフを制御できないのでしょうか。

別の方法があります。delay()関数は指定された時間(ms)だけ一時停止しますが、delayMicroseconds()関数は指定された時間(μ秒)だけ一時停止させることができます。

時報のメロディーを鳴らしてみる

では、時報のプ、プ、プ、ポーンを実現してみましょう。プは440Hz、ポーンは880Hzですね。次のようなスケッチになります。

#define BZ_PIN 9 // ブザーは9番ピンに接続されている
const int duration = 568; // 880Hz
void setup() {
  pinMode(BZ_PIN, OUTPUT); // ブザーが繋がっている9番ピンを出力モードに設定
  for (int i = 0; i < 3; i++) { // プ、プ、プ
    for (int j = 0; j < 100; j++) {
      digitalWrite(BZ_PIN, HIGH); // 9番ピンを5Vにする
      delayMicroseconds(duration * 2); // 440Hz
      digitalWrite(BZ_PIN, LOW); // 9番ピンを0Vにする
      delayMicroseconds(duration * 2); // 440Hz
    }
    delay(500); // 小休止
  }
  for (int i = 0; i < 600; i++) { // ポーン
    digitalWrite(BZ_PIN, HIGH); // 9番ピンを5Vにする
    delayMicroseconds(duration); // 880Hz
    digitalWrite(BZ_PIN, LOW); // 9番ピンを0Vにする
    delayMicroseconds(duration); // 880Hz
  }
}
void loop() { // 繰り返し処理は無し
}

よく見るとfor文が3つあり、最初の2つのfor文はネスト(入れ子)構造になっています。for文をネストすると、繰り返しの繰り返しとなります。「for (int j = 0; j < 100; j++)」で1つの「プ」の音を作っていますが、これを「プ、プ、プ」にするために外側にある「for (int i = 0; i < 3; i++)」で3回繰り返しているのです。3つ目のfor文は「ポーン」の音を作っています。

また、loop()関数は空にしました。何度も繰り返し時報が鳴ると変なので。

さて、ここまでdelay()関数、delayMicroseconds()関数を使ってブザーを制御してきました。確かに音は鳴るものの、あまり褒められたスケッチではありません。理由はdelay()関数が呼ばれている間はコンピュータの処理が止まってしまっていて、他の処理を実行することができません。

次回はブザーを鳴動するためのもっとスマートな方法を扱います。tone()関数とnoTone()関数という、PWM制御をしてくれる便利な関数を使います。

コメント