Created
October 4, 2011 13:01
-
-
Save alx/1261590 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
========================================================================================= | |
_______..__ __. ______ ______ .___________. __ ___ .______ | |
/ || \ | | / __ \ / __ \ | || | / \ | _ \ | |
| (----`| \| | | | | | | | | | `---| |----`| | / ^ \ | |_) | | |
\ \ | . ` | | | | | | | | | | | | | / /_\ \ | _ < | |
.----) | | |\ | | `--' | | `--' | | | | `----./ _____ \ | |_) | | |
|_______/ |__| \__| \______/ \______/ |__| |_______/__/ \__\ |______/ | |
========================================================================================= | |
================== SNOOTLAB FIRST HACKATHON : October 2011 ====================== | |
MP3 Player, with LCD display and Joystick and SD Slot (for the music). | |
Recommended use : play it loud with some punk or ska music ! | |
To avoid absolutely : use it in your bath... | |
WARNING : Done with Snootlab's products sell on their website and an MP3 shield rev1 from Sparkfun, also available on Snootlab | |
You have to use the SDfat library, and the Deuligne library, | |
respectively on http://code.google.com/p/sdfatlib/ and https://github.com/Snootlab/Deuligne | |
Shields used : Deuligne , Memoire, MP3 sparkfun | |
And an Arduino UNO. | |
Unplug all the board from the UNO, program it alone then plug the stack on the UNO and finally the power. | |
The file format MUST BE in "8.3" (ie: "snootlab.fun") | |
USE INSTRUCTIONS : | |
LEFT-RIGHT : Vol down/Up | |
DOWN : Scroll the files stored on the SD | |
UP : Play the selected file | |
====================== ALL MY APOLOGIES FOR THIS PIECE OF CODE ! NO TIME, NO COMMENTS, NO CLEAN CODING RULEZ, NO SLEEP... | |
====================== But Hey, let's say it's a draft ok ... ? :) | |
*/ | |
#include <Wire.h> // I2C library include | |
#include <Deuligne.h> // LCD library include | |
/* | |
* Open all files in the root dir and print their filename | |
*/ | |
#include <SdFat.h> | |
#include <SPI.h> | |
#define MP3_XCS 9 //Control Chip Select Pin (for accessing SPI Control/Status registers) | |
#define MP3_XDCS 2 //Data Chip Select / BSYNC Pin | |
#define MP3_DREQ 3 //Data Request Pin: Player asks for more data | |
#define TRUE 0 | |
#define FALSE 1 | |
//VS10xx SCI Registers | |
#define SCI_MODE 0x00 | |
#define SCI_STATUS 0x01 | |
#define SCI_BASS 0x02 | |
#define SCI_CLOCKF 0x03 | |
#define SCI_DECODE_TIME 0x04 | |
#define SCI_AUDATA 0x05 | |
#define SCI_WRAM 0x06 | |
#define SCI_WRAMADDR 0x07 | |
#define SCI_HDAT0 0x08 | |
#define SCI_HDAT1 0x09 | |
#define SCI_AIADDR 0x0A | |
#define SCI_VOL 0x0B | |
#define SCI_AICTRL0 0x0C | |
#define SCI_AICTRL1 0x0D | |
#define SCI_AICTRL2 0x0E | |
#define SCI_AICTRL3 0x0F | |
// SD chip select pin | |
const uint8_t chipSelect = 10; | |
// file system object | |
SdFat sd; | |
SdFile file; | |
Deuligne lcd; // lcd object declaration | |
//Key message | |
char msgs[5][15] = { | |
"Right Key OK ", | |
"Up Key OK ", | |
"Down Key OK ", | |
"Left Key OK ", | |
"Select Key OK" }; | |
int key=-1; | |
int oldkey=-1; | |
char name[13]; | |
unsigned char volume_ratio=40; | |
unsigned char volume[16]; | |
void set_volume(void); | |
char errorMsg[100]; //This is a generic array used for sprintf of error messages | |
void setup() | |
{ | |
Wire.begin(); // join i2c | |
lcd.init(); // LCD init | |
lcd.clear(); // Clear Display | |
lcd.backLight(true); // Backlight ON | |
lcd.setCursor(5,0); // Place cursor row 6, 1st line (counting from 0) | |
lcd.print("Setup"); | |
lcd.setCursor(7,1); // Place cursor row 8, 2nd line (counting from 0) | |
lcd.print("ok"); | |
delay(2000); | |
lcd.clear(); | |
lcd.print("Move Joystick"); | |
volume[0]='V'; | |
volume[1]='o'; | |
volume[2]='l'; | |
volume[3]=':'; | |
// init SD card | |
if (!sd.init(SPI_FULL_SPEED, chipSelect)) Serial.println("Error: SD ini"); | |
//============================= | |
pinMode(MP3_DREQ, INPUT); | |
pinMode(MP3_XCS, OUTPUT); | |
pinMode(MP3_XDCS, OUTPUT); | |
Serial.begin(115200); //Use serial for debugging | |
Serial.println("MP3 Shield Example"); | |
//Setup SPI for VS1053 | |
pinMode(10, OUTPUT); //Pin 10 must be set as an output for the SPI communication to work | |
SPI.begin(); | |
SPI.setBitOrder(MSBFIRST); | |
SPI.setDataMode(SPI_MODE0); | |
//From page 12 of datasheet, max SCI reads are CLKI/7. Input clock is 12.288MHz. | |
//Internal clock multiplier is 1.0x after power up. | |
//Therefore, max SPI speed is 1.75MHz. We will use 1MHz to be safe. | |
SPI.setClockDivider(SPI_CLOCK_DIV16); //Set SPI bus speed to 1MHz (16MHz / 16 = 1MHz) | |
SPI.transfer(0xFF); //Throw a dummy byte at the bus | |
//Initialize VS1053 chip | |
digitalWrite(MP3_XCS, HIGH); //Deselect Control | |
digitalWrite(MP3_XDCS, HIGH); //Deselect Data | |
Mp3SetVolume(20, 20); //Set initial volume (20 = -10dB) | |
//Let's check the status of the VS1053 | |
int MP3Mode = Mp3ReadRegister(SCI_MODE); | |
int MP3Status = Mp3ReadRegister(SCI_STATUS); | |
int MP3Clock = Mp3ReadRegister(SCI_CLOCKF); | |
Serial.print("SCI_Mode (0x4800) = 0x"); | |
Serial.println(MP3Mode, HEX); | |
//Serial.print("SCI_Status (0x48) = 0x"); | |
//Serial.println(MP3Status, HEX); | |
int vsVersion = (MP3Status >> 4) & 0x000F; //Mask out only the four version bits | |
Serial.print("VS Version (VS1053 is 4) = "); | |
Serial.println(vsVersion, DEC); //The 1053B should respond with 4. VS1001 = 0, VS1011 = 1, VS1002 = 2, VS1003 = 3 | |
//Now that we have the VS1053 up and running, increase the VS1053 internal clock multiplier and up our SPI rate | |
Mp3WriteRegister(SCI_CLOCKF, 0x60, 0x00); //Set multiplier to 3.0x | |
//From page 12 of datasheet, max SCI reads are CLKI/7. Input clock is 12.288MHz. | |
//Internal clock multiplier is now 3x. | |
//Therefore, max SPI speed is 5MHz. 4MHz will be safe. | |
SPI.setClockDivider(SPI_CLOCK_DIV4); //Set SPI bus speed to 4MHz (16MHz / 4 = 4MHz) | |
MP3Clock = Mp3ReadRegister(SCI_CLOCKF); | |
Serial.print("SCI_ClockF = 0x"); | |
Serial.println(MP3Clock, HEX); | |
} | |
void loop() { | |
unsigned char indice=0; | |
unsigned char longueur=0; | |
key = lcd.get_key(); // read the value from the sensor & convert into key press | |
if (key != oldkey) // if keypress is detected | |
{ | |
delay(50); // wait for debounce time | |
key = lcd.get_key(); // read the value from the sensor & convert into key press | |
if (key != oldkey) | |
{ | |
oldkey = key; | |
if ( key==2)// touches == UP or DOWN | |
{ | |
if (file.openNext(sd.cwd(), O_READ)) | |
{ | |
file.getFilename(name); | |
file.close(); | |
} else { lcd.clear(); | |
lcd.print("Back 2 beginning..."); | |
delay(500); | |
sd.chdir();} | |
} | |
if(key==0) | |
{ | |
//up volume | |
volume_ratio++; | |
set_volume(); | |
} | |
if(key==3) | |
{ | |
//up volume | |
volume_ratio--; | |
set_volume(); | |
} | |
lcd.clear(); | |
lcd.setCursor(0,0); // | |
lcd.print(name); | |
Serial.print(name); | |
for(indice=0;indice <=15;indice++) | |
{ | |
lcd.setCursor(indice,1); | |
lcd.write(volume[indice]); | |
} | |
if(key==1) | |
{ | |
lcd.clear(); | |
lcd.setCursor(0,0); lcd.print(name); | |
lcd.setCursor(0,1); lcd.print("playing ..."); | |
playMP3(name); | |
} | |
} | |
} | |
} | |
void set_volume(void) | |
{ | |
unsigned char in=0; | |
if(volume_ratio >=12)volume_ratio=0; | |
for (in=4;in <=15;in++) | |
{ | |
volume[in]=' '; | |
} | |
for (in=0;in <=volume_ratio;in++) | |
{ | |
volume[in+4]='#'; | |
Mp3SetVolume((60-(volume_ratio*3)), (60-(volume_ratio*3))); | |
} | |
} | |
//=================================================================== | |
//Write to VS10xx register | |
//SCI: Data transfers are always 16bit. When a new SCI operation comes in | |
//DREQ goes low. We then have to wait for DREQ to go high again. | |
//XCS should be low for the full duration of operation. | |
void Mp3WriteRegister(unsigned char addressbyte, unsigned char highbyte, unsigned char lowbyte){ | |
while(!digitalRead(MP3_DREQ)) ; //Wait for DREQ to go high indicating IC is available | |
digitalWrite(MP3_XCS, LOW); //Select control | |
//SCI consists of instruction byte, address byte, and 16-bit data word. | |
SPI.transfer(0x02); //Write instruction | |
SPI.transfer(addressbyte); | |
SPI.transfer(highbyte); | |
SPI.transfer(lowbyte); | |
while(!digitalRead(MP3_DREQ)) ; //Wait for DREQ to go high indicating command is complete | |
digitalWrite(MP3_XCS, HIGH); //Deselect Control | |
} | |
//Read the 16-bit value of a VS10xx register | |
unsigned int Mp3ReadRegister (unsigned char addressbyte){ | |
while(!digitalRead(MP3_DREQ)) ; //Wait for DREQ to go high indicating IC is available | |
digitalWrite(MP3_XCS, LOW); //Select control | |
//SCI consists of instruction byte, address byte, and 16-bit data word. | |
SPI.transfer(0x03); //Read instruction | |
SPI.transfer(addressbyte); | |
char response1 = SPI.transfer(0xFF); //Read the first byte | |
while(!digitalRead(MP3_DREQ)) ; //Wait for DREQ to go high indicating command is complete | |
char response2 = SPI.transfer(0xFF); //Read the second byte | |
while(!digitalRead(MP3_DREQ)) ; //Wait for DREQ to go high indicating command is complete | |
digitalWrite(MP3_XCS, HIGH); //Deselect Control | |
int resultvalue = response1 << 8; | |
resultvalue |= response2; | |
return resultvalue; | |
} | |
//Set VS10xx Volume Register | |
void Mp3SetVolume(unsigned char leftchannel, unsigned char rightchannel){ | |
Mp3WriteRegister(SCI_VOL, leftchannel, rightchannel); | |
} | |
void playMP3(char* fileName) { | |
if (!file.open( fileName, O_READ)) { //Open the file in read mode. | |
sprintf(errorMsg, "Failed to open %s", fileName); | |
Serial.println(errorMsg); | |
return; | |
} | |
Serial.println("Track open"); | |
uint8_t mp3DataBuffer[32]; //Buffer of 32 bytes. VS1053 can take 32 bytes at a go. | |
//track.read(mp3DataBuffer, sizeof(mp3DataBuffer)); //Read the first 32 bytes of the song | |
int need_data = TRUE; | |
long replenish_time = millis(); | |
Serial.println("Start MP3 decoding"); | |
while(1) { | |
while(!digitalRead(MP3_DREQ)) { | |
//DREQ is low while the receive buffer is full | |
//You can do something else here, the buffer of the MP3 is full and happy. | |
//Maybe set the volume or test to see how much we can delay before we hear audible glitches | |
//If the MP3 IC is happy, but we need to read new data from the SD, now is a great time to do so | |
if(need_data == TRUE) { | |
if(!file.read(mp3DataBuffer, sizeof(mp3DataBuffer))) { //Try reading 32 new bytes of the song | |
//Oh no! There is no data left to read! | |
//Time to exit | |
break; | |
} | |
need_data = FALSE; | |
} | |
//Serial.println("."); //Print a character to show we are doing nothing | |
//This is here to show how much time is spent transferring new bytes to the VS1053 buffer. Relies on replenish_time below. | |
Serial.print("Time to replenish buffer: "); | |
Serial.print(millis() - replenish_time, DEC); | |
Serial.print("ms"); | |
//Test to see just how much we can do before the audio starts to glitch | |
long start_time = millis(); | |
//delay(150); //Do NOTHING - audible glitches | |
//delay(135); //Do NOTHING - audible glitches | |
//delay(120); //Do NOTHING - barely audible glitches | |
delay(100); //Do NOTHING - sounds fine | |
Serial.print(" Idle time: "); | |
Serial.print(millis() - start_time, DEC); | |
Serial.println("ms"); | |
//Look at that! We can actually do quite a lot without the audio glitching | |
//Now that we've completely emptied the VS1053 buffer (2048 bytes) let's see how much | |
//time the VS1053 keeps the DREQ line high, indicating it needs to be fed | |
replenish_time = millis(); | |
} | |
if(need_data == TRUE){ //This is here in case we haven't had any free time to load new data | |
if(!file.read(mp3DataBuffer, sizeof(mp3DataBuffer))) { //Go out to SD card and try reading 32 new bytes of the song | |
//Oh no! There is no data left to read! | |
//Time to exit | |
break; | |
} | |
need_data = FALSE; | |
} | |
//Once DREQ is released (high) we now feed 32 bytes of data to the VS1053 from our SD read buffer | |
digitalWrite(MP3_XDCS, LOW); //Select Data | |
for(int y = 0 ; y < sizeof(mp3DataBuffer) ; y++) { | |
SPI.transfer(mp3DataBuffer[y]); // Send SPI byte | |
} | |
digitalWrite(MP3_XDCS, HIGH); //Deselect Data | |
need_data = TRUE; //We've just dumped 32 bytes into VS1053 so our SD read buffer is empty. Set flag so we go get more data | |
} | |
while(!digitalRead(MP3_DREQ)) ; //Wait for DREQ to go high indicating transfer is complete | |
digitalWrite(MP3_XDCS, HIGH); //Deselect Data | |
file.close(); //Close out this track | |
sprintf(errorMsg, "Track %s done!", fileName); | |
Serial.println(errorMsg); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment