Click here to download the latest Arduino software or "sketch" as it's called. You may have to right click and "Save As" or something similar. It's too long to include directly on the page here. You might want to stick with revision 1.31 if you don't want transceiver mode.
I won't attempt to explain how to use the Arduino IDE (integrated development environment). That's well documented on the Arduino web site. Just one tip here. The serial communications with the PC is a useful debugging tool.
I've tried to include plenty of comments in the code so hopefully it makes some sense to someone reading it.
I think it's written in a reasonably modular way. The code that reads the encoder and push buttons create an "event" with a parameter which the other code then processes. Changing port assignments or adding more buttons would be easy.
Update: March 2013:
See the March 2013 note on the update page about a problem with the buttons.
Programing the DDS-60
To set the DDS to a particular frequency, we need to send the "delta" number to it. The formula is:
delta = (2 ^ 32) * frequency / clock frequency.
Both frequencies are in Hz.
2 ^ 32 means 2 to the power of 32 which is 4,294,967,296. The clock frequency is 180,000,000 Hz plus or minus the calibration offset. That is derived from the 30 MHz oscillator with a 6 times multipler in the AD9851.
These numbers and calculations are bigger than the 32 bit long integer data type.
(2 ^ 32) / 180000000 is 23.860929422 (2 repeating) so one way to do this would be to use floating point math and simply multiply the frequency by 23.860929422. That works but we lose some precision. It's probably good enough but I did some informal tests and output frequencies were often out by several Hz .
Fortunately, the Arduino language has a 64 bit integer type and can do 64 bit arithmetic. This is not mentioned in the standard Arduino documentation. The type is called int64_t and the suffix for literal numbers is LL. Note the declaration of the contstant called TWO_E32. If we multiply the frequency by 2 ^ 32 (essentially a binary a left shift) and then divide the 64 bit result by 180,000,000 we get better precision than the floating point. It also makes the calibration more simple and logical.
I was concerned about the speed of the calculation but the Arduino does it in about 125us which is plenty fast enough for us even if the encoder knob is turned quickly.
I notice that when we use the 64 bit data type and arithmetic, the size of the compiled code jumps considerably. I think it was from about 4 Kb to 12 Kb. I assume this is because we are linking in a whole 64 bit library which means we are probably including a lot of stuff we don't need. I guess if we were tight on program space then the solution would be to write or otherwise obtain just a 64 bit division routine. For now, we are nowhere near the 32 Kb limit so there is no problem.
I initially used the Arduino ShiftOut function to send serial data to the DDS. Since then I've written my own code to do this which is faster.
Using the EEPROM
We make use of the 1 Kb of EEPROM in the Arduino to save the current frequency, calibration offset and stored memory channels so that these remain when the power is off. Excessive write operations shorten the life of the EEPROM so we need to take steps to avoid writing more frequently than we really need to. It would be a problem if we saved the frequency for every step as the encoder knob is turned. Another issue is that the write operation takes several milliseconds so this might create timing problems. The solution implemented here is to check every five seconds and save the frequency only if it has changed since last time we checked. To reduce the writes further, the SaveToEEPROM routines write only those bytes that have actually changed when saving a long or int. With 99 memory channels we still have space left for future enhancements.
Writing to the Display
An initial verison used the Arduino LiquidCrystal library. This worked fine but it was a little slow at times. We only need very basic functionality to so I implemented my own code which writes directly to the LCD.
Whenever the encoder knob is turned in Normal mode, the whole operation takes about 1.8 ms. That's how long it takes to calculate the new delta value, send it to the DDS, convert the frequency to a 10 character string using sprintf, position the display cursor and display the new frequency. We could do that about 550 times per second so timing is quite relaxed. You'd need to move the 64 pulse encoder at a speed equivalent to more than eight revolutions per second before you could beat the code in a timing race. Even then, it wouldn't really be a problem because the encoder is interrupt driven. The worst that would happen is that not every step would be calculated and displayed. Sometimes the frequency might jump by more than one step in a single operation. The encoder dial wouldn't "slip". When it stopped moving, the frequency would be the same as if it was moved slowly.