22.3.2016 UPDATE – if you are looking to build a radio VFO, there is a newer version of this code available, with more radio-oriented features.
I have started to play with amateur radio again and wanted to have a decent VFO/signal generator that could cover most of HF without complicated part switching required. So I have got one of those eBay AD9850 DDS modules and built a VFO using the design from Richard, AD7C: http://www.ad7c.com/projects/ad9850-dds-vfo/
Unfortunately, that Arduino shield has a few flaws:
- The firmware is rather basic and user-unfriendly. If you make a mistake when selecting the tuning step, you have to click through the entire list of steps again, there is no way to go back. If you miss it again on the second pass, argh …
- The frequency selection using the tuning step is fine for when tuning a VFO over a band, but it is very clumsy when having to perform a large frequency change (e.g. switch from 80m to 20m).
- The IF frequency is hardwired in the code. Granted, this isn’t something that is changed often, but it would still be nice to be able to reprogram it without having to reflash new firmware.
- Richard is using a slightly different looking module than I have got (there are several variants on eBay) and mine was unreliable when used with an Arduino, refusing to start occasionally.
- My module is very unreliable when the data are clocked in through the D7 pin of the module. It needs to use the DATA pin instead. Again, this could be due to the difference in the modules.
In order to address these, I have mostly rewritten the original code and have done a few modifications to the circuit as well. I didn’t want to have it as an Arduino shield, because I expected this to be a tool that will get more use than a one-off hack, so I have built it using a standalone ATMega168 microcontroller, running at 8MHz internal clock. It is still Arduino compatible, so one could use a real Arduino instead as well.
The other reason why I have built the VFO as a standalone device and not using an Arduino is the point 4 above. My Arduino is only a 5V version. At 5 volts the DDS often didn’t want to start correctly, producing no output until the circuit was power cycled. Probing the DDS chip with a scope showed that it was not receiving clock from the oscillator. After a lot of headscratching I have found a post at the AVRFreaks forum (http://www.avrfreaks.net/forum/ad9850-run-33v-or-50v) that the problem is likely that the oscillator isn’t rated to work at 5V, even though the DDS chip itself works fine at that voltage. My module uses an integrated 125MHz oscillator from TXC Corporation (part TXCJHT4D):
I haven’t managed to find this exact oscillator module in their product list, but as most of their production is rated only up to 3.3V, I do suspect that this is the problem with my module as well. When running the DDS module from 3.3V, it is completely reliable. A really nice trap to fall into, fortunately no magic smoke has escaped 🙁
So having that sorted out, it left me with either having to power the DDS module from 3.3V and everything else from 5V, providing some sort of voltage conversion between the DDS and the microcontroller. The alternative was to run everything from 3.3V. That necessitates a 3.3V display module (most are designed for 5V – a common 5V LCD module will not work at 3.3V!) and lowering the clock frequency to at most 8MHz (ATMega168 is not rated to run at higher than about 9MHz at 3.3V Vcc). 8MHz is convenient, because that is the frequency of the internal RC oscillator too, so it saves a crystal and the two loading caps.
I have added two buttons, one for resetting the micro and the other is used for setting the frequency. Then I have brought out the UART pins, those are useful for both debugging and for bootloading the device. The remaining additions are the ISP port and the 3.3V voltage regulator used to power everything. If someone wants to use a real 3.3V Arduino to build this, I have marked the matching Arduino pin numbers in the schematics.
What remains to be added is a proper buffer amplifier, so that the module can actually drive a 50Ohm load. Right now the output is unbuffered, that is sufficient for driving something like the NE602/612 mixer, but in general it is not a good idea. A buffer using LMH6703, BGA2748 opamps or the MAR-6 amplifier module would be preferred if this was to be used as a true signal generator (thanks for the tip, James!).
The firmware defaults to the VFO mode, indicated by the text “VFO” in the second row. The encoder changes frequency using the preset tuning step. The step can be changed by pressing the encoder and then rotating it to select the desired tuning step. Pressing the encoder again goes back to frequency tuning.
Pressing the button A (the closest to the encoder) switches into a “cursor” mode (“CUR” string in the second row) – holding down the button allows to move a cursor over the frequency display and releasing it lets the operator change the selected decade directly. This mode is useful when an exact frequency needs to be set quickly. Pressing the encoder switch will change back to normal VFO mode.
Pressing the button B (the middle one) toggles the IF frequency. IF is subtracted from the selected frequency, so if the display is showing 10MHz and the IF is set to 2MHz, the DDS will generate 8MHz. Whether or not IF is active is indicated by the letters “IF” after the frequency display, with the sign indicating whether the IF is added to (“+IF”) or subtracted (“-IF”) from the displayed frequency.
Pressing both button A and B together permits to set the IF frequency. It works the same as the “cursor” mode described above, except the IF frequency is changed instead of the DDS one (“IF” is shown instead of “CUR”). Pressing the buttons again while in this mode toggles whether the IF is additive or subtractive (F+IF or F-IF). Pressing the encoder button will change back to the VFO mode, with the new IF frequency.
The microcontroller will store both the current DDS frequency and IF frequency in the EEPROM after 2 seconds when no more frequency changes are done, so the settings are remembered and restored on the next power on.
The firmware enforces tuning limits between 1MHz and 30MHz, not allowing to tune beyond these. In case that the limit is hit, it will set the DDS frequency to the highest/smallest allowed frequency, as applicable.
In order to compile the Arduino sketch, you will need the following:
- The sketch itself: AD9850vfo.zip
- The Bounce2 button debouncing library. Get it from here: https://github.com/thomasfredericks/Bounce2
- The Rotary encoder library, v 1.1: https://github.com/brianlow/Rotary
- Arduino IDE, version 1.6.3 (might work with older ones, but not tested).
Arduino IDE needs to be set to use the LilyPad Arduino board setting because the circuit uses 8MHz clock (not 16MHz external crystal as the normal Arduino Unos and similar). Otherwise you can modify the boards.txt file to add a new entry for this board or use avrdude and a programmer to flash the microcontroller.
Note: After uploading the compiled code to the microcontroller for the first time, the EEPROM is not initialized, so both the DDS frequency and IF frequency will be invalid. It is therefore best to do the first start without the DDS module connected. To fix that it is necessary to set both frequencies manually for the first time. After 2 seconds they will be written into EEPROM and everything will work fine from that point on. Alternatively, it is possible to pre-program the EEPROM using avrdude, the frequencies are stored in little endian format as follows:
DDS frequency: bytes 0-3 of the EEPROM
IF frequency: bytes 4-7 of the EEPROM
IF mode (0 = subtractive, 1 = additive): byte 8 of the EEPROM
IF on/off (0 = off, 1 = on): byte 9 of the EEPROM
If in doubt, refer to the functions storeFreq() and loadFreq() in the code.
- 1 May 2015 – Added saving of the state of the IF
- 30 Apr 2015 – Added the option for both additive and subtractive IF and more frequency range checking
- 27 Feb 2015 – Updated the code to use the Bounce2 library instead of the obsolete Bounce
- 22 Feb 2015 – initial posting