3 Simple Ways to
Share What You Make

With Instructables you can share what you make with the world — and tap into an ever-growing community of creative experts.

PhotosPhotos

Share one or more photos of a project, recipe, or whatever you've made, quickly and easily.

Step by StepStep-By-Step

Share your step-by-step photos with text instructions of what you made so others can do it too!

VideoVideo

Share your how-to video. You'll need your embed code from a video site such as YouTube.

YEAAAUGH, WHHAT? OKKKAY! Lil Jon Easy Button

Step 3How it works:

How it works:
Hopefully you have a working Lil Jon Button by now. You might be interested in how the code works, so I will explain some sections. Since the code is quite long, I will just refer to sections sequentially.

First, here are a bunch of includes. Of course there are the ones for the processor as well as some standard libraries. What is important here is the header files which correspond to the samples. This is a way to conveniently load data from your computer into the microcontroller at compile time. I wrote a MATLAB script which reads .wav files and declares a large array e.g.:
int mydata[] = {1, 2, 3, 4};

Then there's the section:
void __attribute__((__interrupt__)) _T2Interrupt( void )
{


This is the interrupt for Timer  2, which drives the PWM. A PWM (Pulse-Width Modulator) generates a sort-of-analog waveform by quickly changing the duty cycle of a pulse waveform. This is because the digital outputs of the microcontroller can only generate a logical 0 (which is at ground voltage) or a logical 1 (which is at the battery voltage). If we want to say, approximate a voltage halfway between that, we can use the PWM to turn on the pin half the time and turn it off half the time. As long as this is done very quickly, we can approximate the voltage well enough to sound like the original waveform.

The T2Interrupt function is an interrupt handler for Timer 2. It basically means that the PWM needs another sample, so we read another sample from program memory and stuff it into the OC1RS register. This is how PWM knows what voltage to approximate for the next sample time.

void __attribute__((__interrupt__)) _CNInterrupt( void )
{

This section is an interrupt handler for the change notification. The change notification throws an interrupt (that is, executes the above function) any time one of the pins you have enabled changes state (goes high->low or vice versa). This is how we detect the switch being pressed. Notice that we could poll here, but the change notification peripheral works even when the microcontroller is asleep, allowing us to save a lot of power. What we do here depends on whether a sound is already playing. If a sound is already playing and we are in the first 375ms of the sample, it will simply reset the sample index (thereby restarting playback at the beginning of the same sample). If there is no sound playing or we are past the first 375ms, we will randomly pick a new sound out of the 10 and play that. This allows the Lil Jon Button to repeat the same sample but still have enough random behavior to be interesting (e.g. Ya-Ya-Yeah! OKAY!).

void setupHS()
void setupCN()
void setupPWM()


Here are some setup functions. This is quite complicated and you are best to consult the datasheet for exactly what happens here. Suffice to say the first sets up the processor clock to 66.33 Mhz, the second sets up the change notification, and the third sets up the PWM. For your reference, the datasheet can be found here:
http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en532296

Finally the:
int main ()
{

Which is the main loop. Code starts executing here, so the first thing it does is set up the peripherals. Then the main loop doesn't so a whole lot. It basically just waits around for a bit, and goes to sleep. All the work happens because the interrupts above have been registered, so when something needs attention (e.g. the PWM needs the next sample), the correct interrupt will automatically be called. Typically when the microcontroller goes to sleep, no code gets executed. However, because the change notification peripheral has been enabled, a push on the button will cause the microcontroller to wake up and immediately service that interrupt and start playing a sample.

Final note:
An observant engineer might ask, why did you not use the DAC feature of the microcontroller? In fact the dsPIC33FJ128GP802 has an audio DAC which is designed for this sort of thing. The reason is to simplify the design. Notice that in order to drive a speaker out of a PWM, the only thing that is needed is single digital switch (a BJT in this case with no resistors for biasing). If instead the output was a DAC, a linear amplifier made out of transistors (with additional resistors) or an op-amp would be required. This greatly increases the complexity of the design and the part count. Notice that the input waveforms are simply 8kHz 8bit audio files, so a full CD-quality DAC is overkill in terms of audio quality, but increases the overall expense.

Other final note:
The dsPIC33FJ128GP802's voltage range is only 3.0-3.6V. I have observed it to run at about 2.8V without problems with this code, but stability cannot be guaranteed for the long run. I recommend if you are to experiment with the code to use some lithium AAA batteries: http://www.amazon.com/Energizer-Ultimate-L92BP-4-Lithium-Battery/dp/B0002DUQDQ . They maintain 1.5V for a much longer time than alkaline AAAs.


« Previous StepDownload PDFView All StepsNext Step »

Pro

Get More Out of Instructables

Already have an Account?

close

All Steps Viewing
View all steps of an Instructable on the same page when you're a Pro Member.

Upgrade to Pro today!
0
Followers
1
Author:Zuofu