秋月電子でVS1011EのMP3デコーダーを見て、MP3プレーヤーを自作できそうだと思いました。ネットで調べたら既に先輩たちが作成した例をみつけて、データシートやネット文書などを参照しながら自分のMP3プレーヤーも作成できました。 VS1011EはMP1、MP2、MP3、WAV、PCMのフォーマットを320 kbit/sまで再生することができます。AVRマイコンからSPI通信回路でVS1011Eに繋いで、コマンドバースでVS1011Eへの制御が可能です。コマンドで主に以下のことを制御することができます。
- 再生開始
- 音量制御
- 低音、高音の強調
構成
再生するMP3ファイルは事前にパソコンからSDカードに保存して、AVRマイコンがSDカードから読込んでVS1011Eに転送します。SDカードのファイルを読込するためにChaN氏のPetit FAT File System Moduleを利用しています。プレーヤー全体の構成は下図のようです。
回路図
ダウンロードはこちら
設計&実装
SPIデータ通信
AVRマイコンとSDカード、AVRマイコンとVS1011E、三つのデバイスの通信は全てSPI方式で行われます。AVRマイコンに内蔵するハードウェアSPI送受信レジスタを利用すると、より簡単に高速かつ安定性の高いSPI通信を実現できます。
ATMEGA328PマイコンのハードウェアSPI通信を利用するには、対象デバイスからの入出力ピンをAVRマイコンのMOSIとMISO端子に接続する必要があります。
- MOSI - Master Output Slave Input
- MISO - Master Input Slave Output
- SS - Slave Select
- SCK - 同期クロック信号
SPI通信レジスタは以下の三つがあります。
- SPSR - SPI通信制御レジスタ
- SPCR - SPI通信設定レジスタ
- SPDR - 送受信データ(8ビット)
SPIレジスタの詳細についてATMEGA328Pのデータシートをご参照ください。
SCK同期クロック接続は省略しています。
複数デバイスのSPI切り替え
SDカードとVS1011EはMISOとMOSIピンを共用しています。AVRマイコンからの通信が発生した際に、通信したいデバイスのSSピンをLOWにする必要があります。つまりSSピンはデバイス選択の役を担当します。このピンももちろん共用できません。
ATMEGA328PはSSピン(PB2)がありますので、SDカードのCSピンに接続します。XCSピンはAVRのどの入出力ピンに接続してもかまいません。発信をする前に、対象デバイスのSelectピンをLOWにします。
SS(PB2)をLOWに、PD3をHIGHに、SDカードと通信するのみにします。
逆に設定するとVS1011Eと通信します。
MP3を再生する際に、この切り替えを頻繁に行います。
SPI有効化
AVRマイコンハードウェアSPI通信の場合、まずハードウェアSPIを有効にする必要があります。
void spi_init() { SPCR = (1<<SPE)|(1<<MSTR)|(0<<SPR0)|(0<<SPR1); }
SPEフラグは有効化、MSTRフラグはAVRマイコンの位置づけをMasterに設定します。他のSPR0とSPR1は念のため0に設定します。
SPI送信
SPI送信のためSPDRレジスタに8ビットのデータを保存し、データを自動的に送信します。但し送信完了まで次のデータを送信できないため、ステータスを確認し送信完了まで待ちます。
また、SPIを送信する際には、同時に受信も行います。受信したデータはSPRDに格納されます。送信処理は常にこの受信したデータを返すので、一部のISP通信には便利です。
uint8_t spi_send( uint8_t byte ) { SPDR = byte; // 送信データ while(!(SPSR & (1<<SPIF))); // 送信完了まで待つ return SPDR; // 受信したデータを返す }
SPI受信
上記のように、SPI送信する際に同時に受信も行いますので、受信するための方法はSPI送信と同じく、Dummyデータを送信すれば受信できます。
uint8_t spi_recv( void ) { SPDR = 0xff; // dummyデータ while(!(SPSR & (1<<SPIF))); // 受信完了まで待つ return SPDR; // 受信したデータを返す }
SDカードの読み取り
ChaN氏のPetit FAT File System Moduleはとても使いやすいFATファイルフォーマットのモジュールです。このモジュールはSDカードに限らず、他のデバイス、例えばネットワーク通信のデバイスでも利用できるように設計されているようです。
つまり、基礎通信層の実装が抽出され、自由に書き換えられる汎用性の高い設計です。このモジュールをSDカードに実装するには、三つの通信関数を実現する必要があります。
初期化関数:
DSTATUS disk_initialize (void);
データ読み取り関数:
DRESULT disk_readp ( BYTE* buff, /* Pointer to the destination object */ DWORD sector, /* Sector number (LBA) */ WORD offset, /* Offset in the sector */ WORD count /* Byte count (bit15:destination) */ );
データ書き込み関数:
#if _USE_WRITE DRESULT disk_writep ( BYTE* buff, /* Pointer to the data to be written, NULL:Initiate/Finalize write operation */ DWORD sc /* Sector number (LBA) or Number of bytes to send */ ); #endif
三つ目のdisk_writep関数は、_USE_WRITE
を定義した場合のみ必須となります。今回のMP3プレーヤーはSDカードの書き込み処理はありませんので、SDかーどに書き込む処理は実装していません。
ChaN氏のサイトにもMMCタイプのSDカードの読み取りサンプルを公開しているので、ネコ技術ではそれを利用しています。(一部小さな改修があります)
SDカードの有効化
関数disk_initializeを実現し、SDカードの有効化を行います。
DSTATUS disk_initialize(void) { BYTE cmd, ty, ocr[4]; UINT tmr; spi_init(); // SPIを有効化 DESELECT_MMC(); // SPI通信対象であるSDカードを選択 _delay_ms(10); /* 80 dummy clocks with CS=H */ for (int n = 10; n; n--) spi_recv(); /* ... MMCプロトコル通信部分を省略 ... */ DESELECT_MMC(); spi_recv(); // SPI受信させ、バッファーをクリアする return ty ? 0 : 1; }
データを読み取り
disk_readp関数を実現し、SDカードの読み取り処理を行います。この部分はChaN氏のMMC通信サンプルプログラムをそのまま利用しています。
クロック同期
VS1011Eの動作周波数は23~24MHzが必要ですので、回路に付ける水晶振動子の周波数は12~13、または23~24MHzが必要です。
12~13MHzの水晶振動子を採用する際に、VS1011Eの2倍クロック設定を有効化する必要があります。VS1011Eを初期化する際に、クロック設定コマンドによって2倍クロックを有効化にすることができます。
VS1011E送受信
VS1011Eはコマンド送信とMP3データ送信、二つの送受信タイプがありますが、どちらもSPI送信で行われます。
コマンド送信プログラム:
void mp3_cmd( BYTE reg, DWORD cmd ) { // コマンドの長さは4バイト BYTE buf[4]; buf[0] = 2; buf[1] = reg; buf[2] = cmd >> 8; buf[3] = cmd; // XCS端子をLOWに設定、送信開始 SELECT_XCS; send_array(buf, 4); // XCS端子をHIGHに設定、送信終了 UNSELECT_XCS; }
VS1011Eにコマンドを送信するには、VS1011EのDREQ端子からの入力信号がHIGHになる必要があります。
#define DREQ _BV(PORTD1) // DREQ端子をAVRのPORTD1に接続 void wait_for_ready() { while(!(PIND & DREQ)) { _delay_ms(1); } }
コマンド定義:
#define MC_MODE 0 #define MC_BASS 2 #define MC_VOL 0xB #define MC_CLOCKF 3
VS1011E初期設定プログラム:
// VS1011Eの準備済みステータスを待つ wait_for_ready(); // VS1011Eを初期化する #define SM_SDINEW 11 mp3_cmd( MC_MODE, _BV(SM_SDINEW) ); wait_for_ready(); // クロック設定:2倍クロックを有効化 mp3_cmd( MC_CLOCKF, 0x9800 ); wait_for_ready(); // 音量設定 mp3_cmd( MC_VOL, 0x5050 ); wait_for_ready(); // 低音強調設定 mp3_cmd( MC_BASS, 0x7AF6 ); wait_for_ready();
プレーヤーMainプログラム:
// SDカードをマウント FATFS fs; int rs = pf_mount( &fs ); if (rs == FR_NOT_READY) blink_led(); // Rootフォルダーを開く DIR dir; rs = pf_opendir( &dir, "/" ); // ファイル読込バッファ #define BUF_LEN 512 BYTE buf[BUF_LEN]; // フォルダーの中のファイルを順次再生する while(1) { rs = pf_readdir(&dir, &fno); if (rs != FR_OK || fno.fname[0] == 0) break; if (fno.fattrib & AM_DIR) continue; once_led(); // 再生開始、LEDを1回点滅させる wait_for_ready(); WORD readBytes = 0; BYTE *pbuf; // ファイルの再生完了までバッファの大きさ毎でデータを送信 while(1) { // ファイルからバッファにデータを読込 rs = pf_read( buf, BUF_LEN, &readBytes ); if(readBytes <= 0) break; pbuf = buf; for(i=0; i<readBytes; i++) { // ATMEGA328のSPI送信バッファにデータを設定 SPDR = *pbuf++; while (!( SPSR & (1<<SPIF) )); while (!( PIND & DREQ )); // VS1011Eのステータスをチェック } } } // 再生完了、SDカードをリリース pf_mount( 0 );
ソースコード
ソースコード一式のダウンロードはこちら。
試作機
私はVS1011E変換モジュールで試作回路を先に作成してそのあとプレーヤー機をはんだ付けで製作しました。試作回路の場合プールアップとプールダウンを一部のIOピンに実装していなかったため、よく再生途中で切れたりSDカードの読込エラーが発生したりしていました。その後のはんだ付け機をきちんと全て実装して想定以上の安定さで動作してくれました。
(写真ははんだ付けの試作機です)
今後の展開
前回の停止時点から自動再生
あるMP3プレーヤーには電源をOFFにしても次回から自動的に前回の停止時点から続けて再生する機能があります。この機能を実現するには、AVRマイコンがVS1011Eに送信する際に、送信しながら送信済みバイト数をカウントしSDカードのあるファイルに書き込み、このファイルを確認するようにします。停止時点のMP3ファイル名が存在したらそのバイト数を飛ばして途中からデータをVS1011Eに送信すれば、VS1011Eも途中から再生することが出来ると思います。この機能今後のバージョンアップの際に追加したいと思っています。
Fade In/Fade Outで耳に優しい
再生開始時いきなり大きい音を出さず最初は小さく、3秒掛けて既定の音量になるという機能をFade Inといいます。途中から再生する場合いきなり大きい音を出すよりFade Inで出したほうがより優しいし格好いいでしょう。VS1011Eは音量コントロールコマンドに対応しているので再生最初から0.2秒ずつで音量コマンドを送信すると、Fade In機能が実現できると思います。
課題
このMP3プレーヤーは、電源供給部分が一番難しいと感じました。SDカードの動作電圧が2.7V以上必要ですので、プレーヤー全体の電源供給を3V以上に設定しています。もし携帯用プレーヤーにする場合二本の単3電池が必要です。これでプレーヤー全体のサイズが大きくなり、重さも増えてしまいます。
そしてより小さい充電池も検討しましたが、安全な充放電回路の作成に工夫が必要だし、小さなスペースにどのように収めるか今でも検討しています。もし何かアドバイスがありましたら、ぜひご連絡ください。
回路図、ソースコードを参照、あるいは利用する際は、ご自身の責任において行ってください。
<更新履歴>
- 2014年10月26日 – クロック設定部分を追加、ソースコード一式をアップロード
- 2014年10月23日 – SPI通信部分を追加
- 2014年6月3日 – 掲載開始
今部活で使う装置(エタノールロケット発射台)を設計中で、
その中にmp3再生回路を内蔵する予定なのですが、残念ながら
Arduinoの開発環境しかありません。ネットで調べたらArduinoで
作った例もありましたが回路図非公開では作れない。
スケッチは公開されているので解読したがUI関係が分からない。
チップは同じなので、このソースコードをArduinoスケッチに変換する
方法はありますでしょうか。