AD9833: A Versatile Easy to Use Signal Generator on a Chip. Find out how to use it to generate Sine, Square and Triangle waves at 0.1Hz resolution from 0.1Hz to 12.5MHz

The AD9833 is a Direct Digital Synthesizer that can generate sine, square or triangle waves and is controlled using the SPI protocol.

  • Generate synthesized sine, triangle or square waves.

  • Outputs 0.1Hz to 12.5MHz in 0.1Hz resolution steps.

  • Use the SPI interface for easy connection to a microcontroller.

  • Works from 2.3V to 5.5V.

  • Generates a 600mV output.

  • Some breakout boards have an amplifier for 0 ~ 5V output with a digital POT for PGA (Programmable Gain Control).

A few years ago you would have to pay a lot of money for a DDS now you can get one for $10! Amazing - this thing can generate signals at 0.1Hz resolution and works up to 12.5MHz.

Note: If you want to get complex you can make it perform coding schemes such as FSK, GMSK and QPSK. Here we''ll just keep it simple!

Since the device is in a MSOP package the easiest way to use it is by getting yourself a breakout board. You can buy two types, one comes with an opamp buffer chip and digital attenuator for adjusting the output level.

AD9833 Breakout Board

ad9833 breakout module

The right hand side output is direct from the DDS (labelled Vout) whereas the left side has the output going through the pot and the amplifier (labelled PGA Programmable gain Amplifer).

AD9833

AD9833 Specification

Parameter
AD9833
Voltage Supply (Vs)
 2V3 ~ 5V5
Abs. Max VDD
-0V3 ~ 6V0
Output (Vpp)
600mV
Outputs
Square, Sine, Triangle.
Max output frequency
12.5MHz
Resolution
0.1Hz
Interface
SPI
SPI rate (MHz)
25
Active current (typ, max)
4.5mA / 5.5mA
Standby mode
500uA
Resolution
10bit
INL (Integral Nonlinarity)±1.0LSB
DNL (Differential Nonlinarity) ±0.5LSB
Operating temperature
-40°C ~ 105°C

AD9833 Datasheet

This is the Direct Digital Synthesis chip Datasheet (click to open in new window).

AD9833 Breakout Board

The reason that the opamp is needed is that the output of the AD9833 is about 600mV. The opamp amplifies the signal by 5 to give a 3V output. Using the digital pot allows you to reduce this output to a level you need.

Block Diagram

ad9833 block diagram
One interesting point in the diagram above is that there are two frequency registers FREQ0 and FREQ1 and you can select between them using a mux. This means frequency shift keying is easy i.e. change frequency between two stored frequencies without re-loading a frequency register, so it is fast. The same is true of phase.

Breakout Board Components

Components on the board are:

Component
Description
AD9833 
 DDS chip
MCP41010  Digital Potentiometer
H2A
 AD8051 (see below)

The component that is difficult to identify (before I had located a schematic - below!), is the one marked H2A on the SOT23-5 package. This is an output buffer configured as an inverting op amp and is probably the AD8051 - a very high speed (300MHz 3dB bandwidth) rail-to-rail opamp.

TIP: You can find SMD chip markings here: http://www.smdmark.com

Here is the module schematic diagram:

AD9833 block diagram
[Source:(https://www.handsontec.com/dataspecs/module/Module%20Collection.pdf).]

Breakout Module connector

module pin
Description
Arduino pin
CS
SPI Chip select to MCP41010
CS, 9 - Orange
DAT
SPI MOSI to AD9833 and MCP41010MOSI, 11 - Blue
CLK
SPI Clock to AD9833 and MCP41010
CLK, 13, - White
FSY
SPI Chip select for AD9833
CS, 10, - Yellow
GND
0V
GND, - Green
VCC
3V ~ 5V
VCC, - Red     

Arduino Library

You can find library installation details are here.

Library for the AD9833

Install the library named MD_AD9833

Use the library manager, searching for AD9833.

Library for MCP41010

Install the library named Ardumax MCP41xxx

Use the library manager, searching for MCP41.

Circuit Connections

For the AD9833 make the following connections from an Arduino Uno to the breakout board:

DAT       11          /// SPI Data pin number
CLK       13          /// SPI Clock pin number
FSY       10          /// chip select (FSYNC in AD9833 usage)

Additionally for the MCP41010 make the following connection:

CS          9          /// chip select for MCP41010

AD9833 Sketch

The following sketch allows you to test the AD9833 by entering commands into the serial monitor.

AD9833 Testing

For help type '?' into the send box in the Arduino Serial Monitor.

Typing the following commands outputs a 1kHz waveform to Vout

:f11000;
:ost;       // triangle wave
:oss;       // sine wave
:osq;       // square wave

Now output a 10MHz waveform
:f110000000; // 10MHz

#include <SPI.h>
#include <MD_AD9833.h>

// Pins for SPI comm with the AD9833 IC
#define DATA  11	   ///< SPI Data pin number
#define CLK   13	   ///< SPI Clock pin number
#define FSYNC 10	   ///< SPI Load pin number (FSYNC in AD9833 usage)
#define CS_DIGIPOT 9 // MCP41010 chip select - digital potentiometer.

MD_AD9833	AD(FSYNC); // Hardware SPI
//MD_AD9833	AD(DATA, CLK, FSYNC); // Arbitrary SPI pins

// Character constants for commands
const char CMD_HELP = '?';
const char BLANK = ' ';
const char PACKET_START = ':';
const char PACKET_END = ';';
const char CMD_FREQ = 'F';
const char CMD_PHASE = 'P';
const char CMD_OUTPUT = 'O';
const char OPT_FREQ = 'F';
const char OPT_PHASE = 'P';
const char OPT_SIGNAL = 'S';
const char OPT_1 = '1';
const char OPT_2 = '2';
const char OPT_MODULATE = 'M';
const uint8_t PACKET_SIZE = 20;

void setup()
{
  pinMode(CS_DIGIPOT,OUTPUT);

  Serial.begin(57600);
  AD.begin();
  usage();

  // take the CS pin low to select the chip:
  digitalWrite(CS_DIGIPOT,LOW);
  //  send in the address and value via SPI:
  SPI.transfer(B00010001);
  // write out the value
  SPI.transfer(127);
  // take the CS pin high to de-select the chip:
  digitalWrite(CS_DIGIPOT,HIGH);
}

void usage(void)
{
  Serial.print(F("\n\n[MD_AD9833_Tester]"));
  Serial.print(F("\n?\thelp - this message"));
  Serial.print(F("\n\n:<cmd><opt> <param>;"));
  Serial.print(F("\n:f1n;\tset frequency 1 to n Hz"));
  Serial.print(F("\n:f2n;\tset frequency 2 to n Hz"));
  Serial.print(F("\n:fmn;\tset frequency modulation to n Hz"));
  Serial.print(F("\n:p1n;\tset phase 1 to n in tenths of a degree (1201 is 120.1 deg)"));
  Serial.print(F("\n:p2n;\tset phase 2 to n in tenths of a degree (1201 is 120.1 deg)"));
  Serial.print(F("\n:ofn;\toutput frequency n or modulation [n=1/2/m]"));
  Serial.print(F("\n:opn;\toutput phase n or modulation [n=1/2/m]"));
  Serial.print(F("\n:osn;\toutput signal type n [n=(o)ff/(s)ine/(t)riangle/s(q)uare]"));
}

uint8_t htoi(char c)
{
  c = toupper(c);

  if (c >= '0' && c <= '9')
      return(c - '0');
  else if (c >= 'A' && c <= 'F')
      return(c - 'A' + 10);
  else
      return(0);
}

char nextChar(void)
// Read the next character from the serial input stream
// Blocking wait
{
  while (!Serial.available())
    ; /* do nothing */
  return(toupper(Serial.read()));
}

char *readPacket(void)
// read a packet and return the
{
  static enum { S_IDLE, S_READ_CMD, S_READ_MOD, S_READ_PKT } state = S_IDLE;
  static char cBuf[PACKET_SIZE + 1];
  static char *cp;
  char c;

  switch (state)
  {
  case S_IDLE:   // waiting for packet start
    c = nextChar();
    if (c == CMD_HELP)
    {
      usage();
      break;
    }
    if (c == PACKET_START)
    {
      cp = cBuf;
      state = S_READ_CMD;
    }
    break;

  case S_READ_CMD:   // waiting for command char
    c = nextChar();
    if (c == CMD_FREQ || c == CMD_PHASE || c == CMD_OUTPUT)
    {
      *cp++ = c;
      state = S_READ_MOD;
    }
    else
      state = S_IDLE;
    break;

  case S_READ_MOD: // Waiting for command modifier
    c = nextChar();
    if (c == OPT_FREQ || c == OPT_PHASE || c == OPT_SIGNAL ||
      c == OPT_1 || c == OPT_2 || c == OPT_MODULATE)
    {
      *cp++ = c;
      state = S_READ_PKT;
    }
    else
      state = S_IDLE;
    break;

  case S_READ_PKT: // Reading parameter until packet end
    c = nextChar();
    if (c == PACKET_END)
    {
      *cp = '\0';
      state = S_IDLE;
      return(cBuf);
    }
    *cp++ = c;
    break;

  default:
    state = S_IDLE;
    break;
  }

  return(NULL);
}

void processPacket(char *cp)
// Assume we have a correctly formed packet from the parsing in readPacket()
{
  uint32_t  ul;
  MD_AD9833::channel_t chan;
  MD_AD9833::mode_t mode;

  switch (*cp++)
  {
  case CMD_FREQ:
    switch (*cp++)
    {
    case OPT_1: chan = MD_AD9833::CHAN_0; break;
    case OPT_2: chan = MD_AD9833::CHAN_1; break;
    case OPT_MODULATE: /* do something in future */ break;
    }

    ul = strtoul(cp, NULL, 10);
    AD.setFrequency(chan, ul);
    break;

  case CMD_PHASE:
    switch (*cp++)
    {
    case OPT_1: chan = MD_AD9833::CHAN_0; break;
    case OPT_2: chan = MD_AD9833::CHAN_1; break;
    }

    ul = strtoul(cp, NULL, 10);
    AD.setPhase(chan, (uint16_t)ul);
    break;

  case CMD_OUTPUT:
    switch (*cp++)
    {
    case OPT_FREQ:
      switch (*cp)
      {
      case OPT_1: chan = MD_AD9833::CHAN_0; break;
      case OPT_2: chan = MD_AD9833::CHAN_1; break;
      case OPT_MODULATE: /* do something in future */ break;
      }
      AD.setActiveFrequency(chan);
      break;

    case OPT_PHASE:
      switch (*cp)
      {
      case OPT_1: chan = MD_AD9833::CHAN_0; break;
      case OPT_2: chan = MD_AD9833::CHAN_1; break;
      case OPT_MODULATE: /* do something in future */ break;
      }
      AD.setActivePhase(chan);
      break;

    case OPT_SIGNAL:
      switch (*cp)
      {
      case 'O': mode = MD_AD9833::MODE_OFF;    break;
      case 'S': mode = MD_AD9833::MODE_SINE;   break;
      case 'T': mode = MD_AD9833::MODE_TRIANGLE;  break;
      case 'Q': mode = MD_AD9833::MODE_SQUARE1;  break;
      }
      AD.setMode(mode);
      break;
    }
    break;
  }

  return;
}


void loop()
{
  char  *cp;

  if ((cp = readPacket()) != NULL)
    processPacket(cp);
}

Output Oscilloscope Images

1kHz Sinewave at Vout

Settings 100mV/div, 0.2ms/div : 1/(0.2ms*4.8) = 1041Hz (scope accuracy).

ad9833 sine wave 1kHz on oscilloscope

1kHz Triangle wave at Vout

Settings 100mV/div, 0.2ms/div : 1/(0.2ms*4.8) = 1041Hz (scope accuracy).

ad9833 triangle wave 1kHz on oscilloscopef

1kHz Square wave at Vout

Settings 1V/div, 0.2ms/div : 1/(0.2ms*4.8) = 1041Hz (scope accuracy).

Note: The square wave output has far greater amplitude than the other signals - about 4.4V.

ad9833 square wave 1kHz on oscilloscope

Example of Sweep generation

This code sweeps from 1MHz to 3MHz in 2 seconds.

#include <SPI.h>
#include <MD_AD9833.h>

// Pins for SPI comm with the AD9833 IC
#define DATA 11 ///< SPI Data pin number #define CLK 13 ///< SPI Clock pin number #define FSYNC 10 ///< SPI Load pin number (FSYNC in AD9833 usage) #define CS_DIGIPOT 9 // MCP41010 chip select - digital potentiometer. MD_AD9833 AD(FSYNC); // Hardware SPI void setup() { AD.begin(); }
void loop() { unsigned long f; for( f = 1000000UL; f<3000000UL; f+=10000UL) { delay(10); AD.setFrequency(MD_AD9833::CHAN_0, f); } }

Breakout board IC Pinouts

AD9833 pinout

This is available in 10 lead MSOP i.e. surface mount only. 

AD9833 pinoot (MSOP)

MCP41010 pinout

On the board a surface mount SOIC is used but you can also get it in PDIP.


MCP41010 pinout PDIP or SOIC

Note: The part is specified as MCP41xxx, where when xxx=010 it is a 10k pot and when xxx=100 it is a 100k pot.

AD8051 pinout

This comes in many different packages but this is the SOT23-5 one i.e. very small. The one on the breakout board is marked H2A on the top.
AD8051 SOT23-5 very high speed opamp

IC Datasheets

Either right click the following links and use save-as to save the pdf to your hard disk, or click to open in a new browser window.

AD9833 Datasheet

This is the Direct Digital Synthesis chip Datasheet (click to open in new window).

AD8051 Datasheet

This is the 300MHz rail to-rail opamp Datasheet (click to open in new window)

MCP41010 Datasheet

This is the 256 tap digital pot. Datasheet (click to open in new window).




Comments

Have your say about what you just read! Leave me a comment in the box below.

Don’t see the comments box? Log in to your Facebook account, give Facebook consent, then return to this page and refresh it.




Privacy Policy | Contact | About Me

Site Map | Terms of Use