(SOLVED) AD9833 DDS board - questions

Hi all,

I just acquired a little breakout board with an AD9833 DDS function generator chip. I wrote my own driver code and it’s working fine (sort of).

My question is, the datasheet shows two frequency generator registers (counters) and two phase registers (FREQ 0 and 1, PHASE 0 and 1)

However, it seems like only the zero side is working. I can program an output frequency and waveshape for channel 0, but sending data to channel 1 results in nothing.

Maybe I just don’t UNDERSTAND how the chip is supposed to work. I thought maybe I could program both channels and have their outputs appear “mixed together” or maybe I could set one or the other to different frequencies and toggle between them.

The block diagram for the IC (attached - click for full size) simply shows the two frequency and two phase registers going into a “MUX” (multiplexer I assume), Then the outputs from the frequency and phase registers and summed together (or so I assume since they go to a “sigma” block), then to a SINE rom, another MUX and finally the DAC.

I guess what I need to know is EXACTLY what is this chip supposed to do? Why does it have two separate “channels” and how are they used (independently, but one at a time? summed together? What?).

Thanks for any help!

ad9833.jpg

Page 17 of the datasheet says:

In an FSK application, the two frequency registers of the AD9833
are loaded with different values. One frequency represents the
space frequency, while the other represents the mark frequency.
Using the FSELECT bit in the control register of the AD9833, the
user can modulate the carrier frequency between the two values.

and

The input to the phase accumulator can be selected from either
the FREQ0 register or the FREQ1 register and is controlled by
the FSELECT bit.

Note that it says either ... or.

You can load two separate frequencies (e.g. 1200Hz and 2200Hz) into the registers and then use the FSELECT bit to switch between them (if that's what you need to do). If there was only one frequency register, you'd have to keep loading a new frequency each time the FSK symbol changed. With two registers, you only need to toggle the FSELECT bit.

Pete

el_supremo:
Page 17 of the datasheet says:
and

Note that it says either ... or.

You can load two separate frequencies (e.g. 1200Hz and 2200Hz) into the registers and then use the FSELECT bit to switch between them (if that's what you need to do). If there was only one frequency register, you'd have to keep loading a new frequency each time the FSK symbol changed. With two registers, you only need to toggle the FSELECT bit.

Pete

Holy smokes! After I read this, I looked back at my code and saw that I program the frequency and phase registers, but I never SELECTED which one to use, so it stayed with the reset default ("channel" 0).

Although your reply didn't tell me anything that I didn't already know, it DID make me specifically look at the FSELECT (and PSELECT) bits and it was then that I realized I never set them.

So, in the end you fixed my problem! Thanks a bunch, and a Karma++ for you!

You're welcome and thanks for the Karma++ :slight_smile:

Pete

el_supremo:
You're welcome and thanks for the Karma++ :slight_smile:

Pete

Hey, let me bug you once more...... I've got the frequency and phase thing working fine (THANKS!).

But, now I ran into another problem that's driving me nuts.

Now that I've got the basics working, I next went to implement the "sine / triangle" selection.

Backing up a bit... I made a test loop that did this:

set_freq (60, 0); // set channel 0 to 60 hz.
set_freq (50, 1); // set channel 1 to 50 hz.
while (1) {
    select_freq (0);
    _delay_ms (1000);
    select_freq (1);
    _delay_ms (1000);
}

So far, so good. The audio on my amplifier/speaker/monitor thing goes "HMMM...hmmm...HMMM...hmmm" and the scope shows alternating 60/50/60/50....

Then I added the waveform select code and put it in the same kind of loop. It also works... on channel 0 I get alternating 60 hz sine and triangle waveforms.

Last thing I did was to do both... select 0/set sine ... select 1/set triangle.

The dang thing alternates between a 60 and 50 hz sine wave... no triangle to be seen!

If I comment out the "channel selects", then I get channel 0 alternating sine and triangle.

But, I can't get BOTH to work at once!

Now, this may be a clue (actual code which selects the frequency register):

double AD9833::select_freq_reg (uint8_t reg)
{
    _dds_write (reg ? FSEL : 0); // select freq 0 or 1
    // return ACTUAL frequency
    return (double)(_freq_reg[reg ? 1 : 0] / CFREQ);
}

Actual code that selects sine or triangle:

void AD9833::set_waveform (uint8_t wave)
{
    _dds_write (wave ? MODE : 0); // select triangle or sine
}

As I said, calling these individually does not work. However, when I tested this:

double AD9833::select_freq_reg (uint8_t reg)
{
    _dds_write (reg ? (FSEL | MODE) : 0); // experiment both freq and waveform at once
    // return ACTUAL frequency
    return (double)(_freq_reg[reg ? 1 : 0] / CFREQ);
}

THIS worked (that is, I got alternating 60 hz sine and 50 hz triangle).

Any ideas? I can post more code or attach the whole thing if you want.

Thanks again in advance!

Just a guess but I think that when you write the two bits separately, select_freq_reg sets/clears the FSEL bit. Then calling set_waveform sets/clears the MODE bit but it will implicitly clear the FSEL bit also so that you would always get frequency zero with either a sine or triangle wave. To do this correctly, you should probably have a function to set the control register. The function's input argument would be any combination of the control bits. E.G.

void AD9833::set_ctlreg_mode (uint16_t mbits)
{
    _dds_write (mbits);
}

and call it like this (with appropriate definitions of the bits)

 set_ctlreg_mode(FSEL|MODE);

This would select frequency 1 and triangle.
Hmmm. I notice that the argument to your functions which set control bits is only uint8_t. The control register is 16 bits and FSELECT and PSELECT are in the high order byte while the Mode bit is in the low order byte. Are you writing these bits correctly into the 16-bit control word?

If that's not it, you'll have to post all your code.

Pete

el_supremo:
Just a guess but I think that when you write the two bits separately, select_freq_reg sets/clears the FSEL bit. Then calling set_waveform sets/clears the MODE bit but it will implicitly clear the FSEL bit also so that you would always get frequency zero with either a sine or triangle wave. To do this correctly, you should probably have a function to set the control register.

Thank you again for your reply and help.

Last night when I posted the second question, I had been at it all day and was bleary-eyed.

This morning the solution popped into my mind and it's so obvious I could kick myself.

First of all, you mentioned my use of uint8_t. That is correct. Those are actually used as boolean (0 or 1).

Anyway, I saw clearly that if I set one thing (like for example selecting frequency register 1), the bit pattern ONLY selected frequency register 1 and wiped out anything previous (such as a waveform select bit).

The solution is simple: I have a shadow "register" (a single uint16_t variable) that saves the state of the control register. So, to select frequency register 0 or 1, I AND off or OR on the appropriate bit in the "shadow" and then write it to the DDS.

So, anything PREVIOUSLY set gets re-written to the DDS and selections now "stick".

Here's a piece of the code to explain what I'm doing (although I know you already "get it") :slight_smile:

void AD9833::reset (void)
{
    // assert reset & 28 bit mode
    _ctrl_reg = (B28 | RESET);
    _dds_write (_ctrl_reg);
    // de-assert reset, leave 28 bit mode
    _ctrl_reg = B28;
    _dds_write (_ctrl_reg);
}

void AD9833::set_fclock (uint8_t on)
{
    // set sleep1 bit on or off (stops or starts FCLK)
    on ? _ctrl_reg &= ~SLEEP1 : _ctrl_reg |= SLEEP1;
    _dds_write (_ctrl_reg);
}

void AD9833::set_waveform (uint8_t mode)
{
    // set sine or triangle waveform
    mode ? _ctrl_reg |= MODE : _ctrl_reg &= ~MODE;
    _dds_write (_ctrl_reg);
}

This is even useful in places that I didn't suspect. For example, setting the value of a phase register seems to be considered by the chip as a single 16 bit write (12 actually) whereas setting a frequency register is a full 32 bit (28 actually) write.

Because writing to a phase register is only a single 16 bit write, it clears the "B28" (28 bits dual write select) bit, causing future 32 bit writes (such as a frequency register write) to fail.

By doing a....

_dds_write (_ctrl_reg);

...immediately after setting a phase register, it re-establishes the proper setting of all control bits. I even do it after a 32 bit frequency register update "just for good luck" even though it's not necessary.

This insures that the control register is always "current" (i.e. up to date).

It took me quite a while to finally figure out and get the feel for the AD983x chips. Their data sheets leave a lot to be desired. They seem to be written by someone who knows the chip inside and out and as a result they leave out little (but important) bits of information.

Anyway, thanks again for all your help! I'd still be beating my head against the wall without your input.