Signal Generator AD9833

Introduction: Signal Generator AD9833

A signal generator is a very useful piece of test gear. This one uses an AD9833 module and an Arduino Nano - that's all, not even a PCB. You can optionally add an OLED display. The AD9833 can gererate sine, triangle and square waves from 0.1 Hz to 12.5 MHz - the software in this project is limited to 1Hz to 100kHz.

There have been other Instructables using an Arduino and an AD9833, here and here. This is simpler and can be used as a sweep generator. Sweep generators help test the frequency response of filters, amplifiers and so on. Unlike the other Instructables designs, this does not include an amplifier or amplitude control but you could add them if you wanted.

Step 1: Simplest Signal Generator

For the simplest Signal Generator, you just solder the AD9833 module onto the back of the Arduino Nano. No PCB is needed.

The AD9833 module I chose is similar to this one. I'm not saying that's the best or cheapest supplier but you should buy one that looks like that photo (or the photo above).

The connections between the modules are:

  • grounds connected together
  • D2 = FSync
  • D3 = Clk
  • D4 = Data
  • D6 = Vcc of AD9833

The AD9833 is powered from data pin D6 of the Arduino - the Arduino can supply sufficient current. I've added a 100n decoupling capacitor because I thought I "ought" to but I couldn't see any difference - there is already a decoupling capacitor on the AD9833 module board.

If you were being fancy, you might worry about "analogue ground" vs "digital ground" but if you were being fancy, you'd be spending more than £4.

The simplest Signal Generator is controlled and powered over a USB lead from a PC. The USB emulates a serial port running at 115200bps (8-bits, no parity). The commands are:

  • '0'..'9': shift digit into "min" frequency array
  • 'S': set AD9833 frequency and produce sine wave
  • 'T': set frequency and produce triangle wave
  • 'Q': set frequency and produce square wave
  • 'R': reset the AD9833
  • 'M': copy "min" frequency array into "max" array
  • 'G': sweep from "min" to "max" over 1 second
  • 'H': sweep from "min" to "max" over 5 seconds
  • 'I': sweep from "min" to "max" over 20 seconds

The Arduino program contains two 6-character arrays "min" and "max. If you transmit a digit then it is shifted into the "min" array. If you send an 'S' then the "min" array characters are converted into a longint frequency and sent to the AD9833. So sending the string

002500S

will set the AD9833 output to a 2500Hz sine wave. You must always send all 6 digits. The minimum frequency is 000001 and the maximum frequency is 999999.

If you send an 'M' then the "min" array is copied into the "max" array. If you send an 'H' then the AD9833 repeatedly outputs a gradually increasing frequency over 5 seconds. It starts at "min" frequency and 5 seconds later is at "max" frequency. So

020000M000100SH

sweeps from 100Hz to 20kHz. The frequency change is logarithmic so after 1 second the frequency will be 288Hz, after 2 seconds 833Hz then 2402, 6931 and 20000. The frequency is changed every milliSecond.

The loop stops when the Arduino receives another character so be careful not to send the command followed by carriage-return or line-feed. That extra character would terminate the loop. If you're using the Serial Monitor, there's a box at the bottom right that might say for instance "Both NL & CR" which (I think) sends characters after your command. Set it to "No line ending".

You can download the Windows EXE program below which will send the required commands or you could write your own. The Arduino INO file is also here.

Step 2: Add an OLED

If you add an OLED and two buttons, the signal generator can work alone without a PC.

Those of you who have read my oscilloscope Instructable will recognise the similarity. The AD9833 module can be added to my oscilloscope to produce an "Oscilloscope and Signal Generator in a Matchbox".

The display is a 1.3" OLED running at 3.3V which is controlled by an SH1106 chip via an I2C bus.

Search eBay for 1.3" OLED. I don't want to recommend a particular seller as links quickly go out of date. Choose one that looks like that photo, says "I2C" or "IIC" and has four pins labelled VDD GND SCL SDA. (Some displays seem to have the pins in a different order. Check them. The proper name for the clock of I2C is "SCL" but on eBay the boards can be labelled "SCK" like my one in the photo.)

A fuller description of the OLED library is in my oscilloscope Instructable in Step 8. You should download and install the driver library SimpleSH1106.zip which is in Step 8. (I don't want to upload another copy here and have to maintain two copies.)

The INO file can be downloaded below. The pin numbers used for the OLED are declared around line 70. If you have built my "Oscilloscope and Signal Generator in a Matchbox" and want to test this INO file with it, alternative pin numbers are enabled via a #define.

I've shown a stripboard layout for the circuit. There are two stripboards - one for the Nano and the AD9833 and one for the display. They should form a sandwich. The boards are shown from the component side. Fine flexible wires join the two boards. Attach the boards together with soldered stand-offs. In my diagram, the copper of the stripboard is shown in cyan. Red lines are wire links on the stripboard or flexible wires joining the boards together. I haven't shown the power and "signal" leads.

The AD9833 module is soldered on the copper side of the stripboard - on the opposite side from the Nano. Solder pins onto the copper strips then fit the AD9833 onto them and solder it on.

The display shows either a single frequency or the "min" and "max" frequencies.

There are two pushbuttons: a "Horizontal" button to select a digit of the frequencies and a "Vertical" button to change that digit.

I power the signal generator from the circuit I'm developing - I always have 5V available at my workstation.

Step 3: Future Developments

Could it be battery powered? Yes, just add a 9V PP3 connected to the RAW pin of the Nano. It typically uses 20mA.

Could it be powered by a single lithium cell? I don't see why not. You should connect the OLED Vdd and its pull-up resistor to the 3.7V battery (I doubt if the 3.3V output of the Arduino would work properly).

A sweep generator is more useful when testing the frequency response of a filter if you can graph amplitude vs frequency. Measuring the amplitude of a signal is tricky - you have to trade off the decay of your envelope detector vs ripple for low frequencies and response time for high frequencies. Having built your amplitude detector, you could feed its output into the ADC of the Arduino of the "Simplest Signal Generator" then send the result, along with the current frequency to the PC.

This page is a useful starting point or search Google for "envelope detector" or "peak detector". In the suggested circuit above, you would set the signal frequency, wait for it to stabilise, set the Arduino A0 pin to output digital low, wait to discharge C, set A0 to input, wait, then measure with the ADC. Let me know how you get on.

2 People Made This Project!

Recommendations

  • Toys & Games Contest

    Toys & Games Contest
  • Big vs Small Challenge

    Big vs Small Challenge
  • Fix It Challenge

    Fix It Challenge

34 Comments

0
spacetribe
spacetribe

6 months ago

Hi Peter, and thanks for this.

Sorry if this is a silly question. In the datasheet I fail to see a clear mention of the feature I'm looking for, which is that the frequency register is maintained without power. I mean, it comes with some pre-set frequency so there must be some way to do this .Yet all the tutorials I see put an emphasis on RESET as an essential procedure. What if I just need a specific sine frequency, always, and want the SPI programming inputs disconnected after programming?

Worst case scenario, if a power-up reset-and-programming step is always required, what would be the simplest preliminary-stage component driving such a system? Arduino nano? Or is AD9833 simply not the way to go and some other single component, such as AD9850, is required?

Cheers

0
Peter Balch
Peter Balch

Reply 6 months ago

That's a good question - but I don't know the answer.

The AD9833 datasheet says "When the AD9833 is powered up, the part should be reset" which implies that it doesn't remember its settings.

The AD9850 doc talks about "This shutdown mode prevents excessive current leakage in the dynamic registers of the device" which implies that its registers are dynamic - they don't remember their settings.

Both chips have a shutdown/sleep mode. Could you make the chip sleep when it's not in use - so it would remember its settings when it wakes up? I think the answer is no. The AD9833 takes 0.5mS when in sleep mode - which is huge. I can't see where the AD9850 doc specifies the shutdown current.

> what would be the simplest preliminary-stage component driving such a system?

I think it would have to be a processor of some sort. If it were me, I'd probably use a PIC12Fxxx chip which is about £0.50.

Are you making a product? How big is the production run? It's always a pain having to program chips but you can buy PICs pre-programmed.

Or is this a one-off personal project? In which case, to save time, go with the chip you already know.

> Or is AD9833 simply not the way to go and some other single component, such as AD9850, is required?

What is it used for? What frequency? How accurate must the frequency be? Is it a sine wave or square? How pure a sine wave? There are lots of other ways of generating signals.

Peter

0
spacetribe
spacetribe

Reply 6 months ago

Thanks for the input. It's appreciated. I would love for it to be around the upper limit of what the AD9833 can put out and still get a relatively clean sine. It's for R&D but will be implemented for a product at some point so it would be nice keep the architecture stable. I will look into the chip you mentioned. I'm looking for an easy way to get a stable sine at a set frequency in the 10-20MHz range. It's actually a requirement that is simple enough to have it met as a single component but maybe I'm missing something. I see the Pic12F683 does up to 8MHz. I'll have a look around the Microchip website then.

0
schopra1980
schopra1980

9 months ago

Hello dear,
Thank you for ths simple and effective project. I have a specific requirement. Can you help me?
I want to make a 1-10MHz sweep generator, but I want have a KNOB to vary the frequency, and manual options to select waveform (square or sine).
Can you please guide me here?
Once I find a resonant point say at 7MHz, I want to run it at ONLY this resonant frequency.

0
Peter Balch
Peter Balch

Reply 9 months ago

The easiest way is to have a rotary encoder. If you seach Instructables for "Rotary Encoder Arduino" you find several of projects which will teach you what to do:

https://www.instructables.com/circuits/howto/Rotary+Encoder+Arduino/

So you'll replace the pushbuttons of my design with rotary encoder inputs.

If you look at my INO file, you will see the pins for the buttons defined as BtnHorz and BtnVert. Serach the whole of the INO for BtnHorz and BtnVert.

You'll see that when they're pressed, the functions incSelSG() and incAdjSG() are called. You should call incSelSG() and incAdjSG when the rotary encoders turn.

But you want to increment or decrement depending on which way the encoder is turning. You'll see that I always do the same thing using ++ or --. For instance:

freqSGLo[SelSG]++;
SelSG--;

You should increment or decrement depending on which encoder turns.

> Once I find a resonant point say at 7MHz, I want to run it at ONLY this resonant frequency.

You could store the value in EEPROM so that next time the circuit is switched on, it goes to that frequency. Seach Instructables for "arduino eeprom".

0
ian111carruthers
ian111carruthers

Question 10 months ago on Introduction

Hello, I need to produce a frequency sweep from 1Hz to 80Khz at a rate of 8 times per second, could I use an external programmable clock such as the Si51351 to achieve this?
Kind Regards

0
Peter Balch
Peter Balch

Answer 10 months ago

Do you mean the Si5351? Why is that better than just using the internal timing of the Arduino? What is it you're wanting to achieve?

My Arduino software changes the frequency every millisecond. So 1/8 sec is 125 steps. The sweep is logarithmic so the frequency will increase by about 3.6% every step. Is that resolution good enough? Do you need the sweep continuous rather than discrete?

0
ian111carruthers
ian111carruthers

Reply 9 months ago

Hello Peter, Yes, I am requiring the sweep to be continuous, the reason I am interested in using the Si5351 for clock purposes is to enable a high resolution when employing a sweep between 1Hz and 80Khz, would it be possible to adapt the software to run on Android and use the display on the Android device (Tablet)?

0
Peter Balch
Peter Balch

Reply 9 months ago

The AD9833 cannot do a continuous sweep. The frequency will always increment in steps. You need an analogue circuit if you want a true continuous sweep.

I chose 1mS for the step period. I guess it could be shorter but I haven't tested how much shorter.

Yes you could write an Android app to talk to the AD9833 but you'd need hardware, such as an Arduino, to translate the USB from the phone into SPI for the AD9833.

I don't write Android apps but there are lots of web pages explaining how to use an Android device to control an Arduino.

Peter

0
VemulapallyP
VemulapallyP

10 months ago

Hi Peter,
i have used your code for this setup and found the result you have mentioned. Although i tried changing your code to include 1 digit in the frequency so as to increase the frequency from a max of 999999 to 9999999. I also changed the freqSGLo and freqSGHi arrays accordingly. i am unable to generate more than 999999 hz frequency. can you suggest a way to increase the frequency using your code.

0
Peter Balch
Peter Balch

Reply 10 months ago

Did you also change the value of numberOfDigits?

What hex values are being sent to the chip?

0
VemulapallyP
VemulapallyP

Reply 10 months ago

const byte numberOfDigits =7; // number of digits in the frequense
byte freqSGLo[numberOfDigits] = {0, 0, 0, 1, 0, 0, 0}; // 1000Hz
byte freqSGHi[numberOfDigits] = {0, 0, 0, 0, 2, 0, 0}; // 20kHz

i didn ot change the hex values to the register.

0
Peter Balch
Peter Balch

Reply 10 months ago

> i did not change the hex values to the register.

So what values are being sent? Use a Serial.print statement to see what you're telling the chip to do. Does that match what the chip is is expecting?

0
VemulapallyP
VemulapallyP

Reply 10 months ago

perfect.
i modified the hex values now i am able to generate up to 4 MHZ waves. thankyou

0
emil.cristofor
emil.cristofor

1 year ago

Hi Peter,
I find your " Step1: Simplest Signal Generator" a very nice project to understand the functioning of AD9833! Everything OK but, I have a problem with SWEEP function, it doesn´t works on my hardware.
My hardware configuration is almost identical with the one in your exemple: Arduino UNO R3 together with one module AD9833 identical with the one in your picture and the same connections.
Have you an idea way the comand 002000M000100SH ( and also other similar combinations) didn´t works ?
Thanks for your answer!
Sincerely yours, Emil

0
Peter Balch
Peter Balch

Reply 1 year ago

So it looks like the loop inside the Sweep function only executes once.

The loop is
do {
...
} while (!Serial.available());

The loop stops when the Arduino receives a character. Could you be sending

002000M000200SI

followed by carriage-return or line-feed? And that extra character is terminating the loop?

The Serial Monitor has a box at the bottom right tha might say for instance "Both NL & CR" which I think might send characters after your command. Try setting it to "No line ending".

0
Peter Balch
Peter Balch

Reply 1 year ago

Good. I'll update the Instructable to warn about the problem.

Thanks.

0
emil.cristofor
emil.cristofor

Reply 1 year ago

Thank you Peter! You´ve found the solution!
That was the problem: the editing of a new line.
I have blocked the NL in Seriall Monitor and now SWEEP works wonderfull.
Thanks a lot again! Sincerelly yours, Emil

0
emil.cristofor
emil.cristofor

Reply 1 year ago

Thanks for your answer.
After your addendum I received the following messages on Serial monitor:
Command:
002000M000200SI
Monitor replay:
fmin200
fmax2000
f200
Sorry, but no sweep !
Emil

0
Peter Balch
Peter Balch

Reply 1 year ago

I don't know why it's going wrong just with that command.

You could put some debugging statements into the sweep function. See below. Then use the Arduino Tools SerialMonitor to see what Sweep thinks it's doing.


void Sweep(int n) {
int fmin,fmax;
fmin = calcFreq(freqSGLo);
fmax = calcFreq(freqSGHi);
Serial.print("fmin ");Serial.println(calcFreq(freqSGLo));
Serial.print("fmax ");Serial.println(calcFreq(freqSGHi));
int i=0;
do {
long f = exp((log(fmax) - log(fmin))*i/(n-1) + log(fmin)) +0.5;
Serial.print("f ");Serial.println(f);
SG_freqSet(f, waveType);
delay(1);
i++;
if (i >= n) i = 0;
} while (!Serial.available());

SG_freqSet(calcFreq(freqSGLo), waveType);
}