So-net無料ブログ作成

踏切カンカン [Arduino]

LED 2個を交互に点灯して「踏切チカチカ」にするのはビギナー過ぎるのですが、音をならして「踏切カンカン」にしようとすると、とたんにレベルが上がるような気がします。
fk_real.jpg

Arduino playground - PCMAudio
http://www.arduino.cc/playground/Code/PCMAudio

ここを参考にすると、音声を再生できそうですが、このソースを見てもさっぱり意味がわかりません。

ただ、原理としては、通常のPWMは490Hzくらい(普通に聞こえる音になってしまう、、)なのですが、これを超高速PWMにして、RC回路を使い、PWMの高周波成分をローパスフィルタで除去かつ波形をなめらかにすることで音声波形を出力できるようです。さらにはスピーカ自体が適当な時定数を持っているため、この回路すら必要ないらしい。
(エレキジャックBASIC No.1 Sept.2010 p.205- の記事を参考にしました。)

技術のない部分は、アイデアと力技で補完します。

まずは、踏切の音の解析をしてみました。
fk_wave.png

なんだか複雑そうな波形です。
しかし、よく見てみると、なんとなく鋸歯状の波形で、数100~1000Hzくらい、それが次第に減衰していく、これを繰り返すというように簡略化して考えればよさそう。

超高速PWMも自前で作成すればなんとかなりそうです。

// 踏切くん
byte sigLED = 0;      // LEDの状態

void setup() {
  DDRD |= B00000100;       // ピン2を出力に設定
  pinMode(12, OUTPUT);     // LED
  pinMode(13, OUTPUT);     // LED
}

void loop() {
  unsigned int t;
  byte  pwm, rep, smp;

  digitalWrite( 12, sigLED );           // LEDを交互に光らせる
  digitalWrite( 13, sigLED = 1 - sigLED );
  
  for(t = 1; t < 750; t++) {            // 音を鳴らす
    smp = (t % 5) * 4 * (1000 - t) / 1000;  // 波形を作る
    for(rep = 0; rep < 100; rep++) {          // 高速PWMで擬似アナログ出力
      PORTD |= B00000100;                       // ピン2をHIGH
      for(pwm =   0; pwm < smp; pwm++)  asm("nop");
      PORTD ^= B00000100;                       // ピン2をLOW (反転)
      for(pwm = smp; pwm <  16; pwm++)  asm("nop");
    }
  }
}

こんな感じ。
波形の擬似サンプリングの作成の部分、
  (t % 5) * 4  の部分で 0~16まで(0, 4, 8, 12, 16)の鋸歯状波をつくる
  (1000 - t) / 1000  の部分で tが1~750まで変動することで99.9%→25%となり、この倍率をかけて減衰させることで、擬似サンプリングは 0~15までの値をとることになります。
PWMも0~15までの16段階で出力します。

このスケッチで使った値は、いろいろ試行錯誤し、手探りでなんとなく踏切っぽく聞こえる音になった値で、根拠のある値ではありません。



圧電サウンダの代わりに、スピーカにしてみたらすこし音質もあがりました。
speaker.jpg



エレキジャックベーシック 2010年 09月号 [雑誌]

エレキジャックベーシック 2010年 09月号 [雑誌]

  • 作者:
  • 出版社/メーカー: CQ出版
  • 発売日: 2010/08/25
  • メディア: 雑誌


カエル 4匹 [Arduino]

前回のスケッチをいじって、圧電サウンダを4つ使い本当に輪唱にしてみました。
kaeru4.jpg

tone()では和音ができないようなので、1:1 dutyの矩形波を4つ自作しました。
digitalWrite()は処理に時間がかかるので、ポートを直接操作することにしました。
また今回分かったことですが、intではなく、unsigned intにすることでも高速化できました。

// カエルの歌 (4つの圧電サウンダ使用バージョン)
#define Do 262
#define Re 294
#define Mi 330
#define Fa 349
#define So 392
#define La 440
#define Ti 494

int kaeru[] = {
  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 };
unsigned int kaeruT[128][4];          // 一音ごとの周期 T/10000
unsigned int t, part, i, bzData;

void setup() {
  for (t = 0; t < 128; t++) {
    for(part = 0; part < 4; part++) {
      kaeruT[t][part] = 65535;        // 無音で初期化、無音のときは周期は無限大(≒ 65535)
      if( (t/16-part+4)/4 == 1 ) {    // パートの演奏時間帯の場合
        if( kaeru[t-part*16] ) kaeruT[t][part] = 10000 / kaeru[t-part*16]; // 音があれば周期を指定
      }
    }
  }
  DDRD = DDRD | B11111100;            // ピン2~7を出力に設定
}

void loop() {
  for (t = 0; t < 128; t++) {
    for(i = 0; i < 5000; i++) {
      bzData = 0;                     // 出力するデータ初期化
      for(part = 0; part < 4; part++) {
        if( i % kaeruT[t][part] > kaeruT[t][part]/2 ) bzData |= 1 << part;
      }
      PORTD = bzData << 2;            // ピン2~5 に出力
    }
  }
}

周期T(kaeruT)は、10000 を周波数で割った値にしましたが、10000には根拠はありません。なんとなくきれいに聞こえた値です。(音程の周波数は適当です)


タグ:カエル

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