Вы находитесь на странице: 1из 45

All of my software code

(Included code to test mp3 player shield, force sensitive resistor square, WiFly Shield, basic 16x2 character LCD and codes that combined all code)

Step 1: Code to test the mp3

/* 4-28-2011 Spark Fun Electronics 2011 Nathan Seidle

player shield

This code is public domain but you buy me a beer if you use this and we meet someday (Beerware license). This example code plays a MP3 from the SD card called 'track001.mp3'. The theory is that you can load a microSD card up with a bunch of MP3s and then play a given 'track' depending on some sort of input such as which pin is pulled low.

It relies on the sdfatlib from Bill Greiman: http://code.google.com/p/sdfatlib/ You will need to download and install his library. To compile, you MUST change Sd2PinMap.h of the SDfatlib! The default SS_PIN = 10;. You must change this line under the ATmega328/Arduino area of code to uint8_t const SS_PIN = 9;. This will cause the sdfatlib to use pin 9 as the 'chip select' for the microSD card on pin 9 of the Arduino so that the layout of the shield works.

Attach the shield to an Arduino. Load code (after editing Sd2PinMap.h) then open the terminal at 57600bps. This example shows that it takes ~30ms to load up the VS1053 buffer. We can then do whatever we want for ~100ms before we need to return to filling the buffer (for another 30ms). This code is heavily based on the example code I wrote to control the MP3 shield found here: http://www.sparkfun.com/products/9736 This example code extends the previous example by reading

the MP3 from an SD card and file rather than from internal memory of the ATmega. Because the current MP3 shield does not have a microSD socket, you will need to add the microSD shield to your Arduino stack. The main gotcha from all of this is that you have to make sure your CS pins for each device on an SPI bus is carefully declared. For the SS pin (aka CS) on the SD FAT libaray, you need to correctly set it within Sd2PinMap.h. The default pin in Sd2PinMap.h is 10. If you're using the SparkFun microSD shield with the SparkFun MP3 shield, the SD CS pin is pin 9. Four pins are needed to control the VS1503: DREQ CS DCS Reset (optional but good to have access to) Plus the SPI bus Only the SPI bus pins and another CS pin are needed to control the microSD card.

What surprised me is the fact that with a normal MP3 we can do other things for up to 100ms while the MP3 IC crunches through it's fairly large buffer of 2048 bytes. As long as you keep your sensor checks or serial reporting to under 100ms and leave ~30ms to then replenish the MP3 buffer, you can do quite a lot while the MP3 is playing glitch free.

*/

#include <SPI.h>

//Add the SdFat Libraries #include <SdFat.h> #include <SdFatUtil.h>

//Create the variables to be used by SdFat Library Sd2Card card;

SdVolume volume; SdFile root; SdFile track;

//This is the name of the file on the microSD card you would like to play //Stick with normal 8.3 nomeclature. All lower-case works well. //Note: you must name the tracks on the SD card with 001, 002, 003, etc. //For example, the code is expecting to play 'track002.mp3', not track2.mp3. char trackName[] = "track001.mp3"; int trackNumber = 1; char errorMsg[100]; //This is a generic array used for sprintf of error messages

#define TRUE 0 #define FALSE 1 //MP3 Player Shield pin mapping. See the schematic #define MP3_XCS 6 //Control Chip Select Pin (for accessing SPI Control/Status registers) #define MP3_XDCS 7 //Data Chip Select / BSYNC Pin #define MP3_DREQ 2 //Data Request Pin: Player asks for more data #define MP3_RESET 8 //Reset is active low //Remember you have to edit the Sd2PinMap.h of the sdfatlib library to correct control the SD card.

//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

void setup() { pinMode(MP3_DREQ, INPUT); pinMode(MP3_XCS, OUTPUT); pinMode(MP3_XDCS, OUTPUT); pinMode(MP3_RESET, OUTPUT); digitalWrite(MP3_XCS, HIGH); //Deselect Control digitalWrite(MP3_XDCS, HIGH); //Deselect Data digitalWrite(MP3_RESET, LOW); //Put VS1053 into hardware reset

Serial.begin(57600); //Use serial for debugging Serial.println("MP3 Testing");

//Setup SD card interface pinMode(10, OUTPUT); //Pin 10 must be set as an output

for the SD communication to work. if (!card.init(SPI_FULL_SPEED)) Serial.println("Error: Card

init"); //Initialize the SD card and configure the I/O pins. if (!volume.init(&card)) Serial.println("Error: Volume ini"); //Initialize a volume on the SD card. if (!root.openRoot(&volume)) Serial.println("Error: Opening root"); //Open the root directory in the volume.

//We have no need to setup SPI for VS1053 because this has already been done by the SDfatlib //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); speed to 1MHz (16MHz / 16 = 1MHz)

//Set

SPI

bus

SPI.transfer(0xFF); //Throw a dummy byte at the bus //Initialize VS1053 chip delay(10); digitalWrite(MP3_RESET, HIGH); //Bring up VS1053 //delay(10); //We don't need this delay because any register changes will check for a high DREQ

//Mp3SetVolume(20, 20); //Set initial volume (20 = -10dB) LOUD Mp3SetVolume(40, 40); //Set initial volume (20 = -10dB) Manageable //Mp3SetVolume(80, 80); //Set initial volume (20 = -10dB) More quiet

//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

Serial.print("SCI_ClockF = 0x"); Serial.println(MP3Clock, HEX); //Now that we have the VS1053 up and running, increase the 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);

//MP3 IC setup complete }

void loop(){

//Let's play a track of a given number sprintf(trackName, "track%03d.mp3", trackNumber); //Splice the new file number into this file name playMP3(trackName); //Go play trackXXX.mp3

//Once we are done playing or have exited the playback for some reason, decide what track to play next trackNumber++; //When we loop, advance to next track!

if(trackNumber > 100) { Serial.println("Whoa while(1); } } there cowboy!"); //Soft limit. We shouldn't be trying to open past track 100.

//PlayMP3 pulls 32 byte chunks from the SD card and throws them at the VS1053 //We monitor the DREQ (data request pin). If it goes low then we determine if //we need new data or not. If yes, pull new from SD card. Then throw the data //at the VS1053 until it is full.

void playMP3(char* fileName) {

if (!track.open(&root, 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(!track.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(!track.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

track.close(); //Close out this track sprintf(errorMsg, "Track %s done!", fileName); Serial.println(errorMsg); }

//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); }

Step 2: Code to test the force sensitive resistor - square

/* AnalogReadSerial Reads an analog input on pin 0, prints the result to the serial monitor This example code is in the public domain. */

void setup() { Serial.begin(9600); pinMode(13,OUTPUT); } void loop() {

Serial.println(analogRead(0), DEC); if(analogRead(0) > 800) { Serial.println("LED on"); digitalWrite(13,255); } if(analogRead(0) < 800) { Serial.println("LED off"); digitalWrite(13,0); } } Other: Code to test the WiFly Shield /* * WiFly Tutorial Example * Copyright (c) 2010 SparkFun Electronics. All right reserved. * Written by Chris Taylor * * This code was written to demonstrate the WiFly Tutorial from SparkFun Electronics * * This code will associate with the SparkFun WiFi network, serve a simple page, and * "say" typed words using the Voice Box Shield * * http://www.sparkfun.com

*/

#include <string.h> #include <SoftwareSerial.h> #include <avr/pgmspace.h> // SC16IS750 Register Definitions #define THR #define RHR #define IER #define FCR #define IIR #define LCR #define MCR #define LSR #define MSR #define SPR #define TXFIFO #define RXFIFO #define DLAB #define IODIR #define IOSTATE 0x00 << 3 0x00 << 3 0x01 << 3 0x02 << 3 0x02 << 3 0x03 << 3 0x04 << 3 0x05 << 3 0x06 << 3 0x07 << 3 0x08 << 3 0x09 << 3 0x80 << 3 0x0A << 3 0x0B << 3

#define IOINTMSK 0x0C << 3 #define IOCTRL #define EFCR 0x0E << 3 0x0F << 3

#define DLL #define DLM #define EFR #define XON1 #define XON2 #define XOFF1 #define XOFF2

0x00 << 3 0x01 << 3 0x02 << 3 0x04 << 3 0x05 << 3 0x06 << 3 0x07 << 3

// SPI port definitions #define CS #define MOSI #define MISO #define SCK 10 11 12 13

// Voice Box Shield definitions #define RES 3 #define SPK 4 #define txPin 2 SoftwareSerial speakjet = SoftwareSerial(0, txPin); #define WORD_LIST_LENGTH 100

#define ASSOCIATE_TIMEOUT 5000

char is_get = 1;

// "Associated" char message[] = { 132,187,187,137,8,189,8,128,154,191,129,176,0,255};

// SpeakJet numbers char zero[] = { 167,7,128,7,149,164,0,255}; char one[] = { 147,14,135,8,141,0,255}; char two[] = { 8,191,162,31,0,255}; char three[] = { 8,190,148,8,128,0,255}; char four[] = { 186,7,137,153,0,0,255}; char five[] = { 186,157,166,0,255}; char six[] = { 8,187,129,14,194,7,187,0,255}; char seven[] = { 8,187,7,131,166,131,141,0,255}; char eight[] = { 154,4,191,0,255}; char nine[] = { 141,14,157,141,0,255};

char *numbers[] = { zero, one, two, three, four, five, six, seven, eight, nine };

char ping[] = { 220,0,255}; char dot[] = { 175,8,135,191,0,255};

// WORD LIST // Phoneme strings for SpeakJet // Placed in program memory to save RAM prog_char sj_the[] PROGMEM = { 8,169,8,128,0,0,255}; prog_char sj_of[] PROGMEM = { 8,134,166,0,0,255}; prog_char sj_and1[] PROGMEM = { 8,132,8,141,177,0,0,255}; prog_char sj_a1[] PROGMEM = { 154,128,0,0,255}; prog_char sj_to[] PROGMEM = { 8,191,162,0,0,255}; prog_char sj_in[] PROGMEM = { 8,129,8,141,0,0,255}; prog_char sj_is[] PROGMEM = { 8,129,167,0,0,255}; prog_char sj_you[] PROGMEM = { 8,160,0,0,255}; prog_char sj_that[] PROGMEM = { 169,8,132,8,191,0,0,255}; prog_char sj_it[] PROGMEM = { 8,129,8,191,0,0,255}; prog_char sj_he[] PROGMEM = { 183,128,0,0,255}; prog_char sj_for1[] PROGMEM = { 186,153,0,0,255}; prog_char sj_was[] PROGMEM = { 147,134,167,0,0,255}; prog_char sj_on[] PROGMEM = { 135,135,142,0,0,255}; prog_char sj_are[] PROGMEM = { 152,0,0,255};

prog_char sj_as[] PROGMEM = { 132,8,167,0,0,255}; prog_char sj_with[] PROGMEM = { 147,129,8,8,190,0,0,255}; prog_char sj_his[] PROGMEM = { 183,129,167,0,0,255}; prog_char sj_they[] PROGMEM = { 8,169,154,0,0,255}; prog_char sj_at[] PROGMEM = { 132,8,191,0,0,255}; prog_char sj_be[] PROGMEM = { 170,128,128,0,0,255}; prog_char sj_this1[] PROGMEM = { 8,169,8,129,187,0,0,255}; prog_char sj_from[] PROGMEM = { 186,148,134,8,140,0,0,255}; prog_char sj_i1[] PROGMEM = { 157,0,0,255}; prog_char sj_have[] PROGMEM = { 183,8,132,166,0,0,255}; prog_char sj_or1[] PROGMEM = { 153,0,0,255}; prog_char sj_by[] PROGMEM = { 171,157,0,0,255}; prog_char sj_one[] PROGMEM = { 147,14,135,8,141,0,0,255}; prog_char sj_not1[] PROGMEM = { 141,135,191,0,0,255}; prog_char sj_what[] PROGMEM = { 185,8,135,191,0,0,255}; prog_char sj_all[] PROGMEM = { 8,136,8,146,0,0,255}; prog_char sj_when[] PROGMEM = { 185,8,131,8,141,0,0,255}; prog_char sj_we[] PROGMEM = { 147,8,128,0,0,255}; prog_char sj_there[] PROGMEM = { 8,169,150,0,0,255}; prog_char sj_can[] PROGMEM = { 194,8,132,141,0,0,255};

prog_char sj_an[] PROGMEM = { 132,132,141,0,0,255}; prog_char sj_your[] PROGMEM = { 128,153,0,0,255}; prog_char sj_which[] PROGMEM = { 185,129,8,182,0,0,255}; prog_char sj_their[] PROGMEM = { 8,169,150,0,0,255}; prog_char sj_if1[] PROGMEM = { 129,186,186,0,0,255}; prog_char sj_do1[] PROGMEM = { 174,162,0,0,255}; prog_char sj_will[] PROGMEM = { 147,129,8,145,0,0,255}; prog_char sj_each[] PROGMEM = { 128,128,4,182,0,0,255}; prog_char sj_about[] PROGMEM = { 134,173,163,191,0,0,255}; prog_char sj_how[] PROGMEM = { 184,8,163,0,0,255}; prog_char sj_up[] PROGMEM = { 8,134,199,0,0,255}; prog_char sj_out[] PROGMEM = { 163,191,0,0,255}; prog_char sj_them[] PROGMEM = { 8,169,131,8,140,0,0,255}; prog_char sj_then[] PROGMEM = { 8,169,131,8,142,0,0,255}; prog_char sj_she[] PROGMEM = { 8,189,8,128,0,0,255}; prog_char sj_many[] PROGMEM = { 140,131,141,128,0,0,255}; prog_char sj_some[] PROGMEM = { 8,187,134,140,0,0,255}; prog_char sj_so[] PROGMEM = { 8,188,7,164,7,147,0,0,255}; prog_char sj_these[] PROGMEM = { 8,169,8,128,7,167,0,0,255}; prog_char sj_would[] PROGMEM = { 147,8,138,177,0,0,255};

prog_char sj_other[] PROGMEM = { 134,190,151,0,0,255}; prog_char sj_into[] PROGMEM = { 129,141,191,162,0,0,255}; prog_char sj_more[] PROGMEM = { 140,7,137,153,0,0,255}; prog_char sj_her[] PROGMEM = { 183,151,0,0,255}; prog_char sj_two[] PROGMEM = { 8,191,162,0,0,255}; prog_char sj_like[] PROGMEM = { 145,7,135,7,155,196,0,0,255}; prog_char sj_him[] PROGMEM = { 183,8,129,140,0,0,255}; prog_char sj_see[] PROGMEM = { 187,187,128,128,0,0,255}; prog_char sj_time[] PROGMEM = { 8,191,157,8,140,0,0,255}; prog_char sj_could[] PROGMEM = { 195,138,138,177,0,0,255}; prog_char sj_no[] PROGMEM = { 142,164,0,0,255}; prog_char sj_make[] PROGMEM = { 140,154,196,0,0,255}; prog_char sj_than[] PROGMEM = { 169,8,132,8,142,0,0,255}; prog_char sj_first[] PROGMEM = { 186,151,187,191,0,0,255}; prog_char sj_now[] PROGMEM = { 142,163,0,0,255}; prog_char sj_people[] PROGMEM = { 198,7,128,198,8,145,0,0,255}; prog_char sj_my[] PROGMEM = { 140,155,0,0,255}; prog_char sj_over[] PROGMEM = { 8,137,7,166,151,0,0,255}; prog_char sj_down[] PROGMEM = { 175,163,141,0,0,255}; prog_char sj_only[] PROGMEM = { 137,142,145,128,0,0,255};

prog_char sj_find[] PROGMEM = { 186,155,141,177,0,0,255}; prog_char sj_use[] PROGMEM = { 160,167,0,0,255}; prog_char sj_may[] PROGMEM = { 140,154,0,0,255}; prog_char sj_water[] PROGMEM = { 147,135,191,7,151,0,0,255}; prog_char sj_long1[] PROGMEM = { 146,135,8,144,0,0,255}; prog_char sj_little[] PROGMEM = { 145,129,191,131,145,0,0,255}; prog_char sj_feed[] PROGMEM = { 186,128,128,176,0,0,255}; prog_char sj_very[] PROGMEM = { 166,150,7,128,0,0,255}; prog_char sj_after[] PROGMEM = { 132,186,191,7,151,0,0,255}; prog_char sj_me[] PROGMEM = { 140,128,128,0,0,255}; prog_char sj_words[] PROGMEM = { 147,151,176,7,167,0,0,255}; prog_char sj_just[] PROGMEM = { 165,133,187,191,0,0,255}; prog_char sj_cat[] PROGMEM = { 194,132,191,0,0,255}; prog_char sj_where[] PROGMEM = { 147,150,0,0,255}; prog_char sj_most[] PROGMEM = { 140,8,137,187,191,0,0,255}; prog_char sj_know[] PROGMEM = { 142,137,164,0,0,255}; prog_char sj_dead[] PROGMEM = { 174,131,174,0,0,255}; prog_char sj_get[] PROGMEM = { 8,178,8,131,191,0,0,255}; prog_char sj_through[] PROGMEM = { 8,190,148,8,139,0,0,255}; prog_char sj_back[] PROGMEM = { 170,8,132,4,196,0,0,255};

prog_char sj_much[] PROGMEM = { 140,134,182,0,0,255}; prog_char sj_go[] PROGMEM = { 8,179,8,164,0,0,255}; prog_char sj_good[] PROGMEM = { 8,179,138,138,177,0,0,255}; prog_char sj_new1[] PROGMEM = { 141,160,0,0,255}; prog_char sj_write[] PROGMEM = { 148,155,191,0,0,255};

PROGMEM const char *SpeakJetDictionary[] = { sj_the,sj_of,sj_and1,sj_a1,sj_to,sj_in,sj_is,sj_you,sj_that,sj_it,sj _he,sj_for1,sj_was,sj_on,sj_are,sj_as,

sj_with,sj_his,sj_they,sj_at,sj_be,sj_this1,sj_from,sj_i1,sj_have, sj_or1,sj_by,sj_one,sj_not1,sj_what,sj_all,

sj_when,sj_we,sj_there,sj_can,sj_an,sj_your,sj_which,sj_their,s j_if1,sj_do1,sj_will,sj_each,sj_about,sj_how,

sj_up,sj_out,sj_them,sj_then,sj_she,sj_many,sj_some,sj_so,sj_ these,sj_would,sj_other,sj_into,sj_more,sj_her,

sj_two,sj_like,sj_him,sj_see,sj_time,sj_could,sj_no,sj_make,sj_t han,sj_first,sj_now,sj_people,sj_my,sj_over, sj_down,sj_only,sj_find,sj_use,sj_may,sj_water,sj_long1,sj_little ,sj_feed,sj_very,sj_after,sj_me,sj_words,

sj_just,sj_cat,sj_where,sj_most,sj_know,sj_dead,sj_get,sj_throu gh,sj_back,sj_much,sj_go,sj_good,sj_new1,sj_write

}; // SpeakJet lookup table // Local searchable table to find indices for phoneme strings char *SpeakJetLookup[] = {

"the","of","and","a","to","in","is","you","that","it","he","for","was"," on","are","as","with","his","they","at","be","this",

"from","i","have","or","by","one","not","what","all","when","we","t here","can","an","your","which","their","if","do","will", "each","about","how","up","out","them","then","she","many","so me","so","these","would","other","into","more","her","two","like",

"him","see","time","could","no","make","than","first","now","peopl e","my","over","down","only","find","use","may","water","long",

"little","feed","very","after","me","words","just","cat","where","mo st","know","dead","get","through","back","much","go","good", "new","write"

};

// Flags char incoming_data; char SPI_init_flag = 0; char polling = 0;

char TX_Fifo_Address = THR;

int i = 0; int j = 0; int k = 0; char clr = 0;

struct SPI_UART_cfg // Defines register values for SC16IS750 { char DivL,DivM,DataFormat,Flow; }; struct SPI_UART_cfg SPI_Uart_config = { 0x60,0x00,0x03,0x10};

char auth_level[] = "3";

char auth_phrase[] = "56925100"; char port_listen[] = "80"; char ssid[] = "ChocoMocha";

char ip_address[30]; // Stores IP address for SpeakJet void setup() { // SPI Init pinMode(MOSI, OUTPUT); pinMode(MISO, INPUT); pinMode(SCK,OUTPUT); pinMode(CS,OUTPUT); digitalWrite(CS,HIGH); //disable device

// SpeakJet Init pinMode(txPin, OUTPUT); pinMode(SPK, INPUT); pinMode(RES, OUTPUT); // SPI Start SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR1)|(1<<SPR0); clr=SPSR; clr=SPDR; delay(10); Serial.begin(9600); speakjet.begin(9600); // Reset the SpeakJet digitalWrite(RES, LOW); delay(100); digitalWrite(RES, HIGH);

Serial.println("\n\r\n\rWiFly Shield Terminal Routine"); // Initialize and test the SPI-Uart bridge if(SPI_Uart_Init()){ Serial.println("Bridge initialized successfully!"); } else{

Serial.println("Could not initialize bridge, locking up.\n\r"); while(1); }

// Autoconnect to local WiFi using parameters from the global vars autoconnect(); }

void loop() { // Exit command mode if we haven't already SPI_Uart_println("exit"); delay(500); Flush_RX(); while(1) { while(!Have_Client()); // Wait on client Serial.println("\n\rConnection opened."); // Send HTML HTML_print("<html>"); HTML_print("<title>SparkFun SpeakJet Server</title>"); HTML_print("<h1><center>"); HTML_print("<img src=\"http://static.sparkfun.com/images/framework/logo.gif\">"); HTML_print("</center></h1>"); HTML_print("Type some shizz up:"); HTML_print("<form method=\"post\">"); HTML_print("<input type=\"text\" name=\"%\" size=\"100\">"); HTML_print("<input type=\"submit\" value=\"Submit\">"); HTML_print("</form>"); // Send word list HTML_print("<h1>Word List:</h1><p>"); for(int wordn = 0; wordn < WORD_LIST_LENGTH; wordn++) { HTML_print(SpeakJetLookup[wordn]); HTML_print(" "); } name=\"input\" action=\"\"

HTML_print("</html>"); delay(500);

// Enter command mode and close connection SPI_Uart_print("$$$"); delay(500); SPI_Uart_println("close"); delay(1000); SPI_Uart_println("exit"); delay(500); Serial.println("Connection closed.\n\r"); Flush_RX(); } }

void select(void) // Select SC16IS750 { digitalWrite(CS,LOW); } void deselect(void) // Deselect SC16IS750 { digitalWrite(CS,HIGH); }

char Have_Client(void) { if(SPI_Uart_ReadByte(LSR) & 0x01){ // Wait for characters from a connection Serial.println("Client request..."); is_get = 1; Parse_Request(); // Check if request is a POST Flush_RX(); return 1; } else{ return 0; } }

char SPI_Uart_Init(void) // Initialize SC16IS750 { char data = 0; SPI_Uart_WriteByte(LCR,0x80); // 0x80 to program baudrate SPI_Uart_WriteByte(DLL,SPI_Uart_config.DivL); 9600 with Xtal = 12.288MHz SPI_Uart_WriteByte(DLM,SPI_Uart_config.DivM); //0x50 =

SPI_Uart_WriteByte(LCR, 0xBF); // access EFR register SPI_Uart_WriteByte(EFR, SPI_Uart_config.Flow); // enable enhanced registers SPI_Uart_WriteByte(LCR, SPI_Uart_config.DataFormat); // 8 data bit, 1 stop bit, no parity SPI_Uart_WriteByte(FCR, 0x06); // reset TXFIFO, reset RXFIFO, non FIFO mode SPI_Uart_WriteByte(FCR, 0x01); // enable FIFO mode

// Perform read/write test to check if UART is working SPI_Uart_WriteByte(SPR,'H'); data = SPI_Uart_ReadByte(SPR);

if(data == 'H'){ return 1; } else{ return 0; }

void SPI_Uart_WriteByte(char address, char data) // Write single byte to register at <address> { long int length; char senddata[2]; senddata[0] = address; senddata[1] = data;

select(); length = SPI_Write(senddata, 2); deselect(); } long int SPI_Write(char* srcptr, long int length) // Write string to SC16IS750 { for(long int i = 0; i < length; i++) { spi_transfer(srcptr[i]); } return length; }

void SPI_Uart_WriteArray(char *data, long int NumBytes) // Write string to THR of SC16IS750 { long int length; select(); length = SPI_Write(&TX_Fifo_Address,1);

while(NumBytes > 16) { length = SPI_Write(data,16); NumBytes -= 16; data += 16; } length = SPI_Write(data,NumBytes);

deselect(); }

char SPI_Uart_ReadByte(char address) // Read SC16IS750 register at <address> { char data;

address = (address | 0x80);

select(); spi_transfer(address); data = spi_transfer(0xFF); deselect(); return data; }

char autoconnect(void) // Automatically connect to WiFi network using parameters in global variables { // Exit command mode if we haven't already SPI_Uart_println(""); SPI_Uart_println("exit"); delay(500);

// Enter command mode SPI_Uart_print("$$$"); delay(500); // Reboot to get device into known state Serial.println("Rebooting"); SPI_Uart_println("reboot"); delay(3000);

// Enter command mode Serial.println("Entering command mode."); SPI_Uart_print("$$$"); delay(500);

// Set the security authorization level of the WiFly using <auth_level> SPI_Uart_print("set w a "); SPI_Uart_println(auth_level); delay(500); Serial.print("Set wlan to authorization level "); Serial.println(auth_level);

// Set the WiFly authorization phrase using <auth_phrase> SPI_Uart_print("set w p ");

SPI_Uart_println(auth_phrase); delay(500); Serial.print("Set security phrase to "); Serial.println(auth_phrase); // Set the WiFly localport using <port_listen> SPI_Uart_print("set i l "); SPI_Uart_println(port_listen); delay(500); Serial.print("Set IP localport to "); Serial.println(port_listen);

// Turn off WiFly UART connection indicators SPI_Uart_println("set comm remote 0"); delay(500); SPI_Uart_println("set comm open 0"); delay(500); SPI_Uart_println("set comm close 0"); delay(500); // Join WiFi network <ssid> Serial.print("Joining '"); Serial.print(ssid); Serial.println("'"); delay(100); Flush_RX(); delay(100); SPI_Uart_print("join "); SPI_Uart_println(ssid); for(int p = 0; p < 5; p++) // Give WiFly time to associate { Serial.print("."); speakjet.print(ping); delay(ASSOCIATE_TIMEOUT/5); } Serial.println("");

// Flush characters from SC16IS750 Flush_RX();

// Check for association SPI_Uart_println("show c"); if(Wait_On_Response_Char(13) != '0') // Failed, try again { Serial.print("Failed to associate with '"); Serial.print(ssid); Serial.println("'\n\rRetrying..."); Flush_RX(); autoconnect(); } else // Success, SpeakJet says "Associated" and IP address { Serial.println("Associated!"); speakjet.print(message); delay(1000); SPI_Uart_println("get ip"); get_ip(); } }

void Flush_RX(void) // Flush characters from the SC16IS750 so we only see what we want to see on the terminal { int j = 0; while(j < 4000) { if((SPI_Uart_ReadByte(LSR) & 0x01)) { incoming_data = SPI_Uart_ReadByte(RHR); } else { j++; } } }

void Parse_Request(void) // Parse POST data into separate words, look up, and "say" words using SpeakJet { int j = 0, i = 0, k = 0; char a = 0; char printflag = 0; char message[128]; char poem_word[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 char buffer[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 char sentinel = 0; while(j < 4000) // Read in full message { if((SPI_Uart_ReadByte(LSR) & 0x01)) { incoming_data = SPI_Uart_ReadByte(RHR); if(printflag){ message[i-3] = incoming_data; i++; } else if(incoming_data == '%'){ printflag = 1; } } else { j++; } } i -= 3; j = 0; while(k<i) // Split <message> into words and say { while(message[k] != '+' && k < i) { poem_word[j] = message[k]; k++; j++; }; };

} k++; for(int index = 0; index < WORD_LIST_LENGTH; index++) { if(strcmp(poem_word,SpeakJetLookup[index]) == 0) // Look up word in SpeakJetLookup { // Serial.print(index,DEC); // Print index of word for debugging strcpy_P(buffer, (char*)pgm_read_word(&(SpeakJetDictionary[index]))); // Copy word from program memory using lookup index speakjet.print(buffer); // Send string to the VoiceBox is_get = 0; // Flag as POST request delay(600); // Wait in between words } } j = 0; for(char c = 0; c < 16; c++){ // Clear buffers poem_word[c] = 0; buffer[c] = 0; } } }

char Wait_On_Response_Char(char num) // Check for character number <num> from a response string { i = 1; while(1) { if((SPI_Uart_ReadByte(LSR) & 0x01)) { incoming_data = SPI_Uart_ReadByte(RHR); //Serial.print(incoming_data, debugging if(i == num){ return incoming_data; } else{ BYTE); // Print data for

i++; } } } } void SPI_Uart_println(char *data) // Write <data> to THR of SC16IS750 followed by a carriage return { SPI_Uart_WriteArray(data,strlen(data)); SPI_Uart_WriteByte(THR, 0x0d); } void HTML_print(char *data) // Write <data> to THR of SC16IS750 followed by a delay { SPI_Uart_WriteArray(data,strlen(data)); delay(30); } void SPI_Uart_print(char *data) // Obfuscation of SPI_Uart_WriteArray that uses strlen instead of hardcoded length { SPI_Uart_WriteArray(data,strlen(data)); } char spi_transfer(volatile char data) { SPDR = data; // Start the transmission // Wait for the end of the

while (!(SPSR & (1<<SPIF))) transmission { }; return SPDR; }

// return the received byte

void get_ip(void) // Parse out IP address of WiFly and "say" it with the speakjet

{ char returns = 0; char ip_index = 0; char a = 0; // Parse while(returns < 6) { if((SPI_Uart_ReadByte(LSR) & 0x01)) { incoming_data = SPI_Uart_ReadByte(RHR); if(incoming_data == 0x0d) { returns++; } if(returns > 4) { //Serial.print(incoming_data,BYTE); ip_address[ip_index] = incoming_data; ip_index++; } } } delay(1000);

// Say IP address while(ip_address[a] != ':') { if(a < 5){ } else if(ip_address[a] == '.'){ Serial.print(ip_address[a]); speakjet.print(dot); delay(500); } else{ Serial.print(ip_address[a]); speakjet.print(numbers[(ip_address[a] - 48)]); delay(500); } a++;

} Serial.println(""); Flush_RX(); // Do not print characters }

Step 3: Code to test the basic 16x2 character LCD

/* LiquidCrystal Library - Hello World

Demonstrates the use a 16x2 LCD display. The LiquidCrystal library works with all LCD displays that are compatible with the Hitachi HD44780 driver. There are many of them out there, and you can usually tell them by the 16-pin interface.

This sketch prints "Hello World!" to the LCD and shows the time.

The circuit: * LCD RS pin to digital pin 12 * LCD Enable pin to digital pin 11 * LCD D4 pin to digital pin 5 * LCD D5 pin to digital pin 4 * LCD D6 pin to digital pin 3 * LCD D7 pin to digital pin 2 * LCD R/W pin to ground * 10K resistor: * ends to +5V and ground * wiper to LCD VO pin (pin 3)

Library originally added 18 Apr 2008 by David A. Mellis library modified 5 Jul 2009 by Limor Fried (http://www.ladyada.net) example added 9 Jul 2009 by Tom Igoe modified 22 Nov 2010 by Tom Igoe

This example code is in the public domain.

http://www.arduino.cc/en/Tutorial/LiquidCrystal */

// include the library code: #include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

void setup() { // set up the LCD's number of columns and rows: lcd.begin(16, 2); // Print a message to the LCD. lcd.print("hello, world!"); }

void loop() { // set the cursor to column 0, line 1 // (note: line 1 is the second row, since counting begins with 0): //lcd.setCursor(0, 1); // print the number of seconds since reset: //lcd.print(millis()/1000); }

Last step: Code that combined all code

/* 4-28-2011 Spark Fun Electronics 2011 Nathan Seidle

This code is public domain but you buy me a beer if you use this and we meet someday (Beerware license).

This example code plays a MP3 from the SD card called 'track001.mp3'. The theory is that you can load a microSD card up with a bunch of MP3s and then play a given 'track' depending on some sort of input such as which pin is pulled low.

It relies on the sdfatlib from Bill Greiman: http://code.google.com/p/sdfatlib/ You will need to download and install his library. To compile, you MUST change Sd2PinMap.h of the SDfatlib! The default SS_PIN = 10;. You must change this line under the ATmega328/Arduino area of code to uint8_t const SS_PIN = 9;. This will cause the sdfatlib to use pin 9 as the 'chip select' for the microSD card on pin 9 of the Arduino so that the layout of the shield works.

Attach the shield to an Arduino. Load code (after editing Sd2PinMap.h) then open the terminal at 57600bps. This example shows that it takes ~30ms to load up the VS1053 buffer. We can then do whatever we want for ~100ms before we need to return to filling the buffer (for another 30ms).

This code is heavily based on the example code I wrote to control the MP3 shield found here: http://www.sparkfun.com/products/9736 This example code extends the previous example by reading the MP3 from an SD card and file rather than from internal memory of the ATmega. Because the current MP3 shield does not have a microSD socket, you will need to add the microSD shield to your Arduino stack.

The main gotcha from all of this is that you have to make sure your CS pins for each device on an SPI bus is carefully declared. For the SS pin (aka CS) on the SD FAT libaray, you need to correctly set it within Sd2PinMap.h. The default pin in Sd2PinMap.h is 10. If you're using the SparkFun microSD shield with the SparkFun MP3 shield, the SD CS pin is pin 9.

Four pins are needed to control the VS1503: DREQ CS DCS Reset (optional but good to have access to) Plus the SPI bus

Only the SPI bus pins and another CS pin are needed to control the microSD card.

What surprised me is the fact that with a normal MP3 we can do other things for up to 100ms while the MP3 IC crunches through it's fairly large buffer of 2048 bytes. As long as you keep your sensor checks or serial reporting to under 100ms and leave ~30ms to then replenish the MP3 buffer, you can do quite a lot while the MP3 is playing glitch free.

*/ // include the library code: #include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins //LiquidCrystal lcd(7, 8, 9, 10, 11, 12); LiquidCrystal lcd(5, 4, 13, 3, 11, 12);

#include <SPI.h>

//Add the SdFat Libraries #include <SdFat.h> #include <SdFatUtil.h>

//Create the variables to be used by SdFat Library Sd2Card card; SdVolume volume; SdFile root; SdFile track;

//This is the name of the file on the microSD card you would like to play //Stick with normal 8.3 nomeclature. All lower-case works well. //Note: you must name the tracks on the SD card with 001, 002, 003, etc. //For example, the code is expecting to play 'track002.mp3', not track2.mp3.

char trackName[] = "track001.mp3"; int trackNumber = 1;

char errorMsg[100]; //This is a generic array used for sprintf of error messages #define TRUE 0 #define FALSE 1

//MP3 Player Shield pin mapping. See the schematic #define MP3_XCS 6 //Control Chip Select Pin (for accessing SPI Control/Status registers) #define MP3_XDCS 7 //Data Chip Select / BSYNC Pin #define MP3_DREQ 2 //Data Request Pin: Player asks for more data #define MP3_RESET 8 //Reset is active low //Remember you have to edit the Sd2PinMap.h of the sdfatlib library to correct control the SD card.

//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

void setup() {

// set up the LCD's number of columns and rows: lcd.begin(16, 1); // Print a message to the LCD. lcd.print("mushion");

pinMode(MP3_DREQ, INPUT); pinMode(MP3_XCS, OUTPUT); pinMode(MP3_XDCS, OUTPUT); pinMode(MP3_RESET, OUTPUT);

digitalWrite(MP3_XCS, HIGH); //Deselect Control digitalWrite(MP3_XDCS, HIGH); //Deselect Data digitalWrite(MP3_RESET, LOW); //Put VS1053 into hardware reset

Serial.begin(57600); //Use serial for debugging

Serial.println("MP3 Testing"); //Setup SD card interface pinMode(10, OUTPUT); //Pin 10 must be set as an output

for the SD communication to work. if (!card.init(SPI_FULL_SPEED)) Serial.println("Error: Card

init"); //Initialize the SD card and configure the I/O pins. if (!volume.init(&card)) Serial.println("Error: Volume ini");

//Initialize a volume on the SD card. if (!root.openRoot(&volume)) Serial.println("Error: Opening root"); //Open the root directory in the volume.

//We have no need to setup SPI for VS1053 because this has already been done by the SDfatlib

//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 delay(10); digitalWrite(MP3_RESET, HIGH); //Bring up VS1053 //delay(10); //We don't need this delay because any register changes will check for a high DREQ

//Mp3SetVolume(20, 20); //Set initial volume (20 = -10dB) LOUD Mp3SetVolume(0, 0); //Set initial volume (20 = -10dB) Manageable //Mp3SetVolume(80, 80); //Set initial volume (20 = -10dB) More quiet

//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

Serial.print("SCI_ClockF = 0x"); Serial.println(MP3Clock, HEX); //Now that we have the VS1053 up and running, increase the 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);

//MP3 IC setup complete

void loop(){

/*lcd.setCursor(0, 1); // print the number of seconds since reset: lcd.print(millis()/1000);*/

Serial.println (analogRead (0), DEC); if (analogRead (0) > 800) { Serial.println ("LED on"); digitalWrite (13, 255); }

if (analogRead (0) < 800) { Serial.println ("LED off"); digitalWrite (13, 0); }

if(analogRead(0) > 800) { Serial.println("pushed!!!!!! HEARTBEATS ON"); //Let's play a track of a given number sprintf(trackName, "track%03d.mp3", trackNumber); //Splice the new file number into this file name playMP3(trackName); //Go play trackXXX.mp3

/*Serial.println ("LED on"); digitalWrite (13, 255);*/

//Once we are done playing or have exited the playback for some reason, decide what track to play next //trackNumber++; //When we loop, advance to next track! } /*if(trackNumber > 5) { Serial.println("Whoa there cowboy!"); //Soft limit. We

shouldn't be trying to open past track 100. while(1); }*/ }

//PlayMP3 pulls 32 byte chunks from the SD card and throws them at the VS1053 //We monitor the DREQ (data request pin). If it goes low then we determine if //we need new data or not. If yes, pull new from SD card. Then throw the data //at the VS1053 until it is full. void playMP3(char* fileName) {

if (!track.open(&root, 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(!track.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(!track.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

track.close(); //Close out this track

sprintf(errorMsg, "Track %s done!", fileName); Serial.println(errorMsg); } //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); }

Вам также может понравиться