1. 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.
    Dismiss Notice

Demo Assembly Code AD9850 DDS Signal Generator

Assembly code that demonstrates programming an AD9850 DDS module.

  1. jpanhalt
    This article consists of Assembly code (MPASM) that demonstrates how to program an inexpensive DDS module. The module was purchased from an eBay vendor. There are three attachments:

    Attachment 1: Image of the device on a breadboard (ribbon cable is for ICSP)
    Attachment 2: Schematic
    Attachment 3: Zipped Excel file to calculate tuning word based on a 125 MHz oscillator

    The code includes code from other individuals. If using this code for other than personal use, please respect our copyrights and cite the sources, including this repository.

    Regards, John

    EDIT: 10/28/15 The schematic shown here for attaching the other style of DDS module works fine with this program. The buffer/amplifier was not tested. Link: http://nyfiken.org/arkiv/date/2014/10
    EDIT:11/09/15 Updated Excel Tuning Word spreadsheet to include rounding.
    Code (ASM):
    ;                                                                             *
    ;    Filename:       AD9850 DDS Signal Generator 12F683 v.3.0.asm             *
    ;    Date:           10.27.15                                                 *
    ;    File Version:   Added calculations and cleaned                           *
    ;    Author:         JPAnhalt                                                 *
    ;                                                                             *
    ;    Files Required: P12F683.INC                                              *
    ;                                                                             *
    ;    Notes:      
    ;    1) This code demonstrates programming one of the inexpensive
    ;    DDS modules that are based on the AD9850/AD9851 chips. A practical program
    ;    would probably require several changes, for example, one might want to
    ;    expand the number of user-set frequencies available.  The code is written
    ;    for any Microchip mid-range (14-bit core) device. Appropriate changes in
    ;    the initialization, for example bank changes, may be needed depending on
    ;    which device is used.  If one is using an enhanced mid-range device,
    ;    some of the instructions will need to be modified.  Such modifications
    ;    will also allow reduction in the code size.
    ;    2) For serial programming of the AD9850, five, 8-bit bytes (40 bits) are
    ;    sent LSB to MSB for the frequency (4 words/bytes) plus phasing and
    ;    control data as the last byte. For many purposes, byte 5 can be just
    ;    null (zeros). The datasheet from Analog Devices refers to tuning
    ;    words.  I have tried to maintain the distinction that the tuning word
    ;    (singular) is composed of five bytes.
    ;    3) This code uses several routines of which I am not the original author.
    ;    That credit belongs to the following individuals:
    ;         a) Mike McLaren (K8LH): Delay macro for 14-bit core PIC's.  I also
    ;         credit Mike K8LH with showing me how to do a software serial
    ;         transmit several years ago.
    ;         b) Brian Beard (Lucid Technologies/PicList): BCD to binary conversion.
    ;         b) Jose Pinto Souto: 32-bit x 32-bit multiplication based on
    ;         Microchip AN617: Planet Fastest Unsigned PICmicro Math
    ;         (http://www.piclist.com/techref/microchip/pfu/PFU.ASM).
    ;    4) Recognition also is given to a collaborative project to develop
    ;    a functional VFO based on inexpensive AD9850 DDS modules. Terry Mowles (VK5TM)
    ;    lists those individuals and gives several versions of the code on his site:
    ;    http://www.vk5tm.com/homebrew/dds/dds.php  Specifically, parts of
    ;    code in PEGen2-1f.asm and PEGen7_1a.asm were used.                    
    ;              ---0---
    ;         Vdd |1     8| Vss          
    ; DDS_clk/GP5 |2     7| GP0/ICSPDAT/PB0/DDS_reset
    ; DDS_dat/GP4 |3     6| GP1/ICSPCLK/PB1
    ;    MCLR GP3 |4     5| GP2/DDS_load
    ;              -------
                    list    p=12F683  
                    #include <p12F683.inc>
                    errorlevel -302, -207, -305  
                    RADIX   dec
    ;                             DEFINITIONS
    #define         DDS_clk GPIO,5          ;AD9850 write clock (output)
    #define         DDS_dat GPIO,4          ;AD9850 serial data input (output)
    #define         DDS_load GPIO,2         ;Update pin on AD9850 (output)
    #define         PB1     GPIO,1          ;Pushbutton (input)
    #define         Rest    GPIO,0          ;Used for resetting AD9850 (output)
    ; ****************************************************************************
    ;                             VARIABLES    
    ; ****************************************************************************
                    CBLOCK  0x20            ;data block in general purpose RAM            
                    byte2send               ;used in Send_DDS_Word      
                    delayhi                 ;K8LH delay macro
                    count                   ;multipurpose counter
                    d_count                 ;digit count used in BCD2BIN
                    c_hold                  ;carry bit hold
    ;Following registers shoud be kept in order in each block; blocks can be moved
                    ACb0                    ;32-bit Accumulator b, lsb+0 (LSByte)
                    ACb1                    ;32-bit Accumulator b, lsb+1
                    ACb2                    ;32-bit Accumulator b, lsb+2
                    ACb3                    ;32-bit Accumulator b, lsb+3 (MSByte)
                    a32_0                   ;low byte tuning constant
                    a32_3                   ;high byte tuning constant        
                    BCD0                    ;10^0
                    BCD1                    ;10^1
                    BCD2                    ;10^2
                    BCD3                    ;10^3
                    BCD4                    ;10^4
                    BCD5                    ;10^5
                    BCD6                    ;10^6
                    BCD7                    ;10^7
                    AD9850_0                ;low byte tuning word
                    AD9850_3                ;high byte tuning word
                    AD9850_4                ;phase byte tuning word
    clock           equ     8               ; 4, 8, 12, 16, 20 (MHz), etc.
    usecs           equ     clock/4         ; cycles/microsecond multiplier
    msecs           equ     clock/4*1000    ; cycles/millisecond multiplier
    DelayCy         macro   delay           ; 11..327690 cycle range
                    movlw   high((delay-11)/5)+1
                    movwf   delayhi
                    movlw   low ((delay-11)/5)
                    call    uDelay-((delay-11)%5)
                    ORG     0x0000          ;Processor reset vector
                    goto    Init            ;Go to beginning of program
                    ORG     0x0004          ;Interrupt vector location
                    bcf     STATUS,RP0      ;                                        |BO
                    clrf    ADCON0          ;Disable ADC, ADCON0<0> is clear = off            |B0
                    movlw   b'00000111'     ;                                        |B0
                    movwf   CMCON0          ;Turn comparators off                        |B0
                    bsf     STATUS,RP0      ;Switch to Bank1                            |B1
                    clrf    ANSEL           ;Allow digital I/O, disable comparators            |B1
                    movlw   b'001010'       ;GP3(MCLR)&PB1 are input,others output           |B1
                    movwf   TRISIO          ;GPIO defines pin names, TRISIO defines direction    |B1
                    movlw   b'00000000'     ;WPU enabled, internal clock                      |B1
                    movwf   OPTION_REG      ;                                        |B1
                    bsf     WPU,1           ;set WPU for GP1, default anyway                  |B1
                    movlw   b'01110000'     ;Oscillator settings                              |B1
                                            ; x-------     ;Unimplemented
                                            ; -111----     ;Osc (MHz): 111 =8, 110 =4
                                            ; ----0---     ;Startup timeout status
                                            ; -----0--     ;High-frequency status bit<2>, 1=stable
                                            ; ------0-     ;Low-frequency status bit
                                            ; -------0     ;System Clock Select, 0=defined in config
                    movwf   OSCCON          ;not applicable with XT                        |B1
                    btfss   OSCCON,2        ;Check oscillator stable                          |B1
                    goto    $-1             ;                                                 |B1
                    bcf     STATUS,RP0      ;Switch to Bank0                                  |B0
                    bsf     Rest            ;Reset AD9850, alternatively some users send
                    DelayCy (2*msecs)       ;all zeros (5 bytes)twice (See: VK5TM)
                    bcf     Rest          
                    btfsc   PB1             ;weak P/U makes open state high
                    goto    Make_10MHz
                    DelayCy (10*msecs)      ;button pressed ,wait
                    btfss   PB1             ;still clear?
                    goto    PutFreq  
                    movlw   0x47    
                    movwf   AD9850_0
                    movlw   0xE1
                    movwf   AD9850_1
                    movlw   0x7A
                    movwf   AD9850_2
                    movlw   0x14
                    movwf   AD9850_3
                    clrf    AD9850_4
                    call    Send_DDS_Word
                    goto    SelectFreq
    ;Enter desired frquency in Hz in the following table after the dt directive.
    ;Use double quotation marks around the value as in the example below.
    ;Recompile and run.  The entry must have 8 digits and be right justified.
    ;Example:     Freq
    ;             addwf     PCL,f
    ;             dt "16777215",0
                    addwf   PCL,f
                    dt      "05000000",0
                    movlw   BCD7            ;MSByte of new frequency registers  
                    movwf   FSR
                    clrf    count           ;digit pointer offset
                    movf    count,w
                    call    Freq            ;get digit from value entered
                    xorlw   0               ;test for 0 to end looping
                    btfsc   STATUS,2
                    goto    SubCall         ;convert 8 digit BCD string into binary/hex
                    movwf   INDF
                    movlw   0x30
                    subwf   INDF,f          ;substract ascii offset
                    incf    count,f
                    decf    FSR,f
                    goto    Loop      
                    call    BCD2BIN         ;convert BCD to 32-bit binary
                    call    Calc_DDS_Word
                    call    Send_DDS_Word
                    goto    SelectFreq
    ;Routine takes 2721 Tcy or 1.3605 mS @ 8 MHz.
                    movlw   32              ;D'32', Outer loop
                    movwf   count           ;bit counter
                    movlw   BCD7            ;
                    movwf   FSR             ; most significant digit
                    movlw   8               ;Was D'10', Inner loop
                    movwf   d_count         ;digit counter
                    movlw   0x05            ;Weight of next MS-Digit's carry bit
                    clrf    c_hold          ;0 --> carry_hold
                    bcf     STATUS,0        ;Clear Carry bit
                    rrf     INDF,f          ;Right shift 0 into bcd digit
                    rlf     c_hold,f        ;Carry --> c_hold,0
                    btfsc   c_hold,1        ;Add five if carry from previous rotate is
                    addwf   INDF,f          ;set, else no change
                    decf    FSR,f           ;adjustment for listing BCDx low to high
                    decfsz  d_count,f       ;Decrement digit counter,
                    goto    bcd2b2          ;repeat if > 0
                    rrf     c_hold,f        ;Last carry from digit rotate --> C
                    rrf     ACb3,f          ;Ripple Carry bits
                    rrf     ACb2,f          ;from MSB to
                    rrf     ACb1,f          ;LSB of
                    rrf     ACb0,f          ;32-bit binary accumulator
                    decfsz  count,f         ;Decrement bit counter,
                    goto    bcd2b1          ;repeat if > 0
                    retlw   0
    ;The calculated tuning constant (i.e., the value that when multiplied by the
    ;desired frequency in Hz gives the lowest 4 bytes of the AD9850 tuning word is
    ;2^32/oscillator frequency.  For an oscillator of 125 MHz, that constant
    ;is 34.359738368 dec.  To facilitate integer math, that value is multiplied
    ;by 2^24 and converted to hexidecimal to give a tuning constant of 0x225C17D0.
    ;The value used in this calculation has been adjusted to give a frequency
    ;closer to the target value as determined by a Racal Dana counter.
                    movlw   0xD0            ;0x4E gives a frequency closer to target w/ my xco
                    movwf   a32_0  
                    movlw   0x17            ;0x1E gives a frequency closer to target w/ my xco
                    movwf   a32_1
                    movlw   0x5C
                    movwf   a32_2
                    movlw   0x22
                    movwf   a32_3
                    movf    ACb2,w          ;
                    iorwf   ACb3,w          ;
                    iorwf   ACb1,w          ;
                    iorwf   ACb0,w          ;
                    btfsc   STATUS,2        ;
                    goto    m053            ;            
                    movf    ACb3,w          ;
                    movwf   AD9850_0        ;
                    movf    ACb2,w          ;
                    movwf   r64_2           ;
                    movf    ACb1,w          ;
                    movwf   r64_1           ;
                    movf    ACb0,w          ;
                    movwf   r64_0           ;
                    movlw   32              ;
                    movwf   count           ;
                    bcf     STATUS,0        ;
                    btfss   r64_0,0         ;
                    goto    m052            ;
                    movf    a32_0,w         ;  
                    addwf   AD9850_1,f      ;
                    movf    a32_1,w         ;  
                    btfsc   STATUS,0        ;
                    incfsz  a32_1,w         ;
                    addwf   AD9850_2,f      ;
                    movf    a32_2,w         ;  
                    btfsc   STATUS,0        ;
                    incfsz  a32_2,w         ;
                    addwf   AD9850_3,f      ;
                    movf    a32_3,w         ;  
                    btfsc   STATUS,0        ;
                    incfsz  a32_3,w         ;
                    addwf   AD9850_4,f      ;  
                    rrf     AD9850_4,f      ;r64_7 result highest byte
                    rrf     AD9850_3,f      ;r64_6
                    rrf     AD9850_2,f      ;r64_5
                    rrf     AD9850_1,f      ;r64_4
                    rrf     AD9850_0,f      ;r64_3
                    rrf     r64_2,f         ;
                    rrf     r64_1,f         ;
                    rrf     r64_0,f         ;      result lowest byte
                    decfsz  count,f         ;
                    goto    m051
                    return                  ;52 instructions, min:20, max:924 cycles    
    ; *****************************************************************************
                    bsf     DDS_load        ;needed?
                    bcf     DDS_load        ;needed?
                    movlw   AD9850_0        ;set FSR at AD9850 LSB
                    movwf   FSR             ;transmit LSB first
                    movf    INDF,w          ;
                    movwf   byte2send       ;
                    movlw   0x08            ;set counter to 8
                    movwf   count           ;
                    rrf     byte2send,f     ;test if next bit is 1 or 0
                    btfsc   STATUS,C
                    bsf     DDS_dat
                    btfss   STATUS,C
                    bcf     DDS_dat
                    bsf     DDS_clk
                    bcf     DDS_clk
                    decfsz  count,f         ;whole byte been sent?
                    goto    next_bit        ;no, keep going.
                    incf    FSR,f           ;start the next byte unless finished
                    movlw   AD9850_4+1      ;next byte (past the end)
                    subwf   FSR,w           ;
                    btfss   STATUS,C        ;
                    goto    next_byte       ;
                    bsf     DDS_load        ;send load signal to the AD9850
                    bcf     DDS_load        ;
    ;                              DELAY SUBROUTINE
                    nop                     ; entry for (delay-11)%5 == 4     |B0
                    nop                     ; entry for (delay-11)%5 == 3     |B0
                    nop                     ; entry for (delay-11)%5 == 2     |B0
                    nop                     ; entry for (delay-11)%5 == 1     |B0
    uDelay          addlw   -1              ; subtract 5 cycle loop time      |B0
                    skpc                    ; borrow? no, skip, else          |B0
                    decfsz  delayhi,F       ; done?  yes, skip, else          |B0
                    goto    uDelay          ; do another loop                 |B0
                    return                  ;                                 |B0

    Attached Files:

    Mike - K8LH and Ian Rogers like this.