Sunday, May 20, 2012

VS1053b-based MP3 Player

Part 1
Please note that this will be a multi-part article. This is the first post on this topic and will cover the overview of my learning experience with this project. There will be additional posts in the future dealing with some of the more specific parts.
Usable Features
  • Plays MP3, WAV, WMA and MIDI files
  • Files stored on a 2-gigabyte SD card
  • Song playlist stored as a text file on the SD card
  • Track forward/backward, Play, Pause, Volume up/down buttons
  • MP3 player can be controlled via hardware buttons, IR TV remote, and serial console commands
  • Automatic skip of files that cannot be opened
  • LCD screen displays song data
  • Power-on audio greeting (stored on I2C EEPROM)
  • Parsing of ID3 tags (display via serial console)
  • Shuffle (random play) (poor randomization)

TABLE OF CONTENTS (sorry, no hotlinks!)
  • Intro
  • Logic Level Conversion (resistive dividers)
  • Reading data from the SD card
  • Communicating with the VS1033d
  • Dealing with defective products
  • Cheap way of (slowly) handling low voltage logic signals
  • SD read/stream speed issue
  • Out with the VS1033d, in with the VS1053b
  • Maker Faire 2011
  • Getting much higher throughput from the SD card
  • Logic Level Conversion with TXB0108b
Hello everyone! This is project that I have been working on since late November 2010. Initially it was supposed to be started as a team project with a friend of mine but that friend needed to work on another project. I have had a little over a year to work on it. Honestly, like many projects, I pick them up pretty solid for a month or two and then I have to put it away for a bit and then come back to them again. The same is true with this project. Each time the project was resumed it I came up with new features added to it.
I felt very confident in my abilities to work with the Arduino and the more I learn the more I truly enjoy these projects. Making an MP3 player is definitely a step up in complexity from the previous projects that I have done but I enjoy the challenge. For this project, I required the following main ingredients: 328P based Arduino (Boarduino), the VS1033d breakout board, an SD card breakout connector and an SD Card. In lay terms, I put an MP3 on the SD Card, read that data and stream it into the VS1033d which is connected to speakers.
Initially, I actually started with the VS1033b and breakout board from Sparkfun. I purchased two of them actually just in case I messed up the logic/voltage level handling. This chip required 2.7v for Vcc although it would actually handle 3.3v logic. The Arduino uses 5v logic so signals from the Arduino could damage the VS1033b. Initially I used resistors as voltage dividers so that the VS1033b would only see 3.3 volts from the Arduino. Unfortunately, the VS1033b used 2.5v for a logic “1” which is below the boundary of what the Arduino would accept as a “1”. As a result, receiving data back from the VS1033b was very problematic although it could be sent data from the Arduino. image
The picture to the right shows a simple schematic drawing of how I was configuring the resistive dividers for the SPI interface. It is very important to put the resistors in the correct order so that you have the correct resulting voltage at the mid-point between the resistors. You can test that the resistors are correct by using a volt-meter to test the middle point between the resistors and ground. If the voltage value is correct then everything is fine.
So, for now, I had solid 1-way communication to the MP3 decoder chip, the VS1033b. Later on I would use a different method to handle the logic-level conversion.

Now I needed a way to read the data from the SD card, interpret the FAT file system, and read the data. I therefore needed to acquire knowledge of file-system structure and then be able to easily add MP3s to the SD Card just as I would with any other MP3 player. I figured that all I really needed to do in code was initiate the MP3 decoder, read the data from the SD card, and send the data to the decoder. Simple streaming from file beginning to file end. For MP3 files you could actually start the file stream anywhere in the file which means that the potential for fast-forward and reverse exists.
So, I looked for specification documentation on the SD command set and how the FAT file system worked. First, I found out that SD cards can be accessed via the SPI protocol, which I was already familiar with. Next, I discovered that SD cards worked from 2.7v to 3.3 volts so I would need to handle the logic/voltage levels similar to the VS1033b. Initially I used resistive dividers just like the MP3 decoder.
I did read the specifications for the FAT file system but soon found that the Arduino team released the 0022 IDE which included a library for using SD Cards which was based on sdfatlib by Bill Greiman and the MemoryCard library by Philip Lindsay. A (older) version of which was included in the Arduino 0022 IDE. Thankfully, Thanks to that, the SD Card portion turned out to be a real "piece of cake."
I first started with the included example programs to read and write to the SDCARD. It worked rather well and was simple to access. I read through Bill Greiman’s SDFAT library for Arduino and I found that it was very complicated but very neat. I’m glad that I did not need to write the code for the SD Card and FAT. I also noticed some features that were not used by the version released with the Arduino IDE.
The data returned from the SD card was not always consistent. I knew that the resistive dividers (for logic level conversion) are not perfect.  Sometimes the data was garbled and sometimes it was not. I discovered that the SPI communication required much shorter wires than I was using. Also, the speed that I could access the SD card was limited to the slower speeds because the higher speeds would result in more errors. I tested for errors by creating a large plain-text file, saved it to the SD Card, and then read it back via the serial console so that I can see any errors in the text. Slower speeds resulted in no errors while faster speeds would have errors. I limited the speed to 4 Mhz which is half speed SPI on the Arduino.
One was complete now that I could read the SD card and it was now time to figure out how to communicate with the VS1033d. I started reading the 78-page datasheet for the VS1033d MP3 decoder chip. In retrospect, the datasheet actually was pretty clear for the most part but initially it was quite overwhelming. I just had to chew it up piece by piece. The first thing to understand is that there are two different main interfaces on the MP3 decoder chip. The interfaces are Serial Control Interface(SCI) and Serial Data Interface (SDI). SCI would be used to modify settings such as the decoder’s clock frequency or multiplier, and the volume and bass and treble settings. SDI was used for the actual MP3 data stream. The VS1033d chip uses the Serial Peripheral Interface Bus (SPI) which I was familiar with after working with the digital potentiometers from the SuperCar project. The default SCI register values would allow you to send data in the "SDINEW" mode which means that there are separate ChipSelects (CS) lines for the SCI and SDI but they both can share the same clock and data in or out lines because technically only one of the interfaces should be active at a time which is a benefit of SPI. Apparently, the older method (Compatibility Mode) actually used separate clock and data in/data out lines. One more thing, this MP3 decoder chip includes a Data Request pin (DREQ) which will go LOW if the VS1033d can not keep up with the data stream. This acts as an indicator/governor that can be interpreted by your microcontroller program to either "Slow down" or "Send some more data". Seems like a rather nice chip, but, there would be problems with it which I will explain fully later. If you are working with one of the now older Sparkfun breakout boards for this chip then read on and please, allow me to save you some serious stress. :)
So, from the Arduino code standpoint the process is rather simple, initiate the SD Card, open a file, read some data from it, stream it to the VS1033d and then close the file on the SD Card when you are done. However, all I ever got was a clicking from the speaker attached to the MP3 decoder. This was confusing at first so I took out my very basic logic probe and put it on the speaker pin and heard the same pattern of clicking. I then put the probe on the Master Out Slave In (MOSI) from the Arduino and I was shocked. The pattern of "clicking" was exactly the same. How can this be? Am I listening to the raw data bits? Wasn't the VS1033d chip supposed to decode this data and turn it into some real music?
Apparently, the VS1033d was NOT doing its job but I wasn't about to just blame the chip. I had bought two of them just in case I messed one up. I very quickly replaced the first chip with the second chip and double and triple checked ALL wiring, my voltage dividers for the voltage logic level conversion and the LM317 adjustable linear regulator that I was using to power the VS1033d with the 2.5v DC that it wanted. Everything checked out.
OK, turn it all back on and get ready to listen to some sweet music. Nope, not today, "no music for you!" Just those clicking sounds. I’m not a computer so they definitely did NOT sound like music to me.
I rebuilt the complete circuit several times just to convince myself that I had not made a careless mistake. Let’s just say that I got to know my circuitry very well.

At one point I managed to fit the entire MP3 player (minus buttons) on a small breadboard.

Neither breakout board would work. Two defective breakout boards from Sparkfun? Seriously??? ... No, that could not possibly be it. It must be my code. I drilled in so deep, coded and recoded, studied the VS1033d datasheet like it was the only thing that mattered, I rewired the whole circuit multiple times to neaten it up and to prove to myself that I wasn't crazy and I had actually correctly wired it all. After the time and intensity that I put into this problem I had to come to the conclusion that it simply couldn't be my fault. No, this was not my ego. I was seriously very certain that I did everything right. The only part of the circuit that I had not focused on was the breakout circuit board for the VS1033d itself. I had decided to purchase a breakout board for the VS1033d chip from Sparkfun. This would save me a lot of trouble configuring the crystal isolator and all the supporting circuitry for the VS1033d chip itself. I decided to confirm that the circuit on the breakout board was correct.
I researched the datasheet thoroughly and I was convinced that I could not have made any mistakes that would eliminate ALL MEANINGFUL OUTPUT from the MP3 player. I researched via the Internet, the Sparkfun forums and the comments of the actual webpage that the breakout board is sold from. Some of the customers had commented that the "TEST pin was left unconnected." Floating pins? Well, some pins simply are just not used for some applications right? So, I went back to the datasheet and sure enough, it stated that the "TEST" pin needed to be tied HIGH. Floating pins are not HIGH, as they can float between HIGH and LOW, thus the term “floating.” I used the datasheet to determine where this test pin was on Sparkfun’s breakout board and sure enough it was not connected to anything. A clear mistake on Sparkfun's part, verified by myself via the datasheet and reported by several customers about their breakout board. In a way this was vindication as I now knew that it wasn't my fault!
Still, I had to figure out how to tie that floating pin high on a surface mount IC chip with nearly no room between pins. An hour with a magnifying lens, some flux, solder braid to fix all the accidental pin bridging, I was finally able to take one of the conductor wires from some CAT5 Ethernet cable and solder that wire to the TEST pin. I used my continuity tester on my multi-meter to confirm that I did not bridge any pins with the wire and that all previous solder bridges were removed. Success! Next, time to test and hear that sweet music.
An early design of the MP3 player using resistive dividers for logic level conversion.
It worked!! … Well… it beeped anyway.

So at this point my Arduino code was only designed to take advantage of the beep test mode of the VS1033d. The beeping sound was the sound of success! That beep sound was very annoying but I did not care. However, my success was short lived as my roommate could hear the annoying high pitched beeping. It was now very clear that I needed to also have some success with the volume settings. Actually, this turned out to be easy so I quickly coded a low volume for MP3 decoder so the volume was high enough to hear but not high enough to cause tension with the occupants of the house. :)

So, I sent Sparkfun an email to inform them about the clear error with their breakout board. This email included specific information from the datasheet, my own observations, and the observations of other people on Sparkfun’s own forums and comment boards. They responded back to offer to test it and I said that I was able to get it working only by tying the “TEST” pin HIGH which again was “floating” on their breakout board. I asked for a refund, which they gave me.  Hard to argue with the facts that I included. They now sell a modified version of their breakout board, no doubt because it was drawn to their attention. Of the (unusually minimal) number of blogs/websites building something out of this breakout board, my information was quite specific. So anyway, my very rickety-rig-fixed VS1033d was working so moving on.
So, now at this phase, I am able to send data to the SDI on the VS1033d and can hear the beep-test sounds. The only remaining issue is with the DREQ pin which only has a digital HIGH of just below 2.5v. The Arduino requires a digital signal above 2.5v to interpret a logic "1". I could not interpret a such a low-voltage logical “1” with my digital pins but I came up with a cheap idea. The Arduino can interpret ANALOG signals so I would have the Arduino listen to DREQ on an analog pin. The DREQ wasn’t technically oscillating like an AC or timing circuit would but it would dance around that 2.5v boundary enough to confuse the digitalRead() read feature of the Arduino. The analog pin could see signals below a logic “1”. The ADC (Analog to Digital Converter) on the Arduino can see a value between 0 and 5 volts as a value of 0 to 1023. Half of 5 volts is 2.5 volts and if 5 volts is equal to 1023 on the ADC then 2.5 volts must be equal to half that. So now, by checking if the analog reading of DREQ is above or below 511 I can see if it is “HIGH'” or “LOW”. … Yes I know that I could have configured a transistor or a buffer IC to handle this instead but I didn’t at this time. The ADC DREQ trick and my earlier (poor) soldering worked to give me a much more stable control of the beep test functions of the VS1033d. Finally, some progress!
I tried buffering portions of the MP3 file and then sending that data via SDIA but the results were of very poor quality. It seemed that I could hear the audio but it sounded like a CD track skipping. There was no CD and there was no track so what was actually happening? The file I used was of a high bitrate. I figured (incorrectly) that my Arduino or the MP3 decoder simply could not handle the high data rates. 64 kilobits was the highest that seemed to be of intelligible play quality so I downloaded a 64 kilobit copy of a podcast I like called “Security Now” which is done with and Steve Gibson of GRC and Leo Laparte of TWIT. The MP3 played but played at the wrong speed. I figured that this was because I had not properly initialized the MP3 decoder but still, IT WAS  PLAYING AN MP3! Anyway, I put the MP3 player in my car and drove to my friend’s house to share my progress. Especially exciting (more-so to us nerd types) was that I was able to use my cassette converter to play the audio from the MP3 player out of the speakers of my car! Yes, there was a giant solder-less breadboard on my passenger seat with all kinds of wires and stuff attached to it and it was hooked up to my car radio! Nerd-cred!
I was uncertain about my belief that the Arduino and/or MP3 decoder was unable to decode higher bitrate files. I tried to fiddle around with some of the register settings of the VS1033d but could never get consistent data when I would read back the register. So, I couldn’t tell if the data I was sending to the register was actually being accepted. This was probably due to the 2.5v logic coming back from the VS1033d. My ADC trick wouldn’t work in this case because I was using the built-in (and much faster than manual) SPI hardware built into the Arduino chip. I simply had no certain way of determining what settings the VS1033d actually received. I started to consider alternatives to how I was handling logic level conversion.
Then something horrible happened. The (very weak) solder connection between the “TEST” pin and the Vcc of the VS1033d broke. I was unable to fix it and was pretty certain that I heat-damaged the chip while trying. The pin spacing was so small I was surprised that I even got it to work in the first place.. I was also unable to re-fix the other VS1033d that I bought. This was a major bummer and thus ends the reign of the VS1033d and my project.
But wait! NO! I was not willing to let all of my research, experiments, and success go to waste! Heck no I wasn’t! This MP3 player was GOING to be built! But I wasn’t about to purchase another VS1033d from Sparkfun when I know that they don’t work without difficult modification because of the floating “TEST” pin issue. To my surprise, Sparkfun had fixed all of their old stock of VS1033d AND VS1053b… with the fix that I suggested to them!! They did a MUCH better job of soldering that little piece of green wire than I did. Ok, I’m ready, I’ll buy another MP3 decoder.
I noticed that the VS1053b was also for sale and ALSO had my fix applied to it. I looked through the datasheet for the VS1053b and found that the internals were mostly the same as the VS1033d but it could be powered with 3.3v! This is a big deal because now my Arduino shouldn’t have any trouble interpreting the digital “1’s” from the MP3 decoder. So I bought one VS1053d and made the necessary small adaptations to my existing code and to my circuit.
Almost immediately the chip started working. I felt like I was right back in the game but now with an upgraded MP3 decoder. Believe me, that felt good. It felt like all the trouble and hard work with the VS1033d chip was still serving my project. 
Although it was not my featured project and incomplete, I decided to bring my MP3 player to the last day of Maker Faire. At this point, the MP3 player had the addition of Infrared Control via TV Remote (Thanks again to _______ for their IR library.) I had also started using a “songlist.txt” file on the SD card to act as a playlist and to save lots of RAM on the Arduino. In addition I had began implementing MIDI support and volume control. This was also a stress-test for my MP3 player as I had it playing all day long. Other than some small glitches that I quickly fixed in the code on the spot, everything worked as expected. 07312011_(142347)_02-23-47PM_Sunday
My MP3 player on display at Maker Faire 2011.

I had made a lot of progress on this project but there were still some issues that remained. Priority number one was figuring out how to play files at bitrates above 64k. Having to convert each and every file down to 64k was a hassle and took away from the utility of the device. So I created a play list that included MP3s at different bitrates. I analyzed the playback of these MP3 songs constantly. I noticed that at higher than 64 kilobit bitrates that it may not have been a matter of processing power but a matter of speed. The song wasn’t skipping because of some processing error, the skips were PAUSES. Now, what could cause a pause? Insufficient data stream speed! I was using the Sparkfun SD library included with Arduino IDE 0022 and at this point now it was nearly a year old. I wondered if the library that it was based on had been upgraded. If it was upgraded then I decided that I would adapt all of my code to use the new library, which would be a lot of work but worth it if it worked.
First I created some experiments. I would try to read the MP3 from the SD card directly into the MP3 decoder. I used a for() loop that would read data from the SD card and then immediately send it to the MP3 decoder one byte at at time. The results were horribly slow. I then tried to fill a 32 byte buffer on the Arduino with MP3 data from the SD card and then send the contents of the buffer. The results sounded better but still had the same ticking/pausing/blank noise from before. I then tried to make the buffer as big as I could. This is when I learned what happens when you use too much RAM. Weird, seemingly unexplainable things happen. Using the “freeram()” function built into the SD library I reworked my code to better use RAM. I disabled all features that were unnecessary to have accessible for this test. I found that my test showed some noticeable improvement over the other tests but it still had the same problem. I wondered how the data was actually being read from the SD card. For this I would need to read the datasheet on the SD specification as well as the FAT specification. It did not take long to find what I needed.
I found that SD cards must be accessed an entire sector at a time and a sector is 512 bytes. I came to believe that there was 511 bytes of unnecessary IO (at least) for every byte of data that I pulled from the SD card. Considering all the overhead as well as unnecessary I/O then that’s it! EUREKA! The SD card library could only read data 1 byte at a time and not in groups. Unfortunately this behavior I could not easily modify in the Sparkfun SD library.
Feeling like I needed to upgrade to the “Big Boy” library I looked to the current version of Bill Greiman’s SDFAT library which the Sparkfun SD library was based on. I was impressed by how complex it was. I was even more impressed by how well it was documented. Even with the complexity of his library his documentation was very easy to read and I found what I needed. I found that I could read a specified amount of data bytes from the SD card directly to a memory address, dumping chunks of data into my local buffer. So, I loaded both libraries up at the same time and then changed one line of my MP3 code at a time, checking to make sure everything still worked. Once it was fully converted I unloaded the old library and used the new read commands in the new library to completely fill my buffer with each read of the SD card. Immediately I was able to play MP3s at up to 320k. Success! Heck ya! That pretty much every MP3 out there with a sane bit-rate!
  • Conversion from Sparkfun’s SD library wrapper (included with Arduino IDE 0022) to Bill Greiman’s updated version that had a very useful feature of allowing multi-byte reads instead of single-byte reads.
    • Increased bitrate up to 320k
    • Able to play 1114k WAV files if that is the only thing that the Arduino is doing.
  • Conversion from digitalWrite to direct-port-manipulation when toggling the ChipSelect pins on SPI devices
  • Physical interface buttons (5-way control stick)
    • Saving I/O pins by using parallel-in / Serial-out shift registers
    • Adding an additional shift register for more buttons!
    • Conversion from digitalWrite to direct-port-manipulation when reading data from the shift registers
  • The Arduino can accept control commands from an infra-red remote control (TV remote)
  • Upgrade the voltage/logic level conversion hardware to use an 8-channel Bi-directional Level Converter (TXB0108b) instead of resistive dividers.
  • Connected an LED to DREQ so that the status of DREQ is visible without the Arduino needing to handle it.
  • Using the proper commands to end songs when they complete. This was especially important when using formats other than MP3 such as MIDI.
This project was huge and I could not have done it nearly as fast if it were not for the resources of the Internet and the many many websites that I read through. However, most of the websites were about FAT, SPI, C++, electronic theory. I found very few websites that actually had a working VS1033d or VS1053b project.

Resources / Accreditation(s):

No comments:

Post a Comment

Keep it clean. :)