チラシのうら

レゴとか、工作とか。

8pinoで圧電スピーカーを鳴らす その3

午後の部は

午前の部 で宣言した通り

PWMを使う方式で、深堀してみます。

結果から言いますと

できました!カエルの合唱(輪唱)をお楽しみ下さい。 輪唱の担当別にLEDを光らせています!

なんでしょう、この、俺はいったい何をしているんだ感は。

まぁ、まだ(あまり)誰もやっていない事だからと、胸を張ることで、自分をごまかします。

以下は、自分の備忘録的に。

音階と周波数の関係

こちらのサイトにありました。
「ドは261.6Hz」とかを学ぶ。

PWMが使えるPin

Trinketのサイト に、記載がありました。

The 3 independent IO pins have 1 analog input and 2 PWM output as well. The 2 shared IO pins have 2 more analog inputs and one more PWM output.

もっと、具体的には、Pin0, Pin1, Pin4がPWMとして使用可能なようです。 私の環境では、常にPin0をNeopixel Ringにアサインしているので、今回はPin1, Pin4を使うことにします。

PWMの周波数の変更

PWMへの周波数の指定は、0-255の整数値でしかできません。

8pinoのデフォルトの周波数は8MHzな様ですので、これを256分割したところで、ドとかレとかの数百Hzレベルの細かい周波数指定はできません。うーん。

色々調べてみると、8pinoで使われている、ATTiny85には、PWMの基準の周波数(上の8MHz)を変更できる仕組みが入っているようです。こいつを使います。 ATtiny85 Datasheet によると、Pin1に関しては、TCCR0A及び、TCCR0BのWGMにFast PWMモードを設定し、TCCR0BのCS0*で基準の周波数を変更出来ることがわかります。

f:id:tetunori_lego:20150525185315j:plain 色々、選択肢があるので、ドレミを最もよく表現してくれるオプションを探すため、下記のような表を作りました。
(OCR0AはPWMに設定する0-255の値で、
frequency = 8 MHz/xxx/(OCR0A+1) です。
) f:id:tetunori_lego:20150525185508j:plain これにより、8MHz/256 = 32KHzが一番丁度良さそうであることがわかりました。
(上の表で言うと、真ん中の列を採用するという事)

結局、下記のような表を得ます。

音階 OCR0A
118
106
94

長くなりましたが、Pin1から、PWMでドレミ波形を出すには、下記の設定が必要となります。

// Configure Registers for PWM of PB1(PIN No.1)
// 1. Waveform Generation Mode -> Fast PWM
// 2. Clock Select -> 8MHz/256 = 32KHz.
// 3. Compare Match Output B Mode
// -> Clear OC0A/OC0B on Compare Match, 
// set OC0A/OC0B at BOTTOM
TCCR0A = _BV (COM0B1) | _BV (WGM01) | _BV (WGM00);
TCCR0B = _BV (WGM02) | _BV (CS02);

やっと音が鳴るが

ド、レ、ミと鳴るコードを書いて、音が出たものの、今度は何故か、delay関数が上手く動かない。 高い音になると、delayが短くなるように聞こえる。

どうやら、delayのタイマーは、OCR0Aが絡んでいるようだ。

もう、デバッグする気力もなく、直感で

delay( xxx / OCR0A );

としたら、いい感じにwww。直感大事。 正直調べていないので、理由はわかりませんwww

というわけで、Pin1に関しては、音を出せるようになりました。

Pin4も似た感じ

Pin1との違いは、扱うレジスタが違う位です。 基準の周波数の設定はTCCR1とGTCCRで行います (勿論、Pin1と合わせて32KHzに設定します)。 また、音階の指定はOCR1Cで行います。

まとめると、Pin4から音を出すには、下記の設定が必要です。

// Configure Registers for PWM of PB4(PIN No.4)
// 1. Comparator A Mode -> Set the OC1A output line.
// 2. Time/Counter1 Prescale Select -> 8MHz/256 = 32KHz.
// 3. Enable Pulse Width Modulator B.
// 4. Comparator B Mode -> Set the OC1B output line
TCCR1 = _BV (COM1A1) | _BV (COM1A0) | 
_BV (CS13) | _BV (CS10);
GTCCR = _BV (PWM1B) | _BV (COM1B1) | _BV (COM1B0);

カエルの合唱のコード

#include <Adafruit_NeoPixel.h>
#ifdef __AVR_ATtiny85__ // Trinket, Gemma, etc.
 #include <avr/power.h>
#endif

#define NEOPIXEL_IN_PIN 0
#define NUM_OF_PIXELS 16
#define NEOPIXEL_BRIGHTNESS 30
Adafruit_NeoPixel pixels = Adafruit_NeoPixel( NUM_OF_PIXELS, NEOPIXEL_IN_PIN );

// Definitions
#define _8PINO_TONE_START ( 0x00 )
#define _8PINO_TONE_STOP  ( 0x01 )

#define _8PINO_TONE_NOT_USED ( 0 )

#define _8PINO_TONE_SO_2 ( 158 )
#define _8PINO_TONE_RA_2 ( 141 )
#define _8PINO_TONE_SI_2 ( 126 )
#define _8PINO_TONE_DO_3 ( 118 )
#define _8PINO_TONE_RE_3 ( 106 )
#define _8PINO_TONE_MI_3 (  94 )
#define _8PINO_TONE_FA_3 (  89 )
#define _8PINO_TONE_SO_3 (  79 )
#define _8PINO_TONE_RA_3 (  70 )
#define _8PINO_TONE_SI_3 (  62 )
#define _8PINO_TONE_DO_4 (  59 )

// Global variables
#define _8PINO_TONE_SPEAKER_PIN_1 ( 1 )
#define _8PINO_TONE_SPEAKER_PIN_2 ( 4 )

#define SIZE_OF_TONE_ARRAY 192
PROGMEM prog_uint16_t noteArray[] =
{
  
  // Wait...
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED,
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED,
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED,
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED,
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED,
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED,
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED,
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED,
  
  // Ka-E-Ru-No-U-Ta-Ga
  _8PINO_TONE_DO_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP, 
  _8PINO_TONE_RE_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP, 
  _8PINO_TONE_MI_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP, 
  _8PINO_TONE_FA_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP,
  _8PINO_TONE_MI_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP, 
  _8PINO_TONE_RE_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP, 
  _8PINO_TONE_DO_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, 
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP, 
  
  // Ki-Ko-E-Te-Ku-Ru-Yo
  _8PINO_TONE_MI_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP, 
  _8PINO_TONE_FA_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP,
  _8PINO_TONE_SO_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP,
  _8PINO_TONE_RA_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP,
  _8PINO_TONE_SO_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP,
  _8PINO_TONE_FA_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP,
  _8PINO_TONE_MI_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, 
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP, 
  
  // Qua-Qua-Qua-Qua
  _8PINO_TONE_DO_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, 
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP, 
  _8PINO_TONE_DO_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, 
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP, 
  _8PINO_TONE_DO_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, 
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP, 
  _8PINO_TONE_DO_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, 
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP, 
  
  // Kero-Kero-Kero-Kero
  _8PINO_TONE_DO_3, _8PINO_TONE_STOP, _8PINO_TONE_DO_3, _8PINO_TONE_STOP, 
  _8PINO_TONE_RE_3, _8PINO_TONE_STOP, _8PINO_TONE_RE_3, _8PINO_TONE_STOP, 
  _8PINO_TONE_MI_3, _8PINO_TONE_STOP, _8PINO_TONE_MI_3, _8PINO_TONE_STOP, 
  _8PINO_TONE_FA_3, _8PINO_TONE_STOP, _8PINO_TONE_FA_3, _8PINO_TONE_STOP, 
  
    // Qua-Qua-Qua
  _8PINO_TONE_MI_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP, 
  _8PINO_TONE_RE_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP, 
  _8PINO_TONE_DO_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, 
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP, 
  
  // Wait...
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED,
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED,
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED,
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED,
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED,
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED,
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED,
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED,
  

};

void setup(){
  
  // Initialize PIN mode.
  pinMode( _8PINO_TONE_SPEAKER_PIN_1, OUTPUT );
  pinMode( _8PINO_TONE_SPEAKER_PIN_2, OUTPUT );
  
  // Configure Registers for PWM of PB1(PIN No.1)
  //  1. Waveform Generation Mode -> Fast PWM
  //  2. Clock Select -> 8MHz/256 = 32KHz.
  //  3. Compare Match Output B Mode
  //    -> Clear OC0A/OC0B on Compare Match, 
  //        set OC0A/OC0B at BOTTOM
  TCCR0A = _BV (COM0B1) | _BV (WGM01) | _BV (WGM00);
  TCCR0B = _BV (WGM02) | _BV (CS02);
  
  // Configure Registers for PWM of PB4(PIN No.4)
  //  1. Comparator A Mode -> Set the OC1A output line.
  //  2. Time/Counter1 Prescale Select -> 8MHz/256 = 32KHz.
  //  3. Enable Pulse Width Modulator B.
  //  4. Comparator B Mode -> Set the OC1B output line
  TCCR1 = _BV (COM1A1) | _BV (COM1A0) | 
             _BV (CS13) | _BV (CS10);
  GTCCR = _BV (PWM1B) | _BV (COM1B1) | _BV (COM1B0);
  
  // NeoPixel LED
  pinMode( NEOPIXEL_IN_PIN, OUTPUT );
  
#ifdef __AVR_ATtiny85__ // Trinket, Gemma, etc.
  if( F_CPU == 16000000 ) clock_prescale_set( clock_div_1 );
#endif
  pixels.begin();
  pixels.setBrightness( NEOPIXEL_BRIGHTNESS );
  pixels.show(); // Initialize all pixels to "off"
  
}

void loop(){
  
  for( int count = 32; count < SIZE_OF_TONE_ARRAY; count++ )
  {
    if( pgm_read_word( &noteArray[count] ) == _8PINO_TONE_NOT_USED ){
      // Do Nothing.
    }else if( pgm_read_word( &noteArray[count] ) == _8PINO_TONE_STOP ){
      _8pinoTone( _8PINO_TONE_SPEAKER_PIN_1, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP );
      setNeopixelColor( _8PINO_TONE_SPEAKER_PIN_1, _8PINO_TONE_STOP );
    }else{
      _8pinoTone( _8PINO_TONE_SPEAKER_PIN_1, pgm_read_word( &noteArray[count] ), _8PINO_TONE_START );
      setNeopixelColor( _8PINO_TONE_SPEAKER_PIN_1, pgm_read_word( &noteArray[count] ) );
    }
    
    if( pgm_read_word( &noteArray[count-32] ) == _8PINO_TONE_NOT_USED ){
      // Do Nothing.
    }else if( pgm_read_word( &noteArray[count-32] ) == _8PINO_TONE_STOP ){
      _8pinoTone( _8PINO_TONE_SPEAKER_PIN_2, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP );
      setNeopixelColor( _8PINO_TONE_SPEAKER_PIN_2, _8PINO_TONE_STOP );
    }else{
      _8pinoTone( _8PINO_TONE_SPEAKER_PIN_2, pgm_read_word( &noteArray[count-32] ), _8PINO_TONE_START );
      setNeopixelColor( _8PINO_TONE_SPEAKER_PIN_2, pgm_read_word( &noteArray[count-32] ) );
    }
    
    pixels.show();
    delay( 8000 / OCR0A );
  }
  
}

void _8pinoTone( int pin_speaker, uint8_t freq, uint8_t startstop ){
  if( startstop == _8PINO_TONE_START ){
    pinMode( pin_speaker, OUTPUT );
    if( pin_speaker == _8PINO_TONE_SPEAKER_PIN_1 ){
      OCR0A = freq;
      OCR0B = OCR0A - 1;
    }else{
      OCR1C = freq;
      OCR1B = OCR1C - 1;
    }
  }else{
     pinMode( pin_speaker, INPUT );
  }
}

void setNeopixelColor( int speaker_id, int tone ){
  
  uint8_t led_index = 0;
  int i = 0;
  
  if( speaker_id == _8PINO_TONE_SPEAKER_PIN_1 ){
    switch( tone ){
      case _8PINO_TONE_DO_3:
        led_index = 12;
        break;
      case _8PINO_TONE_RE_3:
        led_index = 13;
        break;
      case _8PINO_TONE_MI_3:
        led_index = 14;
        break;
      case _8PINO_TONE_FA_3:
        led_index = 15;
        break;
      case _8PINO_TONE_SO_3:
        led_index = 0;
        break;
      case _8PINO_TONE_RA_3:
        led_index = 1;
        break;
    }
    
    if( tone == _8PINO_TONE_STOP ){
      for( i = 0; i < 2; i++ ){
        pixels.setPixelColor( i, 0, 0, 0 );
      }
      for( i = 12; i < 16; i++ ){
        pixels.setPixelColor( i, 0, 0, 0 );
      }
    }else{
      for( i = 0; i < 2; i++ ){
        pixels.setPixelColor( i, 0, 0, 0 );
      }
      for( i = 12; i < 16; i++ ){
        pixels.setPixelColor( i, 0, 0, 0 );
      }
      if( led_index < 2 ){
        for( i = 0; i < led_index + 1; i++ ){
          pixels.setPixelColor( i, 255, 0, 0 );
        }
        for( i = 12; i < 16; i++ ){
          pixels.setPixelColor( i, 255, 0, 0 );
        }
        
      }else{
        for( i = 12; i < led_index + 1; i++ ){
          pixels.setPixelColor( i, 255, 0, 0 );
        }
      }
    }
    
  }else{
    switch( tone ){
      case _8PINO_TONE_DO_3:
        led_index = 11;
        break;
      case _8PINO_TONE_RE_3:
        led_index = 10;
        break;
      case _8PINO_TONE_MI_3:
        led_index = 9;
        break;
      case _8PINO_TONE_FA_3:
        led_index = 8;
        break;
      case _8PINO_TONE_SO_3:
        led_index = 7;
        break;
      case _8PINO_TONE_RA_3:
        led_index = 6;
        break;
    }
    
    if( tone == _8PINO_TONE_STOP ){
      for( i = 6; i < 12; i++ ){
        pixels.setPixelColor( i, 0, 0, 0 );
      }
    }else{
      for( i = 6; i < 12; i++ ){
        pixels.setPixelColor( i, 0, 0, 0 );
      }
      for( i = led_index; i < 12; i++ ){
        pixels.setPixelColor( i, 0, 255, 0);
      }
    }
    
  }
  
}

音階のdefine名、長すぎた…

読んでくれた方も

お疲れさまでした。 とりあえず、アウトプットを出せてホッとしております。

次回以降はなるべく軽い投稿を目指します…