圧電スピーカをつないでメロディを鳴らします

〔PICの動かせ方入門に戻る〕



圧電スピーカを使ってメロディを鳴らす事が出来ます。
普通のスピーカを使ってメロディを鳴らす場合はこちらを参考にして下さい。

__delay_usに指定する時間は変数が使用出来ないので、タイマー機能で実現しようと考えましたが、
考えているとぉ頭がぁオーバーフローして眠くなってきました、
よって、__delay_usを固定値で使用するがその分サブルーチンを作成という簡単な方法で行っています。 でも、この方法はプログラムサイズが増えるというデメリットが有りますが。

最初は12F675を使ってましたが、やっぱりプログラムメモリが不足、
12F683なら2Kワードあるから大丈夫でしょうが、もってません、で、2Kワードある16F819にしました。
が、コンパイルしたら98%でした、HI-TECH C PROなら、でも.....、
ソースを見直して再コンパイルした結果現在68.3%あります、少し余裕が出来ました。
この余裕分おすきに改造して下さい。

Melody1

今回使用するピン番号は14番(VDD)と5番(VSS)と18番(RA1)のデジタル出力です。

@まずは、下記図画面の様に配線しましょう。

Melody2

AMPLAB IDEを起動させます。

B下記がプログラムソースです、ちょっと長いです。
 プロジェクトを作成して新規ファイルにコピーペーストして貼り付けて下さい。
 プログラムソースをダウンロードしてプロジェクトに取込む事も出来ます。
---------------------------------------------------------------------
#include <pic.h>
#include <htc.h>              // delay用に必要

#define _XTAL_FREQ 8000000    //  delay用に必要(クロック8MHzを指定)
#define BZ_PIN     RA1        // ブザーを出力するピンを指定
#define TEMPO      60         // 4分音符=60bpm (4分音符の音価で入力)

long NOTE ;
long I ;
long LEN ;

// デバッグしない(DEBUGDIS):低電圧プログラミング機能使用しない(LVPDIS)
// メモリを保護しない(UNPROTECT):外部リセット信号は使用せずにデジタル入力(RA5)ピンとする(MCLRDIS)
// 電源電圧降下常時監視機能ON(BOREN):電源ONから72ms後にプログラムを開始する(PWRTEN)
// ウオッチドックタイマ無し(WDTDIS):内部クロックを使用する(INTIO)
__CONFIG(DEBUGDIS & LVPDIS & UNPROTECT & MCLRDIS & BOREN & PWRTEN & WDTDIS & INTIO) ;

// EEPROMにメロディデータを書込む
__EEPROM_DATA(0x58,0x58,0x88,0x98,0xa8,0x98,0x84,0x68);
__EEPROM_DATA(0x68,0x58,0x48,0x58,0x04,0x00,0x00,0x00);

/* 休符 */
void kyufu()
{
	LEN = NOTE / 2272 ;
     // ラ音と同じ長さのパルス(1サイクル)を繰返す
     for (I = 0; I < LEN ; I++) {
          __delay_us(2272) ;
     }
}
/* ドの音を出す */
void C4()
{
     LEN = NOTE / 3816 ;
     // 同じ長さのパルス(1サイクル)を繰返す
     for (I = 0; I < LEN ; I++) {
          BZ_PIN = 1 ;
          __delay_us(1908) ;
          BZ_PIN = 0 ;
          __delay_us(1908) ;
     }
}
/* レの音を出す */
void D4()
{
     LEN = NOTE / 3401 ;
     // 同じ長さのパルス(1サイクル)を繰返す
     for (I = 0; I < LEN ; I++) {
          BZ_PIN = 1 ;
          __delay_us(1700) ;
          BZ_PIN = 0 ;
          __delay_us(1700) ;
     }
}
/* ミの音を出す */
void E4()
{
     LEN = NOTE / 3030 ;
     // 同じ長さのパルス(1サイクル)を繰返す
     for (I = 0; I < LEN ; I++) {
          BZ_PIN = 1 ;
          __delay_us(1515) ;
          BZ_PIN = 0 ;
          __delay_us(1515) ;
     }
}
/* ファの音を出す */
void F4()
{
     LEN = NOTE / 2865 ;
     // 同じ長さのパルス(1サイクル)を繰返す
     for (I = 0; I < LEN ; I++) {
          BZ_PIN = 1 ;
          __delay_us(1432) ;
          BZ_PIN = 0 ;
          __delay_us(1432) ;
     }
}
/* ソの音を出す */
void G4()
{
     LEN = NOTE / 2551 ;
     // 同じ長さのパルス(1サイクル)を繰返す
     for (I = 0; I < LEN ; I++) {
          BZ_PIN = 1 ;
          __delay_us(1275) ;
          BZ_PIN = 0 ;
          __delay_us(1275) ;
     }
}
/* ラの音を出す */
void A4()
{
     LEN = NOTE / 2272 ;
     // 同じ長さのパルス(1サイクル)を繰返す
     for (I = 0; I < LEN ; I++) {
          BZ_PIN = 1 ;
          __delay_us(1136) ;
          BZ_PIN = 0 ;
          __delay_us(1136) ;
     }
}
/* シの音を出す */
void B4()
{
     LEN = NOTE / 2024 ;
     // 同じ長さのパルス(1サイクル)を繰返す
     for (I = 0; I < LEN ; I++) {
          BZ_PIN = 1 ;
          __delay_us(1012) ;
          BZ_PIN = 0 ;
          __delay_us(1012) ;
     }
}
/* ド(1高)の音を出す */
void C5()
{
     LEN = NOTE / 1912 ;
     // 同じ長さのパルス(1サイクル)を繰返す
     for (I = 0; I < LEN ; I++) {
          BZ_PIN = 1 ;
          __delay_us(956) ;
          BZ_PIN = 0 ;
          __delay_us(956) ;
     }
}
/* レ(1高)の音を出す */
void D5()
{
     LEN = NOTE / 1703 ;
     // 同じ長さのパルス(1サイクル)を繰返す
     for (I = 0; I < LEN ; I++) {
          BZ_PIN = 1 ;
          __delay_us(851) ;
          BZ_PIN = 0 ;
          __delay_us(851) ;
     }
}
/* メインの処理 */
void main()
{
	int i , dt , scale ;
	
     OSCCON = 0b01110100 ;     // 内部クロックは8MHzとする
     ADCON1 = 0b00000110 ;     // アナログは使用しない、RA0-RA4をデジタルI/Oに割当
     TRISA  = 0b00000000 ;     // 1で入力 0で出力 RA0-RA7全て出力に設定(RA5は入力専用)
     TRISB  = 0b00000000 ;     // RB0-RB7全て出力に設定

     while(1) {
          // EEPROMから0x00を読み出すまで繰り返す
	     for (i=0 ; i < _EEPROMSIZE ; i++) {
	          dt = eeprom_read(i) ;      // EEPROMからデータを読込む
	          if (dt == 0) break ;       // データ0で終了
	          NOTE = 0xf & dt ;          // 読込みデータから音符を取出す
	          if (NOTE == 0) NOTE = 16 ;
	          NOTE = ((60000000L/TEMPO)*4) / NOTE ;
	          scale = (dt >> 4) ;        // 読込みデータから音階を取出す
	          if (scale == 0) kyufu() ;  // 休符
	          if (scale == 2) C4() ;     // ド
	          if (scale == 3) D4() ;     // レ
	          if (scale == 4) E4() ;     // ミ
	          if (scale == 5) F4() ;     // ファ
	          if (scale == 6) G4() ;     // ソ
	          if (scale == 7) A4() ;     // ラ
	          if (scale == 8) B4() ;     // シ
	          if (scale == 9) C5() ;     // ド(1高)
	          if (scale ==10) D5() ;     // レ(1高)
	     }
          // 3秒後に繰り返す
          for (i=0 ; i < 300 ; i++) {
               __delay_ms(10) ;
          }
    }
}
---------------------------------------------------------------------

CコンパイルPIC書き込みを実行して下さい。

DPICをブレッドボードに取付けてならしましょう。

《やさしく解説》

圧電スピーカについて

圧電スピーカは極性なしどちら側に配線してもOKです、音声は無理です、ブザー音のみしか鳴らせません。また、普通のブザーのようにつなげれば鳴る品物でなく、電気を流したり切ったりを高速で繰り返す事により鳴ります。

Melody3

ON(電気入)とOFF(電気切)の時間の長さを可変すると音程を変える事が出来ますが、ONとOFFの長さが長すぎると音は出ません。
尚、つなぐ圧電スピーカ種類でかなり音のなり方が異なったりします。

プログラムについて

プログラムソースのコメントを読んでもらえば大体何をしているのか分かると思います。

#define BZ_PIN RA1
 他のピンに圧電スピーカをつなぐ場合はRA1を書き換えて下さい。

while(1)
 main()の中の処理は1回実行すると終了します、
 だからwhile(1)の、{ }の中に処理を書き込めば無限に繰り返します。

__delay_us(value)
 プログラムを指定した時間だけ一時停止します。
 value:停止したい時間を指定します、単位はマイクロ秒です。
 クロック8MHz動作では98560usまでしか指定できません。
 __delay_msなら98msまでです。

デジタルI/O
 デジタルピンの設定は下記のレジスターにて設定します。
 ADCON1 = 0b00000110 ; この設定でアナログは使用しない、RA0-RA4をデジタルI/Oにて使用する
                 その他の赤い数字部の組み合わせはこちらを参照して下さい。

ポートA
 TRISA = 0b00000000 ; RA0-RA7ピンの入出力を設定、1で入力 0で出力
               (右からRA0で赤数字RA5は入力専用です)
ポートB
 TRISB = 0b00001000 ; RB0-RB7ピンの入出力を設定、1で入力 0で出力
               (右からRB0で赤数字RB3のみ入力するです)

EEPROMへのメロディデータ作成方法

@テンポの設定を行います。
 #define TEMPO    60
 この行で指示します、これは4分音符=60BPM(M.M.=60)と言う事です。
 4分音符=80BPMにしたい場合は60を80に書き換えます、4分音符の音価での入力です。
 (もし、2分音符でM.M.=60にしたいならTEMPO=120にします)

A音階(休符)や音符データの登録を行います。
 __EEPROM_DATAの( )内に8バイト単位ずつに分けて記入して行きます。
 __EEPROM_DATA(0x58,0x58,0x88,0x98,0xa8,0x98,0x84,0x68);

 データは上位4ビットが音階で下位4ビットが音符です。 例)ラ音で4分音符→0x74
 音階 : ド=2 レ=3 ミ=4 ファ=5 ソ=6 ラ=7 シ=8 ド(1高)=9 レ(1高)=10(0xa) 休符=0
 音符 : 全音符=1 2分音符=2 4分音符=4 8分音符=8 16分音符=0
 休符 : 音階を0として音符を付けます、例えば8分休符なら→0x08 です。

 また、データ最後は必ず0x00を入れます、終了の目安とします。
 16F819はEEPROMはMAX256バイトです、約255音符(32行)ほどは可能でしょう。

音階を追加する人

あと、プログラム余裕が31.7%程あります。
音階サブルーチンをあと5個ぐらいは追加出来そうです。
こちらを参考に音階の周波数を調べて下さい。
例えばレ音(D5)は587Hzです、(1サイクルを計算します)
計算1 : (1000000 / 587) = 1703.577
計算2 : (1000000 / 587) / 2 = 851.788
void D5()
{
     LEN = NOTE / 1703 ;     ← 上の計算1から1703か1704にします(ここが音符の長さです)
     for (I = 0; I < LEN ; I++) {
          BZ_PIN = 1 ;
          __delay_us(851) ;  ← 上の計算2から851か852にします(ここが音階に影響します)
          BZ_PIN = 0 ;          (どちらを選択するかは耳で聴き比べて下さい)
          __delay_us(851) ;  ← (同上)
     }
}
メロディを何曲か登録してスイッチの切替えで曲を選んだりの改造もたぶんOKでしょう。

《その他》

今回は18番ピンを使用しましたが他のピンを使用する場合は下記の表を参照して下さい。
また、4番ピンRA5は入力しか出来ません、出力不可です。
ポートA
ピン番号 16 15 18 17
デジタル入出力ビット名 RA7 RA6 RA5 RA4 RA3 RA2 RA1 RA0

ポートB
ピン番号 13 12 11 10
デジタル入出力ビット名 RB7 RB6 RB5 RB4 RB3 RB2 RB1 RB0




【きむ茶工房ガレージハウス】
Copyright (C) 2006-2011 Shigehiro Kimura All Rights Reserved.