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
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).
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
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.
#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_AD9833AD(FSYNC);// Hardware SPI
//MD_AD9833 AD(DATA, CLK, FSYNC); // Arbitrary SPI pins
// Character constants for commands
constcharCMD_HELP='?';
constcharBLANK=' ';
constcharPACKET_START=':';
constcharPACKET_END=';';
constcharCMD_FREQ='F';
constcharCMD_PHASE='P';
constcharCMD_OUTPUT='O';
constcharOPT_FREQ='F';
constcharOPT_PHASE='P';
constcharOPT_SIGNAL='S';
constcharOPT_1='1';
constcharOPT_2='2';
constcharOPT_MODULATE='M';
constuint8_tPACKET_SIZE=20;
voidsetup()
{
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);
}
voidusage(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_thtoi(charc)
{
c=toupper(c);
if(c>='0'&&c<='9')
return(c-'0');
elseif(c>='A'&&c<='F')
return(c-'A'+10);
else
return(0);
}
charnextChar(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
{
staticenum{S_IDLE,S_READ_CMD,S_READ_MOD,S_READ_PKT}state=S_IDLE;
staticcharcBuf[PACKET_SIZE+1];
staticchar*cp;
charc;
switch(state)
{
caseS_IDLE:// waiting for packet start
c=nextChar();
if(c==CMD_HELP)
{
usage();
break;
}
if(c==PACKET_START)
{
cp=cBuf;
state=S_READ_CMD;
}
break;
caseS_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;
caseS_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;
caseS_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);
}
voidprocessPacket(char*cp)
// Assume we have a correctly formed packet from the parsing in readPacket()
{
uint32_tul;
MD_AD9833::channel_tchan;
MD_AD9833::mode_tmode;
switch(*cp++)
{
caseCMD_FREQ:
switch(*cp++)
{
caseOPT_1:chan=MD_AD9833::CHAN_0;break;
caseOPT_2:chan=MD_AD9833::CHAN_1;break;
caseOPT_MODULATE:/* do something in future */break;
}
ul=strtoul(cp,NULL,10);
AD.setFrequency(chan,ul);
break;
caseCMD_PHASE:
switch(*cp++)
{
caseOPT_1:chan=MD_AD9833::CHAN_0;break;
caseOPT_2:chan=MD_AD9833::CHAN_1;break;
}
ul=strtoul(cp,NULL,10);
AD.setPhase(chan,(uint16_t)ul);
break;
caseCMD_OUTPUT:
switch(*cp++)
{
caseOPT_FREQ:
switch(*cp)
{
caseOPT_1:chan=MD_AD9833::CHAN_0;break;
caseOPT_2:chan=MD_AD9833::CHAN_1;break;
caseOPT_MODULATE:/* do something in future */break;
}
AD.setActiveFrequency(chan);
break;
caseOPT_PHASE:
switch(*cp)
{
caseOPT_1:chan=MD_AD9833::CHAN_0;break;
caseOPT_2:chan=MD_AD9833::CHAN_1;break;
caseOPT_MODULATE:/* do something in future */break;
}
AD.setActivePhase(chan);
break;
caseOPT_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;
}
voidloop()
{
char*cp;
if((cp=readPacket())!=NULL)
processPacket(cp);
}
Unlock the secrets of Arduino scrolling displays! This beginner-friendly guide shows you how to create real-time, dynamic graphics using an SSD1306 OLED, perfect for tracking sensor data and building…
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.