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*で基準の周波数を変更出来ることがわかります。
色々、選択肢があるので、ドレミを最もよく表現してくれるオプションを探すため、下記のような表を作りました。
(OCR0AはPWMに設定する0-255の値で、
frequency = 8 MHz/xxx/(OCR0A+1)
です。)
これにより、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( ¬eArray[count] ) == _8PINO_TONE_NOT_USED ){ // Do Nothing. }else if( pgm_read_word( ¬eArray[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( ¬eArray[count] ), _8PINO_TONE_START ); setNeopixelColor( _8PINO_TONE_SPEAKER_PIN_1, pgm_read_word( ¬eArray[count] ) ); } if( pgm_read_word( ¬eArray[count-32] ) == _8PINO_TONE_NOT_USED ){ // Do Nothing. }else if( pgm_read_word( ¬eArray[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( ¬eArray[count-32] ), _8PINO_TONE_START ); setNeopixelColor( _8PINO_TONE_SPEAKER_PIN_2, pgm_read_word( ¬eArray[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名、長すぎた…
読んでくれた方も
お疲れさまでした。 とりあえず、アウトプットを出せてホッとしております。
次回以降はなるべく軽い投稿を目指します…