SSブログ

踏切ガジェット [Arduino]

先日の踏切カンカンスリープモード付きを、電池ボックスケース(単3電池4本用で、2本を電池ボックスとして使用)に収めてみました。
gfk_kansei.jpg

前回のコードはボタンの判定やスリープに無駄があったのでちょっと改善してみました。
// 踏切音くん (ATtiny13, 9.2MHz), Sleep mode 付き
#include <avr/sleep.h>

volatile boolean swFlg  = 0;                       // スイッチの状態

void setup() {
  DDRB   = B11001;                                 // PB0(OC0A)をサウンド出力, PB3,PB4をLED ,
  PORTB  = B10010;                                 // PB1(INT0)をスイッチ入力 (内部PullUp有効) とする
  TCCR0A = _BV(COM0A1) | _BV(WGM01) | _BV(WGM00);  // 8bit高速PWM
  TCCR0B = _BV(CS00);                              // 分周なし
  TIMSK0 = 0;                                      // Arduino の割り込みを潰す
  attachInterrupt(0, wakeUpNow, LOW);              // use interrupt 0 (pin 2) 
}

void wakeUpNow() {       // here the interrupt is handled after wakeup
  swFlg = 1;                                       // スイッチが押されていたらフラグをたてる 
  detachInterrupt(0);                              // disables interrupt 0 on pin 2 so the
}

void sleepNow() {        // here we put the arduino to sleep
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);           // sleep mode is set here
    sleep_enable();                                // enables the sleep bit in the mcucr register
    sleep_mode();                                  // here the device is actually put to sleep!!
    sleep_disable();                               // first thing after waking from sleep:
}
  
void loop() {
  unsigned int pwmVal, t;  
  if( !swFlg ) {                                   // スイッチが押されていなければスリープモードに入る
    OCR0B = 0;                                     // 消音
    PORTB &= ~B11000;                              // LEDを消灯
    sleepNow();                                    // sleep function called here
    PORTB |=  B10000;                              // LED点灯開始
  }
  swFlg = 0;                                       // スイッチの確認フラグを降ろしておく
  attachInterrupt(0, wakeUpNow, LOW);              // use interrupt 0 (pin 2) 
  PORTB ^= B11000;                                 // LEDを交互に光らせる
  for( t = 0; t < 15000; t++ ) {                   // 音発生用のループ
    pwmVal = (t & B111111)  * (255 - (t>>6)) >> 6; // 計算式で音の波形をつくる
    while( !(TIFR0 & _BV(TOV0)) );                 // タイマーがオーバーフローするのを待つ
    TIFR0 |= _BV(TOV0);                            // Timer/Counter0 Overflow Flag をクリア
    OCR0A = pwmVal;                                // PWMの値を指定
  }
}

基板はこんな感じ
gfk_kairo.png
gfk_kiban.jpg
LEDは交互に光るので電流制限抵抗は1つでOK。ただLEDが明るすぎたので、後でもう少し抵抗値を上げました。

ケースはこんな感じで加工。
gfk_case.jpg

完成したのがコレ。

電車好きの息子のために、手作りクリスマスプレゼント。

Arduinoやブレッドボードでプロトタイピングは簡単にできても、そこから完成品を作るのはやっぱり慎重になります。
ハンダ付けしたら戻すのは一苦労。マイコンもケースに収めるためにはICソケットは使えないし。
でも完成品を作らなくては自己満足で終わってしまう、、。
来年はなるべく完成品ができるようにがんばりたいです。

AVRISP mkII を使う [ツール]

以前に買ったものの全く使っていなかった AVRISP mkII を引っ張りだしてきました。

結論から言うと、次のことに注意すればOK。

● ドライバの問題
 ・AVRISP mkII に付属のドライバはあくまで「AVR studio」用
 ・Arduinoで使うにはArduinoに付属のドライバが必要
   (arduino-1.0.x\hardware\tools\avr\utils\libusb\bin にある)
 ・両ドライバは共存できない

● VCCの扱い
 ・VCCのラインは出力ではないらしい
 ・AVRマイコンに他から電源を確保する

● スケッチ(あるいはブートローダやFuse)の書き込み
 ・「ツール」 → 「書き込み装置」 → 「AVRISP mkII」 を選択しておくだけ
 ・あとはいつも通り

avrispmkii.jpg

いままで ATtiny13, 45, 85 などで出ていた気持ち悪いメッセージ:
avrdude: please define PAGEL and BS2 signals in the configuration file for part ATtiny13
avrdude: please define PAGEL and BS2 signals in the configuration file for part ATtiny13

これも、出なくなりました。

タグ:AVR ISP

カエルの輪唱 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

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