This is one of those “because I can” projects. Can I generate a sine wave using a simple PIC employing no PWM and no DAC, no software timers or interrupts, just turning an output pin on and off? We all know it can be done because our music players boast of the 1-bit DACs inside.
Simplistically, we know if our output pin puts out 5 volts when ON and 0 volts when OFF, a 50% duty cycle should give us an average output voltage of 2.5. But the actual method is a little cleverer than that, which is what makes it fun. I picked it up from Glen Leinweber, VE3DNL, a great ham tinkerer in the realms of RF and programming.
Want to write a ~1000 line program by staring at a printout of 1,703 ones and zeros and writing the code to produce them? Me neither. So my QBASIC program also gets to write the bulk of the source code. After the bit sequence is stored in an array, the program then examines it for repeats and writes the code to implement the bit sequence efficiently.
I also wanted a way to turn the output on or off in response to user input (telegraph key?) on another pin. To do this, I just wrote the code to sense the pin and if its state tells me to turn the sound off, I configure the output pin to be an input instead of an output. Each time through the code, it reads the control pin and configures the output pin accordingly. The sound generating code continues to run as always. I had to count the number of cycles this code took and then insert it in place of an equal number of “time wasting” cycles in an area where the output pin is not changing.
OK, say I get it running. How do I look at the output and verify that I’ve achieved my goal? If I just look at the output pin with my o’scope, I’ll see a pulse train of varying density. So just as I did in software, I have to integrate the pulse train in hardware by connecting a resistor and capacitor to the pin. Here’s the tricky part: the R/C network is a low pass filter, and with enough filtering even a square wave can be turned into a perfect sine. So to make sure I’m not “cheating”, I make sure the corner frequency of my filter is much higher than my fundamental frequency. The proof is in the pictures, with a shot of several cycles showing a nice sine wave, but a close-in zoom of part of the cycles show the jaggies caused by the individual bit transitions.
A fun variation might be to do a more complex waveform – say the sum of sine waves of 1,000 Hz and 1,500 Hz. The period would be that of the difference frequency, 500 Hz and the resultant would be something you couldn’t fake with low pass filtering. Combining two musically related notes would be even better – easier on the ears.
One more relevant bit of info. Most PICs these days include high speed and fairly accurate oscillators you can use and save the price of a crystal and two I/O pins. The thing is pretty accurate, but has frequency trimming registers if you want to get closer. My ‘D’ note started off 8 Hz low with the factory value and I was able to trim it to within 1 Hz.
What’s it good for? Don’t you hate that question? But I guess it’s a stable audio sine wave source for the price ($2 or so?) of a simple PIC chip. Not too flexible though – to change the frequency you have to re-run your QBASIC program to generate revised source code and then re-program the chip.
Now the links to the code.
pic-sine.asm is the PIC assembler code. 1bitsine.bas is the
QBASIC code that was used to generate the tedious part of the source
Now the links to the code. pic-sine.asm is the PIC assembler code. 1bitsine.bas is the QBASIC code that was used to generate the tedious part of the source code.
Oh, here's the link to the blog. It's
got a couple photos of the waveforms. You may have to click the
October 2009 entry to see it ...
Oh, here's the link to the blog. It's got a couple photos of the waveforms. You may have to click the October 2009 entry to see it ...
Oh yeah, I thought I'd show you what the bit pattern produced by 1bitsine.bas looks like: