A DDS using a PIC and a DAC chip ...
First, I apologize for having all the pictures piled up at the top. Someday I'll figure out how to insert them at the proper locations in the flow of the text.
Frequency synthesizers using the DDS (Direct Digital Synthesis) technique are lots of fun and I’ve built, let’s see … , four of them as stand-alone boards. I thought it would be fun to build one not by using an integrated DDS chip, but rather by using a microcontroller (PIC) and a DAC chip. Obviously I wouldn’t be achieving RF speeds, but this is just for the learning experience.
Within my PIC’s program, here’s the task at hand: The program runs in a loop that sends the proper voltage info (0 to 255) to the DAC each time through. One run through the loop always takes the same amount of time, 4 microseconds in my case. I want to produce a sine wave of a specified frequency. So I need to know, for each 4 microsecond advance in time, where I am in the 360 degree period of a sine wave of the desired frequency. Knowing that, I can look up the closest value of the sine wave in a table (there’s not enough time to calculate it) and put that out to the DAC. This is how any frequency up to the upper limit can be generated even though the loop is outputting numbers at a constant rate.
That upper limit is the based on sampling theory which says that with a minimum of two samples per cycle, the sine wave can be recovered. In practice a bit more than two is considered the actual limit. With my 4 u-sec loop time, I’d need 8 u-sec to send out two samples and my theoretical limit would be 1/8E-6 or 125,000 Hz.
The data (for a sine wave) is a 256 sample look-up table. Therefore, 256 = 360 degrees. For greater accuracy, 24 bits are used to allow a high precision phase increment.
The frequency of operation for the DDS is
f = PI*fclock/2^24 where fclock is the effective "frequency" of the program loop which is 1/T, where T is the time it takes to execute it, or 250,000 Hz in my case.
So, PI = f*2^24/fclock
I have to use the above to calculate a new PI every time I change the DDS output frequency. The PI is added to a phase accumulator in each passage through the loop. Since there are only 256 samples, only the most significant byte is examined to point to a sample in the table. Consider my 24-bit phase accumulator to be an integer plus fraction where the high byte (MSD) represents a full cycle (360 degrees) and the other 16 bits are fractional parts of a cycle.
To write the DDS loop, I need a 3-byte accumulator and a 3-byte PI. Calculation of the PI is done outside the loop, so assume it's known. Here's what the main loop does:
- Add the PI to the accumulator. The MSD will be the current phase angle of the sine wave to 8-bit accuracy. (MSD is the most significant digit, also known as the high byte of the 24-bit number.)
- Take the MSD of the accumulator and use it to look up the corresponding value of the sine wave in a table.
- Put that value out to the DAC
- Repeat
So the only bit of information supplied from outside the loop is the PI. When the external control routines want to change the frequency of the DDS, it just alters the PI.
Hardware details -
I decided to use a DAC chip I have in my junkbox, which is an AD7530 10-bit ADC. ($1.35 from B.G. Micro.) I'll tie the two highest bits to ground and my maximum output will be 1/4th of what it would be using all bits. Yes, I had enough bits in my calculation to use all 10, but that would make my update routine take longer and lower my maximum frequency.
I wasn’t sure how to apply this chip. The data sheet shows using an inverting op-amp on the output, which it appears is really necessary. This means I need a negative supply, unfortunately. Plus the chip needs +15V to power it. So now I need +15V, +5V (PIC) and negative for Vref. Vref can be positive or negative, but since the op-amp inverts it, I'm going with negative so I'll have a positive output. I'll also us the negative Vref on the negative op-amp supply so I won't require a rail-to-rail op-amp to get to 0 volts.
For the negative supply, I took a wall wart rated 9V at 200mA which puts out 14.5 V no load. Took that through 200W to a 10 V zener to get about -9.79 volts as a reference. Experimenting on a breadboard, I get +2.45 volts out with all 8 lines high, close to the expected value. The step is about 10 mV per bit. My op-amp is currently a 4558. It's GBP is over 3 MHz, but open loop gain goes from 100 dB at low frequency down to 30 dB at 100,000 Hz, so I'm not sure if it's fast enough or not.
The 2.45 volt maximum output might need a little boost, so I could use the other half of the 4558 for that, with a gain of 2.
I'll add a photo of the board here ...
Well, guess I can't add it here. Everything goes to the top it seems.
Schematic -
Which is no doubt going to happen to the schematic when I add it too ... I keep reading the "help" on this Google Blogger thing but it's not much help to me. But it's free and it works, to a degree.
Anyway, the schematic is in here somewhere. Mostly it's a connection diagram, but some details are presented in schematic form.
One of my favorite parts isn't part of the No-DDS at all but is the RS-232 to TTL interface. This is the two 2N7000 MOSFET circuits shown at the bottom. I've fooled with a lot of level translation circuits, especially the MAX232 chips and that family. I like this one because it uses a two dime active devices and two resistors and takes less connections than a MAX232, though to be fair, the chip does two conversions in each direction. One drawback of the circuit is that the signal out to the RS232 device doesn't go negative. Maybe I could have made that happen in the No-DDS circuit, since I do have negative voltage available.
Controlling it - the PC software interface ...
I needed a way to tell the PIC what frequency to generate, so I altered an old piece of DOS software I'd written earlier. A slight problem was in how to get the PIC's attention. The update loop needs to be as tight as possible, so it can't afford to poll for external input. The logical thing then is to use an interrupt. But I discovered that all the lines that were interrupt capable, I'd already used for I/O with the DAC. So I wondered if using the -MCLR line would work. This effectively forces the PIC to re-boot every time that line is actuated. It turned out to work OK. When the PC software wants to get the PIC's attention, it bangs the -MCLR line. The PIC restarts and part of the start-up code is for it to check its serial port for incoming data. That data will be the new PI, which will be stored before the PIC goes permanently (until the next restart) into the update loop.
I'm putting in a picture of the DOS screen. Some of the fields aren't used, since this program was developed to control full featured RF DDSs that were used as VFOs. I wrote this program in 8088/8086 assembly language using the A86 shareware assembler.
Conclusions
It worked pretty well. That rope-like waveform appearance was due to my scope acting up a bit. Like the 1-bit sine generator, I built this thing but am not sure why. I think I thought it would be fun, and I guess it sort of was. I combined a little electronics, a little PIC programming, and a little PC programming to produce a circuit that worked as imagined.
-Nick
1 comment:
To move pictures, I find the easiest way is to switch over to the "edit html" view, where you will see a huge mass of stuff starting with "a onblur=", which goes on for about 6 lines per picture. Just cut that and paste it into the text where you actually want it. It's really unfortunate that blogger still doesn't put the pictures where you really want them...
Post a Comment