How much can 328p handle?

Oh yeah - way faster
add this
#include <SPI.h>

Get rid of all these
 #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).

use this:
// pin 13 to clock
// pin 12 to devices data out pin if you will read anything back
// pin 11 to devices data in pin that you are sending data to
 #define FQ_UD 10       // Pin 10 - connect to freq update pin (FQ)
 #define RESET 9      // Pin 9 - connect to reset pin (RST).

Put your 5 bytes in an array, and add this in place of the bit banging you have now"

PORTB = PORTB & B11111011; // clear pin 10
SPI.transfer(dataArray[0]);
SPI.transfer(dataArray[1]);
SPI.transfer(dataArray[2]);
SPI.transfer(dataArray[3]);
SPI.transfer(dataArray[4]);
PORTB = PORTB | B00000100; // set pin 10

Well, it compiles, but it seems to hang somewhere ?

I was also wondering if the array is needed, instead of dataArray = (freq & 0xFF);, just sending the data byte in the for loop?
```
**#include <SPI.h>
#define W_CLK 13    // Pin 13 - connect to AD9850 module word load clock pin (CLK)
#define DATA 11      // Pin 11 - connect to serial data load pin (DATA)
#define FQ_UD 10    // Pin 10 - connect to freq update pin (FQ)
#define RESET 9      // Pin 9 - connect to reset pin (RST)
#define pulseHigh(pin) {digitalWrite(pin, HIGH); digitalWrite(pin, LOW); }

// frequency calc from datasheet page 8 = * /2^32
void sendFrequency(double frequency) {
  byte dataArray[5];
  int32_t freq = frequency * 4294967295/125000000;  // note 125 MHz clock on 9850
  for (int b=0; b<4; b++, freq>>=8) {
    dataArray[b] = (freq & 0xFF);
  }
  dataArray[4] = 0x000;  // Final control byte, all 0 for 9850 chip
  PORTB = PORTB & B11111011; // clear pin 10
  SPI.transfer(dataArray[0]);
  SPI.transfer(dataArray[1]);
  SPI.transfer(dataArray[2]);
  SPI.transfer(dataArray[3]);
  SPI.transfer(dataArray[4]);
  PORTB = PORTB | B00000100; // set pin 10
  pulseHigh(FQ_UD);  // Done!  Should see output
}

void setup() {
  Serial.begin(115200);
  Serial.println("DDS Sweep");
  // 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() {
  long F1 = 1;
  long F2 = 10;
  long long freq = 1;
  Serial.print("Start Frequency: ");
  Serial.print(long (freq));
  Serial.println(" Hz");
  long timeStart = micros();
  while (freq <= F2) {
    sendFrequency(freq);  // freq
    freq++;
  }
  long timeStop = micros();
  Serial.print("Start Time: ");
  Serial.print(timeStart);
  Serial.println(" us");
  Serial.print("Stop Frequency: ");
  Serial.print(long (freq));
  Serial.println(" Hz");
  Serial.print("Stop Time: ");
  Serial.print(timeStop);
  Serial.println(" us");
  Serial.print("Sweep Time: ");
  Serial.print(timeStop - timeStart);
  Serial.println(" us");
  Serial.print("Switching Time: ");
  Serial.print((timeStop - timeStart) / long(freq));
  Serial.println(" us/pt");
  Serial.println("");
}**
```

..
int32_t freq_start = (frequency1 * 4294967296LL)/125000000;
int32_t freq_stop = (frequency2 * 4294967296LL)/125000000;
int32_t freq_step = (frequency3 * 4294967296LL)/125000000;
..

and then something like this:

for (f=freq_start; f<=freq_stop; f=f+freq_step) {
sendDDSword(f);
}

PS: shall it be 4294967295 or 4294967296 ?? :slight_smile:

int32_t freq = frequency * 4294967295/125000000;

I doubt that that will work. It would have a better chance if you force the size of the integers and get 2^32 right:

int32_t freq = frequency * 4294967296LL/125000000L;

Pete

pito:

You need to do this calculation in 64-bit integer arithmetic (which makes using the Due look better!)

This works on the Uno:

unsigned long long DDSword, freq, DDSclock;

DDSclock = 125000000LL;
freq = 50000000LL;  // required freq
DDSword = (0x100000000LL) * freq ;
DDSword = DDSword / DDSclock;

The above works :slight_smile:
You may do a check with:

Serial.println((long)DDSword, 16);

against:
http://designtools.analog.com/dt/dds/ad9850.html

Hi Folks. Thanks a lot for the inputs! I'm still digesting how to use the code snip-its provided.

I also noticed the 2^32 calculation and sent the update (int32_t freq = frequency * 4294967296LL/125000000L;). My freq counter is limited to 10 Hz resolution , so perhaps I am just not able to measure the difference - the output is 9,999,96 Hz when set to 10 MHz output. I'll have to dig out another counter...

I see what you're saying pito, thanks.

I'm still trying to get the faster code to run, and also played with putting the sendfreq call in an encoder interrupt, and that did nothing?

http://www.analog.com/static/imported-files/data_sheets/AD9850.pdf

"This is the 8-bit data port for iteratively loading the 32-bit frequency and the 8-bit phase/control word."

I don't see that you have an control word being created in the array data.
See pages 9-10 of the data sheet.

I think your formula is not correct
int32_t freq = frequency * 4294967295/125000000;

page 8 says Freq out = (tuning word * clock in(MHz))/2^32
= (32-bit word*125,000,000)/0xFFFFFFFF
= (32-bit word * 125,000,000) >> 32
so you probably need a 64 bit to start with for the multiplication, that gets shifted down for the 2^32 divide.
125,000,000 = 0x0773,5940
So it would seem that the the tuning word would have to be some minimum amount to avoid getting divided down to 0, yes?
Like 23 or more:
0x0773 5940 x 22 = 0xFD51 DA80 >> 32 = 0
0x773 5940 * 23 = 0x1 04C5 33C0 >> 32 = 1

Oh, this is like the opening scene with Mr. Roarke and Tattoo...

Is this not the control word in the array: dataArray[4] = 0x000; // Final control byte, all 0 for 9850 chip

The formula does seem messed up, not only the value. I'm surprised I'm getting output. I'll change that tonight and see how it pans out, but, is it not this way because we are calculating the tuning word value based on frequency? And there was something about the largest 32-bit integer value being 2^32-1.

I know decimal math, if I plug in a tuning word value of "1", I get a frequency value of 0.0291, which is the minimum frequency resolution of the DDS (spec). If the tuning word value is 34.35974, the frequency value is 1. If the tuning word value is 0, then I suspect DC output?

Again, simply verify your DDSword calculation (see my post above). When it is correct (it MUST be the same as the calc result on the ADI page above), try to pass it to the DDS chip.

Do NOT expect you will see an accurate DDS output frequency on the freqmeter, because you do NOT know the actual frequency of your 125MHz DDSclock. It could be 125.009.447,786 Hz - this DDSclock needs to be

a) measured by the freqmeter and its value has to be changed in the code accordingly, or,

b) "calibration mode": set the DDSfreq to ie. 10.000.000Hz output, pass it to DDS chip, do measure the DDS output freq (you may see something like "9.997.234Hz"), and do change the DDSclock value such until you see "10.000.000Hz" on the freqmeter (provided your freqmeter is precise enough), and do save the new DDSclock value somewhere.

I used that "calibration" mode with my DDS stuff I did long time back (so I did not care what the actual DDSclock freq was like, and, the actual DDSclock hex value was saved into an EEPROM after the calibration - btw. you can do the calibration anytime then, even when your DDS generator is already built into a nice shiny case :slight_smile: )..

See bottom of page 12.
You may have to flip the SPI bit order:

http://www.ad7c.com/projects/ad9850-dds-vfo/
http://code.google.com/p/ad9850-arduino/
http://www.theladderline.com/node/10

http://webshed.org/wiki/AD9850_Arduino
http://code.google.com/p/rocketnumbernine/source/browse/trunk/AVR/AD9851/ad9851-spi.pde

http://www.knology.net/~gmarcus/

http://www.kerrywong.com/2012/11/06/dds-function-generator-build/#more-7090

http://rockingdlabs.dunmire.org/home/ad-9850ddssynthesizer

http://www.tarapippo.net/arduino/sweep.ino
http://www.ad7c.com/2013/02/
http://wsprnet.org/drupal/node/3834

PS: the 9851 and 9850 are identical except 1 bit..

For a serial load, the relevant information is Table IV on page 12.

The formula I gave is correct:

int32_t freq = frequency * 4294967296LL/125000000L;

This calculates the tuning word (given the misleading variable name "freq" here) from the desired frequency. The tuning word is then sent to the device.

Pete

int32_t freq = frequency * 4294967296LL/125000000L;

To be safe:
int32_t freq = (frequency * 0x100000000LL)/125000000L;
PS: Mind the 125000000L is not the correct number with those cheap modules..

Thanks Folks! I'd seen some of those sites before, and interesting that the same 2^32 number comes up incorrectly in the code in a few different places.

That is a pretty clever way to calibrate the DDS clock. I was thinking of adjusting the sent frequency value with a cal variable in the sendfrequency module.

I think I'm armed with enough to get something done now. :~

The only correct calculation of the DDSword is with 64bit multiplication and LL integers (as above). Some sites show calculation with floating point - bad idea - as the float is not precise enough for the DDSword calculation! (there are even AD DDS chips with 48bit DDSword!).
I would use the libraries, but applying the above LL calc for DDSword.
Also mind when doing sweep with "adding the frequency step" (instead of a freq calculation of each sweep point) the error of the frequency accumulates, when the step is not a precise number.. The same occur when using an encoder as the input dial (and doing adding/subtracting a fixed step).

Hi All,

I finally got the DDS freq update to happen via an spi transfer (The spi divider is set to 4, mode0, and LSB first).

I call the SetFrequency function via a while loop, which happens quickly for each frequency setting. I print a time before and after the loop completes and calculate the point-to-point sweep time. Without the below function, the loop takes about 4 us to complete (might be faster, but that is all I'm able to count). When I add the SetFrequency function, I get a freq set time of ~280 us.

I would like to get the function to process faster if I can.

void SetFrequency(unsigned long frequency)
{
int32_t tuning_word = (frequency * 0x100000000LL)/124999100L; //calc tuning word
PORTB = PORTB & B11111011; // clear pin 10
SPI.transfer(tuning_word);
SPI.transfer(tuning_word >> 8);
SPI.transfer(tuning_word >> 16);
SPI.transfer(tuning_word >> 24);
SPI.transfer(0x0);
PORTB = PORTB | B00000100; // set pin 10

I think crossroads mentioned something about a control word and I'm also not sure the last byte is correct. I also wonder if it's better to put the spi transfer in a for loop rather than coding each byte manually as I have? It seems it would take longer for the for loop since that is just one more thing to do...?

Also, the datasheet seems to say that each bit is transferred on rising edge of W_CLK. If W_CLK is 4 MHz (the spi clock with 4 x divider), then 40 bits would take 10 us, plus some settling time for the device to process (18 clocks of DDS system clock, 125 MHz?, plus a few more ns...). So it seems that if I'm able to get on the order of 10 us frequency switching time, then I'm done!

int32_t tuning_word = (frequency * 0x100000000LL)/124999100L; //calc tuning word

On my Arduino Uno, this formula takes almost 250 us. I expect the divide is the time suck.

Try to go with 8Mhz SPI - 9850 is fast enough..
Why do you try to calculate ddsfreqword at each sweep step??
Do calculate the ddsword for fstart and fstop, do calculate the ddsword for fstep and simply add it to dds_freq within a loop - that must be faster (when you do a linear sweep).

unsigned long ddsword_fstart = calculate...
unsigned long ddsword_fstop = calculate...
unsigned long ddsword_fstep = (ddsword_fstop - ddsword_fstart)/number of sweep points


unsigned long ddsword_freq = ddsword_fstart

while (ddsword_freq <= ddsword_fstop) 
   {
      send ddsword_freq to 9850 via SPI @8MHz
      ddsword_freq = ddsword_freq + ddsword_fstep
}

That will take 11usec a loop, I bet :slight_smile:

TanHadron:

int32_t tuning_word = (frequency * 0x100000000LL)/124999100L; //calc tuning word

On my Arduino Uno, this formula takes almost 250 us. I expect the divide is the time suck.

:astonished:

Thanks, pito. I might even get those calcs out of the function and move them to the config function. Nothing should happen until the parameters are configured and then exit setup mode.