In one of my various orders to Sparkfun, I picked up a vs1002 IC on a breakout board. Just yesterday I got round to having a good look at it, and I figured if I show my code then others will not have to do the boilerplate and just get it working. My setup is a ATMEGA88 running at 3.686MHz on a STK500 board, but it should work for most anyone with a decent clock speed. Just remember that all code is ATMEGA88 specific and may need to be changed for other devices.
Things that may be useful: the Sparkfun page, the datasheet.
Communication modes
(Note: The vs1002 has a backwards compatability mode with the vs1001. We will not be using it)
The vs1002 IC does all meaningful communication over the SPI port. It has two submodes if you will, one being SCI (Serial Command Interface) and the other being SDI (Serial Data Interface). SCI is for things like play control, reading and writing registers and small technical changes. Conversely, SDI is there for MP3 data and a few testing modes.
Since the AVR has only one SPI bus, we will be sharing the CS (Chip Select) line with both submodes.
Ok, enough. Get to the code!
Not quite yet, first we need to wire it up! Since it is a fairly simple circuit, I am going to put my wiring in list form (AVR -> VS1002):
- SS -> CS
- MOSI -> SI
- MISO -> SO
- SCK -> SCLK
- Connect the GND pin to GND and the Vin pin to +3.3v
- Wire the DREQ pin to an input on the AVR (if you intend to expand outside this tutorial)
//spi.h // spi functions // pin setup #define DDR_SPI DDRB #define PORT_SPI PORTB #define DD_CS 2 #define DD_MOSI 3 #define DD_MISO 4 #define DD_SCK 5 void spi_init(); // init the SPI subsystem void cs_low(); // helpers void cs_high(); char spi_send(char byte); // send a byte; return rx byte
//spi.c // spi helper functions #include <avr/io.h> #include "spi.h" void spi_init() { DDR_SPI = 0xFF; // set the whole port to output DDR_SPI &= ~(1 << DD_MISO); // change back to input SPCR = (1 << SPE) | (1 << MSTR); // set spi clock rate to fck/4 } void cs_low() { PORT_SPI &= ~(1 << DD_CS); } void cs_high() { PORT_SPI |= (1 << DD_CS); } char spi_send(char byte) { SPDR = byte; //send byte while(!(SPSR & (1 << SPIF))); //wait until its done return SPDR; //return the rx byte }
//vs1002.h // chip control functions //SCI_MODE register defines (p. 26 datasheet) #define SM_DIFF 0 #define SM_SETTOZERO 1 #define SM_RESET 2 #define SM_OUTOFWAV 3 #define SM_PDOWN 4 #define SM_TESTS 5 #define SM_STREAM 6 #define SM_PLUSV 7 #define SM_DACT 8 #define SM_SDIORD 9 #define SM_SDISHARE 10 #define SM_SDINEW 11 #define SM_ADPCM 12 #define SM_ADPCM_HP 13 int sci_read(char address); // serial command interface read/write funcs void sci_write(char address, int data); void send_sinewave(char pitch); // send the sinewave test
//vs1002.c // chip control functions #include <avr/io.h> #include "spi.h" #include "vs1002.h" int sci_read(char address) { unsigned int temp; cs_low(); spi_send(0x03); //send read command, don't worry about return byte spi_send(address); //now send the address to read temp = spi_send(0x00); //dummy byte to get out data MSBs temp <<= 8; //shift it along so we can fit more data in temp += spi_send(0x00); // get the LSB cs_high(); return temp; } void sci_write(char address, int data) { cs_low(); spi_send(0x02); //send write command spi_send(address); //send address we are going to write to spi_send((data >> 8) & 0xFF); //send the first 8 MSBs of the data spi_send(data & 0xFF); //send the LSBs cs_high(); } void send_sinewave(char pitch) { cs_high(); //this is different to SCI, it is data over SDI (see p.21 datasheet) /* we need to send the following bytes (see p.35) 0x53 0xEF 0x6E 0 0 0 0 */ spi_send(0x53); spi_send(0xEF); spi_send(0x6E); spi_send(pitch); for (int i=0; i < 4; i++) spi_send(0x00); // send the filler bytes cs_low(); }
//vs1002test.c // this program will initialise the vs1002 ic and send it the sine wave test command #include <avr/io.h> #include "spi.h" #include "vs1002.h" int main(void) { spi_init(); //set up SPI registers cs_high(); // probably a good idea sci_write(0x00, (1<<SM_TESTS)|(1<<SM_SDISHARE)|(1<<SM_STREAM)|(1<<SM_SDINEW)); cs_low(); // for data interface send_sinewave(170); }
Great post – I was looking to use the VS1011 I picked up from Sparkfun as well. Since the VS1011 can work in 1002 mode this looks like a great primer.
Do you have any working code for playing MP3s? Did you mount an SD card or connect it directly to a PC for testing? Have you tried streaming?
If I understand your post correctly you used SCI for the initial test, but you’d switch to SDI if you were going to send actual MP3 data – correct?
Thanks
Hey ToddV,
I have recently had some computer troubles so I have lost a lot of things but I’ll have a look for my code. If I remember correctly, to play an MP3 you simply read the file from whatever source and send it one byte at a time over SDI (cs_high(); spi_send(byte);). You don’t need to bother with headers or anything, just stream the file and it will play. SCI is reserved specifically for *how* the MP3 is played back, not the MP3 itself.
I was doing it over UART with a slow crystal so it sounded quite sad. But I imagine that if you can stream the file faster than it plays, it will fill the RAM on the chip and set the DREQ line to tell you to stop sending data. That way it would have consistent playback.
Let me know when you finish your project!
Nice little intro. I’m looking to make a “show control” board for a Halloween display this year to flicker lights in timing with sound and this is probably how i will do it.
I tried to do the sine wave test, however, I only get the sound when I stop the JTAG debugger! If I run it without JTAG it does not even work. Do you have any idea?
That is quite an odd problem. Are you sure you get the proper sine wave when you run stop the debugger? I never used this chip with JTAG and it worked perfectly for me.
I use the JTAG with ATMEGA128.
I do not know what the sine wave sounds like. I just get a tone when i stop the debugger. Can you suggest any way to solve this problem.
I figured out the problem. It was Bsync not asserted correctly. Now I can hear the mp3 files playing but in Slow Motion. Very Slow, I think it has to do with the sampling rate. Any suggestions?
Sam,
Did you write your own code or use mine? If you set SM_SDISHARE the clock on BSYNC is automatically generated. See p16 of the datasheet: “If SM SDISHARE is 1, pin XDCS is not used, but the signal is generated internally by inverting XCS.”
The sound was very slow for me also; my guess is because you aren’t sending data at the bitrate that it is playing; for example, if you don’t send >=128kbit/s for a 128kbit/s mp3, it will have to slow down. Try sending a very low bitrate mp3 (like 24 or 32kbit) and see if that works.
I’m using slightly different code. But it is similar.
After writting and reading back CLOCKF register, I found the return value always 0! my value (0×9800) was never written to the register to activate the clock doubler! I had to write it few times to correctly set the register! But after I did that, the audio sounded more normal since now it is activating the clock doubler.
The audio still needs alot of improvment, for example, the audio sound have vibration effect to it and it is skreetching alot. Any ideas?
Is there no need to isolate the two components? The AVR is operating at 5V and the VS1002 at 3.3V. Is this ok?
You need to isolate the components. According to Section 4.1 in the datasheet, the maximum supply voltage is 3.6V. If you are using an STK500, you can just change the board voltage level to 3.3v.
hello, my avr atmega8535L not detecting when i connect my ISP (STK 200), why? i use regulator LM 2576 in my circuit for suppying AT8535L & VS1002, then my vs1002 is hot!!why?
Thank you..
Hey,
I’m trying to get an MP3 file on an SD Card to play but am having troubles. The MP3 is playing in slow motion. My atmega328 is operating at 16Mhz, and I’m using the VS1002 breakout board from Sparkfun. I’m indeed setting the clock to 0×9800. I’ve tried all the possible SPI clock divisions on my atmega328.
From what I’m reading from the datasheet and application notes, if I have more than one slave sharing the SPI bus, I shouldn’t use SDISHARE, so I’m not. I’m using BSYNC/xDCS to control the SDI bus. Every 32 bytes, I’m checking to see if the DREQ register is clear, and if not, I have it wait until it is clear.
Is there anything else I should be setting?
kbryant,
I’m honestly not sure. I’m in the middle of final exams right now but I’ve been thinking of rewiring the circuit and getting it working during my time off. I had that problem originally but I put that down to my slow uart connection.
One thing you have to remember is to set a low enough bitrate on the mp3; I very much doubt a 320kbps mp3 will play well off an AVR through SPI. You didn’t mention what rate you were using, but try to use an mp3 that is as low as 8/16kbps and work up from there.
Hope this helps.
Alright, thank you for the quick response. I’ll keep working on this, and if I figure anything out I’ll repost here. Good luck on finals!
I just bought the Olimex Dev mod-mp3 board from spark fun, out the box the EEPROM will play/pause/fwd/rev mp3s no problem, i am stunned how great this little board performs. Iam not sure if you had a chance look at this board and how its wired up, I dont want to spend to much time trying to programing sense all I need is just basic play back functions, I saw the VS1002 has a UART onboard? is this for command/control of the chip? Iam not to sure how the EEPROM works with the toggle buttons to send the mp3 data to the decoder, Is it possible to use the UART to control playback of the mp3s??
Or would it be better to just use a AVR and SD card?
Hi. When running your sine test code, I got nothing at the output. Upon some debugging, I noticed that for me, the SM_SDINEW bit was reset initially in contrast to what the datasheet says. Setting this bit made it work correctly. Not sure why this happened, but I’d like to let others know to try this if they can’t get the sine test working.
I figured out why this code wasn’t working for me (and some other people).
You need to change the following line in vs1002test.c:
sci_write(0×00, (1<<SM_TESTS)|(1<<SM_SDISHARE)|(1<<SM_STREAM));
Have it include (re)setting SM_SDINEW, by changing the line to this:
sci_write(0×00, (1<<SM_TESTS)|(1<<SM_SDISHARE)|(1<<SM_STREAM)|(1<<SM_SDINEW));
Thanks for the code!
oh lol zeneslev beat me to it. Why dident i reread these comments before i went at it again today? lol
oh well good times.
Thanks guys. It does need the SM_SDINEW bit to stop it emulating the VS1001. Interestingly enough, my original code has that bit set; I must have accidentally deleted it when wordpress mangled my code.
All fixed now.
I believe if your using a 12.288Mhz crystal all you want to do is activate the clock doubler, not set any other bits in the CLOCKF register.
The other bits are used to correct the value if the clock is other than 24.576Mhz. Once you double 12.288 it dosent need to be corrected at all. So setting the CLOCKF register to 0×8000 seems to make more sense to me than setting it to 0×9800.
When I tried setting it to 0×9800 the sine test would not work. But when I set it to 0×8000 it worked again.
Ive been working to get the vs1002 playing nice with an atmega644 and a SD card using FAT FS to play MP3s off the SD. I am somewhat successful, im just trying to figure out the speed issue like everyone else here.
Im willing to shear to anyone who can shed some light on the subject! me@pauljmac.com
I am wrong. You must set CLOCKF to 0×9800. I cant figure out how it works. But anyway I am going to just load the code on the VS1002 to act as a standalone player with direct connection to the SD and control via an AVR. Seems to be a lot simpler than going though the AVR with FAT.
Keep the updates coming Paul! I am going to set up my board again after mid semester exams and I will give it another shot!
Its actually very easy. I more or less have it working now. It allows you to connect a SD card directly up to the vs1002 and just use a micro controller to control playback.
I have it working on my bench right now. The audio is not the best quality and im not sure why just yet, but I intend to find out.
10 times easier than using an AVR with FAT. The only down side is you need an AVR with something like 16k of program space, as this code takes up just under 8k, which im sure would be pushed over 8k when playback control is added. But 8k is a whole lot better as compared to the 50+k FAT FS was occupying on the AVR.
Anyway I have some data here on my personal website: http://pauljmac.com/projects/index.php/Vs1002d_MP3_player regarding the way to make this work. Its defiantly a work in progress. If you have any questions or wana give me a hand figuring this out stop me a line at me@pauljmac.com. I will try and remember to come back here to check for comments on Jeremys site but cant make any promises.
Paul,
I originally got the ic working the same way you describe: with poor quality sound. However I am not sure why your FAT code is taking 50k of space; I managed to fit sd + vs1002 on a mega32 just fine!
Keep up the good work!
I hadn’t gone though and timed the FAT code down yet.
I wonder if the poor quality sound is expected. Humm… I have a vs1011e breakout from sf that I may try and do the same thing with. I thought this would be a very good solution but if the sound quality doesn’t get any better its a bust.
Success! To correct the poor audio problem while in standalone mode all you must do is clear the SM_STREAM bit, or just don’t set it in the first place. After correcting that the audio is fine. There was a bit of “wiggle” when playing 320kbps mp3s. But when I switched to 128, 188, and 192 it sounded fine. I will update my site with the (slightly) modified code.
I’m trying to run this on an Arduino. My understanding is that the code should work fine, just with different ports:
#define DD_CS 10
#define DD_MOSI 11
#define DD_MISO 12
#define DD_SCK 13
I’m not able to get the sinewave output. Any pointers?
Thanks, I just got a VS1002 breakout board from Sparkfun and your project looks like a very simple intro
Peter: Don’t confuse the Arduino pin numbering with the actual hardware pin numbering. The AVR has ports consisting of 8 pins, in this case bit 2-5 of PORT B is used. See http://arduino.cc/en/Reference/PortManipulation.
Hello, I have test this code with a sparkfun VS1001E and an atmega8.
tension is 3.3V
the atmega8 is configured with 8 Mhz internal clock
=> it doesn’t work… no signal out
When I see the transmission with electronic USB oscilloscope, all seems ok (sck,mosi,cs).
I have tried different frequency…
I tried also to read memory of vs1001E : but it doesn’t respond : nothing on miso.
I don’t know what can i do ?
If someone have any idea…
thks
Bert
Great site i love it