kpower
Published © LGPL

Arduino controlled AD9833 function generator with gain

Use the AD9833 and Arduino to make a versatile function generator. Add a MCP601 op amp and an MCP4131 digital pot for variable gain

IntermediateFull instructions provided3 hours8,295
Arduino controlled AD9833 function generator with gain

Things used in this project

Hardware components

Arduino UNO
Arduino UNO
×1
Rail-to-Rail Input/Output Dual Op-Amp
Microchip Rail-to-Rail Input/Output Dual Op-Amp
×1
Analog Devices AD9833 Function Generator
×1
Microchip MCP4131 103
×1
Resistor 1k ohm
Resistor 1k ohm
×1

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Custom parts and enclosures

AD9833 Spec Sheet

Spec Sheet

Schematics

Schematic

Circuit Diagram

Code

AD9833 Control from Serial Monitor

Arduino
Control Arduino controlled waveform generator
/*
Program to control AD9833 and MCP4131
Based on example provided with AD9833 library
AD9833 connected to pins 5,6,7
MCP4131 connected to pins 10,11,13
Menu for controlling AD9833 and gain available on Serial Monitor
*/


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

#define DATA  6	   ///< SPI Data pin number
#define CLK   7	   ///< SPI Clock pin number
#define FSYNC 5	   ///< SPI Load pin number (FNC on AD9833 breakout)

long frequency;
const int chipSelect = 10; // Define chipselect pin for MCP4131
unsigned int wiperValue; // variable to hold wipervalue for MCP4131

MD_AD9833	AD(DATA, CLK, FSYNC); // Arbitrary SPI pins
MCP4131 Potentiometer(chipSelect);

// 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 CMD_GAIN = 'G';
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_GAIN = 'A';
const uint8_t PACKET_SIZE = 20;

void setup()
{
  Serial.begin(57600);
  AD.begin();
  usage();
  wiperValue = 64;
  Potentiometer.writeWiper(wiperValue); // Set MCP4131 to mid position
}

// Set up menu that displays on Serial Monitor
void usage(void)
{
  Serial.print(F("\n[MD_AD9833_Tester]"));
  Serial.print(F("\n?\thelp - Type ?"));
  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: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 [n=1/2]"));
  Serial.print(F("\n:opn;\toutput phase n [n=1/2]"));
  Serial.print(F("\n:osn;\toutput signal type n [n=(o)ff/(s)ine/(t)riangle/s(q)uare]"));
  Serial.print(F("\n:gan;\tset gain n 1 to 6"));
}

// Convert to ASCII
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 contents
{
  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 || c == CMD_GAIN)
    {
      *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_GAIN)
    {
      *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()
// Depending on input from Serial Monitor, send commands to AD9833 and MCP4131
{
  uint32_t  ul; // Frequency Value
  uint32_t wV; // Gain Value
  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_GAIN:  break;
    }

    ul = strtoul(cp, NULL, 10);
    // Serial.println(ul); for debug purposes
    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_GAIN:
    *cp++;
    wV = strtoul(cp, NULL, 10);
    // Serial.println(wV); for bebug purposes
    wV = map(wV, 1, 5, 12, 65);
    // Serial.println(wV); for debug purposes
    Potentiometer.writeWiper(wV);
    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;
      }
      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;
      }
      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);
}

Credits

kpower

kpower

19 projects • 6 followers
Qualified Electrical Engineer with experience in software and hardware development

Comments