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 of1300kbps 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.]

Now I believe this is a perfect 8 bit stereo wav player. Because I compared the same converted files played in my PC and in my player, but I couldn't find any difference in the playback quality. You can see it on the video (it is taken through my mobile phone, so the recorded audio may not be as good as the real one). Any way, since the PWM have some limitations, I also tried the R2R ladder DAC... But I couldn't find any more improvement because it is already at it's top quality... Also, I think since I converted MP3 to WAV, so even if I increase the sampling rate in the conversion process, just for testing the maximum capacity of the player, but that couldn't help me to find the difference of PWM and R2R because the original audio is having a sampling rate of only 44.1KHz... So I am not going for an R2R dac and instead I am using the PWM itself...

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

(Pictures of the mmc module taken through a high resolution camera :-))

Features:

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).

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:

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.

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

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

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

Hey,This 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?

Mine didn't work with D0-D3 grounded, took a few hours to figure out why it wouldn't initialize.Now I got another problem, it keeps saying my SDCARD is not FAT16, but it is... Well, going to debug that...

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.Going 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 !

I want to update the code to support SD cards.. I will update it soon....

It 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?Also 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.

@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)

Hi 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....:-)

Hello 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?

work with Atmega32 or 16 with 12MHz crystal, and for the sd card formatted under windows as FAT the line In functionvoid mmc_read_sector (int sector){sector+=63; // work for my sdcards , thx goebish for this line

Hi Vinod,It 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.comMob : 09038089813

For 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.

You 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