Programming the AD9834 DDS chip

This is a software project for building a VFO with the 75MHz clocked AD9834 synthesizer chip by Analog Devices. Due to the Nyquyst theoreme with a maximum clock rate of 75 MHz a frequency of 37.5 MHz can be achieved. When overclocking the chip to 100 MHz (which has been succesfully tested in many cases) the maximum output frequency theoretically rises up to 50 MHz. But when coming close to these boundaries signal quality deteriorates severely. So it is recommended not to produce higher output frequencies than 25 MHz (75 MHz clock) or respectively 33 MHz (100MHz clock).

Theoretical outline

The AD9834 is a low power (20mW power consumption when VDD=3.3V) DDS module. It can handle up to 5.5 V as VDD (2.3V min.), so 5V single supply use makes circuitry simple. It comes in a 20 lead TSSOP case, breakout boards are available.

SPI signal structure

Programming a desired frequency into the DDS chip is performed by a 3 line communication, a serial peripheral interface (SPI). These three lines are called

  • SCLK (the clock signal)
  • FSYNC (the signal that determines the end of the transfer of a single word (16 bits)
  • SDATA (the frequency or control information packed in 16 bit word)

The timing diagram found in AD’s datasheet gives the precise structure of the signal communication:

AD9834 DDS SPI Timing diagram
AD9834 DDS SPI Timing diagram

FSYNC is  high when the first word (16 bits) is going to be transferred. SCLK is also high in this moment. Then FSYNC is set low, 16 bits subsequently are transmitted via the SDATA line. After one bit has been transmitted, SCLK is set low for 10 ns minimum and then goes high again for the next bit. Transfer starts with MSB (D15).

After 16 bits have been transmitted, FSYNC is set high again, showing the DDS chip that the word has been completely transferred. As soon a FSYNC is low again the DDS is ready for the transmission of the next 16 bits.

The DDS chip “language”

Frequency set

The chip has two frequency registers (FREQ0 and FREQ1). These registers contain 28 bits of frequency information. They can be addressed individually and are divided into two 14-bit sections each (MSB and LSB). In addition it is possible to load the LSB independently from the MSB if only a minor frequency change is required.

Frequency registers are selected by the first two MS-bits (DB15 and DB14) of a 16 bit structure sent to the DDS. “01” determines a frequency load for FREQ0, “10” loads the FREQ1 register.

Control transmission

Besides the frequency information some controls must be sent to the DDS chip. A control is also 16 bits wide. A control is initiated by a “00” starting sequence for DB15 and DB14.

Summary

So we can distinguish the purpose of a word by its first two bits:

  • “01” + 14 following bits loads the FREQ0 register,
  • “10” + 14 following bits loads the FREQ1 register,
  • “00” + 14 following bits transfers a control word.

One important control bit under the looking glass

There are many features to control the AD9834 chip. We want to limit this to the absolute basics. The most important bit is DB13. If you set this to “1” the chip is informed that the frequency information for the register to be loaded next will come in two consecutive 16 bit words addressing the respective frequency register with the 14 + 14 bits of frequency information.

So the first step is to transfer the “00” signaling a control code and next the “1” signaling that the user wants to write two 16 bit words for changing the frequency. The rest of the 16 bits of this control can be left “0”. This results in

“0010000000000000” (0x2000)

is the first word to be transmitted.

Frequency calculation and transfer

The frequency data of the waveform the user wants the chip to put out is determined by 28 bits, a so called “frequency word”. The formula is

frequency word = 2^28 / fclk * f

  • frequency word: a floating point number that will later be converted into a long integer containing the frequency information for the chip,
  • fclk: The master clock rate of the clock oscillator connected to the DDS [Hz],
  • f: The frequency the user wants to be generated [Hz].

Example

With a clock rate of 75 MHz a user frequency of 1 MHz would be calculated as a frequency word of

268435456 / 75000000 * 1000000 = 3579139,413333333

By leaving only the integer part of the number we get 3579139 which now is the frequency word that must be transferred to the chip.

Converted to binary this number figures out as

00001101101001110100000011

This is now split into two parts, 14 bits each:

0000110110100 1110100000011

Now we must tell the DDS in which of the two frequency registers we want to store this. Therefore we add the 2-digit-code for the desired frequency register in front of the respective number. In this example the destination is FREQ0, so we add “01”. The result are two words of 16 bits each:

010000110110100 011110100000011

Together with the control that allows us to write the 2 words consecutive into the chip we get a complete sequence of

0010000000000000 011110100000011 010000110110100

because the correct order is CONTROL first, then LSB, and MSB last. In HEX this is 0x2000, 0x3D03, 0x21B4.

Coding

Code examples in C for the AVR family follow. First the declarations, then some defines in advance so that you can adapt the code easily to your layout:

//Declarations SPI for DDS
void spi_start(void);
void spi_send_bit(int);
void spi_stop(void);
void set_frequency2(unsigned long);
// Defines SPI DDS (AD9834)
#define DDS_PORT PORTC 
#define DDS_FSYNC 1   //PC0
#define DDS_SDATA 2   //PC1 
#define DDS_SCLK 4    //PC2
#define DDS2_RESETPIN 3  //PC3

Before a transfer starts we need to send a “start” command to the SPI to set SCLK and FSYNC adequately. This is coded as:

void spi_start(void)
{
    DDS_PORT |= DDS_SCLK;     //SCLK hi
    DDS_PORT &= ~(DDS_FSYNC); //FSYNC lo
}

After a word has been transmitted this mus be shown with the “stop” command to inform the chip that 16 bits have been sent. So we set FSYNC to high:

void spi_stop(void)
{
    DDS_PORT |= DDS_FSYNC; //FSYNC hi
}

With these two functions we can initiate and terminate the transfer of 16 bits of data to the chip.

Next we must learn how to transfer data. This will be done by sending just one bit to the DDS and afterwards switching the clock accurately:

void spi_send_bit(int sbit)
{
    if(sbit)
    {
        DDS_PORT |= DDS_SDATA; //SDATA hi
    }
    else
    {
        DDS_PORT &= ~(DDS_SDATA); //SDATA lo
    }
    DDS_PORT |= DDS_SCLK; //SCLK hi
    DDS_PORT &= ~(DDS_SCLK); //SCLK lo
}

And now for computing and sending the frequency word:

void set_frequency2(unsigned long f)
{
    double fword0;
    long fword1, x;
    int l[] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    int m[] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, t1;

    fword0 = (double) 3.579139413 * f; // 3.579139413 = 268435456 / 75000000
    fword1 = (long) fword0;

    //Transfer frequency word to byte array
    x = (1 << 13); //2^13
    for(t1 = 2; t1 < 16; t1++)
    {
        if(fword1 & x)
        {
            l[t1] = 1;
        }
        x >>= 1;
    }

    x = (1L << 27); //2^27
    for(t1 = 2; t1 < 16; t1++)
    {
        if(fword1 & x)
        {
            m[t1] = 1;
        }
        x >>= 1;
    }

    //Transfer to DDS
    //Send start command
    spi_start();
    for(t1 = 15; t1 >= 0; t1--)
    {
        spi_send_bit(0x2000 & (1 << t1));
    }
    spi_stop();

    //Transfer frequency word 
    //L-WORD
    spi_start();
    for(t1 = 0; t1 < 16; t1++)
    {
        spi_send_bit(l[t1]);
    }
    spi_stop();

    //M-WORD
    spi_start();
    for(t1 = 0; t1 < 16; t1++)
    {
        spi_send_bit(m[t1]);
    }
    spi_stop();
}

I use a set of 2 arrays as predefined words including the start sequence for FREQ0 and then writing each single bit into the respective array.

To start the DDS correctly a short reset sequence should be placed in your main()-function:

//Reset DDS (AD9834) 
_delay_ms(10); 
DDS_PORT |= (1 << DDS_RESETPIN); //Bit hi
_delay_ms(10); 
DDS_PORT &= ~(1 << DDS_RESETPIN); //Bit lo
_delay_ms(10);

Alternative: Tie the RESET-Pin of the AD9834 permanently to GND. Notable that also a software rest is possible, but I prefer the hardware method.

By set_frequency(Value) you can start using this DDS.

73 de Peter (DK7IH)

Leave a Reply

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