Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

AD9850 Dev Board Serial Issue

Status
Not open for further replies.
Hi,

Why how fast do you need the oscillator to start up?

I see a possible problem on that web site linked. It looks like he is using all integers to calculate the frequency control bytes. That could result in rounding which would then make the frequency setting look off. Integer divides result in integers even if it is mid calculation. Using floats might be better, then at the last step convert to integer.
So instead of 4/3 for example, 4.0/3.0 i think forces float math operations.
4/3 probably results in the integer 1. 4.0/3.0 probably results in 0.75 possibly approximated in type float (mid calculation). So 5*4/3 probably results in 5, and 5*4.0/3.0 results in 3.75 which is the more accurate calculation, and rounded in the usual way it would come out to the integer 4. That's unless the language forces floats when dividing.
 
Using integers is no problem. For set values, I would just use a large table. For variable inputs, I have that math worked out.

1) Calculate tuning word per Hz in decimal. Multiply result by 2^24 to give an integer of sufficient precision (i.e., 225C17D0h);
2) Multiply that integer by the frequency you need in Hz, which gives up to an 8 byte (64 bit) result;
3) Grab bytes 3-6 of that result to make your frequency word. Byte 7 means a frequency that is too high.

Here is the tuning word for 10kHz:
upload_2015-10-22_8-42-33.png


The 32X32 scheme (PicList) I am using took 545 cycles (272.5 uS @ 8 MHz) for the calculation. I can shorten that time (reducing rrf's) for this specific application.

The crystal doesn't start in its own (waited 20 seconds). It needs to be "kicked" to start. That is my problem for today to solve.

John
 
Last edited:
The crystal doesn't start in its own (waited 20 seconds). It needs to be "kicked" to start.
An interesting thing.
I have seen a few occasions when my DDS does not appear to give any output.
The old reliable power cycle usually fixes it.
It may be worth my while investigating the oscillator to see if it is slow starting.

As for calculating the tuning word I use a method which feels rather clunky, mathematically speaking but works well in the practical world.

I calculate the tuning words for each of 10MHz, 1MHz, 100kHz..... 10Hz, 1Hz . These values are stored as constants in the ASM file.

The frequency I want is saved in registers, one register for 10s of MHz, one register for 1s of MHz ... one register for 1s of Hz.

Say I want a frequency of 14.123 MHz, I add the 10MHz value to the tuning word, add the 1MHz value four times, the 100kHz value once, the 10kHz value twice, and the 1kHz value three times.

The tuning word is then sent to the AD9850, an out pops 14.313Mhz.
(on edit, What am I smoking here? I should have said 14.123 MHz. Am I going crazy?)

I have also noted that the oscillator on the AD9850 module can be 2 or 3 kHz away from the nominal (125.000MHz) frequency. If you want better frequency setting accuracy, it is a good idea to take this into account when calculating the tuning word constants.

JimB
 
Last edited:
An interesting thing.
I have seen a few occasions when my DDS does not appear to give any output.
The old reliable power cycle usually fixes it.
It may be worth my while investigating the oscillator to see if it is slow starting.

At least, I am in good company. If you look at the picture I posted of the frequency counter, you will see this thing on the power supply:
upload_2015-10-22_9-57-5.png


That is my kick starter. :D A few quick cycles does it.

Did some reading on difficult to start oscillators (Microchip, Freescale, and TI have nice app notes). I will be trying some of those things today.

ADDENDUM:
Just wanted to confirm my suspicion from last night and reading the aforementioned app notes. Lowering the voltage a little, but not too much helps it start. It starts well at 4.27 V, but not at 5V. Added a small signal diode (1N4148) in the VCC line to the DDS board, and now it starts easily at 5V from the supply. If the voltage to the DDS board is too low, then the AD9850 may not work well at 125 MHz. So, it's a compromise. What might work would be a low voltage start and then switch to full voltage. I think a small mosfet, capacitor, resistor, and maybe a diode would do that.

John
 
Last edited:
Hi,

That's interesting about the osc starting problem, i did not notice that with mine.
My board is slightly different however, the one with no jumpers.

John:
I did not mean that you might be calculating wrong, i just meant that the web site linked looked like they were doing it wrong. Mainly because an integer divide usually results in another integer, and if the two integers to divide are close in value, the result will be severely rounded. So 4/5 could come out to equal zero which would screw up the whole calculation when it is multiplied by another number. 4.0/5.0 would at least result in a float, but that's not quite good enough either. I miss having the use of double floats.

Yeah, my preferred method might be using long longs too. I didnt get to that yet because i was just doing some basic testing using hand calculations for the program bytes.
 
VK5TM's website gave me a lot of good ideas; however, I typically test routines before using them. The math routine, as best I can tell, does not always work, which is why I started fresh with the PicList routines. In fact, the PicList routine I chose was about 25% faster and worked all the time, including 0xffffffff x 0xffffffff. Here is the site I used to confirm the multiprecision hex math: http://www.csgnetwork.com/hexmultdivcalc.html

I also used Mike's (K8LH) version of software serial transmission with appropriate modifications (multibyte, toggled clock instead of using delays) to send the tuning word to the AD9850, and I implemented a reset. I prefer using BTFSS/BTFSC after the rrf to send 0 or 1 , respectively, instead of the branch used by VK5TM. The reset may not be necessary, since I added that while struggling with the non-starting crystal. In sum, I didn't end up using any code from VK5TM's site, but I still consider that site as a valuable resource for someone getting started. It certainly helped make sense out of the AD9850 datasheet.

John
 
Hi,

Oh yes that sounds good.

For what it is worth, i read in a couple places that the chip had to be reset because there was no guarantee that it would be reset on power up, so i also included a reset pin for mine. The code starts out as:

// define Ardu pins for AD9850 signals:
int pin_CLK=9;
int pin_DIN=12;
int pin_UPDATE=10;
int pin_RESET=11;

Obviously pin 11 is used to reset the chip before any frequency setting bits are sent.

I also tested in parallel mode. That requires transfers of 8 bits at a time but allows things like sweep (frequency modulation) to act faster, if needed that is.
 
While playing with the math routines, I needed a simple cross-check. The attached Excel file may help others who want to calculate the lower 4 bytes of the AD9850/AD9851 tuning word when operating with a 125 MHz crystal.
John
 

Attachments

  • AD9850 Tuning Word.zip
    8.4 KB · Views: 113
What is interesting is that you can buy the Ebay AD9850 boards cheaper than what the IC sells for in the US, at least at Mouser. Mouser seems to have a large markup.
 
While playing with the math routines, I needed a simple cross-check. The attached Excel file may help others who want to calculate the lower 4 bytes of the AD9850/AD9851 tuning word when operating with a 125 MHz crystal.
John

Hi John,

Sorry i have to condemn the use of anything exclusively Microsoft, unless of course you can suggest a non MS reader for the file :)

Not everyone has "Excel" and not everyone wants to have "Excel". Microsoft has more than enough of the pie, they dont need any more :)
 
Hi,

Ok thank you. Keep in mind i dont want to get 'rid' of Microsoft, but i think they need a reality check now and then.
 
I love my MS Excel :p, great piece of software. :woot:
 
Hi Mike,

Well that's good :)
Someone knowledgeable there might have written it, and it did not pass down to other workers too often perhaps so it maintained it's integrity. Not so with Windows op sys, which changed back and forth with several things over the years from version to version. They change some thing in a new version, then change them back in the next version. I've heard many programmers complaints over this behavior. There has to be some laws made that govern what they can do and not do, because it affects too many people.

But back to the main idea of calculating the code bytes...

I have to differ on one entry although i did not check them all.
The entry in the linked file for 10MHz is as follows:
0x147AE147

but i believe the better calculation is:
0x147AE148

Why the slight difference? Because the float value for that last digit rounds up to 8 not down to 7. The reason for that is because rounding up usually occurs at 0.5, so anything lower than 0.5 rounds down to 0, and anything equal to or greater than 0.5 rounds up to 1. This is why i think we need to use either double float or a pseudo double float type. I have not checked the accuracy for using just floats (not double floats) however so there is a chance that will work too, but because of the limited precision relative to the possible length of the control bytes, i dont think it will work as well. Feel free to try it though, and compare results to a properly rounded double float computation.

There are other issues too that come up such as integral frequency vs crystal frequency type distortion, but i'll hold off on that for now.
 
You are referring to whether 343,597,383.68 is rounded before conversion to hex or whether that conversion ignores the 0.68. A lot of respectable programs ignore the decimal when converting to hex. The error is approximately 1 part in over 500,000,000. That error will be swamped by the error in your crystal.

John
 
You are referring to whether 343,597,383.68 is rounded before conversion to hex or whether that conversion ignores the 0.68. A lot of respectable programs ignore the decimal when converting to hex. The error is approximately 1 part in over 500,000,000. That error will be swamped by the error in your crystal.

John

Hi John,

Sure, i agree almost fully.

I happen to come from the school of thought that we dont introduce more errors no matter how small unless we have to. In this case, it may be faster to use integer math if that is entirely possible, so there may be an advantage to that. But for anyone using floating point (that is double floats not just floats) rounding is just old hat, something that is done for every number after some other operation.

Another method which is statistically based involves rounding every other calculation up *ALWAYS* (actually an integer increment) and not rounding every other other calculation at all (no increment). So there we would get the result with the '7' on one calculation, then on the very next calculation we would get '8', then the next calc 7 again, then 8 again, etc. It's based on the spread of errors over floats during lots of calculations. [Note the statistical average of 7 and 8 is 7.5 and 7.5 is closer to 7.68 than either 7 or 8].
I wouldnt do it here though i dont think, unless i was going to generate lots of frequencies fast from raw calculations and i wanted a nice spread of frequencies. It would be faster than using any type of float.

I should mention again that i dont think the precision of a regular float (not double) is good enough because it does not have enough accurate digits for all possible codes. It is contained in four bytes like the control bytes, but not all those bits are used for the base precision as some are used for the exponent. I have not verified this though.

But anyway, i am not trying to make a big deal out of rounding up i just thought i would mention that. We could also look at other frequencies too and possibly improving them by moving them away from an integer multiple of the clock.
Most of my needs probably wont be that critical anyway though so i could generate 10,000,010 Hz instead of 10MHz and it probably would not matter :)
At some point i may try to generate an FM signal though, like 9.9MHz to 10.1MHz centered on 10MHz just for kicks, or something like that anyway.
 
Hi,

I think i found a reasonable way to calculate the control bytes with no loss of accuracy.

First, we use type unsigned long long math. Type ULL is a 64 bit unsigned number.
The magic number in hex is:
M=0000 0022 5C17 D02B

and as you can see that is a 64 bit number and has some leading zeros.
All we have to do is multiply that number by the frequency, and then grab the upper 32 bits of the result (ignoring the lower 32 bits).

For example, for 10MHz we have:
F=10000000;
C=M*F;

The entire 64 bit hex result is:
147A E147 9969 4780

from which we can grab the upper 32 bits (and round if the 9th digit is 8 or above if we care too, but i wont round here):
147A E147

and so the control bytes are:
14, 7A, E1, 47

or:
14, 7A, E1, 48
if you care to round.

For another example, 1000Hz:

C=M*F;

the 64 bit result is:
0000 8637 BD05 27F8

and grabbing the upper 32 bits we get:
0000 8637

and if we care to round we get:
0000 8638

so the control bytes are 00, 00, 86, 37 or 00, 00, 86, 38.

Looking back at some of the previous posts, i see you may have done something like this already just with a little less precision, which still isnt bad :)
For 10kHz i get:
0005 3E2D 6233 8FB0

and so the control bytes are: 00, 05, 3E, 2D with no possibility of rounding, which is the same result you got for 10kHz.
 
Last edited:
Ah... Adding code for rounding fixed the problem I was having.

Thank you... Mike
C:
    unsigned long ftw;               // the 32-bit Frequency Tuning Word

/************************************************************************
 *  Calculate AD9850 'FTW' (Frequency Tuning Word)      Mike McLaren    *
 *                                                                      *
 *  The 32-bit 'f' input is frequency*100 (0.01-Hz resolution) which    *
 *  allows scaling the DDS constant up to 32-bits.  Multiply the two    *
 *  32-bit terms and divide the 64-bit product by 2^32.  The 32-bit     *
 *  result is the frequency tuning word in the 'ftw' variable.          *
 *                                                                      *
 *  FTW * 2^32 = freq*100 * 2^32/RefClk*2^32/100                        *
 *  FTW * 2^32 = freq*100 * 1475739526                                  *
 *                                                                      *
 *  Fitting the frequency*100 value into a 32-bit variable makes our    *
 *  upper frequency limit ~42,949,672.95-Hz.                            *
 *                                                                      *
 *  --- target --  ---- ftw ----  --- actual --                         *
 *  42,949,672.00  1,475,739,493  42,949,672.00                         *
 *  37,000,000.00  1,271,310,320  37,000,000.01                         *
 *  25,000,000.00    858,993,459  24,999,999.99                         *
 *  10,000,000.00    343,597,384  10,000,000.01                         *
 *   1,000,000.00     34,359,738     999,999.99                         *
 *     125,000.00      4,294,967     124,999.99                         *
 *      10,000.00        343,597       9,999.99                         *
 *                                                                      *
 *  XC8 example                        (42 words, 665 cycles)           *
 ************************************************************************/
   void calcFTW(unsigned long f)    // calculate AD9850 32-bit "FTW"
   { long c = 1475739526;           // the "constant" term
     unsigned char n = 32;          //
 /*                                                                     *
  *  multiply 32-bit freq*100 value by our 32-bit "constant" and use    *
  *  the upper 32-bits ('ftw') of the 64-bit result                     *
  *                                                                     */
     asm("mult32x32:            "); //
     asm("clrc                  "); //                                  |00
     asm("btfss   calcFTW@c+0,0 "); //                                  |00
     asm("bra     nextbit       "); //                                  |00
     asm("movf    calcFTW@f+0,W "); // frequency lo                     |00
     asm("addwf   _ftw+0,F      "); //                                  |00
     asm("movf    calcFTW@f+1,W "); // frequency hi                     |00
     asm("addwfc  _ftw+1,F      "); //                                  |00
     asm("movf    calcFTW@f+2,W "); // frequency ul                     |00
     asm("addwfc  _ftw+2,F      "); //                                  |00
     asm("movf    calcFTW@f+3,W "); // frequency uh                     |00
     asm("addwfc  _ftw+3,F      "); //                                  |00
     asm("nextbit:              "); //                                  |00
     asm("rrf     _ftw+3,F      "); //                                  |00
     asm("rrf     _ftw+2,F      "); //                                  |00
     asm("rrf     _ftw+1,F      "); //                                  |00
     asm("rrf     _ftw+0,F      "); //                                  |00
     asm("rrf     calcFTW@c+3,F "); //                                  |00
     asm("rrf     calcFTW@c+2,F "); //                                  |00
     asm("rrf     calcFTW@c+1,F "); //                                  |00
     asm("rrf     calcFTW@c+0,F "); //                                  |00
     asm("decfsz  calcFTW@n,F   "); // done? yes, skip, else            |00
     asm("bra     mult32x32     "); //                                  |00
     asm("movlw   0x80          "); // rounding...                      |00
     asm("addwf   calcFTW@c+3,W "); //  "                               |00
     asm("movlw   0             "); //  "                               |00
     asm("addwfc  _ftw+0,F      "); //  "                               |00
     asm("addwfc  _ftw+1,F      "); //  "                               |00
     asm("addwfc  _ftw+2,F      "); //  "                               |00
     asm("addwfc  _ftw+3,F      "); //  "                               |00
   }
 
Last edited:
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top