Testing an eBay AD9850 DDS module with Arduino Uno

Like a lot of Hams, while browsing for electronics on eBay I came across some of these super inexpensive DDS modules being sold and shipped from China (less than $6 as of August 2012).  That’s cheaper than you can buy the 9850 chip by itself here in the States!  Without any research whatsoever I went ahead and added 2 to a recent order of some other components I needed.

About 2 weeks later my package arrived and here’s the board I ended up with.Anyway, I had visions of dragging out the breadboard, hooking up some jumpers, maybe throwing a couple of capacitors in there…   As anyone can tell you that’s bought one of these on impulse – it’s a little more complicated than that.  There is actually some math involved, bits, bytes, hexadecimal, longwords even.  Stuff I hadn’t seen since college (what I remember of college anyway).  So, I finally sat down the other day and dug in to what it would take to get this thing going.

Long story short, to get any output from one of these you need to send 40 bits of data to the chip, either via it’s serial or parallel interface.  It’s all explained in the official Analog Device datasheet here – http://www.analog.com/static/imported-files/data_sheets/AD9850.pdf

I read it the other night to help me fall asleep.

Ahh, just kidding.  It’s actually pretty cool stuff.  You can interface to the board via your computers serial or parallel port, but as a recent Arduino convert, I decided to go that route.  I ended up modifying slightly an Arduino sketch that was written for the 9851, putting the AD9850 on a UNO prototype shield, and testing it in single frequency mode.  I can envision using these as a crystal eliminator for some older tube type transmitting gear I have here, maybe controlled with an Atmel 90S2313 or PIC.  It’s about comparable in cost to buying an actual crystal for the older gear, and you’re not stuck to one frequency.

Ok, so here are the two board versions, with pinouts labeled that I’ve seen on eBay.

I used a prototype shield with breadboard, although it’s not necessary.  You could just jumper to the main Arduino board.

Using digital pins 8 through 11, proto shield installed on the UNO.  Pin 8 to CLK, 9 to FQ, 10 to DATA, and 11 to RST.

Here is the modified sketch for the 9850.  Just cut and paste, and upload to your Arduino board.  At the very end of the program you can choose which frequency you would like.

/*
 * A simple single freq AD9850 Arduino test script
 * Original AD9851 DDS sketch by Andrew Smallbone at www.rocketnumbernine.com
 * Modified for testing the inexpensive AD9850 ebay DDS modules
 * Pictures and pinouts at nr8o.dhlpilotcentral.com
 * 9850 datasheet at http://www.analog.com/static/imported-files/data_sheets/AD9850.pdf
 * Use freely
 */

 #define W_CLK 8       // Pin 8 - connect to AD9850 module word load clock pin (CLK)
 #define FQ_UD 9       // Pin 9 - connect to freq update pin (FQ)
 #define DATA 10       // Pin 10 - connect to serial data load pin (DATA)
 #define RESET 11      // Pin 11 - connect to reset pin (RST).

 #define pulseHigh(pin) {digitalWrite(pin, HIGH); digitalWrite(pin, LOW); }

 // transfers a byte, a bit at a time, LSB first to the 9850 via serial DATA line
void tfr_byte(byte data)
{
  for (int i=0; i<8; i++, data>>=1) {
    digitalWrite(DATA, data & 0x01);
    pulseHigh(W_CLK);   //after each bit sent, CLK is pulsed high
  }
}

 // frequency calc from datasheet page 8 = <sys clock> * <frequency tuning word>/2^32
void sendFrequency(double frequency) {
  int32_t freq = frequency * 4294967295/125000000;  // note 125 MHz clock on 9850
  for (int b=0; b<4; b++, freq>>=8) {
    tfr_byte(freq & 0xFF);
  }
  tfr_byte(0x000);   // Final control byte, all 0 for 9850 chip
  pulseHigh(FQ_UD);  // Done!  Should see output
}

void setup() {
 // configure arduino data pins for output
  pinMode(FQ_UD, OUTPUT);
  pinMode(W_CLK, OUTPUT);
  pinMode(DATA, OUTPUT);
  pinMode(RESET, OUTPUT);

  pulseHigh(RESET);
  pulseHigh(W_CLK);
  pulseHigh(FQ_UD);  // this pulse enables serial mode - Datasheet page 12 figure 10
}

void loop() {
  sendFrequency(10.e6);  // freq
  while(1);
}

Right click this Download Link and choose “save as” to download.

And here’s the result.

This is a look at a 10 MHz sinewave from pin 10 on my board.  The HP 5334A frequency counter uses a GPS phase corrected ovenized crystal oscillator as it’s reference source, so it’s extremely accurate.  A little tweeking of the <sys clock> variable in the Arduino sketch and this thing would be balls on, or close to it.

Pretty impressive little board.

51 thoughts on “Testing an eBay AD9850 DDS module with Arduino Uno

  1. Hi, nice work.
    but the code can’t be verified by arduino 1.0.1
    Error-messages:
    sketch_sep08a:20: error: stray ‘\’ in program
    sketch_sep08a:31: error: stray ‘\’ in program
    sketch_sep08a.cpp: In function ‘void tfr_byte(byte)’:
    sketch_sep08a:20: error: expected `)’ before ‘u00d701′
    sketch_sep08a.cpp: In function ‘void sendFrequency(double)’:
    sketch_sep08a:31: error: expected `)’ before ‘u00d7000′

    would be great, if you fix that
    or do I have to use a different Arduino-Shell ?
    Greatings from Germany
    Kurti

  2. Kurti, thanks for the heads up! I didn’t realize WordPress changed the formatting of the code. I had to install a plug-in so it would display correctly. You should now be able to copy and past the code and it will compile correctly. Let me know how it works out.

  3. Hi,
    afer deleteing the ”
    I got the following message:
    sketch_sep10a.cpp: In function ‘void tfr_byte(byte)’:
    sketch_sep10a:19: error: expected `;’ before ‘)’ token
    sketch_sep10a.cpp: In function ‘void sendFrequency(double)’:
    sketch_sep10a:28: error: expected `;’ before ‘)’ token

    I think a download of the sketch would solve the problem.
    Thanks again for your work
    an Greatings from Germany
    Kurti

  4. Ok, that’s strange, I was getting the same errors you are. I just re-posted the code and I can cut and paste, and it compiles fine in the IDE. I am going to add a link for direct download of the sketch.

  5. I’m K4WDE, though very inactive. I’ve been searching for a sketch to test the DDS module I purchased. Looks like yours will be the simplest. Thanks a ton for taking the time.

    Walt

    • Hey Walt, thanks for commenting. Good luck with the DDS module. I think I’m going to be using mine with a buffer amp as a crystal replacement for an old 2&6 meter tube rig I’m restoring (Ameco TX62). I think it would be neat to use the 9850 with a LCD keypad shield and be able to just cursor over to the digit you want to change, hit up or down, and then select. Should be easy to do, and who knows it’s probably already been done, just need to find it.
      73, Ron

  6. Hi Walt,
    thanks for your listing to test an AD9850 you buying via eBay.
    I have exact the same one here.
    Your code is compiling and working great.
    I have only one problem. The smallest step-rate I can use is 1 Hz.
    Following the datasheet and other publications it must-be possible to use smaller step-rates. What I need it a steprate from abt. 1.46 Hz.
    I hope you can help me with this.

    Regards and vy73 de Erwin DU1MGA (W3WAZ)

  7. Thanks for posting your frequency programming sketch. I am still waiting for my boards from Hong Kong (they must really be on the “slow boat”).

    Do you know what function R13, the little trimpot, performs?

    73

  8. Pingback: Procrastination Paid Off | Used Radio Mall

  9. Pingback: DDS Update | Used Radio Mall

  10. Thanks for the video explanation. I wouldn’t have guessed that it was a square wave duty cycle adjust.

    Since my last comment, I have received my AD9850 boards and had it running on the bench in about 5 minutes!

    Thanks for posting your experience and your code.

  11. Question about AD9850_nr80.ino source
    Thanks for the code! Now a question: Your line:
    int32_t freq = frequency * 4294967295/125000000;
    Obviously 4294967295 = 2^32 and 125000000 = RefClk
    But it does not jive with the datasheet page 8 that says:
    Fout = (delta Phase x CLKIN) / 2^32
    Your multiplication then division are not the same as the datasheet formula.
    What am I not understanding?
    Also:
    When I use the Interactive Design Tool at Analog.com
    http://designtools.analog.com/dt/dds/ad9850.html
    It does not give the same results as your formula.
    It must be something obvious, but what?
    73, Doug WD0UG

    • Hi Doug,

      Perhaps you’ve been misled by the variable name ‘freq’ and you think this is the same as ‘Fout’ in the datasheet. What the program is calculating (and calls ‘freq’) is actually the frequency tuning word or the the thing called ‘delta Phase’ in the datasheet. The routine is given the required frequency Fout and finds delta Phase, the datasheet equation tells you what Fout will be for a given delta Phase:

      Fout = (delta Phase * Refclk) / 2^32

      so the program computes:

      delta Phase = Fout * 2^32 / Refclk

      There may be a small difference between the tuning word calculated by this program and the tool you mentioned, but it will only be in the least significant bits and is simply due to differences in the way rounding to the nearest integer is done.

      Hope that helps,

      David

  12. Hi Ron,
    Thanks for the sketch, I have had my arduino ages, but lack the gumption to get stuck in to it. I bought one of these dds boards for £3.70 delivered from Hong Kong after hearing about it in Radcom. I had it wired and running within a matter of minutes. The big rig had it within about 10hz, that’s pretty amazing in my book.
    Thanks for putting this on your blog. I’d like to take it further, maybe add an encoder and have it as a dds vfo for some future qrp rig, maybe for the new 60 metre segments we have here now.

    73,
    Ian MM0GYX

  13. Thanks, I was able to have your sketch up and running with Energia on a Stellaris Launchpad within seconds. The module gets its power from the Launchpad as well.

  14. How tight is the output sine at low (e.g. audio) freq’s?
    At the moment, I’m using a PIC12F1822 and it’s 5 bit DAC. Ugly at best, even after a decent filter…

    • Just a note to anybody that might get tripped up…
      The chip doesn’t like to share the CLK and DATA lines with other devices, e.g. if you’re trying to use it on a SPI bus.
      meh…I could be wrong. At least in my circuit, it didn’t start working right until I split everything out.

  15. Thank you very much, I also managed to have the DDS up and running in 5 minutes, an essential starting point. Great work!
    Marco

  16. Hi, Ron
    Many thanks for sharing info and code for using AD9850 along with arduino.
    I just used your code to make a nice CW qrpp beacon:
    Hope usefull to others.
    can be downloaded from http://www.verfotos.es/intercambio/arduinobeacon.ino

    // Modified and adapted as a qrpp CW beacon using arduino uno and AD98550 board
    // Hardware wiring and Code to send a sinewave carrier with AD9850 taken from
    // http://nr8o.dhlpilotcentral.com/?p=83 Thanks Ron!!
    // CW sending code was taken from http://brainwagon.org Thanks Mark!!
    // Almost all merit belongs to the above authors. I only did the adapting job and added “+ – = ” codes
    // I am not a good programmer, but it works!. Feel free to improve and correct bugs.
    // If you modify will be nice to send a brief note to Mark and Ron, real fathers of code
    // I also will appreciate you send a note with the improved/modified code to ea5ehs[at]gmail.com Niceto Muñoz
    // 73 es DX from Niceto (EA5EHS)

    // Modificado y adaptado como baliza CW qrpp, usando “Arduino uno” y una placa con “AD9850″
    // El conexionado y el código para generar una señal senoidal con el AD9850 se ha tomado de
    // http://nr8o.dhlpilotcentral.com/?p=83 Gracias Ron!!
    // El código para generar los caracteres CW se ha tomado de http://brainwagon.org Gracias Mark!!
    // Casi todo el mérito pertenece a los autores anteriores. Yo únicamente hice el trabajo de adaptar los dos códigos,
    // añadiendo tambien los caracteres cw para “+ – =”.
    // Yo no soy un buen programador, pero funciona!. El código es totalmente libre para mejorarlo y corregir errores.
    // Si lo modificas, estaría bien que enviaras una pequeña nota a Mark y Ron, padres reales del código.
    // Tambien me gustaría que me enviaras una copia del código mejorado/modificado a mi correo ea5ehs[arroba]gmail.com
    // 73 es Dx Niceto (EA5EHS)

    struct t_mtab { char c, pat; } ;

    struct t_mtab morsetab[] = {
    {‘+’, 42},
    {‘-’, 97},
    {‘=’, 49},
    {‘.’, 106},
    {‘,’, 115},
    {‘?’, 76},
    {‘/’, 41},
    {‘A’, 6},
    {‘B’, 17},
    {‘C’, 21},
    {‘D’, 9},
    {‘E’, 2},
    {‘F’, 20},
    {‘G’, 11},
    {‘H’, 16},
    {‘I’, 4},
    {‘J’, 30},
    {‘K’, 13},
    {‘L’, 18},
    {‘M’, 7},
    {‘N’, 5},
    {‘O’, 15},
    {‘P’, 22},
    {‘Q’, 27},
    {‘R’, 10},
    {‘S’, 8},
    {‘T’, 3},
    {‘U’, 12},
    {‘V’, 24},
    {‘W’, 14},
    {‘X’, 25},
    {‘Y’, 29},
    {‘Z’, 19},
    {’1′, 62},
    {’2′, 60},
    {’3′, 56},
    {’4′, 48},
    {’5′, 32},
    {’6′, 33},
    {’7′, 35},
    {’8′, 39},
    {’9′, 47},
    {’0′, 63}
    } ;
    #define W_CLK 8 // Pin 8 – connect to AD9850 module word load clock pin (CLK)
    #define FQ_UD 9 // Pin 9 – connect to freq update pin (FQ)
    #define DATA 10 // Pin 10 – connect to serial data load pin (DATA)
    #define RESET 11 // Pin 11 – connect to reset pin (RST).

    #define pulseHigh(pin) {digitalWrite(pin, HIGH); digitalWrite(pin, LOW); }

    #define FREQUENCY 10133500
    #define FREQUENCY2 0 //putting a second frequency different from 0, you also generate an inverse keying CW signal that can be heard using FM mode :)
    #define N_MORSE (sizeof(morsetab)/sizeof(morsetab[0]))
    #define SPEED (12)
    #define DOTLEN (1200/SPEED)
    #define DASHLEN (3*(1200/SPEED))

    void
    dash()
    {
    sendFrequency(FREQUENCY);
    delay(DASHLEN);
    sendFrequency(FREQUENCY2);
    delay(DOTLEN);
    }

    void
    dit()
    {
    sendFrequency(FREQUENCY);
    delay(DOTLEN);
    sendFrequency(FREQUENCY2);
    delay(DOTLEN);
    }

    void
    send(char c)
    {
    int i ;
    if (c == ‘ ‘) {
    delay(7*DOTLEN) ;
    return ;
    }
    for (i=0; i<N_MORSE; i++) {
    if (morsetab[i].c == c) {
    unsigned char p = morsetab[i].pat ;

    while (p != 1) {
    if (p & 1)
    dash() ;
    else
    dit() ;
    p = p / 2 ;
    }
    delay(2*DOTLEN) ;
    return ;
    }
    }
    /* if we drop off the end, then we send a space */
    Serial.print("?") ;
    }

    void
    sendmsg(char *str)
    {
    while (*str)
    send(*str++) ;
    }
    // transfers a byte, a bit at a time, LSB first to the 9850 via serial DATA line
    void tfr_byte(byte data)
    {
    for (int i=0; i>=1) {
    digitalWrite(DATA, data & 0×01);
    pulseHigh(W_CLK); //after each bit sent, CLK is pulsed high
    }
    }

    // frequency calc from datasheet page 8 = * /2^32
    void sendFrequency(double frequency) {
    int32_t freq = frequency * 4294967295/125000000; // note 125 MHz clock on 9850
    for (int b=0; b>=8) {
    tfr_byte(freq & 0xFF);
    }
    tfr_byte(0×000); // Final control byte, all 0 for 9850 chip
    pulseHigh(FQ_UD); // Done! Should see output
    }

    void setup() {
    // configure arduino data pins for output
    pinMode(FQ_UD, OUTPUT);
    pinMode(W_CLK, OUTPUT);
    pinMode(DATA, OUTPUT);
    pinMode(RESET, OUTPUT);

    pulseHigh(RESET);
    pulseHigh(W_CLK);
    pulseHigh(FQ_UD); // this pulse enables serial mode – Datasheet page 12 figure 10
    }

    void loop() {
    sendmsg(” V V V XX0XX/B QRPP BEACON E E E E E E E E E E”) ;
    delay(1500) ;
    }

    73 es DX from Niceto EA5EHS

  17. Thanks for posting this guide; it really helps speed up the learning curve (reducing it to almost zero). I was able to get my AD9850 board working in only about 30 minutes (taking my time) using this info.

    PS:
    2^32 is 4294967296, but your code shows … 295 (=2^32 – 1).
    Arduino main loop doesn’t require the while(1) statement.

    • Hi Bill,
      Just wondering about not needing the while(1). If it is omitted, won’t the Arduino keep writing the same value over & over again to the AD9850? Or are you saying because that won’t cause any harm, just let it ?

  18. Hallo, Bill
    Thanks for info for “while(1)”. I
    I copied code from the two oringins and adapted. You can see following links in the code comments area. This is my first Arduino ide code (excepting pin 13 led blinking). I used my limited Turbo C knowledge (last time I used was 20 years ago) to make it work.
    If you put …296, arduino ide can´t compile:
    “error: integer constant is too large for ‘long’ type”

    Best 73 from Niceto

    • “…296 can’t compile…”

      Of course! Good one.

      My next iteration will move the constant calculation outside the main loop. No need to repeatedly recalculate it, but I’ll need to think carefully about where the truncation of significant digits or bits occurs. We don’t want to lose resolution of frequency steps.

      Thank you.

  19. Thanks for the info. I ordered 4 of them – probably won’t get them until after Dayton. Presently using a PICAXE to control an AD9851, but the freq>word conversion is considerably more difficult with a PICAXE.

  20. HI, Congratulation ! This is a very neat job you did. I tried it, and it really work. At the end you could choose what ever frequency you want and that is Great ! Do you have a code where you could input a range of frequency say from 500khz to 30mhz? I would appreciate it very much if you could help me out with this, I’m new to this ad9850 and Arduino. More power and Thank you.

  21. Hi,, I have a problem with Arduino Mega 2560 and AD9850
    Need help..
    What AD9850 source code to produce frequency output 20Mhz with clock in 100Mhz??

  22. Thanks for the review, and the code. I have a question, if you please. I wonder if there is a DC bias in the output waveform. I mean, does the output, referenced to GND, vary from -2.5v and +2.5v, or from 0 and 5v, assuming 5v working voltage? If the latter is true, do you happen to know of a method to remove the bias? (I ask just in case).

  23. Pingback: DDS-генератор на основе микросхемы AD9850 | MyLinks

  24. Ron
    I’ve just published Arduino libraries for controlling AD9850-based modules.

    There’s a general library, which uses soft SPI here:
    https://github.com/m0xpd/DDS

    and a specific library for the Arduino DUE, which achieves faster control of the module using hardware SPI, here:
    https://github.com/m0xpd/DueDDS

    A general description, including details of the simple API and physical hook-up, appears on my blog:
    http://m0xpd.blogspot.co.uk/2014/03/dds-and-duedds-libraries.html

    Thanks for starting all this!
    73
    Paul
    m0xpd

  25. Hello Ron, first many thanks for your article on this eBay AD9850 DDS Module. I have successfully produced SINE WAVES using AD9850 Module.
    How ever I seem to find no documentation that will let me enable SQUARE WAVES. On some other version of similar module, for example,
    http://www.siliconray.com/media/catalog/product/cache/1/image/9df78eab33525d08d6e5fb8d27136e95/a/d/ad9850-2.jpg
    one just enables or disables the plastic link. All help will be greatly appreciated.
    Many thanks

    • Hello Ron & All
      Recently, I also sent question to the eBay Seller who in turn pointed me to technical material which I already have seen before. So lookjing at the schematic and doing countinuity checks on my AD9850 Module,
      (the following schematic might be useful
      http://4.bp.blogspot.com/-eGs5CgvQQvc/T2Qao2YipGI/AAAAAAAAECY/X8xitGVNmk4/s1600/AD9850_51_DDS_02.jpg
      )
      It appears that on our AD9850 Module similar to your GREEN BOARD (mine was sent to me as Blue Board) the square wave should be appearing on the SQUARE WAVE PINS Qout1 and Qout2 whenever the Sine Wave appears on the pins SINA and SINB of the AD9850 Module.
      If so, then in my case the COMPARATOR section is probably to blame.
      I will greatly appreciate if anyone can confirm or otherwise what I think is happening. To make the matters worse, I lost my old Tek2430A scope also. Tried ArduinoScope but is very limiting. I am planing this time buy RIGOL DS2072 Oscilloscope. I done with secondhand scopes. Well this was truly an example of Murphy’s at its best.
      Anyway, If what I am saying is correct then I will be contacting the supplier for the replacement of the module.

      Here are couple of pictures:
      http://imagizer.imageshack.us/v2/640x480q90/841/9iery.jpg
      http://imagizer.imageshack.us/v2/640x480q90/835/5icx.jpg

      Many thanks.
      ragbir

      • Hi Ron & Everyone – UPDATE on my AD9850 DDS Module
        last night it occurred to me that as in your SQUARE WAVE YouTube Video POT R13 adjusts the duty cycle of the SQURE WAVE. Next I thought, what if my unit left the factory not setting R13 Trim Pot properly (50-50 duty-cycle). Continuity check revealed that there cannot be anything wrong with the module. As I stated earlier my old Tek2430A died. Next in an emergency I set up Arduino-Scope. Its really very weak. I loaded 10Hz freq into my AD9850 DDS Module. Next I adjusted R13, voila,I got the SQUARE WAVE adjusted within the limits of Arduino Scope.

        Arduino Scope in Emergency
        http://imagizer.imageshack.us/v2/640…0/845/ihdc.png

        My AD9850 DDS Module
        http://imagizer.imageshack.us/v2/640…/841/9iery.jpg

        Rife LLLT Therapy Healing Unit under construction
        http://imagizer.imageshack.us/v2/640…0/842/z0oy.jpg

        Thanks everyone
        Ragbir

  26. Has anyone tried to use the AD9850 DDS module for GPS tracking , (HAB)?
    I would like to use an Arduino UNO for high altitude HF balloon tracking using RTTY.
    I have it transmitting using this method so far and very impressed with this inexpensive module.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>