DDS VFO with PIC16F84 and AD9850
by George Heron, N2APB


Here's a wonderfully-simple project first done by Curtis Preuss, WB2V in the July '97 issue of QEX. The article was entitled "Building a Direct Digital Synthesis VFO" and was geared as a straightforward starting point for building and evaluating a DDS VFO based on the Analog Devices AD9850 chip. Click here for that article: page1, page2, page3, page4, page5


Curtis used the PIC1654 microcontroller, but this chip is somewhat difficult to use in that it's a UV-erasable device and the developer needs to put it into a UV lamp eraser when making any changes to the program. Also for some reason the 1654 doesn't fall into the same "class" of PICs that are programmable by some of the (cheap) programmers on the market.

Well, long story short, I ported the 1654 software to work on the 16C84 ... the nice, newer EEPROM-based PIC that you can quickly and electrically erase again and again. The 16C84 is electrically compatible with the 1654, so you can use it in the same circuit board without modifications. See, the programmer for the 16C84 here!.

NEWS FLASH !!! -- Both Curtis and I used the SPASM assembler mnemonics (8051-like instructions) in this project. Well, Craig Johnson, AA0ZZ recently ported these SPASM instructions over to the MPASM instructions that are more "native" to all the Microchip app notes as well as being used in the Easy PIC'n books! Now you can start with either version for your 16F84-based DDS VFO project! Click here to see what Craig has to say about his port to MPASM, and you can see/download his software source code at the bottom of this page along with my original SPASM code.


Here's the block diagram of the project:

The surface mount DDS device was not easy to solder in place, but with some deep steadying breaths, a magnifying glass and some Solder Wick, I was able to get it in place.

The circuit board uses SMD chip capacitors and inductors in the low pass filter output stage, which are a bit easier to solder in place but not as plentiful in ye ole junk box.

Here's the software you can use as a starting point for your own experimentation with the PIC16C84 and DDS VFO. I made register and architectural modifications to the WB2V code necessary to get it working on the 16C84, and only some minor formatting changes to better suit my taste for readability. Otherwise, the program is the same as described in Curtis' QEX article.

You can view the software files by left-clicking on the links below. You may also download the files by right-clicking on the links and then selecting "Save as..." to get the file onto your local system.

dds_vfo4.txt -- source code, compiled with Microchip tools (MPASM)

dds_vfo4.hex -- binary code suitable for directly programming the 16C84

This project is a fun and easy one ... you should give it a try. It's a pretty simple next-step of plugging this VFO into your Sierra, OHR, or whatever QRP rig, thus giving you a stable and precisely controlled VFO down to 1 Hertz resolution! It also can serve as a precise RF signal source for the bench.


73, by George Heron, N2APB


Signal Generator (VFO) with Direct Digital Synthesis
Version 3a by Bruce AA0ED and Craig AA0ZZ

Bruce AA0ED and Craig AA0ZZ have recently completed a new version of the Sig_Gen DDS project. They have fixed a number of bugs and have modified many of the subroutines to make them more efficient and easier to understand. Most of all, they documented the code thoroughly. Subroutines now have headers with inputs and outputs listed. Also there are quite extensive comments throughout the code itself. Their purpose was to make the code easy to understand so that it can be used in other projects as well.
We still list Curt, WB2V, as the author. They have not really added major functionality, but did changed some limits, such as extending the upper limit to 30 MHz. Craig redesiged the output low-pass filter to accommodate this.

They sent this code to Curt to get his permission to put it out in the public. He was gave his enthusiastic go ahead. They have called this Version 3a.

This sig_gen code is a continuation of a project started by Curtis Preuss, WB2V, and described in his QEX article in July, 1997. Subsequently, Curt modified the code to add the calibrate and band select functions.

This summary provides hints and information about how to extend the original project to the current version.

1) Versions of Sig_Gen

You need to choose between three major versions of code.

Version 1 is Curt's original and dds_vfo. It works if with some LCDs, but not with some of the "cheapie" LCDs.

Version 2 of dds_vfo was done by George Heron, N2APB. It has some timing improvements that make it work with the "cheapie" LCDs also. See dds_vfo2.txt on the Ham-Pic Web page for SPASM source code, dds_vfo4.txt for MPASM source code, and dds_vfo2.hex for the code to load into the PIC chip.)

(Versions 1 and 2 work "as is", without any circuit board modifications.)

Version 3 is an enhancement by Curt, WB2V. It is now called sig_gen. It has some significant new features, including calibrate mode and band selection. This version needs a circuit board modification. (See section 2 below.) Curt's SPASM version is available on the Ham-Pic Web page. Bruce, AA0ED, has an MPASM translation available as sig_gen2.asm.txt, sig_gen2.lst.txt, and sig_gen2.hex.txt.

Version 3a is this version of sig_gen. The functionality remains the same as Version 3, but Bruce and I have added many fixes and modifications. MPASM translation available as sig_gen3.asm, sig_gen3.lst, and sig_gen3.hex.
1) Fix a bug which caused the frequency to jump to the maximum when going towards zero.
2) Fix several SMASM to MPASM translation bugs.
- Code worked, but several cases of "hard coded" constants remained that should have been changed to labels to allow data tables to be moved and/or modified. This bug could cause a reference to a wrong variable.
- PortB vs TRISB causing confusion.

1) The lower frequency is changed from 1Khz to zero.
2) Added band table entries of 0 Hz and 30 MHz.
3) Changed wait routine names for clarity.
4) Added comments throughout.
5) Subroutine headers added.
- Inputs and Outputs specified
6) Changed some data labels for clarity.
7) Changed some routines for efficiency.
- Improve path length and save memory

NOTE: OUR GOAL was to make this code clear and easy to understand so that it can be used as a springboard for additional changes.
By documenting subroutines as clearly as possible, we hope readers will be able to easily use the subroutines in other projects.

2) Circuit Boards from Far Circuits

Make sure you have the corrected version of the FAR Circuits' board. Far Circuits had an early version that had some errors in it; it had several missing traces. The easiest visual way to distinguish the two versions of the boards is to see if it has a diagonal trace in the lower left hand corner between the 10K pot and the mounting hole. If it has a diagonal trace, it is the new version. If it does not, then it is the older version and you have to make several wiring changes, including a couple in the area of the AD9850. I advise you to get the new version.

Note that this new board is still for Version 1 and 2 of the PIC code. FAR Circuits does not have a board with the modifications for Version 3 code, as far as I know.

You need to make a modification to the FAR Circuits' board to make it work with Version 3 or 3a code. This enhanced PIC code requires a hardware change to allow for the push button which is used to select the calibrate mode and to do band selection. The change removes the +5 volt connection to pin 2 of the PIC chip, and connects a 10K ohm pull-up resistor to pin 2. The push button is connected between pin 2 and ground, so that the pin is grounded when the button is pressed and held high when the button is released. In addition, instead of using the output of Pin 2 to power the LCD and control the LCD contrast, the LCD is connected directly to +5 volts.

The change can be done as follows:

1. Locate the PC trace which connects pin 2 of the PIC chip to the LCD contrast control pot. This was originally used to power the
LCD by setting bit 3 of Port A (pin 2) high. Drill a small hole (#60 - .040") through this trace as near as possible (1/16") to pin 2 of the PIC socket.

2. Near pin 2 of the PIC socket, cut the trace about 1/8" beyond the new hole. Using a 1/4" drill, remove the foil around the hole on the ground plane side of the board to avoid shorting pin 2 to ground when the resistor gets inserted in the hole (Step 4).

3. Locate the +5 volt trace that passes between pin 1 and pin 2 of the PIC socket. Drill a new hole through this trace in the place where it is close to pin 1. Remove the ground side foil from around this hole with a 1/4" drill to avoid shorting +5 v to ground when the resistor lead is inserted.

4. Install a 10K resistor in the new PC board holes. Mount it vertically, with the short resistor lead in the +5 v trace hole and the other end bent close to the resistor body and down into the hole near pin 2 of the PIC socket. Make the solder connection to the traces only, not to ground plane foil on the other side of the board. Make sure both sides of the +5 v trace are soldered to the resistor lead.

5. Connect a wire from one side of the push button to the pin 2 side of the new 10K resistor. This can be done by connecting the wire to the top of the vertically mounted resistor. Connect another wire from the other side of the push button to ground.

6. Using a bit of solid wire, connect the pin of the LCD contrast pot that used to be connected to pin 2 of the PIC socket to the +5 volt trace. This now supplies power to the LCD.

3) Circuit Board Construction hints

On this board you will notice that all components are mounted on the trace side of the board. The bottom side is a ground plane. You need to mount the PIC socket up off the board, so you can get your soldering iron underneath to solder each pin to its pad. Take your time. I would suggest mounting the chip socket first, so you have easy access to all the pins. If you are going to use headers for attaching the rotary encoder and/or LCD, this is also rather tricky. (I used headers for the encoder because it is an expensive part, but I wired a ribbon cable directly to my "cheapie" LCD.)

I would suggest you build the "left side" of the board first, and get it going before you add the "right side" components (clock scillator, AD9850, and filter). You will be able to see the LCD display work in response to turning the rotary encoder and push button(s).

Make sure you put all the ground jumpers in. Some components need to be soldered on both sides of the board. In addition, there are several holes that need jumpers soldered on both sides.

4) Clock Oscillators

You can get clock oscillators from a variety of sources, but the higher speed ones (above 80 MHz) are a little more difficult to find. I got my 100 MHz oscillator from Jameco for $2.75 . Jameco's phone number is 1-800-831-4242 and their Website is .

There is one more thing that you need to be very careful of. With the faster oscillators, you have to make sure that pin 1 is NOT GROUNDED. You can either cut the trace on the board and grind off the ground plane connection on the back of the board, or else just clip pin 1 of the oscillator and let it hang by the 3 pins. In the Jameco catalog they mention that Pin 1 should not be connected. (Apparently some of the slower oscillators can/must be grounded, but the faster ones cannot.) When they are grounded the output voltage is dropped to a very low value.

5) Setting Clock Oscillator values

The current code has is set up to work with a 100 MHz clock oscillator. It is very easy to change it to use other clock values. Here is the table (also in the code symbolics) which you can refer to, or you can do your own calculations. The clock variables are set in variables ref_osc_3, ref_osc_2, ref_osc_1, and ref_osc_0.

ref_osc represents the change in the frequency control word which results in a 1 Hz change in output frequency. It is interpreted as a fixed point integer in the format <ref_osc_3>.<ref_osc_2><ref_osc_1><ref_osc_0>

The values for common oscillator frequencies are as follows:

Frequency ref_osc_3 ref_osc_2 ref_osc_1 ref_osc_0

120.00 MHz 0x23 0xCA 0x98 0xCE
100.00 MHz 0x2A 0xF3 0x1D 0xC4
90.70 MHz 0x2F 0x5A 0x82 0x7A
66.66 MHz 0x40 0x6E 0x52 0xE7
66.00 MHz 0x41 0x13 0x44 0x5F
50.00 MHz 0x55 0xE6 0x3B 0x88

To calculate other values:
ref_osc_3 = (2^32 / oscillator_freq_in_Hertz).
ref_osc_2, ref_osc_1, and ref_osc_0 are the fractional part of
(2^32 / oscillator_freq_in_Hertz) times 2^24.
Note: 2^32 = 4294967296 and 2^24 = 16777216

For example, for a 120 MHz clock:
ref_osc_3 is (2^32 / 120 x 10^6) = 35.791394133 truncated to 35 (0x23)
ref_osc_2 is the high byte of (.791394133 x 2^24) = 13277390.32
13277390.32 = 0xCA98CE, so high byte is CA.
ref_osc_1 is the next byte of 0xCA98CE, or 98
ref_osc_0 is the last byte of 0xCA98CE, or CE

Then set the variables as follows for a 120 MHz clock:
ref_osc_3 equ 0x23 ; Most significant osc byte
ref_osc_2 equ 0xCA ; Next byte
ref_osc_1 equ 0x98 ; Next byte
ref_osc_0 equ 0xCE ; Least significant byte

6) Upper Frequency Limit

The original sig_gen code has an upper limit of 20 MHz. To allow operation all the way up through the 10 meter amateur band, we made some modifications to change the limit to 30 MHz. This requires a clock oscillator of at least 90 MHz and a redesign of the output low pass filter.

Here are the values that I calculated for the 30 MHz filter. Part numbers are for DigiKey (1-800-DIGI-KEY or ).

C1 original: 180pf new: 100pf (PCC101CGCT-ND)
C2 original: 150pf new: 100pf (PCC101CGCT-ND)
C3 original: 10pf new: 10pf (PCC100CNCT-ND)
C4 original: 33pf new: 33pf (PCC330CGCT-ND)
C5 original: 270pf new: 150pf (PCC151CGCT-ND)
L1 original: .47uh new: .39uh (DN10391CT-ND)
L2 original: .39uh new: .33uh (DN10331CT-ND)

7) Calibrate

CALIBRATE MODE is entered if the external push button is pressed during power on. The display is set to "10,000.000 CAL" and remains fixed, even as adjustments are being made. If the push button is held pressed, then turning the shaft encoder will increase or decrease the value "osc" used to calculate the DDS control word. The basic calibrate adjustment rate is very low (on the order of a few cycles per turn of the encoder). A somewhat faster adjustment speed is available by pressing the encoder shaft down while turning. An external frequency counter on the DDS output is required to observe this adjustment. To exit calibrate mode, release the external push button and turn the shaft encoder one more time. The calibrated value of "osc" will then be stored in EEPROM memory.

8) Start-up Frequency

The current code is set up to start at 14.025 MHz. If you want to set it to start at another frequency, you can do it as follows.

The variables default_3, default_2, default_1, and default_0 contain the default startup frequency as a 32 bit integer. It has the form of <default_3><default_2><default_1><default_0> . Thus 14.025 MHz would be represented as decimal 14025000 and converted to hex value of 00D60128 .

Thus, change the default variables in the symbolics as follows:
default_3 equ 0x00 ; Most significant byte for 14.025 MHz
default_2 equ 0xD6 ; Next byte
default_1 equ 0x01 ; Next byte
default_0 equ 0x28 ; Least significant byte

9) Parallax SPASM vs Microchip MPASM assembler code

The differences between Parallax SPASM and Microchip MPASM are quite extensive. The Parallax SPASM has some advantages, and in some ways may be easier to understand. In fact, many SPASM instructions are actually composites of several MPASM instructions. I.e. SPASM instructions may translate into 2 to 4 MPASM instructions. Therefore the Parallax code actually has fewer assembler
instructions. Of course, the end result is the same number of machine instructions.

We wanted to use MPASM for a variety of reasons. First, there is a lot of MPASM code available to look at. Microchip is the company that designs and manufactures the PIC microprocessors. Microchip has a free CD (available from their Web site) which bundles their assembler and a very good simulator along with an abundance of source code that is ready to use in projects.

Another reason why we wanted to use MPASM code is because we have been reading the Easy PIC'n books by Dave Benson, available from Square One. These books are excellent, and give the reader a good working knowledge of PIC processors. These books use MPASM assembler symbolics.

- Craig, AA0ZZ ( [email protected] )
- Bruce, AA0ED ( [email protected] )

PS: Thanks to Curtis Preuss WB2V for the original project and code in QEX.


 [ About me | Acronyms  | CW | Data Sheets | Docs | Download | E-mail | HOME | Ham projects | Hobby circuits | Photo galery | PIC | QTH photos |
Sign in my guestbook | View my guestbook ]

© 2001 - YO5OFH, Csaba Gajdos