言語ゲーム

とあるエンジニアが嘘ばかり書く日記

Twitter: @propella

Arduino で音を鳴らす

とある締め切りがあるので現実逃避しております。

いままで人の作ったプログラムばかり動かしてきたので、自分でも何かやってみます。Arduino で音をだしてみます。探すと、http://arduino.cc/en/Tutorial/Melody にサンプルがありました。9 番と GND に写真にあるようなブザーを付けると音が鳴ります。このプログラムも十分ややこしいので、そこから最低限の物だけ抜き出すと以下のようになりました。440Hz の音を出すプログラムです。かなりうるさくてイライラします。440Hz ぶんの 2272 us を HIGH と LOW の分二で割った時間ウエイトするだけです。

int speakerPin = 9;
int pitch = 1000000 / 440; /* = 2272 us */

void setup() {
  pinMode(speakerPin, OUTPUT);
}

void loop() {
  digitalWrite(speakerPin, HIGH);
  delayMicroseconds(pitch / 2);
  digitalWrite(speakerPin, LOW);
  delayMicroseconds(pitch / 2);
}

しかしこのイライラする音はやってられません。delayMicroseconds の精度がイマイチなんじゃ無いかなと疑ってみます。タイミングを測るには、上のように delay を使う他、タイマを使った方法もあるようです。Arduino Interrupts http://www.uchobby.com/index.php/2007/11/24/arduino-interrupts/ によると、タイマというのはクロックと共に数字が増えてゆくカウンタで、数字が増えすぎて溢れると割り込みが起こるので、その瞬間面白い事をするとある一定時間置きに面白い事が出来る仕組みです。クロックをそのまま使うと速くて大変ですが、プリスケールという仕組みを使うとゆっくり数えます。例えば

  • Arduino のクロックは 16MHz なので
  • 256 のプリスケールなら、16000000 / 256 = 62500 Hz
  • つまり 1秒間に 62500 回カウントする。
  • 440Hz の一波長の間には 62500 / 440 = 142 回数える。
  • 実際にはその間に HIGH の時と LOW の時があるので、71 回毎に信号を入れ替えると 440Hz の音が鳴る。

という事で、71 なら 8 ビットの範囲に収まるので 8 ビットのタイマ2が使えます。

#include <avr/interrupt.h>
#include <avr/io.h>

int speakerPin = 9;
int frequency = 440;
unsigned char halfInterval = F_CPU / 256 / frequency / 2;
unsigned char timerLoadValue = 256 - halfInterval;
char signal = 0;

void setup() {
  pinMode(speakerPin, OUTPUT);
  TCCR2A = 0; // Normal Timer
  TCCR2B = 1<<CS22 | 1<<CS21 | 0<<CS20; // clk/256 prescale
  TIMSK2 = 1<<TOIE2; // Timer/Counter2 Overflow Interrupt Enable
  TCNT2 = timerLoadValue;
}

ISR(TIMER2_OVF_vect) { // Timer/Counter2 Overflow
  TCNT2 = timerLoadValue; // Reset the timer
  digitalWrite(speakerPin, signal); // Output signal
  signal = !signal;
}

void loop() {}

うーむ。さっきよりマシという程度か。。。include 文は不要ですが、かっこいいので残したままにしています。ここで難しいのは setup の所で、タイマ2にノーマル設定とプレスケールについて伝えて、TOIE2 で溢れた時に割り込みが発生するようにしています。 TCCR2A や TOIE2 といった謎めいた定数はデータシートに説明が載っています。

最終的に音の高さは timerLoadValue で決まります。440Hz の例では、71 回数えれば良かったので最初タイマに 256 - 71 を設定します。そして 71 回数えて 256 になると溢れて割り込みが発生し ISR(TIMER2_OVF_vect) が起動するという仕組みです。ではでは。