Become a subscriber (Free)

Join 29,000 other subscribers to receive subscriber sale discounts and other free resources.
:
:
Don't worry -- youre-mail address is totally secure. I promise to use it only to send you MicroZine.

AD9833 A DDS Signal Generator

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

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

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.

AD9833 module photo

ad9833 breakout module

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

AD9833 Specifications

Max output frequency: 12.5MHz (using 25MHz clock).
Accuracy: 0.1Hz with 25MHz clock.
Vp-p : 600mV
Output type: square, sine, triangle.
Voltage range: 2.3V ~ 5.5V

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

The breakout board does not seem to have any schematics available but it is easy to follow the board traces. The components on the board are:

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

The component  that is difficult to identify, 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

Hold up I found the AD9833 module schematic here (https://www.handsontec.com/dataspecs/module/Module%20Collection.pdf).

Breakout Module connector

module pin
Description
CS
SPI Chip select to MCP41010
DAT
SPI MOSI to AD9833 and MCP41010
CLK
SPI Clock to AD9833 and MCP41010
FSY
SPI Chip select for AD9833
GND
0V
VCC
3V ~ 5V

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 pasing 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.

ad9833 square wave 1kHz on oscilloscope

Example of Sweep generation

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

// 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.

AD8105 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).




New! Comments

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




Privacy Policy | Contact | About Me

Site Map | Terms of Use



Visit our Facebook Page:
To Visit Click Here


Recent Articles

  1. Arduino Interrupt : There are Some You May Never Have Heard About!

    Arduino Interrupt Tutorial: Find out how many external there are on an Arduino Uno - The answer is more than two!

    Read more

  2. Easy Switch Debounce

    Switch debounce: Three different ways to debounce input push switches with one amazing method that you can't miss.

    Read more

  3. How to use the ADS1115

    A tutorial on using the ADS1115 precision 16 bit ADC for low power use.

    Read more

  4. How to use the ADXL345 for movement sensing and more.

    With the ADXL345 acellerometer you can detect up to 16g! You can also find out how to use it for tap detection and more.

    Read more

  5. ESP8266 Webserver in Lua

    ESP8266 Webserver: This code shows you how to use lua to create a webserver using html button inputs to contrtol an LED on the ESP module.

    Read more

  6. ESP8266 NodeMCU Firmware: ESP8266 flash

    How to flash nodemcu firmware into a ESP8266 so you can use the LUA scripting language.

    Read more

Readers Comments

"I wanted to thank
you so so so much
for all the information
you have provided in
your site it's

SUPERB and FANTASTIC."

- Ranish Pottath

"This site really is
the best and my favorite.
I find here many useful
projects and tips."

- Milan

bursach<at>gmail.com<

"Awesome site,
very, very easy and nice
to navigate!"


- Matt
matt_tr<at>
wolf359.cjb.net


Learn Microcontrollers

"Interested in
Microcontrollers?"

Sign up for The
Free 7 day guide:

FREE GUIDE : CLICK HERE


"I am a newbie to PIC
and I wanted to say
 how great your
site has been for me."


- Dave

de_scott<at>bellsouth.net

"Your site is a great
and perfect work.
congratulations."


- Suresh

integratredinfosys<at>
yahoo.com

"I couldn't find the correct
words to define
yourweb site.

Very useful, uncovered,
honest and clear.

Thanks so much for
your time and works.
Regards."


- Anon

Back to Top