Home » Blog » Projects » Electronics Projects » Arduino AD9850 Control Library

Arduino AD9850 Control Library

When you are writing an Arduino AD9850 library, you need to create two files. The first is a header file, shown immediately below. The header file describes a class, which is then implemented in the C++ file, shown below. The header file is included in your main Arduino program.

// AD9850B.h

#ifndef _AD9850B_h
#define _AD9850B_h

#if defined(ARDUINO) && ARDUINO >= 100
	#include "arduino.h"
#else
	#include "WProgram.h"
#endif

#define DDS_CLOCK 125000000
#define FREQ_FACTOR 4294967296

typedef enum SweepState { ssOff, ssRunning, ssFinished };

class AD9850B
{
 private:
	 int lineCLOCK;
	 int lineLOAD;
	 int lineDATA;
	 int lineRESET;
	 unsigned long _frequency = 10000000;
	 double _calfactor = 0;
	 boolean _active = false;
	 void PowerDown();
	 void Reset();

 public:
	AD9850B(int gW_CLK, int gFQ_UD, int gDATA, int gRESET);
	void Init();
	boolean GetActive();
	void SetActive(boolean param);
	unsigned long GetFrequency();
	void SetCalibration(long param);
	void SetFrequency(unsigned long param);
	
};

extern AD9850B dds;
#endif

The header file contains definitions which are used by the library and which may also be used by the main program. I prefer to use #define statements liberally. “Defines” are essentially text substitution which will be done by the compiler. For example, in calculating the tuning data to be used in Arduino AD9850 Control, we need to multiply the frequency by 2^32 and then divide by the AD9850 clock frequency. The #define statements contain these values which will be substituted later.

The header file also defines a type of variable called an enumeration which tells the Arduino program that there are only three states to consider when sweeping: Off, Running and Finished.

Finally, the header file creates an external object of the AD9850 class called “dds”. The main Arduino AD9850 control program simply uses this object, without having to know any details about how it is implemented.

The Arduino program needs to be able to turn the AD9850 on and off, as well as inquire as to whether it is on or off. This is done by using a class property called Active. Setting Active=true turns on the signal generator and sets its frequency. Setting Active=false powers down the output.

/*
Library Class to control AD9850 Synthesizer Module
January 5, 2015
Sources of ideas:
  http://webshed.org/wiki/AD9850_Arduino
  https://github.com/F4GOJ/AD9850
  https://github.com/gonya707/ARDUINO_AD9850_Library
*/ 


#include "AD9850B.h"

#define pulseHigh(pin) {digitalWrite(pin, HIGH); delayMicroseconds(10); digitalWrite(pin, LOW); }

AD9850B::AD9850B(int gW_CLK, int gFQ_UD, int gDATA, int gRESET)
{
	lineCLOCK = gW_CLK;
	lineLOAD = gFQ_UD;
	lineDATA = gDATA;
	lineRESET = gRESET;
}

void AD9850B::PowerDown()
{
	int PDword = 0x04;
	int i; 
	pulseHigh(lineLOAD);
	for (i = 0; i<8; i++)
	{
		if ((PDword >> i) & 0x01)
			digitalWrite(lineDATA, HIGH);
		else
			digitalWrite(lineDATA, LOW);
		pulseHigh(lineCLOCK);
	}
	pulseHigh(lineLOAD);
	_frequency = 0;
}

void AD9850B::Reset()
{
	digitalWrite(lineCLOCK, LOW);
	digitalWrite(lineLOAD, LOW);
	pulseHigh(lineRESET);
	pulseHigh(lineCLOCK);
	digitalWrite(lineDATA, LOW);
	pulseHigh(lineLOAD);
}

void AD9850B::Init()
{
	digitalWrite(lineRESET, LOW);
	digitalWrite(lineCLOCK, LOW);
	digitalWrite(lineLOAD, LOW);
	digitalWrite(lineDATA, LOW);

}

boolean AD9850B::GetActive()
{
	return _active;
}

void AD9850B::SetActive(boolean param)
{
	if (_active != param)
	{
		switch (param)
		{

		case true:
			Init();
			Reset();
			SetFrequency(_frequency);
			_active = true;
			break;

		case false:
			PowerDown();
			_active = false;
			break;
		}
	}
}

unsigned long AD9850B::GetFrequency()
{
	return _frequency;
}

void AD9850B::SetFrequency(unsigned long param)
{
	unsigned long tuning_word = (param * FREQ_FACTOR) / (DDS_CLOCK + _calfactor);
	digitalWrite(lineLOAD, LOW);
	digitalWrite(lineCLOCK, LOW);
	shiftOut(lineDATA, lineCLOCK, LSBFIRST, tuning_word);
	shiftOut(lineDATA, lineCLOCK, LSBFIRST, tuning_word >> 8);
	shiftOut(lineDATA, lineCLOCK, LSBFIRST, tuning_word >> 16);
	shiftOut(lineDATA, lineCLOCK, LSBFIRST, tuning_word >> 24);
	shiftOut(lineDATA, lineCLOCK, LSBFIRST, 0x0); 
	pulseHigh(lineLOAD);
	_frequency = param;
}

void AD9850B::SetCalibration(long param)
{
	_calfactor = param;
}

You will notice that my Arduino AD9850 control defines frequency using an integer (unsigned long) rather than a floating point number. I always prefer to use an integer number for Hz (for example 10000 for 10 kHz) rather than a floating point number (such as 0.010 MHz or 10.0 kHz). Integers do not round so they are more exact, and computers usually do integer calculations a little faster. If you are doing Arduino AD9850 control using integers, you want to use a data type of “unsigned long” which has a range of 0 to 4,294,967,295. This seems like overkill, but a normal “long” can be negative and you want to restrict yourself to only positive frequencies in this type of application.

A final word about calibration. The AD9850 module uses a crystal oscillator as a standard frequency for its work. In my module, the oscillator runs at 125 MHz. When you first power up your AD9850 module and measure its output, you will notice that the actual output frequency is no exactly what you have programmed. For example, when I commanded an output of 10.0 MHz, my result was 9.99980 instead. This is because the crystal oscillator was not exactly on 125.0 MHz. By programming a calibration factor (positive or negative) you can compensate for this in software. When I enter a calibration factor of –1250 Hz, everything works fine.

Arduino AD9850 Reference Sites

These sites provide good information about Arduino AD9850 control:

Using the Analog Devices AD9850 DDS with an Arduino board

DDS Function Generator Build

DDS generator with Arduino board (translation)

AD9850 DDS VFO (translation)

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.