(updated the complete source code + makefile + hex + asm + .out files on bitbucket repository)
This is my first AVR based hobby project and the most successful one compared to my all previous stuff. I am 100% satisfied with this work.. Few months ago, I tried to make a wav player using a PIC16F877A. It worked anyway, but the audio quality was not so good for higher sampling rate because that chip doens't have enough ram and thus I couldn't implement a good data buffer. But when I bought an atmega32 microcontroller, the first thing came to my mind is to make a good wav player...Now, I have completed my work and the audio quality is really amazing...
NOW I can say that, my wav player IS ABLE TO PLAY 8 BIT MONO/STEREO with maximum bitrate of 1300kbps for mono and 1600kbps for stereo ... ie it can play an 8 bit mono wav of sampling frequency upto 160KHz and stereo upto 96KHz without any noise or trouble!!!!! (at OSC 16.450MHz).
Note:
[The full range of human hearing is between 20 Hz and 20 kHz. The minimum sampling rate that satisfies the sampling theorem for this full bandwidth is 40 kHz. The 44.1 kHz sampling rate used for Compact Disc was chosen for this and other technical reasons. High-end audio equipment, such as in SACD or DVD-Audio players or studio equipment, can reach as high as 192 kHz.]
Another interesting feature of my player is that, it could be controlled with a Philips TV remote (RC5 protocol). Also I had implemented few functions on the remote control, ie NEXT SONG, PREVIOUS SONG, PAUSE, PLAY, FORWARD 15 SECONDS, and some funny effects like PLAY SPEED INCREMENT and DECREMENT. Also I am still trying to include more features on the remote... The player is now a beta_version ;-)....
Also, I didn't used any external FAT libraries for the low level MMC accessing, instead I had written my own code for that... So you may not be able to find any standard 'C' FILE operations on my source code....
Updated photo after testing an R-2R lader DAC... [01/03/2012]
New code and circuit diagram for wav stereo player with R2R DAC will be updated soon....
My R2R DAC...
New code and circuit diagram for wav stereo player with R2R DAC will be updated soon....
My R2R DAC...
- TV remote control to control the player remotely.
- Hi quality audio output
- Maximum bit rate supported - 144 kilobytes/second.
- Stereo support
- Automatic repeat from the top after all the songs are played.
- Additional bitrate adjustment on remote ( << , default, >> ).
- Forwarding (fwd) option while playing. (seconds could be set on a macro).
(more features will be added on next update)
A brief explanation about the working:
MMC card initialization, reading, writing and interfacing are already explained on my previous post http://vinodstanur.blogspot.com/2011/07/attempt-to-access-memory-card-mmc-using.html , so I think no need to repeat in again. Any way, in short words, MMC/SD cards are to be initialized by proper commands and it enters to working mode (SPI mode) only if it is initialized successfully.
So, here I used a 16x2 lcd to display many things. At first, it displays error message (if any) while trying to initialize the mmc. When it is initialized successfully, it shows MMC INITIALIZED message on lcd..
Now, the next step is to check the boot sector of the MMC card (sector 0) to check if it contains a FAT16 file system. For this, we need to read the sector 0 of MMC card to a buffer. Here I used a 512 byte buffer. From the boot sector data, we could see what file system is there in the MMC card. My code is only for FAT16, so if I found it is not FAT16, then it display an error ie NOT A FAT16. If it found FAT16, then it reads few more data from the buffer and calculates sector number for the data start, fat start and root directory start. Also it detects the sectors per clusters. Each sector is 512 bytes. These four data is required for the further activities on MMC/SD card. So those are stored as global variables.
Now the next step is to get into the root directory. (MMC commands and the sector reading is covered in my previous post as linked above). Now we need to load the first sector of root directory into the 512 byte buffer. Then, each root directory entry is of 32 byte (in general for 8.3 file name format). Each entry contains details of a file or folder in the root directory. From there we can read the file name, file attribute, actual file starting address (cluster address) and many more... We are interested in the file name extension (WAV), the file attribute and the file starting cluster address. So we compare the extension with the string "WAV" and if it matches, then we return the cluster address of the wav file.
Now we could read the first cluster (a group of sectors, size depends on the size of MMC/SD). We could find the sector address from the cluster number using as equation.(u can see that on my code). Now after reading and playing all the sectors in the first cluster, (playing the data will be explained after this) then we need to find the next cluster number of the same file. A file may not be distributed on the memory as one section. Instead, it can be splitted into parts and placed here and there to utilize the free memory effectively.... (actually this happens only when there are some deleted files and we add new files to the MMC/SD). So we can't predict that the next cluster of the file will be successive numbers... But all the cluster order for each file is perfectly tracked on a linked list called the FAT...(File Allocation Table). Each cluster number have a unique position on the FAT. We already calculated the FAT starting address. From the FAT, we could get the next cluster number of the file. Since it is a linked list, the 16 bit data present on the 1st cluster number position will be the second cluster number. Now after reading the second cluster, we check which is the third cluster by checking the data on the location of second cluster position,,,This continues until we read a 0xffff from a location on FAT... This denotes the end cluster for the particular file..
We have first cluster number and we calculated the sector starting address of the particular cluster number. Now we read the data from first sector of the file and from there we could get the bitrate, sample rate, number of channel (stereo, mono) and many more.. We take the bit rate and use it to set the timer interrupt frequency... Now the timer interrupt is generated according to the bitrate and channel number. Now on each timer interrupt, an 8 bit data is introduced to the OCR register of Timer PWM module. Accordingly it generate PWM signal in background without any CPU resource. This PWM signal could be easily demodulated with an RC filter. If the capacitor value increases or resistor value decreases ,then it will affect the audio quality ie it may filter out some higher frequency components of the audio and may feel it like hearing some thing from an AM MW radio..:-)...So care must be given while choosing RC..
Now, we know, if we use a single buffer for both playing data and collecting data, then there will be a small contuinity problem while the song is played.... So, it will be really irritating if we are hearing our fav music like that... Thus, here I had implemented two special 512 bytes buffer for audio data only. This buffer is filled and played by a special technique.. ie when one buffer is playing (used inside timer interrupt) , the other buffer will be filling.,, This buffers will be exchanged alternatively... By this technique, I could obtain a pure uninterrupted high quality audio .... Thats all about the working ..............;-)
Now, decoding the TV remote is a simple process, ie using a timer interrupt, we sample the incoming signal on each 1778 us. To sample the data at mid point of the first half of the manchester code, I made a small delay of about 400ms from the time zero (ie the time when the start bit is detected)...After that the timer is activated to generate interrupt flag on each 1778us. So Now u can check my ISR(INT2_vect) code to see how RC5 is decoded. Also, check my previous code about an RC5 decoder which shows the structure of RC5 code. Other wise a simple google image search for "RC5 structure" will show the required data....
I had implemented RC5 decoding on the same Atmega32. But I think it is not a good method. Becuase, it will be inside the Timer ISR for more time while playing the file. So, most time, RC5 external interrupt will be triggered when the processor is handling the Timer interrupt. Then the external interrupt will be handled only after that and this results an invalid start bit detection inside the rc5 interrupt handler and that will be treated as an invalid RC5 signal. So if we are lucky enough, the first keypress itself will do the job, else we need to press the key for a while or retry after key release... But probably, it will work with in 1 or 2 keypress... So at present I think, the best method is to decode the RC5 outside the atmega32 and send the value via a serial interface for a better performance..
Circuit Diagram[wav player with PWM output]:
Wave structure:
The WAVE file format is a subset of Microsoft's RIFF specification for the storage of multimedia files. A RIFF file starts out with a file header followed by a sequence of data chunks. A WAVE file is often just a RIFF file with a single "WAVE" chunk which consists of two sub-chunks -- a "fmt " chunk specifying the data format and a "data" chunk containing the actual sample data.
As an example, here are the opening 72 bytes of a WAVE file with bytes shown as hexadecimal numbers:
52 49 46 46 24 08 00 00 57 41 56 45 66 6d 74 20 10 00 00 00 01 00 02 00 22 56 00 00 88 58 01 00 04 00 10 00 64 61 74 61 00 08 00 00 00 00 00 00 24 17 1e f3 3c 13 3c 14 16 f9 18 f9 34 e7 23 a6 3c f2 24 f2 11 ce 1a 0d
Source code[for WAV player with PWM output]:
Download source code + hex + asm + object + makefile from below link
Screen shots:
Development tools:
I am using avr-gcc in linux.
We need avr-gcc, binutils-avr & avr-libc to be installed before trying to build the hex file. These are available via synaptic package manager.
Also, I am using avrdude package to burn the avr, hardware used is usbasp. So the makefile is made according to that... We can also use a simple parallel port burning circuit but in my case I don't have a parallel port in my lap, so I stick with the usbasp. It contains an atmega8 microcontroller programmed with the hex available at usbasp homepage.
here are the photos of my usbasp programmer, made according to the circuit diagram and firmware provided at the usbasp home page.
We need avr-gcc, binutils-avr & avr-libc to be installed before trying to build the hex file. These are available via synaptic package manager.
Also, I am using avrdude package to burn the avr, hardware used is usbasp. So the makefile is made according to that... We can also use a simple parallel port burning circuit but in my case I don't have a parallel port in my lap, so I stick with the usbasp. It contains an atmega8 microcontroller programmed with the hex available at usbasp homepage.
here are the photos of my usbasp programmer, made according to the circuit diagram and firmware provided at the usbasp home page.
You could download the repo from the my bitbucket link.. (posted just below the source code).
How to make?
cd into the directory where the Makefile and c file is moved.
just need to type "make" to build the hex
How to burn it using usbasp?
Just need to type "sudo make burn_hex"
How to burn fuse bits?
Just need to type "sudo make burn_fuse"
How to clear the build?
Just need to type "make clean"
How to build & burn it without using Makefile?
avr-gcc -mmcu=atmega32 -O2 main.c avr-objcopy -j .text -j .data -O ihex a.out a.hex sudo avrdude -c usbasp -p m32 -U flash:w:a.hex:i sudo avrdude -c usbasp -p m32 -U lfuse:w:0xef:m
Finished......;-)
Fuse bits: LFUSE = 0b11101111
UPDATES:
[2/3/2012]
Updated source code:
1> Modified the frequently used normal global variables INT_i ,TOGGLE_BUFFER and STEREO to register variables. This increased the maximum bitrate support since the access time of these variables are reduced inside timer ISR...
2> Modified the MMC initialization function to return 1 if not success and 0 if success. Then mmc initialization function is called in a while loop until it return 0. Now, if no MMC is inserted, it waits for MMC and start playing automatically when we insert an MMC with FAT16 and 8bit wav files :-)
3> Also I modified the speed optimization to -O2
3> Also I modified the speed optimization to -O2
If needed, we can try to UN-LOOP the for(i=0;i<512,i++) a[i]=spi_read(); and this will improve the quality little more. But any way, since that improvement is very small and not able to measure the difference, so I am not updating it....
very nice!!
ReplyDeleteNice project, what are the fuse settings used ?
ReplyDeletefuse bits are now added at the bottom of the post...
ReplyDeleteSo, you're running Atmega32 at 16.450Mhz. Isn't that overclocking for the device as it's rated for 16Mhz max.
ReplyDeleteYes it is overclocking...
ReplyDeleteBut it doesn't makes any problem any way because this .450MHz is nothing compared to the 16MHz.
Also I don't have a 16MHz crystal with me....
Hey,
ReplyDeleteThis is indeed a very nice project. However, I've a very question, which may be pretty off-topic.
How do you set the fuse bits with the usbasp programmer? I've bought 2X usbasp programmer built out from the same source as you but both doesn't detects the chip at all when the chip is working at 1Mhz internal clock.
I've to just visit my college and set the fuse to use external crystal using universal programmer. Later on, the usbasp programmer works like charm.
Is your usbasp programmer capable of setting fuse of BRAND NEW ATMEGA's?
If so, could you please share the firmware & circuit on how YOURS in exactly made?
Thanks.
ACTUALLY in UABASP programmer, u need to short two wires (as specified in the usbasp circuit diagram) to make it work with low frequency clock...
ReplyDeleteThen u can set the fuse bits to external xtal etc....
sorry its not UABASP, but USBASP ;-) (typing error)
ReplyDeleteCrap, I see the slow sck now in the circuit diagram..:$
ReplyDeleteMany thanks for guiding..:D
Hi, there's something I don't understand:
ReplyDeleteWhy do #define F_CPU 12000000 but use a 16.450MHz crystal ?
If I want to use a 16MHz crystal, should I #define F_CPU to 11671732 ?
No it is a mistake, I forgot to update it..
ReplyDeleteNow I just modified it...
Ok, thank you, I'm trying this circuit but don't have 16.450MHz crystals handy ... Well, I know my ATmega32A can support up to 32MHz anyway ;)
ReplyDeleteDo not ground D0-D3 on the LCD, or it won't work, don't ask me why I know ;)
ReplyDeleteI never used to ground it in practical... But I don't think it will make problem if we ground it....;-)
ReplyDeleteMine didn't work with D0-D3 grounded, took a few hours to figure out why it wouldn't initialize.
ReplyDeleteNow I got another problem, it keeps saying my SDCARD is not FAT16, but it is... Well, going to debug that...
In my code, it assumes that the card doesn't have MBR and no partitions.... It is expecting boot sector at sector 0....
ReplyDeleteAlso, hope U have done the required modicication to support it with SD...
My bad, I forgot to read the beginning of the post. My SDCARD initialize without problem, but I understand why it says it's not FAT16 now, because I've a MBR at sector 0.
ReplyDeleteGoing to try to modify the program to handle that, shouldn't be too hard and it will add a bit of fun :)
Thank you for the explanation !
Got it to work, I just had to use a ugly trick: as my boot sector is located at sector #129 I just added
ReplyDeletesector+=129;
at the beginning of mmc_read_sector()
Now going to write a real MBR parser ...
Thanks again !
nice ..;-)
ReplyDeleteHi. Amazing project. Two Question. Would work on atmega8 uc? and ... I can use a 2 GB micro SD card?
ReplyDeleteI want to update the code to support SD cards.. I will update it soon....
ReplyDeleteIt will not work on atmega8. Because here I used two 512 bytes buffer for the perfect audio quality at high bit rates like 160KHz.... So the atmega8 RAM will not be enough for this.... (but u can modify it to adapt it with atmega8 but the maximum bitrate support may be less compared to this)
do you mean that your code only works for MMC and not for SD cards or micro SD cards?
ReplyDeleteAlso i had a question about remote control.
Does all cheap TV remote available in market follow philips PC5 protocol? If not which of them are following the RC5 protocol? please reply me.
hello to pepole of india
ReplyDeletehello to gandi
the value of xtl is not found at any market
what is the compiler?
pls help me
tnx alot
pls mailto rajimahdi2012@yahoo.com
@Anonymous you can use another xtl, 16 or 20MHz for exemple, then you just have to modify this line:
ReplyDelete#define F_CPU 16450000
to match your xtal frequency.
Great work!
ReplyDeleteIs it real to do all that on ATmega16 ?
Hi...really Great Project...
ReplyDeleteI have one doubt...can we use 1GB Micro SD Card.?..
Please help me out.....
@dmitry: We can make wav player using atmega16 also. But my player supports high bitrate wav files since I am using 1KB of RAM for the audio buffer itself. In atmega16 the RAM is only 1KB. So we cannot dedicate 1KB RAM and thus the maximum bitrate support will be less compared to this project... I believe so... (it depends on your code)
ReplyDeleteHi hemanth, the particular code is written only for MMC cards. You can easily modify the MMC initialization part to make it work with SD cards also... You can check out my project - "mounting sd card on linux using parallel port of pc". There I included SD card support along with the MMC card. Just try to modify this MMC initialization according to that.... Then it will support SD card also....:-)
ReplyDeletei want try it on atmega 64 kit...will it work on..and what extra peripherals are required??
ReplyDeletePlease change the code so that option was to use SD cards
ReplyDeletewill work with an 12mhz xtal ???
ReplyDeleteHello Vinod, I am working on a similar project. my project is to convert stored text into audio. I tried using 16F877A but I am only getting a beeping sound.Any suggestion on how to go about it please?
ReplyDeletehow u got this OCR1A = ((double)F_CPU/1000000)*1778; ......, i am using 14.7456 MHZ with atmega2560 ....,??? will it work
ReplyDeletework with Atmega32 or 16 with 12MHz crystal, and for the sd card formatted under windows as FAT the line In function
ReplyDeletevoid mmc_read_sector (int sector)
{
sector+=63; // work for my sdcards , thx goebish for this line
..........................
many thanks Vinod.S and
hi
ReplyDeletehi my friend.imade your waveplayer with m32 16Mhz microsd avr studio and format with fat(default). but i cant play music.
ReplyDeleteiust lcd shows NOT A FAT16 . can you help me ? i really need your help.thanks
maghsoud abureyhani, I had the same issue, read my previous (old) comments.
ReplyDeleteVery nice project. The specs are very good.
ReplyDeleteCan you make it to play 2 stereo wav files or 4 mono wav files simultaneously ?
Hi Vinod,
ReplyDeleteIt indeed an excellent project made by you. but can you customised the device a little bit? Like, can you make the device to work with a 128GB Micro SD card? Also, can you give the user to save say 50 mp3 files, and the flexibility to choose any of the 3 mp3 files to play continuously according to his wish. It would be ok, if you give him to an option to choose the files using a 8 pin DIP switch, and if need the user have to save the files in a particular type of file name, given by you. The device will play the files not randomly, but one by one continuously, until the user press a stop button. (Remote Control is Not necessary, a switch on the device will be fine. In fact remote control is not my requirement.) If "YES" is your answer then contact me in my mail id :
rajusahantc@gmail.com
Mob : 09038089813
Спасибо за открытый программный код. Большое спасибо из Украины
ReplyDeletehow can i play a specific file and not just serial wise next or previous?
ReplyDeleteFor that you have to parse through the file names in the directory and when it matches your file name string, you should find the data start. But if you are using a FAT library like Chan's FAT or any other, you don't want to worry about all these, U can use those like normal file operations in C. Here I implemented all from scratch in weird manner, I did like this to get the maximum performance out of it, but it will definitely affect the readability of the code.
ReplyDeleteam unable to contact you can you please mail me at 4.ayushmishra@gmail.com am having some problem in coding would be glad if you can help thank you
ReplyDeletereply asap thank you
ReplyDeleteFine project! How much (appox) Kb code need to implement for: play mono wav files with names like 1.wav , 2.wav ?
ReplyDeleteWhat do you think : is it usefull for voice informer ? Instead of "BEEEP" :-)
fuse bit setting please
ReplyDeleteL FUSE IS EF
H FUSE IS WHAT
can i use atmega16
ReplyDeleteYou can find some reliable shops where the quality and the originality of the product remain higher than some other imposter shops. There are some shops which have been there for long in the field of selling audio woofers for car stereo systems. stereodevelopment.com
ReplyDeleteIn either case you are gambling harm to the stereo, dash, or other hardware behind the dash. amplifier experts
ReplyDeleteexecuse me sir,can you provide the circuit diagram with DAC??
ReplyDeleteThis is cool blog with the best articles, just found what i was looking for, thanks, Independence Day Quotes By APJ Abdul Kalam in Marathi 2018
ReplyDelete