The Cranky Sysadmin A world of technology, fun, and ignorant rants.

March 10, 2013

Arduino DDS Signal Generator Details

Filed under: Arduino,Electronics,ham radio — Cranky Sysadmin @ 7:44 pm

Past the fold are details on my rendition of an ardiuno DDS based signal generator. Included are the code, some schematics (not a full schematic though), and some hints on setting it up.

You can do this with either a real (and more expensive) arduino, or you can do a barebones arduino like I did. A description of how to interface to the nokia PCD8544 based displays is in order. The one I have is not 5 volt tolerant. I included simple schematics of the voltage dividers I used to get the 5v power supply and arduino output signals to a safe level. The schematic also includes the DDS buffer amplifier.

Buffer amplifier and voltage dividers for signal and power supply

Buffer amplifier and voltage dividers for signal and power supply

Attaching the display, rotary encoder, and DDS module is a straightforward task and can be done by studying the arduino sketch. This sketch requires the PCD8544 library. Without further adieu, here’s the sketch:

#include <PCD8544.h>

// Copyright 2013 John Jorgensen
// Use as you will with attribution.
// I can't be responsible for any damage this script might cause.

// The speed selection switch
#define SPEED 0
// Channel A and B of the rotary encoder
#define CHA 2
#define CHB 3

#define W_CLK 9       // Pin 9 - connect to AD9850 module word load clock pin (CLK)
#define FQ_UD 10      // Pin 10 - connect to freq update pin (FQ)
#define DATA 11       // Pin 11 - connect to serial data load pin (DATA)
#define RESET 12      // Pin 12 - connect to reset pin (RST).

// Adjust these to suit your needs. 
#define FREQMIN  500000  // lowest selectable frequency
#define FREQMAX 31000000 // highest selectable frequency
#define FREQCORR 0       // frequency correction value. May be + or -
                         // My module needs 3600. 
#define pulseHigh(pin) {digitalWrite(pin, HIGH); digitalWrite(pin, LOW); }

volatile unsigned long frequency = 10000000;    // Starts at 10mhz
long tm=millis();       // used for debounce of speed button
long frinc=1000;        // The frequency increment speed. Starts at 1khz
static PCD8544 lcd;

// Send a byte to the AD9850 
void tfr_byte(byte data)
{
  for (int i=0; i<8; i++, data>>=1) {
    digitalWrite(DATA, data & 0x01);
    pulseHigh(W_CLK);   //after each bit sent, CLK is pulsed high
  }
}

// Send frequency and command word to AD9850
void sendFrequency(double frequency) {
  int32_t freq = frequency * 4294967295/(125000000+FREQCORR);  // note 125 MHz clock on 9850
  for (int b=0; b<4; b++, freq>>=8) {
    tfr_byte(freq & 0xFF);
  }
  tfr_byte(0);
  pulseHigh(FQ_UD);
}

// Check the encoder. Adjust the frequency but don't go out of the bounds.
void doEncoder() {
  if (digitalRead(CHA) == digitalRead(CHB)){
    frequency = frequency + frinc;
    frequency = frequency < FREQMAX ? frequency : FREQMAX;
  } else {
    frequency = frequency - frinc;
    frequency = frequency > FREQMIN ? frequency : FREQMIN;
  }
}

void setup() {
  // setup speed button and encoder.
  pinMode(SPEED, INPUT);
  pinMode(CHA, INPUT);
  pinMode(CHB, INPUT);

  // engage internal pullup resistors
  digitalWrite(SPEED,HIGH);
  digitalWrite(CHA,HIGH);
  digitalWrite(CHB,HIGH);

  // interrupt for the rotary encoder
  attachInterrupt(0, doEncoder, CHANGE);

  // Setup and reset the ad9850  
  pinMode(FQ_UD, OUTPUT);
  pinMode(W_CLK, OUTPUT);
  pinMode(DATA, OUTPUT);
  pinMode(RESET, OUTPUT);
  pulseHigh(RESET);
  pulseHigh(W_CLK);
  pulseHigh(FQ_UD);  // this pulse enables serial mode - Datasheet page 12 figure 10

  // PCD8544-compatible displays may have a different resolution...
  lcd.begin(84, 48);

  lcd.setCursor(0, 0);
  lcd.print("Frequency HZ");
}

void loop() {

  // Read and debounce the speed button. There are six increments of
  // frequency increment from 1 to 100k
  if (digitalRead(SPEED) == 0){
    if (tm < millis()-500){
      tm = millis();
      frinc = frinc * 10;
      if (frinc > 100000){
        frinc=1;
      }
    }
  }

  // display the frequency and current speed step
  lcd.setCursor(0, 1);
  lcd.print(frequency, DEC);
  lcd.print("         ");
  lcd.setCursor(0,2);
  lcd.print("Speed:");
  lcd.print(frinc, DEC);
  lcd.print("      ");
  // send the current frequency setting to the ad9850
  sendFrequency(frequency);
  delay(10);
}

Update 2013-07-09:

A commenter asked if I have code for a 16×2 display. The only code I have implements a 16×2 via a 2 wire interface that uses a shift register. You can find circuits and code examples at arduinoshiftreglcd . My signal generator code is here sig_gen.

2 Comments »

  1. Hi,
    I am also trying to assemble an Arduino (ATMega 328) controlled AD9850 DDS board with LCD (16×2), encoder and push button switch. But I am having trouble with the code. Can you send me your code modified for Hitachi LCD (16×2). Thanks a lot in advance.

    With regards,
    (Pravinkumar Anandan)
    ab9xc@yahoo.com

    Comment by Pravinkumar Anandan — June 30, 2013 @ 2:09 pm

  2. I added an update at the end of the post with the code I use for a 16×2 lcd. I use a 2 wire shift register interface to cut down on IO since the original use case was an antenna analyzer with other I/O needs.

    Comment by Cranky Sysadmin — July 9, 2013 @ 6:44 pm

RSS feed for comments on this post.

Leave a comment

Powered by WordPress