SSブログ

カエルの輪唱 PWM [Arduino]

カエルの輪唱の波形を作り出し、PCMとして再生してみます。

SDカードのWAV再生のようにデータをそのまま再生するわけではなく、和音を計算して波形を作るので、少し計算に時間がかかるかなぁと思い、 8分周のPWM (2048 CPU cycles, 約8kHz弱) としました。

kaeru_pwm.jpg

発生する周波数を f とすると、1:1の矩形波を出したいとき、矩形のHigh/Lowが切り替わるまでにかけるループの回数を求める。
16MHz, 8分周のPWM(分周なしで256CPU cycles)の場合には
 halfLength = 16000000 / 256 / 8 / 2 / f
となる。(fが0のときは注意)
16MHz, 8分周だと、1秒間音を出すのに 16000000 / 256 / 8 = 7812.5 回のループ(Hz)が必要となる。
 if( ( i / halfLength ) % 2 ) 、、
という感じで、ループでカウンタ(i)を増やしていき、カウンタ(i)を halfLength で割ったときの偶奇で矩形のHigh/Lowを決める。
Highのときは、波形を足す(0-255までを4和音とするので、1パートあたり63を足す)
 もうすこし、計算をすると、、
 if( ( i / halfLength ) % 2 ) 、、
の部分は、
 if( ( i / (16000000 / 256 / 8 / 2 / f) ) % 2 )、、
となり
 if( ( i / ( 3906.25 / f) ) % 2 )、、
だいたい
 if( ( i / ( 4096 / f) ) % 2 )、、
となり、ビットシフトや、And演算子を使うと
 if( ( ( i * f ) >> 12 ) & 1 )、、
と、表現できた。また、これならfが0でも大丈夫。

それぞれのパート毎の音色の計算は以前のとおり

カエルの輪唱
http://hello-world.blog.so-net.ne.jp/2011-08-15

ブレッドボードが面倒だったので、ずるをしてデジタル5番をGNDとして使いました。
// カエルの歌 輪唱(PWMバージョン)
#define Do 262
#define Re 294
#define Mi 330
#define Fa 349
#define So 392
#define La 440
#define Ti 494

unsigned int kaeru[] = {                            // 8分音符(♪)ごとのデータ
 Do,  0, Re,  0, Mi,  0, Fa,  0, Mi,  0, Re,  0, Do,  0,  0,  0,
 Mi,  0, Fa,  0, So,  0, La,  0, So,  0, Fa,  0, Mi,  0,  0,  0,
 Do,  0,  0,  0, Do,  0,  0,  0, Do,  0,  0,  0, Do,  0,  0,  0,
 Do, Do, Re, Re, Mi, Mi, Fa, Fa, Mi,  0, Re,  0, Do,  0,  0,  0 };

void setup() {
  DDRD  |= B00001000;                               // Arduino pin 3 (ATmega328 PD3, OC2B)をサウンド出力
  TCCR2A = _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);   // 8bit高速PWM
  TCCR2B = _BV(CS21);                               // 8分周
  pinMode( 5, OUTPUT );  digitalWrite( 5, LOW );    // 圧電サウンダの足の都合で5番をGNDにする。
}

void loop() {
  unsigned int  t, part, f[4];
  unsigned long i;
  byte pwmVal;
  for (t = 0; t < 128; t++) {                      // 1音ずつ進めていく
    for(part = 0; part < 4; part++)  f[ part ] = ( ((t>>4)-part+4)>>2==1 ) ? kaeru[t-part*16] : 0;
    for(i = 0; i < 2000; i++) {                      // 1音にかける繰り返し回数 (3906.25で1秒にすると♪=0.5秒)
      pwmVal = 0;                                      // PWMの値を初期化
      for(part = 0; part < 4; part++)  if( ( ( i * f[ part ] ) >> 12 ) & 1 ) pwmVal += 63;
      while( !(TIFR2 & _BV(TOV2)) );                   // タイマーがオーバーフローするのを待つ
      TIFR2 |= _BV(TOV2);                              // Timer/Counter2 オーバーフロー・フラグ をクリア
      OCR2B = pwmVal;                                  // PWMの値を指定
    }
  }
}

音を発生するときの振動で接触不良となり、すこし音がかすれてしまいました。

タグ:カエル PwM

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。