Skip to main content

AVRマイコン+VS1011E 自作MP3プレーヤー

秋月電子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を利用しています。プレーヤー全体の構成は下図のようです。

outline

回路図

TSMP3-0.0.1-ALPHA

ダウンロードはこちら

設計&実装

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のデータシートをご参照ください。

spi_bus

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カードと通信するのみにします。

spi_sdcard

逆に設定するとVS1011Eと通信します。

spi_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カードの読込エラーが発生したりしていました。その後のはんだ付け機をきちんと全て実装して想定以上の安定さで動作してくれました。

464374_3663331904728_1888215457_o IMGP2745

(写真ははんだ付けの試作機です)

今後の展開

前回の停止時点から自動再生

ある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日 – 掲載開始

Jingwood

北海道の田舎で暮らしているプログラマーです。最近山登りにハマりました。

2 thoughts to “AVRマイコン+VS1011E 自作MP3プレーヤー”

  1. 今部活で使う装置(エタノールロケット発射台)を設計中で、
    その中にmp3再生回路を内蔵する予定なのですが、残念ながら
    Arduinoの開発環境しかありません。ネットで調べたらArduinoで
    作った例もありましたが回路図非公開では作れない。
    スケッチは公開されているので解読したがUI関係が分からない。

    チップは同じなので、このソースコードをArduinoスケッチに変換する
    方法はありますでしょうか。

Leave a Reply

Your email address will not be published. Required fields are marked *