※旧スタッフブログから移設転記記事となります。
ATtiny13aによるSleep タイマー付き DFPlayer mini 再生環境の構築
■概要
まず、概要からご説明します。
SNOOPY & FRIENDS DIORAMAという、加藤工芸社製のジオラマ電子オルゴールの修理依頼がありました。
過去に1度修理を行った経験がありますが、残念ながら、COBの制御ICの故障で元の状態には修理できませんでした。記事は、こちら。
そこで、DFPlayer miniというMP3/WAVプレーヤーを換装内蔵して代わりに演奏をしようという修理をいろいろ検討しておりました。詳しい作業内容は、こちらを参照ください。
さて、今回は、その修理記事を参照された方から、お手元にある同じジオラマ 2台も同じように光らなくなり音楽も演奏しなくなったとのことで修理して欲しいとの依頼がありました。詳しい作業内容は、こちらを参照ください。
2台のどちらも、制御ICの故障で交換もできませんでしたので、初回と同様にDFPlayer miniでの換装をご提案したのですが、お気に入りのスヌーピーの音楽が聴けないのなら、換装は不要とのことでした。
さて、今回のスタッフブログは、実は、その修理に際し、旧来のDFPLyer miniの再生環境をバージョンアップ検討をしていたので、実際のインプリはできませんでしたが、最新のATtiny13aによるDFPLyer miniの再生環境についてご紹介します。 検討の経緯を時系列にすると、以下のような順序です。
- 初回は、作業時間の関係でATtiny85による単なるSDカードに保存されている音楽ファイルを再生するといったものであった。こちらを参照。
- 制御ICをより廉価なATtiny13aに移殖し、曲のスキップ機能をSW-420の振動センサーで実装。こちらを参照。
- 追加機能にタイマーによるSleep機能の追加とSleep時の消費電力低減の効果向上。←今回のご紹介記事内容。
さて、今回のバージョンアップとは、どのような内容かをご紹介します。
制御ICは、ATtiny13aのままで、振動センサーもSW-420のままとなりますが、タイマー機能とそれに伴う、タイマー切れ時のSleep機能を追加しました。
このSleep機能とは、当初も検討をしていたのですが、長らく宿題にしていた検案でした。
それは、なぜか?
まず、タイマー機能は、SNOOPY & FRIENDS DIORAMAをご覧になられたことのある方は、ご存知かもしれませんが、しばらく演奏を楽しむジオラマですので、うっかりウトウトと居眠りや眠ってしまった場合、乾電池が減ってしまいます。
ジオラマの取説には、タイマー機能は、見つからなかったのですが、恐らくですが、曲が一巡すると止まるのだと信じています。
取説には、その旨の記載が無く、永遠ループ再生は、仕様的にあり得ないですからね。
ATtinyで換装した場合も、一巡したら演奏停止させてもいいのですが、繰り返し演奏中に、スキップコマンドを発行し、次の曲を演奏すると、その曲の演奏の終了と共に繰り返し演奏モードが解除され、そこで止まってしまう事態に遭遇しました。
さて、困りましたが、であればということで、タイマー機能を実装してしまおうと考えたまででした。
停止時は、Stopコマンドの発行同様に待機状態なので、そのままでは、消費電力も抑えることができません。
そこで、タイマーで停止した際、ジオラマ全体をSleepさせ乾電池の消費を抑えることにしました。
タイマー機能ですが、最終スキップコマンドから1時間の演奏でDFPlayerとSW-420の電源を落とし、ATtimy13aもSleepするという流れです。
では、機能方針が決まりました。それぞれの項目についてさらに検討します。 では、回路の検討とプログラムの検討に項目を分けます。
■回路検討
後述のタイマーなどプログラムが完成したという前提で回路を検討します。 では、Sleep時の消費電力削減について検討します。
機能追加前のDFPlayer mini再生環境のブロック図です。
ブロックは、大きく分けて、ATtiny13a, SW-420, DFPlayer miniの3ブロックです。
最初に、DFPlayer miniの消費電力についてです。
平時の再生時は致し方ないですが、前述のとおりタイマー切れ後は、やはりSleepしてもらうのですが、このDFPlayer miniは、Sleepコマンドを発行して止めても約15mA流れっぱなしです。※実測は、12mA@Vcc = 4.5V 程でした。
うーん、Sleepしてても結構流れており、電池が消耗します。こちらのマニュアルの5.2 の注意点の5にもっと消費電力を落としたければ、ユーザで電源を制御しろとあります。
そこで、ATtinyでDFPleyerとSW-420の電源をSleep後にスイッチングして完全に切ろうと思います。回路を検討します。
SNOOPY&FRIENDS DIORAMAの回路図を解析すると、LEDの点滅モジュールの起動管理をCOBのICが担っており、S8050を負極電源のスイッチングに使用しておりました。ベース抵抗は、300Ωが付いておりましたが、同様にATtiny13aでもトランジスタのスイッチングで電源管理を行います。
IC電流のMax値も考え、スイッチングトランジスタとしては、まずはS8050の使用の前提でベース制御をATtiny13aのポートで行います。
検討回路でベース抵抗の適正値を計算します。
S8050のVBE(sat) = 1.2Vです。hfeは、使用するDランク品で 160-300です。飽和域にて温度変化も考慮し、hfe minの1/2程度の80でも安定してスイッチングできるようにします。
ATtiny13aのGPIOのVOH/IOHは、マージンを考慮し3.5V, 20mAとします。
IB = (3.5 – VBE(sat))/ベース抵抗 R = (3.5 -1.2) / R = 2.3 / R hfe = 80として計算します。
DFPlayer miniの動作時の電流を計測すると、100mA程度流れます。
ですが、モノラルアンプも搭載しており、スピーカーも鳴らしますし、何より起動時や瞬時の過大電流も流せるように設計しないと、瞬停による誤停止(ビーと鳴る)します。
これは、DFPlayer自身が瞬停した場合もそうですが、ATtiny13aの瞬停による再起動とコマンドの再発行による誤動作も含まれます。
安定して動作するためには、(2.3/R) * 80 > 500mA 以上は欲しいです。計算すると、R = 368Ωですので、300Ωとして確認計算します。※なんか、モータードライバ並みです。
IB = 2.3/300 = 7.6mA ・・・IOH規定以内です。
hfe = 80でなら、IC = 613mAとなります。イケそうです。
ここで、前提としていたS8050のICの定格(500mA)を超えますので、SS8050(IC = 1.5A)を選びます。 ※因みに、当初起動電流の読みを誤り、IC = 200mA程度で検討していたのですが、やはり起動できませんでした。
さて、話を負極電源のスイッチングに戻します。以下の回路を検討します。※注意、電源パスコンは、適宜挿入ください。
ATtiny13aは、旧来回路時は、振動検知の割り込み以外は、パワーダウンしてもらっていましたが、タイマー機能を追加するので、常にカウンターとして機能してもらい、振動検知時のスキップコマンド発行と電源制御を別途追加します。
ATtiny13aのポートから起動初期に制御信号を出してスイッチングトランジスタをONにしますが、負極での電源制御は、結局NGでした。
図中の緑の点線の経路にSleep時にGNDのパスが形成されトランジスタをOFFにしてもTXライン経由で電流が流れてやはり12mA程度流れっぱなしになります。
Sleep後にTXを抜くと0mAとなります。ということで、負極ではなく、正極にスイッチングトランジスタを挿入します。
これで無事SleepにDFPlayerとSW-420の電源断を制御できるようになりました。
回路図の電源スライドスイッチは、実際は、おもちゃの電源スイッチに置き換わります。 ですが、注意が必要です。
回路図にも記載している通り、SNOOPY&FRIENDS DIORAMAの電池ボックスは、単三の乾電池3本が制約となります。
SS8050のVCE(sat) = 0.5V(max)なので、電源の4.5V – 0.5V = 4.0Vが、計算上は供給電源になります。
回路を組み電圧を計測したところ、VCE(sat) = 0.5Vですが、実測はDFPlayer miniは、3.6Vで可動していました。
本回路には、振動センサーやAttiny13aもぶら下がっているので、どこらかしこで電流が食われ電圧の降下が起きているようです。
乾電池で動作確認をしていると、電源が約4.0Vを切ったあたりで、DFPlayer mini向けの電圧が足りなくなく、例の”ブー”っというノイズで力尽きます。
DFPlayer miniのマニュアルによると、4.2Vでの動作が推奨されており、安定動作のためには、他の改善策が必要という感じです。
Sleep機能をバイポーラトランジスタで実装設計する場合、VCE分の電圧降下が生じます。
このVCE分の電圧降下は、電源のパスコンの挿入位置や容量にも影響しますので、実装方法とパスコンと電源回路はセットで検討しないといけません。 補足として、DFPlayerの推奨電圧で動作させるためには、その分の電源電圧を上げる必要もあります。
DFPlayerは、食いしん坊なので、演奏時の急峻な電圧降下で自身がハングアップしてしまう事態も考えると、根本的な電源回路の設計が必要です。
今回の検討時にもATtinyの電源とその他の電源分離も試行しました。
電池ボックスの電源は、DFPlayerとSW-420に供給し、ATtiny13aには、大容量の電解コンデンサを設置しショットキーバリアダイオードで分離するといった、分離回路によくある回路構成です。ですが、先の電源である電池ボックスの単三乾電池3本の制約もあり、ダイオードのVf分の引き算とSS8050のVCE分をそれぞれ差し引くと、既に3.0Vを切ってしまいDFPlayerでの実装が不可能であることがわかります。
これも今後の宿題としたいと思います。 最後に、一応念のためですが、電源スライドスイッチの620Ωの抵抗ですが、ATtiny13a用の220uFのディスチャージ用で、これがないとSleep後に電源を入れ直してもATtiny13aが、220uFの電流でSleepしたままになるのを防ぐためです。 さて、これで回路の構成は、一旦Fixとします。
次にプログラムの検討について紹介します。
■プログラム検討
機能追加前のプログラムはこちら紹介しているのですが、新たに機能追加してことでATtiny13aのFlashの容量をオーバーしてしまいました。
開発環境は、Arduino IDEにATtiny13aのマイコン設定をしてArduinoとして開発をしていたのですが、やはりオーバーヘッドが大きくなるので、今回は、全て非ArduinoとしてArduinoのライブラリは、使用しないことにしました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
// DFPlayer mini with Timer by ATtiny13a v1.00 // Target device : ATtiny13a // Target Frequency : 1.2MHz // Date : 2020/10/13 // Author : Takishita Tadayoshi // /* Pin definition * 1 : Reset * 2 : D3 DFPlayer mini power Tr * 3 : D4 SW-420 power Tr * 4 : GND * 5 : D0 * 6 : D1 Vibration input SW-420 @ Internal Pull-down * 7 : D2 TX * 8 : Vcc * Arduino IDE compile : ATtiny 13a @Internal 1.2MHz * Fuse : Default */ // RST 1.--+ 8 VCC // Power Tr DFPLayer PB3 2+ + 7 PB2 TX // Power Tr SW-420 PB4 3+ + 6 PB1 Vibration input // GND 4+--+ 5 PB0 #include <DFPlayer_Mini_ATtiny13a> #include <avr/io> #include <avr/interrupt> #include <util/delay_basic> #include <avr/sleep> // The follow info define in ATtiny13a_Serial.S/ATtiny13a_Serial.h // BAUD_RATE 76800 // The follow info define in ATtiny13a_Serial.S/ATtiny13a_Serial.S // TX 2 // RX 3 // It's an unavailable timer0 overflow interrupt vector under Arduino IDE bitDuino13 environment. // ex. ISR(TIM0_OVF_vect) {...} // DFPlayer parameter #define VAL_LEVEL_0 0 #define VAL_LEVEL_10 10 #define VAL_LEVEL_15 15 #define VAL_LEVEL_20 20 #define VAL_LEVEL_25 25 #define VAL_LEVEL_30 30 #define NORMAL_EQ 0 #define POP_EQ 1 #define ROCK_EQ 2 #define JAZZ_EQ 3 #define CLASSIC_EQ 4 #define BASE_EQ 5 #define T5M 1304 #define T15M 3913 #define T30M 7826 #define T60M 15652 // Globals uint32_t min_timer_count = 0; ISR (TIM0_COMPA_vect) { // Timer0 compare A interrupt vector min_timer_count++; if ( min_timer_count == T60M ) { // 60min sleep timer mp3_sleep(); // DFPlayer sleep delayDeciseconds1M2Hz( 10 ); // Wait 1sec mp3_set_volume (VAL_LEVEL_0); // Volume level 0 delayDeciseconds1M2Hz( 10 ); // Wait 1sec PORTB &= (0<<PB4) | (0<<PB3); // SW-420 Power down & LED module Power down MCUCR = (1<<SE) | (1<<SM1) | (0<<SM0); // Enable power down sleep mode, Low level detection sleep_mode(); } } int main () { cli(); OSCCAL = 92; DDRB = 0b111101; // PB1 input PORTB = 0b000000; // PB1 Pull down TIMSK0 = 0; // Timer/Counter interrupt disable TCCR0A = 0; // Normal mode TCCR0B = (1<<CS02) | (1<<CS00); // 1/1024 Prescale OCR0A = 255; // Timer A interrupt TOP = 0xFF PORTB |= (1<<PB4) | (1<<PB3); // SW-420 Power up & LED module Power up delayDeciseconds1M2Hz( 20 ); // Wait 2sec // DFPlayer mp3_set_EQ (ROCK_EQ); // ROCK EQ delayDeciseconds1M2Hz( 5 ); // Wait 0.5sec mp3_set_volume (VAL_LEVEL_15); // Volume level 15 //mp3_play_file_in_folder(0x01, 0x0B); // 01(0x01) folder, 11(0x0B) truck //mp3_random_play (); // Random play on delayDeciseconds1M2Hz( 5 ); // Wait 0.5sec mp3_play_repeat_all(); // Repeat all //mp3_random_play(); // Random play TIMSK0 |= (1<<OCIE0A); // Enable compare match A interrupt sei(); // Enable global interrupt while(1) { if ( (PINB & 0x02) == 0x02 ) { mp3_next(); min_timer_count = 0; // 60min timer reset delayDeciseconds1M2Hz( 20 ); // Wait 2sec for chattering } } } void delayDeciseconds1M2Hz( uint16_t ds ) { // 0.1sec delay @ 1.2Mhz while( ds-- ) _delay_loop_2( 30000 ); // 25000 if @ 1Mhz } // The end of file. |
タイマー機能は、Timer0のオーバーフロー割込みの回数をカウントして、規定回数で割込みを発生させ、Sleepさせることにしていましたが、ここで思わぬトラップにハマりました。
ISR(TIM0_OVF_vect) {…}が、自分の環境では使用できませんでした。 いろいろ調べてみたのですが、Arduino IDE環境にbitDuino13を適用して使用すると、delay系のライブラリ内で既にISR(TIM0_OVF_vect){…}が使用されており、どうもそのせいで使用不可のようだと勝手に自己完結しています。
確認したファイルは、…\Arduino15\packages\bitDuino13\hardware\avr\1.0.1\cores\arduino配下のwiring_time.cを参照すると、確かに適用されていますが、真偽は定かではありません。※事由をご存知の方は、教えて頂けると助かります。
さて、さて仕方ないので、OCR0A = 255 としてCOMPAの割り込みを使用することでTimer0のオーバーフローと同じ動作をさせています。 規定回数の割り込み発生をカウントしたら、DFPlayer miniをsleepさせて、電源を落として自身がSleep(パワーダウンモード)します。スキップ時は、規定回数のカウントは、初期化します。 以上が、今回検討した内容の紹介になります。