This project is an audio player that plays uncompressed music from an SD card, it’s battery powered and can be charged over USB.
I named this project “Daria box”.
A great source of inspiration for this was Dmitry’s Holiday Card.
– (very) low cost
– powered by rechargeable battery
– low power –> it uses ~30mA
– decent audio quality
– shuffled playlist
– battery charging circuit: Microchip MCP73831
– step down DC-DC converter: Texas Instruments LM3670
– 8 bit microcontroller: Micrcochip PIC18F25FK22
– 16 MHz crystal
– SD card
– class D amplifier for audio: Analog Devices SSM2305 with gain set to 1(only current gain needed)
– Li-Ion battery from old Nokia phone
– passive components
– code written in C, using MplabX IDE and XC8 compiler
– PIC18 is set to use internal PLL to multiply the 16MHz from crystal by 4 and get a 64MHz speed, which actually means 16MHz instructions speed. Crazy, but this is Microchip’s way. Without the PLL multiplier, with a 16MHz crystal the instruction execution would happen at 4MHz
– audio is stored as multiple files on SD card(formated as FAT)
– audio files contain raw 8 bit audio data, sampled at 44100 Hz
– code uses the Petit FAT-fs library written by ELMchan http://elm-chan.org/fsw/ff/00index_p.html
– communication with the SD card is done via SPI bus, and the SPI clock runs at a frequency of 8MHz
– there is an interrupt that runs at 44,1KHz frequency and it puts the audio data from the play buffer in the DAC
– The DAC is actually a PWM channel with a low pass filter at output, as people use when in need of a cheap and simple audio output from microcontrollers but not at very high fidelity
– the project plays the songs one after the other and also uses a skip button if you want to get to the next song
– a shuffling algorithm was implemented that shuffles randomly the songs every time you start the project
First I integrated the full fatfs library developed by ELMchan, but it occupied more than 1KB data memory and I wasn’t left enough memory to hold the buffers that contain audio playback data.
The total amount of data memory on the microcontroller is 1,5KB.
Petit FAT-fs library uses about 300 bytes of data memory and since there was only the need to read data from the SD card, this library was ideal.
The full implemantation has many others bells and whistles that I didn’t need.
This lets room for two 512 bytes buffers which I use for audio reading from SD card and playback.
These fatfs implementation sources can be found in the archives from the site called “pfsample”.
The archive contains examples on how to use it for different types of CPUs.
Besides the fafts implementation files there is also a file that does the SD card initialisation part and contains the processor specific functions for SPI transmit and receive and the delay functions.
I copied the files from the AVR implementation and I did the modifications for xmit_spi(), rcv_spi() and delay functions.
The file that I’m talking about is sd-mmc.c from the petitfat folder.
I added to this source the function hooks for our SPI implementation and delays.
When trying to compile the project the compiler threw an error. It seems that the function send_cmd() was called recursively and my compiler doesn’t allow recursion.
Fortunately copying the function once with another name was enough to get rid of the recursion problem.
Now the project compiled OK, it was listing all the files from the SD card but I could not read data from files, because the read calls would throw an error.
After investigating this issue I found that it was a petitfat configuration problem in the file pffconf.h that contains some configuration options for the petitfat implementation.
After changing _WORD_ACCESS macro from 0 to 1, it fully worked.
Audio data buffering
There are 2 * 512 byte buffers for storing audio.
One is used to copy data from SD-card into it and the other is used for audio playback as a copy of the first buffer.
The buffers length was chosen to be 512 bytes, because we can store in them 11,6ms of audio.
My first attempt was to read a byte and play it immediately, without the need for buffering but I observed that on repeated reads from SD card, sometimes the read would take ~10ms to complete even if I tried to read a single byte or 512 bytes. This meant that I couldn’t meet the time demands for 44,1 KHz playback.
So I buffer 11,6ms of audio and I’m always on the safe side.
The algorithm used is know as Fisher-Yates algorithm and is very simple https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle.
For this algorithm to work it needs to get a random numbers and shuffle the songs. The algorithm uses rand() function to get its random numbers.
The rand() function has the same random seed at every powerup, so it will generate the random numbers in the same order every time.
So if we don’t give it a different random seed at power up the songs will be shuffled in the exact same way, which is not what we want.
To give a different random seed to the rand() function I used the 3 last bits from an ADC reading of a floating pin which is noise coming from 50Hz mains line.
This would give us 8 different values for the seed, which will mean 8 different modes of shuffling the list. It’s not too much, but for this project is OK.
This way of generating randomness is not a good idea at all for applications that need to use real random numbers, like in cryptography.
The random seed is fed in the memory by using the srand() function and it will be used by the rand() function when will generate the numbers.
Creating the raw audio files
To create the raw audio files(44,1KHz sample rate, 8 bit PCM) I used the Audacity tool.
The steps are:
1. Open in Audacity a mp3 or other format audio file that you want to convert it
2. This steps splits your song from stereo to mono. Click on the name of the song on the left of song’s waveform and select: “Split stereo to mono”. Close one of the two channels.
3. Now it’s time to export the audio file as raw audio. Go to File -> Export. In the opened window select the format as “Other uncompressed file”. Click on the “Options…” button and select for “Header” the “RAW(header-less)” type and for “Encoding” select “Unsigned 8 bit PCM”. Now you click “Save” to save your file and copy it on the SD card.
The source code can be downloaded from my Github account here