PICで電飾 音声回路製作 その7 プログラムの解説
ようやくプログラムの解説です。プログラムは前回と同じものをこのページの最後に載せていますが、説明のために字に色を付けています。適宜参照して下さい。
●ボタン受付のための設定
今回、私の回路ではリセットボタンをつけていました。リセットボタンはただつけるだけではダメで、コンフィグの所で使うことを宣言する必要があります。下掲プログラムの緑字にしてある
//コンフィグ設定
#pragma config FOSC = INTOSC
:
#pragma config MCLRE = ON
:
このMCLRE=ONというのがリセットボタン使用の宣言です。リセットボタンを使わない場合は、MCLRE=OFFとしておいてください。
また、RB2とRB3につけたボタンで操作する回路を作っていますが、そのための設定もしてやらなくてはいけません。
//特殊レジスタ の設定のところで、これも緑字で示した通り、
OPTION_REG = 0b00000000 ;//内部プルアップ抵抗を使う
TRISB = 0b00001100 ;//RBの足のうちRB2,RB3を入力に使う
WPUB = 0b00001100 ;//RB2,3に内部プルアップ抵抗をつなぐ
この設定が必要になります。プルアップ抵抗が何かについては、「その2」で説明したとおりです。
0b00001100の0bは2進数で書くよという宣言、残り8桁の0と1はそれぞれの足番号を出力に使うか(0)、入力に使うか(1)です。ボタンを増やしたり、使う足を変更するときはここを変えてください。
●プログラム
プログラムは、イベント発生の有無でおおきく2つの部分に分けられます。
1)イベントが発生していない時(Event==0のとき)、ボタン入力を受け付けます。
2)イベントが発生している時(Event==1のとき)、MP3プレーヤーを操作して音を鳴らします。
イベントは起きているか、起きていないかのどちらかですから、プログラムは1)か2)のどちらかの状態にいます。
これはつまり、このプログラムでは
「音が鳴っている間はボタン入力を受け付けない」
ということです。
音が鳴っている間も入力を受け付けるプログラムは可能ですが、そうしないほうが動作の安定上よさそうだと判断しました。
●ボタンの受付
1)の中身については、見たままですのでほとんど解説はいらないと思います。一応各行で何をやっているかは//以降にコメントをつけています。
if(RB3==0)//もしRB3が押されたら
{
Event=1;//イベントフラグ立てる
GunON=1;//波動砲イベントフラグ立てる
}
if(RB2==0)//もしRB2が押されてたら
{
Event=1;//イベントフラグ立てる
EnON=1;//エンジンイベントフラグ立てる
}
ボタンが押されたら、フラグを立てる、それだけです。
●MP3プレーヤーの操作その1
2)の中身も、波動砲イベントの方はほぼ見たままですので、あまり説明は要らないと思います。
if (Event==1)//イベントが起きたら
{
if (GunON==1) //それが波動砲イベントなら
{
RA3=1;//MP3プレーヤーに通電
__delay_ms(5000);//音楽5秒流す
RA3=0;//プレイヤー通電ストップ
GunON=0;//波動砲イベント終了
Event=0;//イベント終了
}
Event==1、すなわちイベントが起きていて、それが波動砲イベント(GunON==1)なら、RA3に電流を流します。RA3からの電流はトランジスタスイッチを動かしてMP3プレーヤーに通電させます。
MP3プレーヤーは直ちに1曲めの再生を始めます。
その次の行では__delay_ms(5000);としています。5000ミリ秒、プレーヤーに通電して待つということです。
つまり1曲めは5秒間流れます。この長さは、流したい音の長さに応じて自由に変えてもらって結構です。
ただし、もともとのファイルの再生時間を超えないように気をつけて下さい。設定時間が長すぎて2曲めの再生に入ってしまうと、次に同じボタンを押したときに2曲めから再生が始まってしまいます。
待ち時間が過ぎたら、RA3=0;でトランジスタスイッチングの通電が止まり、MP3プレーヤーは止まります。
GunON=0;とEvent=0;でイベントフラグが下ろされ、またボタン入力の受付に戻ります。
再生の時間を適切に設定して、2曲めに行かないようにしてあれば、このMP3プレーヤーは次に通電した時に、1曲めの冒頭に戻って再生しようとします。
ですからRB3のボタンを押すたびに1曲めが流れるのです。
流したい曲がひとつだけなら、これで話は終わり、プログラムもRB2==0やEnON==1に関係するところをまるっと削除して出来上がりです。
しかし2曲目を流したいときは色々要注意です。
ここからが実はこの製作記のキモですので、同様にやってみたい方はもう少しお付き合いください。
●MP3プレーヤーの操作その2
2曲めを流したい場合、そのように指示を送ってやる必要があります。
今回の工作ではSDカードには2曲しか音が入っていないので、曲送りボタンを押すたびに
1曲め→2曲め→1曲め→2曲め…
と再生される曲は代わりばんこに変化します。ですので、プログラムとしては
1)電源を入れる。プレーヤーが1曲めの再生を開始しようとするので、すかさず曲送りボタンを押して2曲めに動かす。
2)必要な秒数だけ2曲めを再生したら、また曲送りボタンを押して1曲めに戻す。その後電源を切る。
という作業を指示すれば、RB2のボタンを押すたびに2曲めを再生するはずです。
プログラムも実際そうなっています。
if (EnON==1) //それがエンジンイベントなら
{
RA3=1;//MP3プレーヤーに通電
__delay_ms(400);//**ボタン受付開始待ち**
RA2=1;//MP3プレーヤーの曲送りボタンを押す
__delay_ms(500);//**ボタン押し認識待ち**
RA2=0;//ボタン戻す
__delay_ms(5000);//音楽5秒流す
RA2=1;//曲送りボタン押す(1曲めに戻す)
__delay_ms(500);//**ボタン押し認識待ち**
RA2=0;//ボタン戻す
__delay_ms(200);//**ボタン戻し認識待ち**
RA3=0;//プレーヤー通電ストップ
EnON=0;//エンジンイベント終了
Event=0;//イベント終了
}
見づらいことにボタン操作の前後にやたら、__delay_ms(XXX);//**○○待ち**と入っていますね。
これが「PICから外部機器を操作する」ときの最大の問題というか、気をつけなければいけないポイントです。
多くの電子機器は、ボタン操作を受け付けるとき、わざと少しニブくなります。
電子機器の認識のスピードは人間を遥かに超えていますので、ボタンからの信号が一瞬でも、それを受け付けることは簡単です。
しかし、一瞬の信号も「ボタンが押されたぞ!」と認識してしまうと、わずかな電気ノイズもやはりボタンが押されたと思ってしまいます。
電子機器ほんらいの敏感さのままでは、むしろ人間の意図と違う動作を始めてしまうのです。
それではまずいので、ボタンに関してはわざとニブくなって、電子機器の感覚で言えば信じられないくらいゆっくりした動きだけを人間の操作だと認識するようにしているのです。
このMP3プレーヤーも同じで、ボタンはゆっくり押してやらないと操作として受け付けません。
それが__delay_ms(XXX)の役割です。
問題はどれくらいゆっくり押せばいいのか、つまりXXXの数値はどうやって決めるのか、です。
あまりXXXが大きいと動作がもっさりするわけですが、しかし小さすぎるとMP3プレーヤーがそれを人の操作だと思ってくれず無視されてしまいます。
MP3プレーヤーの細かい仕様などは公開されていませんから、これは試行錯誤するしかありません。私も何度か試しながら今回の数値にたどり着きました。
しかし、恐らくMP3プレーヤーの個体やバージョンによる差もあるでしょうし、コントロールするPICやトランジスタの癖もあると思われます。
従って、もしこれを読んだ方が同じように回路を組んで同じプログラムを走らせても同じように動くとは限りません。
それぞれで__delay_ms(XXX)のXXXを増減しながら頃合いを見極めてください。
たとえば最初の曲送りがうまく行かず、2曲めがかからない時には
RA3=1;//MP3プレーヤーに通電
__delay_ms(400);//**ボタン受付開始待ち**
RA2=1;//MP3プレーヤーの曲送りボタンを押す
__delay_ms(500);//**ボタン押し認識待ち**
ここの400と500を少し大きめにして、意図したとおりに動いてくれるのを確認しながら少しづつ数値を小さくすればいいでしょう。
また、最初はEボタンを押すと2曲めがかかるのに、2回めに押すと1曲めがかかってしまう場合は、再生終了後の曲送りに失敗しています。
RA2=1;//曲送りボタン押す(1曲めに戻す)
__delay_ms(500);//**ボタン押し認識待ち**
RA2=0;//ボタン戻す
__delay_ms(200);//**ボタン戻し認識待ち**
ここの500と200を少し大きくしてみてください。何度か試しているうちにちょうどいい加減が見つかるはずです。
さあ、これで音声再生の仕組みは出来ました!おつかれさまです!
ここまでやってきた貴方なら、音を3種類に増やしたい場合はどうするかも、見当がつくでしょう。ぜひご自身で手を動かして試してみてください。
これで音を出すところまで行きましたので、次はPIC電飾の花でありながら、同時に「初見殺し」でもある、タイマ機能とそれによるPWM調光について、説明をしてゆくことにします。
プログラム
↓↓↓↓↓↓↓↓↓↓↓ここから↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
#include <xc.h>
#define _XTAL_FREQ 8000000
//コンフィグ設定
#pragma config FOSC = INTOSC
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config MCLRE = ON
#pragma config CP = OFF
#pragma config CPD = OFF
#pragma config BOREN = ON
#pragma config CLKOUTEN = OFF
#pragma config IESO = OFF
#pragma config FCMEN = OFF
#pragma config WRT = OFF
#pragma config PLLEN = OFF
#pragma config STVREN = ON
#pragma config BORV = HI
#pragma config LVP = OFF
//変数と初期値の設定
int EnON=0, GunON=0, Event=0;
//メイン関数
void main(void)
{
//特殊レジスタの設定
OSCCON = 0b01110010 ;//内部クロック8MHz
ANSELA = 0b00000000 ;//RAの足をすべてデジタルI/Oとして使う
ANSELB = 0b00000000 ;//RBの足をすべてデジタルI/Oとして使う
OPTION_REG = 0b00000000 ;//内部プルアップ抵抗を使う
TRISA = 0b00000000 ;//RAの足はすべて出力に使う
TRISB = 0b00001100 ;//RBの足のうちRB2,RB3を入力に使う
WPUB = 0b00001100 ;//RB2,3に内部プルアップ抵抗をつなぐ
PORTA = 0b00000000 ;//RAの初期値はすべてゼロ
PORTB = 0b00000000 ;//RBの初期値はすべてゼロ
while(1)
{
if (Event==0)//イベントが起きていないとき
{
if(RB3==0)//もしRB3が押されたら
{
Event=1;//イベントフラグ立てる
GunON=1;//波動砲イベントフラグ立てる
}
if(RB2==0)//もしRB2が押されたら
{
Event=1;//イベントフラグ立てる
EnON=1;//エンジンイベントフラグ立てる
}
}
if (Event==1)//イベントが起きたら
{
if (GunON==1) //それが波動砲イベントなら
{
RA3=1;//MP3プレーヤーに通電
__delay_ms(5000);//音楽5秒流す
RA3=0;//プレイヤー通電ストップ
GunON=0;//波動砲イベント終了
Event=0;//イベント終了
}
if (EnON==1) //それがエンジンイベントなら
{
RA3=1;//MP3プレーヤーに通電
__delay_ms(400);//**ボタン受付開始待ち**
RA2=1;//MP3プレーヤーの曲送りボタンを押す
__delay_ms(500);//**ボタン押し認識待ち**
RA2=0;//ボタン戻す
__delay_ms(5000);//音楽5秒流す
RA2=1;//曲送りボタン押す(1曲めに戻す)
__delay_ms(500);//**ボタン押し認識待ち**
RA2=0;//ボタン戻す
__delay_ms(200);//**ボタン戻し認識待ち**
RA3=0;//プレーヤー通電ストップ
EnON=0;//エンジンイベント終了
Event=0;//イベント終了
}
}
}
}