目次
スピーカーの駆動原理
前回はLEDの電子ホタルプログラムを書きました。今回も全く同様な手段でスピーカーを鳴らしてみます。使用しているPICはPIC16F1938です。
音とは
音とは、空気の振動を人間が感知したもののことを言います(それはそう)。振動が早ければ早いほど音は高くなります。
大事な概念として周波数があります。これは、1秒間に何回振動するかということですね。これの逆数を取ると、周期になります。これは、1回の振動に何秒かかるかを表します。PICでは、__delay_us()関数を使った方が楽なので、周期を使います。
圧電スピーカーの使い方
圧電スピーカーとは、電圧をかけると内部の板が変形するスピーカーです。電圧のオンオフを高速で繰り返せば、この板が振動し、音として聞こえます。
今回は秋月電子通商の圧電スピーカー(圧電サウンダ)SPT08 (2個入)を使いました。これ以外のスピーカーであっても、これから紹介するプログラムで動かすことができます。
上記の写真のように圧電スピーカーには極性があり、プラスをPICへ、マイナスをGNDへ繋ぎます。ただし、直接PICに繋ぐとうるさいのでお勧めしません。間に500Ω程度の抵抗を挟むといいでしょう。
下の画像のように、ワイヤーの芯を露出させ、ジャンパワイヤに繋ぐと今後工作がしやすくなるのでお勧めです。(これをしないとブレッドボードに刺さらない)今後も使うので、ジャンパワイヤに巻いてそこをはんだ付けするといいでしょう。
音階を作る
「ド」の音を作る
「ド」の音を作ってみましょう。ドに当たる音の周期はこちらのサイトによると3.82ms、つまり3820μsとなります。一回の振動のうち、スピーカーがオンになっているのはその半分の1908μsです。つまり、以下のようにすれば1回板が振動して、ドの音が瞬間的に出ることになります。
RA1 = 1; //RA1(スピーカーがつながっているピン)をオン
__delay_us(1908);
RA1 = 0; //スピーカーをオフ
__delay_us(1908);
これでは瞬間的にしか音が出ないので、これをfor文で繰り返しましょう。ここで、1秒間「ド」を鳴らすようにしてみましょう。一周に3820μsかかるので、1秒間に1/3820μs回=261回繰り返せばいいですね。
void do1(){
for(long i = 1;i<261;i++){
RA1 = 1;
__delay_us(1908);
RA1 = 0;
__delay_us(1908);
}
}
多くの時間繰り返せるようにiはlongにしました。今回はここまで必要ないので、intでもいいでしょう。なんと、これでドの音は完成です!main関数にdo1();と記述し、確かめてみてください。
PICへの書き込み方法はこちら PICにプログラムを書き込んでみよう
ドレミファソラシドを鳴らす
ここまでくれば簡単です。先程ご紹介したサイトを頼りに、他の音階も作ればいいのです。全く同じ手順を踏むので、割愛させていただきます。
下のコードではRA1ではなくsoundという変数を使っています。これは、冒頭で
#define sound RA1
としているだけです。スピーカーがつながっているピンを変えたいときは、この記述を変えれば全て置き換わるので便利です。
void do1(){
for(long i = 1;i<261;i++){
sound = 1;
__delay_us(1908);
sound = 0;
__delay_us(1908);
}
}
void re1(){
for(long i = 1;i<294;i++){
sound = 1;
__delay_us(1700);
sound = 0;
__delay_us(1700);
}
}
void mi1(){
for(long i = 1;i<330;i++){
sound = 1;
__delay_us(1515);
sound = 0;
__delay_us(1515);
}
}
void fa1(){
for(long i = 1;i<349;i++){
sound = 1;
__delay_us(1432);
sound = 0;
__delay_us(1432);
}
}
void so1(){
for(long i = 1;i<392;i++){
sound = 1;
__delay_us(1275);
sound = 0;
__delay_us(1275);
}
}
void ra1(){
for(long i = 1;i<440;i++){
sound = 1;
__delay_us(1136);
sound = 0;
__delay_us(1136);
}
}
void si1(){
for(long i = 1;i<494;i++){
sound = 1;
__delay_us(1012);
sound = 0;
__delay_us(1012);
}
}
void do2(){
for(long i = 1;i<523;i++){
sound = 1;
__delay_us(956);
sound = 0;
__delay_us(956);
}
}
完成したプログラム
基本の設定等はここと変わらないので、コピペで行けます。一応下に全文載せておきます。
// PIC16F1938 Configuration Bit Settings
// 'C' source line config statements
// CONFIG1
#pragma config FOSC = INTOSC // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = OFF // MCLR Pin Function Select (MCLR/VPP pin function is digital input)
#pragma config CP = OFF // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = OFF // Brown-out Reset Enable (Brown-out Reset disabled)
#pragma config CLKOUTEN = OFF // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = OFF // Internal/External Switchover (Internal/External Switchover mode is disabled)
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is disabled)
// CONFIG2
#pragma config WRT = OFF // Flash Memory Self-Write Protection (Write protection off)
#pragma config VCAPEN = OFF // Voltage Regulator Capacitor Enable (All VCAP pin functionality is disabled)
#pragma config PLLEN = OFF// PLL Enable (4x PLL disabled)
#pragma config STVREN = ON // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
#pragma config BORV = LO // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LVP = ON // Low-Voltage Programming Enable (Low-voltage programming enabled)
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
#include <xc.h>
#define _XTAL_FREQ 8000000L
#define sound RA1
void PICinit(){
OSCCON = 0b01110000;
ANSELA = 0b00000000;
ANSELB = 0b00000000;
TRISA = 0b00000000;
TRISB = 0b00000000;
TRISC = 0b00000000;
PORTA = 0b00000000; //2進数で書いた場合
PORTB = 0x00; //16進数で書いた場合
PORTC = 0; //10進数で書いた場合
}
void do1(){
for(long i = 1;i<261;i++){
sound = 1;
__delay_us(1908);
sound = 0;
__delay_us(1908);
}
}
void re1(){
for(long i = 1;i<294;i++){
sound = 1;
__delay_us(1700);
sound = 0;
__delay_us(1700);
}
}
void mi1(){
for(long i = 1;i<330;i++){
sound = 1;
__delay_us(1515);
sound = 0;
__delay_us(1515);
}
}
void fa1(){
for(long i = 1;i<349;i++){
sound = 1;
__delay_us(1432);
sound = 0;
__delay_us(1432);
}
}
void so1(){
for(long i = 1;i<392;i++){
sound = 1;
__delay_us(1275);
sound = 0;
__delay_us(1275);
}
}
void ra1(){
for(long i = 1;i<440;i++){
sound = 1;
__delay_us(1136);
sound = 0;
__delay_us(1136);
}
}
void si1(){
for(long i = 1;i<494;i++){
sound = 1;
__delay_us(1012);
sound = 0;
__delay_us(1012);
}
}
void do2(){
for(long i = 1;i<523;i++){
sound = 1;
__delay_us(956);
sound = 0;
__delay_us(956);
}
}
int main(void){
PICinit(); //PICを初期化
while(1){
do1();
re1();
mi1();
fa1();
so1();
ra1();
si1();
do2();
}
return 0;
}
次回
次回は、このドレミを使った簡単な曲を演奏してみます。次回
ピンバック: Wak-tech » PIC16F1938で「ようこそジャパリパークへ」を演奏してみた
ピンバック: Wak-tech » PICで電子ホタルを作る
ピンバック: ESP32で圧電スピーカーを動かしてみた【wak-tech Advent Calender 2018】 | Wak-tech
ピンバック: PICマイコンに素数を計算させてみた | Wak-tech
ピンバック: 動けばいい人のためのPICマイコン入門 | Wak-tech