DFPlayerをPICで操作する その5 PIC寝ろ、DFPlayer寝てろ
今回は節電についての話です。
DFPlayerは音を出していないただの通電状態でも結構な電力を消費します。
具体的にどれくらい消費するのか数字で示せるといいのですが、私はこんな記事を書いてるくせに電流計を持ってない。まあなんとなくの実感でいうと、単4電池3本なら3日で再生ボタン押しても音が出なくなるくらい。
うっかり電源スイッチを切り忘れるとあっという間に電池がなくなります。
しばらく誰も操作しないならスリープに入らせて節電するというのが一つの手.なのですが、DFPlayerに関していえば、スリープさせてもあまり消費電力が減らないので、意味がありません。
そこで、PICの方に電源管理を任せるという手を使います。
しばらく操作がないと、PICがDFPlayerへの通電を止める。ついでにPICの方もスリープに入る形にする。
スリープ時のPICの消費電力はナノアンペアレベルだそうですので、通電したままでも痛くもかゆくもない。テレビやエアコンのリモコンは通電しっぱなしでも電池なかなか切れないですよね。スリープしている限りあれくらいのレベルで電池はもつはずです。
というわけで、そういう工作とプログラムです。
まずはDFPlayerへの電源管理ですが、以前携帯型MP3プレイヤーを音源にする記事で使ったトランジスタスイッチングを使います。
トランジスタのベース、エミッタ、コレクタをそれぞれ16,17,18の列に挿し、(上の写真のトランジスタとは表裏向きが逆ですので気を付けてください)ベースをPICのRA1と、コレクタは電源とつなぎます。エミッタは18列に挿した時点でPICのVCCにつながっています。
工作はこれだけ。これでPICのRA1から緑の矢印のようにトランジスタのベースに電圧が来ると、黄色の矢印のようにトランジスタのコレクタ、エミッタを通じてDFPlayerに電流が流れます。
あとはプログラムです。また下に置いていますので、コピペして書き込んでください。
(ところで、この記事のために確かめていたところ、なぜかうまくPICに書き込みできないということが起こりました。同じように書き込みできなかったら、書き込みの時だけトランジスタを外して、書き込みができたらまた挿してください。)
以下プログラムの解説です。
まず、メインのプログラムの中でRA1=1と書いて、RAから電流を流します。これがコレクタに流れて、電源からDFPlayerに通電します。
ボタン操作がなくなって何分か後に電源が切るわけですが、 その経過時間は割り込み関数を使って測っています。
あとでLEDのPWMにも使う事も考えて、5ミリ秒ごとに1回割り込み関数のところに飛ぶことにし、5ミリ秒を200回カウントしたら1秒経過したという計算で何秒経ったかを計算しています。このあたりの処理は緑色になっています。
そしてなにかボタンが押されるたびに秒数カウントをリセットしています。
この秒数カウントが300秒になったら、スリープに入ります。スリープ処理のところはオレンジ色にしています。PICのスリープに関しては一応調べたのですが私いまいち理屈がよく分かっていません。こちらのページを参考にさせていただきました。
スリープ解除はRA4に繋がっている、右側のボタンです。
また、スリープに入ったときと立ち上がったときにそうと分かるシグナル音を鳴らすプログラムにしています。鳴らす音の番号は004と005です。もちろんこのあたりは削除しても動作には影響しません。
ところでこの例では300秒操作がなかったらスリープ、という処理にしていますが、基本スリープのままで、ボタンを押す押す⇒スリープ解除⇒DFPlayerに通電、音鳴らす⇒終わったら即スリープというプログラムにしてもいいと思います。
そうすれば電源スイッチなしでも、ボタンを押したら音が鳴り、操作していないときはほぼ電力を使わない回路になります。ただその場合、スリープ解除のところで少し時間がかかるので、ボタンを押した瞬間に鳴らなくてもいいならばという条件が付きます。情景モデルに音を付けたいとかなら使えるかもしれません。
これで大体、今回の銃の工作のために私が覚えたことは書いたんじゃないかと思います。あとは補足的な話です。
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
#include <xc.h>
#define _XTAL_FREQ 8000000 //8Mhzで動かすので_XTAL_FREQは8000000 これ書いとかないとdelayが使えない。
//コンフィグ設定
#pragma config FOSC = INTOSC //内部クロック使用
#pragma config WDTE = OFF //ウオッチドッグタイマーなし
#pragma config PWRTE = ON //電圧の立ち上がり後の安定待ちをする
#pragma config MCLRE = OFF //RA3をリセットに使わない
#pragma config CP = OFF //プログラムメモリ保護なし
#pragma config CPD = OFF //データメモリ保護なし
#pragma config BOREN = ON //電圧降下したらPIC停止(許容する程度はBORVで指定)
#pragma config CLKOUTEN = OFF//クロック出力無効
#pragma config IESO = OFF //2段階起動モードON
#pragma config FCMEN = OFF //外部クロック監視をしない
#pragma config WRT = OFF //書き込み禁止しない
#pragma config PLLEN = OFF //高速クロック(32MHz)使わない
#pragma config STVREN = ON //スタックがオーバーフローしたらリセット
#pragma config BORV = HI //PIC停止電圧設定HIGH
#pragma config LVP = OFF //低電圧プログラミングしない
int Counter, MSecCounter, SecCounter;//カウンター変数宣言
//関数のプロトタイプ宣言
void UART_Write(char data);
void DF_SpecifyPlay(int n);
void DF_Volume(int n);
//メイン関数ここから
void main(void) {
//特殊レジスタの設定
OSCCON = 0b01110010 ; // 内部クロックは8MHzとする
OPTION_REG = 0b00000000 ; // デジタルI/Oに内部プルアップ抵抗を使用する
ANSELA = 0b00000000 ; // アナログは使用しない(すべてデジタルI/Oに割当てる)
TRISA = 0b00011000 ; // RA4、RA3は入力その他のピンは出力
WPUA = 0b00011000 ; // RA4、RA3には内部プルアップ抵抗をつなぐ
PORTA = 0b00000000 ; // 出力ピンの初期化(全てLOWにする)
//シリアル通信の初期設定
TXSTA = 0b00100100 ;
SPEN = 1;
SPBRGL = 51 ;
TXCKSEL = 0 ;
//シリアル通信の初期設定ここまで
// タイマ2設定 5ミリ秒ごとに割り込み関数へ飛ぶ
T2CON=0b00000100;//?プリスケール1、?ポストスケール1
PR2=99;//?PR2初期値99に
TMR2IF = 0; //タイマ2割り込みフラグゼロに
TMR2IE=1;//タイマ2割り込み許可
TMR2ON=1;//タイマ2スタート
PEIE=1;//周辺割り込み許可
GIE=1;//全体割り込み許可
// タイマ2設定ここまで
RA1=1;//DFPlayer電源供給
DF_Volume(31);//ここの数値を変えるとボリュームが変わる(0~31)
while(1) {
if (RA3 == 0 ) { //ボタンを押すと
DF_SpecifyPlay(1) ;//関数DF_SpecificPlayに曲番号1を送る
__delay_ms(1000);
SecCounter=0;//スリープに入るまでの時間カウントリセット
}
if (RA4 == 0 ) { //ボタンを押すと
DF_SpecifyPlay(2) ;//関数DF_SpecificPlayに曲番号2を送る
__delay_ms(1000);
SecCounter=0;//スリープに入るまでの時間カウントリセット
}
// SLEEP:電源入力、もしくは最後のボタン操作からN秒後にスリープに入る。
if (SecCounter==300) { // 300秒経過したか?
DF_SpecifyPlay(4) ;//スリーププロセス開始シグナル音
__delay_ms(1000);
RA1=0;//DFPlayer電源オフ
__delay_ms(200);
//PIC スリープの準備
PORTA = 0b00000000 ; // 出力ピンの初期化(全てLOWにする)
GIE=0;//全体割り込み禁止。タイマをオフにしないと状態割り込みが機能しないっぽい。
IOCAN4=1; //RA4の立下りエッジ(RA4==0)検出ON(データシートP133)
IOCAF=0; //割り込みフラグのリセット
IOCIE=1; //このレジスタONで、状態割り込みシーケンスがデバイスをスリープから復帰させる(P133)
//PICスリープ
NOP();
SLEEP();//PICを寝かしつける
NOP();
//PIC復帰(RA4の立下りエッジを検出したら復帰する。復帰すると以降のステートメントが実行される)
IOCIE=0;//割り込みシーケンスによるデバイス復帰を解除
GIE=1;//全体割り込み許可
RA1=1;//DFPlayer起動
__delay_ms(500);//200ms以上待つようにデータシートに記載がある
DF_SpecifyPlay(5);//DFPlayer復帰シグナル音
__delay_ms(500);
while (RA4==0){}//ボタン押しっぱなしの場合離すまでここで足止め
__delay_ms(50);//ボタンを離した瞬間のチャタリング吸収
SecCounter=0;//タイムカウンターリセット
}//sleep処理ここまで
}//while(1)ここまで)
}//メイン関数ここまで
//タイマ割り込み関数
void __interrupt()XXX(void)//コンパイラのバージョンが2.0以降ならこっち
//void interrupt XXX(void)//それ以前ならこっちをつかう
{
if(TMR2IF)//タイマ2による割り込み
{
PR2=99;//PR2初期値99にもどす
Counter=Counter+1;//カウントアップ
if (Counter==100)//カウント100になったら
{
Counter=0;//?リセットしてカウントを0に
MSecCounter=MSecCounter+5; //スリープ用ミリ秒カウンタに5ミリ秒加算
}
if (MSecCounter>=1000) {//1000ミリ秒経ったら
MSecCounter=0;//ミリ秒カウンタリセット
SecCounter=SecCounter+1;//スリープ用秒カウンタ-に1秒加算
}
TMR2IF=0;//タイマ2による割り込みリセット
}
}
//シリアル通信書き込み用関数
void UART_Write(char data)
{
while(TRMT==0);
{
TXREG = data;
}
}
//シリアル通信書き込み用関数ここまで
//DFPlayer用の関数
//ルートディレクトリのn番目の曲を再生する関数
void DF_SpecifyPlay(int n){
UART_Write(0x7E);
UART_Write(0xFF);
UART_Write(0x06);
UART_Write(0x03);
UART_Write(0x00);
UART_Write(0x00);
UART_Write(n);
UART_Write(0xEF);
}
//ボリュームをコントロールする関数
void DF_Volume(int n){
UART_Write(0x7E);
UART_Write(0xFF);
UART_Write(0x06);
UART_Write(0x06);
UART_Write(0x00);
UART_Write(0x00);
UART_Write(n);
UART_Write(0xEF);
}
//DFPlayer用の関数ここまで