2019年3月13日 星期三

模組介紹 : faya MP3模組


學習目標 : 認識faya MP3模組的功能及使用方式
學習時間 : 60~90 mins
示範模組 : (1) faya brickNano
                   (2) faya MP3模組
                   (3) faya SD卡模組
                   (4) faya雙聲道喇叭模組 (或自備耳機)
工具 : 積木底板、音源線、SD卡


====================功能介紹====================
faya MP3模組使用MP3解碼晶片(代號VS1053B),透過簡單的接線後,只要對第三方所提供的函式下指令,就能從SD卡讀取MP3檔案並播放,函示庫可以到這裡下載,解壓縮後放在Arduino資料夾裡的library裡面。


介紹指令前,先看一下faya MP3模組的外觀,它的左邊有七個埠,分別是RST、DREQ、MISO、MOSI、SCK、XDCS、XCS,其中MISO、MOSI、SCK屬於SPI通訊協定,用來和Arduion互相溝通。

Arduino 的SPI通訊有固定的腳位,對於 faya brickNano而言,我們要把SCK接在D13位置、MISO接在D12位置、MOSI接在D11的位置。

接下來介紹使用指令

  • #include <SFEMP3Shield.h>
使用MP3模組函示庫,還需要引入SD與SPI的函示庫。
  • #include <SdFat.h>
  • #include <SdFatUtil.h>
使用SD卡模組函示庫。
  • #include <SPI.h>
引入SPI介面。 
  • SdFat 物件名稱;
此指令建構了SD卡模組的物件,物件名稱 : 可自行命名。 
  • SFEMP3Shield 物件名稱;
此指令建構了MP3模組的物件,物件名稱 : 可自行命名。  
其他函式功能,大家可以透過Libraries裡的.h檔案,查看呼叫方式

====================原理知識====================
以下解釋提供給有需要知道背後原理的人:


VS1053B將Ogg/MP3/AAC/WMA/MIDI解碼電路整合在一個系統晶片內,並且搭載一DSP控制器,負責處理內部訊號以及外界的溝通。使用者只要外接電阻、電容以及震盪器,就能利用外部的控制器 (Arduino)控制並接收訊號。需要詳細資訊的讀者,可以參考官方的datasheet

模組電路圖如下所示:


====================範例實作(一)====================
了解模組功能(原理)後,我們用以下範例來展示模組的功能,我們直接利用官方所提供的程式,加了些註解,讓使用者可以看到MP3的函式的全部功能,未來要使用此模組時,直接複製裡面的函示來用即可。

目標:
(1) 利用MP3模組撥放SD卡裡面的音樂
(2) 使用Serial Monitor,控制MP3模組的撥放與各種功能
(3) 利用faya雙聲道喇叭模組撥放MP3的音樂

接線:
(1) 電源線連接如下圖所示,連接的說明請看這篇文章簡易版


(2) 訊號線連接
     
     SD卡模組接法如下 : 
                   Arduino_D9   ===> SD卡模組_CS
                   Arduino_D11 ===> SD卡模組_MOSI
                   Arduino_D13 ===> SD卡模組_SCLK
                   Arduino_D12 ===> SD卡模組_MISO
     
     MP3模組接法如下 : 
                   Arduino_D8   ===> MP3模組_RST
                   Arduino_D2   ===> MP3模組_DREQ
                   Arduino_D12 ===> MP3模組_MISO
                   Arduino_D11 ===> MP3模組_MOSI
                   Arduino_D13 ===> MP3模組_SCK
                   Arduino_D7   ===> MP3模組_XDCS
                   Arduino_D6   ===> MP3模組_XCS               

需要連接的訊號線比往常都還要多,如果要連的整齊一點,可以參考以下連接方式

首先將MP3和SD卡模組的SPI介面MOSI, SCLK, MISO對連,注意到MP3模組的SCLK標示為SCK,如下圖中紫色的單芯線。


接著將MP3模組的RST, DREQ, XDCS, XS分別連到Arduino的D8, D2, D7, D6腳位,如下圖中紅色單芯線


然後將MP3模組的MISO和MOSI分別連到Arduino的D12和D11腳位,如下圖中的黃色單芯線


最後將SD卡模組的CS和SCLK分別連接到Arduino的D9和D13腳位,如下圖中的綠色單芯線

             
(3) 音源線連接 - 將音源線插入MP3模組和雙聲道喇叭模組的音源座


安裝SD卡:
將MP3檔案透過讀卡機或筆電的SD卡插槽複製到SD卡裡面,MP3的檔名必須符合程式的格式,從TRACK000.MP3開始編起,第一首歌TRACK000.mp3,第二首歌TRACK001.MP3,依此類推,複製完成後,再將SD卡插到SD卡模組。


範例程式:
// 2018/02/08
// Faya-Nugget 範例程式 (mp3_1.ino)
// 單元: 模組介紹-faya MP3模組
// 網址: https://fayalab.blogspot.com/2019/03/mp3.html
// 目標: (1) 利用MP3模組撥放SD卡裡面的音樂
// (2) 使用Serial Monitor,控制MP3模組的撥放與各種功能
// (3) 利用faya雙聲道喇叭模組撥放MP3的音樂
// 接線: Arduino ==> faya模組
// D9 ==> SD卡模組_CS
// D11 ==> SD卡模組_MOSI
// D13 ==> SD卡模組_SCLK
// D12 ==> SD卡模組_MISO
// D8 ==> MP3模組_RST
// D2 ==> MP3模組_DREQ
// D12 ==> MP3模組_MISO
// D11 ==> MP3模組_MOSI
// D13 ==> MP3模組_SCK
// D7 ==> MP3模組_XDCS
// D6 ==> MP3模組_XCS
#include <SPI.h> //匯入SPI的library
#include <SdFat.h> //匯入SD卡模組的library
#include <SdFatUtil.h>
#include <SFEMP3Shield.h> //匯入MP3模組的library
// Below is not needed if interrupt driven. Safe to remove if not using.
#if defined(USE_MP3_REFILL_MEANS) && USE_MP3_REFILL_MEANS == USE_MP3_Timer1
#include <TimerOne.h>
#elif defined(USE_MP3_REFILL_MEANS) && USE_MP3_REFILL_MEANS == USE_MP3_SimpleTimer
#include <SimpleTimer.h>
#endif
/**
* \brief Object instancing the SdFat library.
*
* principal object for handling all SdCard functions.
*/
SdFat sd; //建構SdFat物件並定義
/**
* \brief Object instancing the SFEMP3Shield library.
*
* principal object for handling all the attributes, members and functions for the library.
*/
SFEMP3Shield MP3player; //建構SFEMP3Shield物件並定義
int16_t last_ms_char; // milliseconds of last recieved character from Serial port. Serial port最後收到的字符的毫秒數
int8_t buffer_pos; // next position to recieve character from Serial port. 下一個從Serial port接收字符的位置。
//------------------------------------------------------------------------------
/**
* \brief Setup the Arduino Chip's feature for our use.
*
* After Arduino's kernel has booted initialize basic features for this
* application, such as Serial port and MP3player objects with .begin.
* Along with displaying the Help Menu.
*
* \note returned Error codes are typically passed up from MP3player.
* Whicn in turns creates and initializes the SdCard objects.
*
* \see
* \ref Error_Codes
*/
char buffer[6]; // 0-35K+null 建立字元BUFFER存放
void setup() {
uint8_t result; //result code from some function as to be tested at later time.
Serial.begin(115200);
Serial.print(F("F_CPU = "));
Serial.println(F_CPU);
Serial.print(F("Free RAM = ")); // available in Version 1.0 F() bases the string to into Flash, to use less SRAM.
Serial.print(FreeRam(), DEC); // FreeRam() is provided by SdFatUtil.h
Serial.println(F(" Should be a base line of 1017, on ATmega328 when using INTx"));
//Initialize the SdCard. 初始化SD卡
if(!sd.begin(SD_SEL, SPI_FULL_SPEED)) sd.initErrorHalt();
// depending upon your SdCard environment, SPI_HAVE_SPEED may work better.
if(!sd.chdir("/")) sd.errorHalt("sd.chdir");
//Initialize the MP3 Player Shield 初始化MP3模組
result = MP3player.begin();
//check result, see readme for error codes.
if(result != 0) {
Serial.print(F("Error code: "));
Serial.print(result);
Serial.println(F(" when trying to start MP3 player"));
if( result == 6 ) {
Serial.println(F("Warning: patch file not found, skipping.")); // can be removed for space, if needed.
Serial.println(F("Use the \"d\" command to verify SdCard can be read")); // can be removed for space, if needed.
}
}
#if (0)
// Typically not used by most shields, hence commented out.
Serial.println(F("Applying ADMixer patch."));
if(MP3player.ADMixerLoad("admxster.053") == 0) {
Serial.println(F("Setting ADMixer Volume."));
MP3player.ADMixerVol(-3);
}
#endif
help(); // 呼叫help指令
last_ms_char = millis(); // stroke the inter character timeout.
buffer_pos = 0; // start the command string at zero length.
parse_menu('l'); // display the list of files to play
}
//------------------------------------------------------------------------------
/**
* \brief Main Loop the Arduino Chip
*
* This is called at the end of Arduino kernel's main loop before recycling.
* And is where the user's serial input of bytes are read and analyzed by
* parsed_menu.
*
* Additionally, if the means of refilling is not interrupt based then the
* MP3player object is serviced with the availaible function.
*
* \note Actual examples of the libraries public functions are implemented in
* the parse_menu() function.
*/
void loop() {
// Below is only needed if not interrupt driven. Safe to remove if not using.
#if defined(USE_MP3_REFILL_MEANS) \
&& ( (USE_MP3_REFILL_MEANS == USE_MP3_SimpleTimer) \
|| (USE_MP3_REFILL_MEANS == USE_MP3_Polled) )
MP3player.available();
#endif
char inByte;
if (Serial.available() > 0) {
inByte = Serial.read();
if ((0x20 <= inByte) && (inByte <= 0x126)) { // strip off non-ASCII, such as CR or LF
if (isDigit(inByte)) { // macro for ((inByte >= '0') && (inByte <= '9'))
// else if it is a number, add it to the string
buffer[buffer_pos++] = inByte;
} else {
// input char is a letter command
buffer_pos = 0;
parse_menu(inByte);
}
buffer[buffer_pos] = 0; // update end of line
last_ms_char = millis(); // stroke the inter character timeout.
}
} else if ((millis() - last_ms_char) > 500 && ( buffer_pos > 0 )) {
// ICT expired and have something
if (buffer_pos == 1) {
// look for single byte (non-number) menu commands
parse_menu(buffer[buffer_pos - 1]);
} else if (buffer_pos > 5) {
// dump if entered command is greater then uint16_t
Serial.println(F("Ignored, Number is Too Big!"));
} else {
// otherwise its a number, scan through files looking for matching index.
int16_t fn_index = atoi(buffer);
SdFile file;
char filename[13];
sd.chdir("/",true);
uint16_t count = 1;
while (file.openNext(sd.vwd(),O_READ))
{
file.getFilename(filename);
if ( isFnMusic(filename) ) {
if (count == fn_index) {
Serial.print(F("Index "));
SerialPrintPaddedNumber(count, 5 );
Serial.print(F(": "));
Serial.println(filename);
Serial.print(F("Playing filename: "));
Serial.println(filename);
int8_t result = MP3player.playMP3(filename);
//check result, see readme for error codes.
if(result != 0) {
Serial.print(F("Error code: "));
Serial.print(result);
Serial.println(F(" when trying to play track"));
}
char title[30]; // buffer to contain the extract the Title from the current filehandles
char artist[30]; // buffer to contain the extract the artist name from the current filehandles
char album[30]; // buffer to contain the extract the album name from the current filehandles
MP3player.trackTitle((char*)&title);
MP3player.trackArtist((char*)&artist);
MP3player.trackAlbum((char*)&album);
//print out the arrays of track information
Serial.write((byte*)&title, 30);
Serial.println();
Serial.print(F("by: "));
Serial.write((byte*)&artist, 30);
Serial.println();
Serial.print(F("Album: "));
Serial.write((byte*)&album, 30);
Serial.println();
break;
}
count++;
}
file.close();
}
}
//reset buffer to start over
buffer_pos = 0;
buffer[buffer_pos] = 0; // delimit
}
delay(100);
}
uint32_t millis_prv;
//------------------------------------------------------------------------------
/**
* \brief Decode the Menu.
*
* Parses through the characters of the users input, executing corresponding
* MP3player library functions and features then displaying a brief menu and
* prompting for next input command.
*/
void parse_menu(byte key_command) {
uint8_t result; // result code from some function as to be tested at later time.
// Note these buffer may be desired to exist globably.
// but do take much space if only needed temporarily, hence they are here.
char title[30]; // buffer to contain the extract the Title from the current filehandles
char artist[30]; // buffer to contain the extract the artist name from the current filehandles
char album[30]; // buffer to contain the extract the album name from the current filehandles
Serial.print(F("Received command: "));
Serial.write(key_command);
Serial.println(F(" "));
//if s, stop the current track
if(key_command == 's') {
Serial.println(F("Stopping"));
MP3player.stopTrack();
//if 1-9, play corresponding track
} else if(key_command >= '1' && key_command <= '9') {
//convert ascii numbers to real numbers
key_command = key_command - 48;
#if USE_MULTIPLE_CARDS
sd.chvol(); // assign desired sdcard's volume.
#endif
//tell the MP3 Shield to play a track
result = MP3player.playTrack(key_command);
//check result, see readme for error codes.
if(result != 0) {
Serial.print(F("Error code: "));
Serial.print(result);
Serial.println(F(" when trying to play track"));
} else {
Serial.println(F("Playing:"));
//we can get track info by using the following functions and arguments
//the functions will extract the requested information, and put it in the array we pass in
MP3player.trackTitle((char*)&title);
MP3player.trackArtist((char*)&artist);
MP3player.trackAlbum((char*)&album);
//print out the arrays of track information
Serial.write((byte*)&title, 30);
Serial.println();
Serial.print(F("by: "));
Serial.write((byte*)&artist, 30);
Serial.println();
Serial.print(F("Album: "));
Serial.write((byte*)&album, 30);
Serial.println();
}
//if +/- to change volume
} else if((key_command == '-') || (key_command == '+')) {
union twobyte mp3_vol; // create key_command existing variable that can be both word and double byte of left and right.
mp3_vol.word = MP3player.getVolume(); // returns a double uint8_t of Left and Right packed into int16_t
if(key_command == '-') { // note dB is negative
// assume equal balance and use byte[1] for math
if(mp3_vol.byte[1] >= 254) { // range check
mp3_vol.byte[1] = 254;
} else {
mp3_vol.byte[1] += 2; // keep it simpler with whole dB's
}
} else {
if(mp3_vol.byte[1] <= 2) { // range check
mp3_vol.byte[1] = 2;
} else {
mp3_vol.byte[1] -= 2;
}
}
// push byte[1] into both left and right assuming equal balance.
MP3player.setVolume(mp3_vol.byte[1], mp3_vol.byte[1]); // commit new volume
Serial.print(F("Volume changed to -"));
Serial.print(mp3_vol.byte[1]>>1, 1);
Serial.println(F("[dB]"));
//if < or > to change Play Speed
} else if((key_command == '>') || (key_command == '<')) {
uint16_t playspeed = MP3player.getPlaySpeed(); // create key_command existing variable
// note playspeed of Zero is equal to ONE, normal speed.
if(key_command == '>') { // note dB is negative
// assume equal balance and use byte[1] for math
if(playspeed >= 254) { // range check
playspeed = 5;
} else {
playspeed += 1; // keep it simpler with whole dB's
}
} else {
if(playspeed == 0) { // range check
playspeed = 0;
} else {
playspeed -= 1;
}
}
MP3player.setPlaySpeed(playspeed); // commit new playspeed
Serial.print(F("playspeed to "));
Serial.println(playspeed, DEC);
/* Alterativly, you could call a track by it's file name by using playMP3(filename);
But you must stick to 8.1 filenames, only 8 characters long, and 3 for the extension */
} else if(key_command == 'f' || key_command == 'F') {
uint32_t offset = 0;
if (key_command == 'F') {
offset = 2000;
}
//create a string with the filename
char trackName[] = "track001.mp3";
#if USE_MULTIPLE_CARDS
sd.chvol(); // assign desired sdcard's volume.
#endif
//tell the MP3 Shield to play that file
result = MP3player.playMP3(trackName, offset);
//check result, see readme for error codes.
if(result != 0) {
Serial.print(F("Error code: "));
Serial.print(result);
Serial.println(F(" when trying to play track"));
}
/* Display the file on the SdCard */
} else if(key_command == 'd') {
if(!MP3player.isPlaying()) {
// prevent root.ls when playing, something locks the dump. but keeps playing.
// yes, I have tried another unique instance with same results.
// something about SdFat and its 500byte cache.
Serial.println(F("Files found (name date time size):"));
sd.ls(LS_R | LS_DATE | LS_SIZE);
} else {
Serial.println(F("Busy Playing Files, try again later."));
}
/* Get and Display the Audio Information */
} else if(key_command == 'i') {
MP3player.getAudioInfo();
} else if(key_command == 'p') {
if( MP3player.getState() == playback) {
MP3player.pauseMusic();
Serial.println(F("Pausing"));
} else if( MP3player.getState() == paused_playback) {
MP3player.resumeMusic();
Serial.println(F("Resuming"));
} else {
Serial.println(F("Not Playing!"));
}
} else if(key_command == 't') {
int8_t teststate = MP3player.enableTestSineWave(126);
if(teststate == -1) {
Serial.println(F("Un-Available while playing music or chip in reset."));
} else if(teststate == 1) {
Serial.println(F("Enabling Test Sine Wave"));
} else if(teststate == 2) {
MP3player.disableTestSineWave();
Serial.println(F("Disabling Test Sine Wave"));
}
} else if(key_command == 'S') {
Serial.println(F("Current State of VS10xx is."));
Serial.print(F("isPlaying() = "));
Serial.println(MP3player.isPlaying());
Serial.print(F("getState() = "));
switch (MP3player.getState()) {
case uninitialized:
Serial.print(F("uninitialized"));
break;
case initialized:
Serial.print(F("initialized"));
break;
case deactivated:
Serial.print(F("deactivated"));
break;
case loading:
Serial.print(F("loading"));
break;
case ready:
Serial.print(F("ready"));
break;
case playback:
Serial.print(F("playback"));
break;
case paused_playback:
Serial.print(F("paused_playback"));
break;
case testing_memory:
Serial.print(F("testing_memory"));
break;
case testing_sinewave:
Serial.print(F("testing_sinewave"));
break;
}
Serial.println();
} else if(key_command == 'b') {
Serial.println(F("Playing Static MIDI file."));
MP3player.SendSingleMIDInote();
Serial.println(F("Ended Static MIDI file."));
#if !defined(__AVR_ATmega32U4__)
} else if(key_command == 'm') {
uint16_t teststate = MP3player.memoryTest();
if(teststate == -1) {
Serial.println(F("Un-Available while playing music or chip in reset."));
} else if(teststate == 2) {
teststate = MP3player.disableTestSineWave();
Serial.println(F("Un-Available while Sine Wave Test"));
} else {
Serial.print(F("Memory Test Results = "));
Serial.println(teststate, HEX);
Serial.println(F("Result should be 0x83FF."));
Serial.println(F("Reset is needed to recover to normal operation"));
}
} else if(key_command == 'e') {
uint8_t earspeaker = MP3player.getEarSpeaker();
if(earspeaker >= 3){
earspeaker = 0;
} else {
earspeaker++;
}
MP3player.setEarSpeaker(earspeaker); // commit new earspeaker
Serial.print(F("earspeaker to "));
Serial.println(earspeaker, DEC);
} else if(key_command == 'r') {
MP3player.resumeMusic(2000);
} else if(key_command == 'R') {
MP3player.stopTrack();
MP3player.vs_init();
Serial.println(F("Reseting VS10xx chip"));
} else if(key_command == 'g') {
int32_t offset_ms = 20000; // Note this is just an example, try your own number.
Serial.print(F("jumping to "));
Serial.print(offset_ms, DEC);
Serial.println(F("[milliseconds]"));
result = MP3player.skipTo(offset_ms);
if(result != 0) {
Serial.print(F("Error code: "));
Serial.print(result);
Serial.println(F(" when trying to skip track"));
}
} else if(key_command == 'k') {
int32_t offset_ms = -1000; // Note this is just an example, try your own number.
Serial.print(F("moving = "));
Serial.print(offset_ms, DEC);
Serial.println(F("[milliseconds]"));
result = MP3player.skip(offset_ms);
if(result != 0) {
Serial.print(F("Error code: "));
Serial.print(result);
Serial.println(F(" when trying to skip track"));
}
} else if(key_command == 'O') {
MP3player.end();
Serial.println(F("VS10xx placed into low power reset mode."));
} else if(key_command == 'o') {
MP3player.begin();
Serial.println(F("VS10xx restored from low power reset mode."));
} else if(key_command == 'D') {
uint16_t diff_state = MP3player.getDifferentialOutput();
Serial.print(F("Differential Mode "));
if(diff_state == 0) {
MP3player.setDifferentialOutput(1);
Serial.println(F("Enabled."));
} else {
MP3player.setDifferentialOutput(0);
Serial.println(F("Disabled."));
}
} else if(key_command == 'V') {
MP3player.setVUmeter(1);
Serial.println(F("Use \"No line ending\""));
Serial.print(F("VU meter = "));
Serial.println(MP3player.getVUmeter());
Serial.println(F("Hit Any key to stop."));
while(!Serial.available()) {
union twobyte vu;
vu.word = MP3player.getVUlevel();
Serial.print(F("VU: L = "));
Serial.print(vu.byte[1]);
Serial.print(F(" / R = "));
Serial.print(vu.byte[0]);
Serial.println(" dB");
delay(1000);
}
Serial.read();
MP3player.setVUmeter(0);
Serial.print(F("VU meter = "));
Serial.println(MP3player.getVUmeter());
} else if(key_command == 'T') {
uint16_t TrebleFrequency = MP3player.getTrebleFrequency();
Serial.print(F("Former TrebleFrequency = "));
Serial.println(TrebleFrequency, DEC);
if (TrebleFrequency >= 15000) { // Range is from 0 - 1500Hz
TrebleFrequency = 0;
} else {
TrebleFrequency += 1000;
}
MP3player.setTrebleFrequency(TrebleFrequency);
Serial.print(F("New TrebleFrequency = "));
Serial.println(MP3player.getTrebleFrequency(), DEC);
} else if(key_command == 'E') {
int8_t TrebleAmplitude = MP3player.getTrebleAmplitude();
Serial.print(F("Former TrebleAmplitude = "));
Serial.println(TrebleAmplitude, DEC);
if (TrebleAmplitude >= 7) { // Range is from -8 - 7dB
TrebleAmplitude = -8;
} else {
TrebleAmplitude++;
}
MP3player.setTrebleAmplitude(TrebleAmplitude);
Serial.print(F("New TrebleAmplitude = "));
Serial.println(MP3player.getTrebleAmplitude(), DEC);
} else if(key_command == 'B') {
uint16_t BassFrequency = MP3player.getBassFrequency();
Serial.print(F("Former BassFrequency = "));
Serial.println(BassFrequency, DEC);
if (BassFrequency >= 150) { // Range is from 20hz - 150hz
BassFrequency = 0;
} else {
BassFrequency += 10;
}
MP3player.setBassFrequency(BassFrequency);
Serial.print(F("New BassFrequency = "));
Serial.println(MP3player.getBassFrequency(), DEC);
} else if(key_command == 'C') {
uint16_t BassAmplitude = MP3player.getBassAmplitude();
Serial.print(F("Former BassAmplitude = "));
Serial.println(BassAmplitude, DEC);
if (BassAmplitude >= 15) { // Range is from 0 - 15dB
BassAmplitude = 0;
} else {
BassAmplitude++;
}
MP3player.setBassAmplitude(BassAmplitude);
Serial.print(F("New BassAmplitude = "));
Serial.println(MP3player.getBassAmplitude(), DEC);
} else if(key_command == 'M') {
uint16_t monostate = MP3player.getMonoMode();
Serial.print(F("Mono Mode "));
if(monostate == 0) {
MP3player.setMonoMode(1);
Serial.println(F("Enabled."));
} else {
MP3player.setMonoMode(0);
Serial.println(F("Disabled."));
}
#endif
/* List out music files on the SdCard */
} else if(key_command == 'l') {
if(!MP3player.isPlaying()) {
Serial.println(F("Music Files found :"));
SdFile file;
char filename[13];
sd.chdir("/",true);
uint16_t count = 1;
while (file.openNext(sd.vwd(),O_READ))
{
file.getFilename(filename);
if ( isFnMusic(filename) ) {
SerialPrintPaddedNumber(count, 5 );
Serial.print(F(": "));
Serial.println(filename);
count++;
}
file.close();
}
Serial.println(F("Enter Index of File to play"));
} else {
Serial.println(F("Busy Playing Files, try again later."));
}
} else if(key_command == 'h') {
help();
}
// print prompt after key stroke has been processed.
Serial.print(F("Time since last command: "));
Serial.println((float) (millis() - millis_prv)/1000, 2);
millis_prv = millis();
Serial.print(F("Enter s,1-9,+,-,>,<,f,F,d,i,p,t,S,b"));
#if !defined(__AVR_ATmega32U4__)
Serial.print(F(",m,e,r,R,g,k,O,o,D,V,B,C,T,E,M:"));
#endif
Serial.println(F(",l,h :"));
}
//------------------------------------------------------------------------------
/**
* \brief Print Help Menu.
*
* Prints a full menu of the commands available along with descriptions.
*/
void help() {
Serial.println(F("Arduino SFEMP3Shield Library Example:"));
Serial.println(F(" courtesy of Bill Porter & Michael P. Flaga"));
Serial.println(F("COMMANDS:"));
Serial.println(F(" [1-9] to play a track"));
Serial.println(F(" [f] play track001.mp3 by filename example"));
Serial.println(F(" [F] same as [f] but with initial skip of 2 second"));
Serial.println(F(" [s] to stop playing"));
Serial.println(F(" [d] display directory of SdCard"));
Serial.println(F(" [+ or -] to change volume"));
Serial.println(F(" [> or <] to increment or decrement play speed by 1 factor"));
Serial.println(F(" [i] retrieve current audio information (partial list)"));
Serial.println(F(" [p] to pause."));
Serial.println(F(" [t] to toggle sine wave test"));
Serial.println(F(" [S] Show State of Device."));
Serial.println(F(" [b] Play a MIDI File Beep"));
#if !defined(__AVR_ATmega32U4__)
Serial.println(F(" [e] increment Spatial EarSpeaker, default is 0, wraps after 4"));
Serial.println(F(" [m] perform memory test. reset is needed after to recover."));
Serial.println(F(" [M] Toggle between Mono and Stereo Output."));
Serial.println(F(" [g] Skip to a predetermined offset of ms in current track."));
Serial.println(F(" [k] Skip a predetermined number of ms in current track."));
Serial.println(F(" [r] resumes play from 2s from begin of file"));
Serial.println(F(" [R] Resets and initializes VS10xx chip."));
Serial.println(F(" [O] turns OFF the VS10xx into low power reset."));
Serial.println(F(" [o] turns ON the VS10xx out of low power reset."));
Serial.println(F(" [D] to toggle SM_DIFF between inphase and differential output"));
Serial.println(F(" [V] Enable VU meter Test."));
Serial.println(F(" [B] Increament bass frequency by 10Hz"));
Serial.println(F(" [C] Increament bass amplitude by 1dB"));
Serial.println(F(" [T] Increament treble frequency by 1000Hz"));
Serial.println(F(" [E] Increament treble amplitude by 1dB"));
#endif
Serial.println(F(" [l] Display list of music files"));
Serial.println(F(" [0####] Enter index of file to play, zero pad! e.g. 01-65534"));
Serial.println(F(" [h] this help"));
}
void SerialPrintPaddedNumber(int16_t value, int8_t digits ) {
int currentMax = 10;
for (byte i=1; i<digits; i++){
if (value < currentMax) {
Serial.print("0");
}
currentMax *= 10;
}
Serial.print(value);
}
view raw mp3_1.ino hosted with ❤ by GitHub

備註:
完成燒錄後,打開串列埠傳輸介面,傳輸速率選擇115200,畫面會顯示歡迎畫面、功能介紹和列出SD卡內的MP3清單



 [l]:小寫L,顯示MP3歌曲清單,左邊是編號,右邊是檔案名稱



 [1-9] : 輸入數字撥放清單上的編號歌曲
 [p] : 暫停/回復撥放
 [s]: 停止撥放
 [+ or -] : 改變聲音大小
 [> or <] : 改變撥放速度
 [l] Display list of music files
 [h] : 顯示所有指令


範例結果:

討論:
    你可以試著加入硬體按鈕直接控制MP3晶片,這樣就可以不用依賴Serial Monitor介面,脫離電腦變成一台獨立的MP3撥放器,只要一顆行動電源就可以選歌聽歌,這樣是不是方便多了呢。

也歡迎大家在底下留言或到我們的粉絲團留言喔!
====================================

沒有留言:

張貼留言