This sends out SPI data as fast as it will go at 8MHz setting: SPI.setClockDivider(SPI_CLOCK_DIV2 );
The # of NOPs were confirmed via logic analyzer - they can't be reduced with affecting the data.
So I suppose you could kick off an SPI transfer, then do what you could in 15 clock cycles before updating the array pointer for the next byte ...
I'm sure now I'll separate the GPS and build it into a 10 MHz reference such as: http://gpsdo.i2phd.com/
If I can get the DDS to sweep with less than 1 ms switching time, I'll likely be happy with that. I'm sure I'll have a hard enough time just doing the frequency step calcs (based on F1 - F2 and step size, plus sweep time) and monitoring the other things I need.
It is going to be a big learning project for me and I really appreciate the help so far.
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
}
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?
"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 )..
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.
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).
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.
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!