## Introduction

About three years ago, I spent some time generating Lissajous figures1 with a couple of AD98502 DDS synthesizers.

Today (October 2018) the AD9850 boards on eBay go for about £10, but you can buy a similar product based around the AD98333 for about £2.50 or £5 with buffers and fancy connectors.

The AD9850 handles higher-frequencies (up to 60MHz) whilst the AD8933 is limited to 12.5MHz. That’s fine for me though: I’m interested in frequencies below 100kHz. To be pedantic the AD9833 can handle frequencies up to half the frequency of the reference clock, but all the eBay boards use a 25MHz oscillator.

I was interested in using these to build a swept-frequency generator i.e. to create a signal which is basically sinusoidal with slowly increasing frequency.

Incidentally you can also get similar boards based around the Silicon Labs Si53514 chip. These generate multiple clocks and cost about £10.

## A conceptual view

At the heart of the AD9833 is a phase accumulator which increments at a rate determined by both the external master clock and the value loaded into one of the chip’s registers. The official block diagram shows this:

You can see there is more than I’ve described. In particular:

• The AD9833 lets you store two different frequencies and then switch between them easily. This makes it easier to implement frequency-shift keying5.
• The phase of the output can also be tweaked. This makes it easier to implement phase-shift keying6.

However, we can ignore these if we just want to generate a slowly changing sine wave.

## Arduino interfacing

Google will furnish many articles on ad9833 arduino7, but it’s not quite clear of their relative merits.

Most of the libraries implement a C++ class which hides the functionality of the chip behind a nice API. Sadly the one I tried seemed to reset the phase accumulator when the frequency changed, leading to discontinuities in the output.

Given the datasheet, It is simple to program the chip to generate a single frequency, so I thought it better to just drive it directly.

There is one wrinkle though: although the interface to the AD9833 is essentially SPI, blog posts commonly talk about it finding it hard to use the system SPI and instead use bespoke bit-banging code. I followed that advice here, and stole the SPI code from Marco Colli’s library8.

// for chip info see https://www.analog.com/en/products/ad9833.html
// SPI code taken from https://github.com/MajicDesigns/MD_AD9833/

const uint8_t _dataPin  = 11;
const uint8_t _clkPin   = 13;
const uint8_t _fsyncPin = 10;

// send raw 16-bit word
void spiSend(const uint16_t data)
{
digitalWrite(_fsyncPin, LOW);

uint16_t m = 1UL << 15;
for (uint8_t i = 0; i < 16; i++)
{
digitalWrite(_dataPin, data & m ? HIGH : LOW);
digitalWrite(_clkPin, LOW); //data is valid on falling edge
digitalWrite(_clkPin, HIGH);
m >>= 1;
}
digitalWrite(_dataPin, LOW); //idle low
digitalWrite(_fsyncPin, HIGH);
}

void setFreq(double f)
{
const uint16_t b28  = (1UL << 13);
const uint16_t freq = (1UL << 14);

const double   f_clk = 25e6;
const double   scale = 1UL << 28;
const uint32_t n_reg = f * scale / f_clk;

const uint16_t f_low  = n_reg         & 0x3fffUL;
const uint16_t f_high = (n_reg >> 14) & 0x3fffUL;

spiSend(b28);
spiSend(f_low  | freq);
spiSend(f_high | freq);
}

void setup() {
pinMode(_clkPin,   OUTPUT);
pinMode(_fsyncPin, OUTPUT);
pinMode(_dataPin,  OUTPUT);

digitalWrite(_fsyncPin, HIGH);
digitalWrite(_clkPin,   LOW);
}

void loop() {
setFreq(800.0);
delay(50);
setFreq(1600.0);
delay(50);
}

## Raspberry Pi

The code above is entirely self-contained, so it is easy to port it to the Raspberry Pi. I switched the code from C to Python, targetting the gpiozero9 API.

import gpiozero

def __init__(self, data, clk, fsync):
self.dataPin  = gpiozero.OutputDevice(pin = data)
self.clkPin   = gpiozero.OutputDevice(pin = clk)
self.fsyncPin = gpiozero.OutputDevice(pin = fsync)

self.fsyncPin.on()
self.clkPin.on()
self.dataPin.off()

self.clk_freq = 25.0e6

def set_freq(self, f):
flag_b28  = 1 << 13
flag_freq = 1 << 14

scale = 1 << 28
n_reg = int(f * scale / self.clk_freq)

n_low = n_reg         & 0x3fff
n_hi  = (n_reg >> 14) & 0x3fff

self.send16(flag_b28)
self.send16(flag_freq | n_low)
self.send16(flag_freq | n_hi)

def send16(self, n):
self.fsyncPin.off()

for i in range(0, 16):

self.clkPin.off()
self.clkPin.on()