Shop OBEX P1 Docs P2 Docs Learn Events
Ad9851 dds — Parallax Forums

Ad9851 dds

g3cwig3cwi Posts: 262
edited 2012-06-11 07:44 in Propeller 1
Hi all

I am trying to get to grips with sending data to an AD9851 DDS chip. However, being completely new to things here (and to the Propellor) it is proving problematic.
http://www.analog.com/en/rfif-components/direct-digital-synthesis-dds/ad9851/products/product.html

The chip needs a 40 bit binary word which can either be transmitted serially or as 5 x 8 bit bytes (much faster). Only the 32 LSBs of the 40 bit word are reqired to define the frequency. These 32 bits are calculated by the formula 32bits = (F_out x 2^32) / F_clock. F_out is the desired frequency.

For an example, using a clock of 125MHz, the words for 10MHz are:

W0 00
W1 14
W2 7A
W3 E1
W4 48

lhttp://designtools.analog.com/dt/dds/ad9851.html

The first problem is calculating the word as the Spin compiler does not like the decimal representation of 2^32 (4294967296). I can fix that by approximating the number to 4294967295, or perhaps pre-dividing by F_clock which is another constant. I then end up with a decimal number representing the 32 bit word.

It could be that it is already the binary number I need but I'm guessing that it's not as it is probably stored in a clever way to account for decimal places and pos/neg (?). If so I need to clean it up so that I can send it out (another puzzle at this stage).

I have looked about on t'internet for code for this application; there is plenty of PIC stuff and some C, but neither helps me at all. Has anyone got anything they would care to share or can anyone give me one or two clues as to some of the Spin elements that I need to get familiar with!

I'm on a steep learning curve here and I'm close to the bottom at the moment, hanging on by my finger nails...

Any clues would be much appreciated.

Thanks

Richard

Comments

  • g3cwig3cwi Posts: 262
    edited 2012-01-29 10:55
    More pouring over the manual makes me think that I could most easily use the >> operator to shift a 32 bit number out. I could then fill the DDS serially a bit at a time. Not sure how I can test what the LSB is to determine the fill yet though...
  • CircuitsoftCircuitsoft Posts: 1,166
    edited 2012-01-29 11:04
    Try

    Word_to_send := ((F_out << 16) / F_clk) << 16

    You may not want 16 in both places. The two numbers should add up to 32, though. The first one should be small enough that F_out won't overflow a 32-bit number, but otherwise as large as possible.
  • g3cwig3cwi Posts: 262
    edited 2012-01-29 11:22
    Thanks! That looks helpful. I see how that works for the calculation.

    Looking at the operators it looks like a bitwise reverse, used with a bitwise encode, a test and a shift left might be a way of shifting out a number correctly. There has to be an easier way though. It looks way too clumsy.
  • ChrisGaddChrisGadd Posts: 310
    edited 2012-01-29 11:30
    Shamelessly adapted from the ctr object:
    PUB Divide(Fout, Fclock) : f
    
      Fout <<= 1
      repeat 32                                                                     'perform long division of (Fout / Fclock) * 2^32
        f <<= 1
        if Fout => Fclock
          Fout -= Fclock
          f++           
        Fout <<= 1
    
    Or if you prefer PASM:
    Divide                                                                          ' Calculate ((Fout / Fclock) * 2^32)        
                            mov       Counter,#32                           
                            mov       Quotient,#0                                          
                            mov       Dividend,Fout                                
                            mov       Divisor,Fclock
                            shl       Dividend,#1
    :Loop
                            shl       Quotient,#1                                   ' Double the quotient     
                            cmpsub    dividend,divisor            wc                ' Attempt to subtract clk_freq from output freq                       
              if_c          add       quotient,#1                                   '  Add one to the quotient if subtraction occurred                    
                            shl       dividend,#1                                   ' Double the output freq                                              
                            djnz      Counter,#:Loop                                ' And repeat until dividend shifted 32 times (multiplied by 2^32)  
                            wrlong    Quotient,par                                                                       
    
  • g3cwig3cwi Posts: 262
    edited 2012-01-29 11:49
    Thanks for the code Chris. Not sure I quite understand how it works yet. I assume that in the Spin version, the result is somehow magically in the variable f? Fclock will likely be an integer but Fout will be a decimal such as 11.255.

    I really need to re-learn my binary maths!

    Cheers.

    I think that I can use & to do my testing:

    If (DDS_word & $0001) > 0 'then LSB is 1

    maybe?
  • g3cwig3cwi Posts: 262
    edited 2012-01-30 10:58
    I finally came up with this (shamelessly using code from elsewhere):
    Var
    
    Long Fout, Fclock, f
    
    PUB CalcF_and_Load
       
     Fout := 70.454 'Test data
     Fclock := 125 'Test data
     f := 0 'Hard clear f
     
    'Calculate the frequency word required
     
       Fout <<= 1
      repeat 32   'perform long division of (Fout / Fclock) * 2^32
        f <<= 1
        if Fout => Fclock
          Fout -= Fclock
          f++           
        Fout <<= 1
    
    'Set pins to outputs
    
    dira[16]~~  'Used for Serial Load                                     
    dira[18]~~  'Used for W_CLK
    dira[22]~~  'Used to update freq after load (FQ_UD)
     
    'Shift out the data LSB first (32 bits)
          f <-= 1                                     ' pre-align lsb
          repeat 32
            outa[16] := (f ->= 1) & 1                 ' output data bit
            Waitcnt (clkfreq/2 + cnt)                 ' let it settle
            !outa[18]                                 ' clock the bit
            Waitcnt (clkfreq/2 + cnt)
            !outa[18]
             
    'add 8 more clock cycles to fill last 8 bits with 0s
          repeat 8 
            Waitcnt (clkfreq/2 + cnt)                
            !outa[18]                               
            Waitcnt (clkfreq/2 + cnt)
            !outa[18]
    
    'Send FQ_UD to transfer data into DDS registers             
          Waitcnt (clkfreq/2 + cnt)                
          !outa[22]                               
          Waitcnt (clkfreq/2 + cnt)
          !outa[22]
          
    
    
    

    It is set to run slow for testing. I dont have an AD9851 here as yet to test it.

    I will report back once I am able to test it "live".

    Regards

    Richard
  • g3cwig3cwi Posts: 262
    edited 2012-05-20 01:14
    I have, at long last, got round to connecting up the AD9851 DDS for some breadboarding. Before I start cutting code, has anyone got an object already done for this device that they might share?

    Regards

    Richard
  • g3cwig3cwi Posts: 262
    edited 2012-05-21 11:15
    AD9851 code for the Quickstart board. Enjoy.
    CON
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
      
      'assign pins
      W_CLK    = 3
      FQ_UD    = 2
      SER_DATA = 1
      RST      = 0
    
      P23      = 23 'Mirrors W_CLK
      P22      = 22 'Mirrors SER_DATA 
      P21      = 21 'Mirrors FQ_UQ 
      P20      = 20 'Mirrors RST
    
    Obj
    
     Debug : "Debug_LCD03"
    
    Var
    Long Fout, f, Fclock
    Byte W0
    
    Pub Main
    
      Debug.Start (21, 9600, 4)
      Debug.Backlight(true)
      Debug.CLS
      Debug.Str(string("LCD Debug"))
      Debug.NL
    
      Reset_AD9851
      
      Debug.CLS
      Debug.Str(string("Reset okay"))
      Debug.NL
       
      Calc_F
      Load_F
    
      Repeat
    
    Pri Reset_AD9851 | i
    
    'Set ports to outputs
    dira[0..3] ~~   'Data to AD9851                                  
    dira[20..23]~~  'Quickstart LED repeater 
    
    'Reset device
        'Toggle RST
        !Outa[RST]
        Dly
        !Outa[RST]
        Dly
    
    'Switch device to serial mode
        'Toggle W_CLK
        !Outa[W_CLK]
        Dly
        !Outa[W_ClK]
        Dly
        
        'Toggle FQ_UD
        !Outa[FQ_UD]
        Dly
        !Outa[FQ_UD]
        Dly
    
    'Clear register
    
      Outa[SER_DATA]~ 'fill register with zeros
      
      'Send 40 clock cycles
      Repeat 80
        !Outa[W_CLK]
        Dly
    
      !Outa[FQ_UD]
      Dly
      !Outa[FQ_UD]
      Dly
      
    Pri Calc_F
       
       Fout:= 2_140_000 'Hz
         
       'System clock
       Fclock   := 180_000_000 'Hz
       
       'Calculate the frequency word required  
        Fout <<= 1
        repeat 32   'perform long division of (Fout / Fclock) * 2^32
          f <<= 1
          if Fout => Fclock
            Fout -= Fclock
            f++           
          Fout <<= 1
    
    Pri Load_F
    
     'Shift out the data LSB first (32 bits)
        f <-= 1                                     ' pre-align lsb
          repeat 32
            outa[SER_DATA] := (f ->= 1) & 1 
            Dly
            !outa[W_CLK]                 ' output data bit                  '
            Dly
            !outa[W_CLK]
            Dly
    
     'Phase registers and clock multiplier
      'Sets clock multiplier to x 6
        W0       := 00_0001
            
     'Shift out word W0
        W0 <-= 1
          repeat 8
            outa[SER_DATA] := (W0 ->= 1) & 1
            Dly               
            !outa[W_CLK]                                
            Dly
            !outa[W_CLK]
            Dly     
    
     'Send FQ_UD to transfer data into DDS registers             
                   
          !outa[FQ_UD]                               
          Dly
          !outa[FQ_UD]
          Dly
          
    PRI Dly
    
    'Mirrors data lines to LEDs and adds delays
    'to help visual debug
    
      If Outa[W_CLK] == 1
        Outa[P23] := 1
      ELSE
        Outa[P23] := 0
    
      If Outa[SER_DATA] == 1
        Outa[P22] := 1
      ELSE
        Outa[P22] := 0
    
      If Outa[FQ_UD] == 1
        Outa[P21] := 1
      ELSE
        Outa[P21] := 0
    
      If Outa[RST] == 1
        Outa[P20] := 1
      ELSE
        Outa[P20] := 0
    
    Waitcnt (clkfreq/100 + cnt)
    

    PS I have no idea how Chris' divide and multiply code actually works. I would love some pointers to that!

    Regards

    Richard
  • LeonLeon Posts: 7,620
    edited 2012-05-21 12:12
    The division works by repeated subtraction.
  • SSteveSSteve Posts: 808
    edited 2012-05-21 12:33
    I'm not sure how similar they are but I made a basic object for the AD9833 a couple years ago. It just does sine waves. I've attached it in case it is of any use to you.
  • g3cwig3cwi Posts: 262
    edited 2012-05-23 12:29
    Having got the basic code working I tried to make a separtate AD9851 object. It all seems to have gone wrong since then.The attached code flashed LEDs on a Quickstart board in sympathy with the data so no AD9851 is needed to see what happens. My first question is why does the FQ_UD LED (P21) flash twice at the ends of the reset? I can see no reason why that should happen.

    The second question is does a call to an object always return from that object irrespective of what the object does?

    I would welcome advice on any other errors you see!

    Cheers

    Richard

    AD9851_Demo - Archive [Date 2012.05.23 Time 20.38].zip
  • g3cwig3cwi Posts: 262
    edited 2012-05-25 12:54
    Finished Spin object in Obex.
  • CncjerryCncjerry Posts: 64
    edited 2012-06-11 07:44
    I am converting my pic to arduino to propeller for an ad9854 then moving to a new chip. Thanks for working thru some of this for me. The 9854 chip uses 2^48 multiply which makes it harder. You then need to shift out 64 bits.
Sign In or Register to comment.