Cheap and dirty measurement of filter response curves

When cleaning up my shack I found one of those very cheap China made DDS modules containing an AD9850 synthesizer. “Let’s do something with it!” was the decision. I connected an ATMega328 to it and wrote a very short piece of software to sweep the frequency between two edging values. This signal is sent thru a filter to get the respective response curve without taking great effort:

Measuring filter response curves with microcontroller and China made DDS modul (AD9850)
Measuring filter response curves with microcontroller and China made DDS modul (AD9850)

I connected the output of the DDS to a ladder filter I wanted to test. On my spectrum analyzer (oldie but goldie HP8558B) I got the expected outcome. But then I started thinking of those amateurs who are not the proud owners of such an instrument. So I connected the filter output to my RIGOL DS1054Z digitial scope. After some tries I was able to synchronize the DDS’ sweep time with the horizontal sweep of the scope an got the filter curve on the screen:


When you put horizontal sweep rate to a very low value you will finaly see the filter response curve. This is not very exact due to the lack of frequency readout on the scope’s x axis but it is OK if you want to check the flatness of a filter. And it is quickly done.

But due to my fascination for exact things I will trigger the scope by the microcontroller so that I can deduce a unit of khz per cm on my scope screen.

73 and thanks for watching my blog!


The software:

 /* Frequency sweeper with ATMega8 + AD9850 */
 /* ************************************************************ */
 /* Mikrocontroller: ATMEL AVR ATmega328p, 8 MHz */
 /* */
 /* Compiler: GCC (GNU AVR C-Compiler) */
 /* Autor: Peter Rachow */
 /* Letzte Aenderung: 16.11.2016 */

#include <inttypes.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <math.h> 
#include <avr/io.h> 
#include <avr/interrupt.h> 
#include <avr/sleep.h> 
#include <avr/eeprom.h> 
#include <util/delay.h>

#define F_CPU 8000000

int main(void);

 // SPI
 //Port usage
 //FQ_UD: PB0 (1) blue
 //SDATA: PB1 (2) green
 //W_CLK: PB2 (4) white
 //RESET AD9850 PD7 yellow

void set_frequency_ad9850(unsigned long);

 // SPI

//Set AD9850 to desired frequency
 void set_frequency_ad9850(unsigned long fx)
 unsigned long clk = 125000000;
 unsigned long x = 1;
 int t1;

double fword0;
 unsigned long fword1;

fword0 = (double) fx / clk * 4294967296;
 fword1 = (unsigned long) fword0;

//Send 32 frequency bits + 8 additional bits to DDS
 //Start sequence
 PORTB &= ~(1); //FQ_UD lo => Bit PD0 = 0

for(t1 = 0; t1 < 32; t1++)
 if(fword1 & x << t1)
 PORTB |= 2; //SDATA Bit PB1 setzen
 PORTB &= ~(2); //SDATA Bit PB1 löschen

//W_CLK hi
 PORTB |= 4; //Bit PB2 setzen

//W_CLK lo
 PORTB &= ~(4); //Bit PB2 löschen

//W32...W39 all bits are 0!
 PORTB &= ~(2); //SDATA Bit PB1 löschen
 for(t1 = 0; t1 Bit PD0 = 1


int main()

unsigned long fx0 = 10000000;
 int swing_f = 10000;
 int delta_f = 5;
 unsigned long fx1 = fx0 - swing_f;
 unsigned long fx2 = fx0 + swing_f;
 unsigned long f;

DDRB = 0xFF; //SPI (PB0..PB2)
 DDRD = 128;

PORTD |= 128; //Bit PD7 set
 _delay_ms(1000); //wait for > 20ns i. e. 1ms minimum time with _delay_s()
 PORTD &= ~(128); //Bit PD7 erase

 for(f = fx1; f 20ns i. e. 1ms minimum time with _delay_s()
 PORTD &= ~(128); //Bit PD7 erase


return 0;

2 thoughts on “Cheap and dirty measurement of filter response curves”

  1. Hello Peter,
    That`s a very interesting experiment. I`m trying to use a similar Chinese DDS module as a VFO for a 20 meters, QRP SSB transceiver (OzQRP`s MST-3 radio kit, actually). I’ll have to adjust the DDS output level to match the SA612 mixer clock input voltage level and I was wondering what was the filter response. It seems to peak between two frequency values on the scope photo. Can you tell at which frequency?

    73 de Yves,

    1. Hi Yves,

      the filter was designed, iirc, for 9830 kHz. But I did such a lot of tests with all the bunches of crystals I have on stock here. They are in the range of 4096 kHz to 15360 kHz (about 10 different values). But 9830 kHz should have been the test from, the photo. But with all experiments there were no preselection measures taken (like selecting the crystals for nearest match to a given frequency by checking them within an oscillator) before. It was just a random choice.

      73 de Peter

      73 de Peter

Leave a Reply

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