AD9850 DDS Generator, using MikroBasicPro, and a PIC 16F877 from Microchip


Par Alain Fort F1CJN, le 13 fevrier 2013     



This program is used to program a AD9850 DDS  between 1 Hz and 30 MHz with a 0.1 Hz resolution.

The DDS card can be get in Ebay for 5 Euros.



                                               1pcs  DDS Signal Generator Module AD9850


                                         AD9850 DDS schematic




The “ Z OUT” filtered O/P (pin 10) is used as the main ouput.



                                   The PIC hardware


-  a Microchip PIC16F877,

-  a standard LCD type HD44780 with 2 x 16 characters lines,

-  5 resistors, an adjustable resistor , 8 capacitors and a rotary encoder with integrated pushbutton,

-  a 8 Mhz Xtal, a 5V regulator, a diode and 2 connectors (HE type).



                                                    Using the card



A frequency is displayed ( MHz at the first power-on).


To change the frequency, push the encoder, the cursor blinks, then move the cursor with the rotary encoder under the figure to be changed.

Push the rotary encoder again and turn the rotary encoder to get the right figure. The activated figure stays underlined for some seconds so during this delay it is still possible to change your mind for the programmed frequency.


The push button is used as a flip-flop between the cursor mode and the frequency select mode.


The displayed frequency is memorized in the PIC EEPROM when the encoder button is pushed during more than 2 seconds.


At the next power up, the programmed frequency is displayed and generated by the AD9850.






                                                    LCD Image


The HD44780 at the first power up.













Source code



program LNB_DDS            '20 janvier 2013


' commande de AD9850 via bus SPI avec référence à 125MHz

' Compatible du circuit imprimé du Synfoxny


'Description du PORTC vers AD9850


' VDD +5V

' PORTB.0   vers encodeur - ,retour encoder commun au VDD

' PORTB.1   vers encoder  + ,retour encodeur commun au VDD

' PORTB.2   vers bouton poussoir de l'encodeur, retour bouton vers le VDD



' Connections AD9850 pour le Mode SPI série


' AD9850 card J2 Pin 2 (D2) connectée à DDS card J3 pin  10  (VSS)                            

' AD9850 card J2 Pin 3 (D1) connectée à DDS card J3 pin  9  (VDD)                            

' AD9850 card J2 Pin 4 (D0) connectée à DDS card J3 pin  9  (VDD)                             

' AD9850 card  J1 Pin 3 (FQ_UD) connectée à DDS card J3 pin 2 (RC1)

' AD9850 card  J1 Pin 4 (DATA) connectée à DDS card J3 pin 6 (RC5)

' AD9850 card  J1 Pin 2 (W_CLK) connectée à DDS card J3 pin 4 (RC3)

' AD9850 card  J1 Pin 1 (VCC) connectée à DDS card J3 pin  9  (VDD)

' AD9850 card  J1 Pin 6 (GND) connectée à DDS card J3 pin  10  (VSS)




 symbol    FQ_UD=PortC.1


'Description du LCD 2x16 caractères



  LCD_RS as sbit at RD2_bit

  LCD_EN as sbit at RD3_bit

  LCD_D7 as sbit at RD7_bit

  LCD_D6 as sbit at RD6_bit

  LCD_D5 as sbit at RD5_bit

  LCD_D4 as sbit at RD4_bit



  LCD_RS_Direction as sbit at TRISD2_bit

  LCD_EN_Direction as sbit at TRISD3_bit

  LCD_D7_Direction as sbit at TRISD7_bit

  LCD_D6_Direction as sbit at TRISD6_bit

  LCD_D5_Direction as sbit at TRISD5_bit

  LCD_D4_Direction as sbit at TRISD4_bit


const Pow10 as longint [11]=(10000000,0,1000000,100000,10000,0,1000,100,10,0,1)


'    Déclarations


dim ligne,counter_cursor,counter_Aff,i,reverse,channel as byte

dim counter_EEPROM as word

dim Freq,Freq_Aff,Freq_W,temp,Freq_32,Freq_33,Freq_old as longword

dim Cursor_pos,old_FF,ecrit as byte  'ancienne position curseur

dim Freq_str as string[10]

dim n,encoderlast as byte


  sub procedure Interrupt()

  Inc(counter_Aff)           'incremente le compteur d'affichage LCD


  Inc (counter_EEPROM)

  if  counter_EEPROM>100 then counter_EEPROM=100 end if

  if  counter_cursor>250 then  counter_cursor=250  end if

  PIR1.TMR1IF = 0            ' clear TMR1IF

  TMR1L = 0x00

  end sub


  sub procedure Write_EEPROM

  FSR = @freq

  for i=0 to 3      'Pf to PF

  EEprom_Write((  i+(4*channel)),INDF)' Write Lo to High value

  EEPROM_write($02,$55)       '



  next i

   EEPROM_write($02,$55)  'Indique que on a memorisé une frequence (au reset)

  end sub


  sub procedure Read_EEPROM

  FSR = @freq ' pointeur vers freq

  for i=0 to 3

  INDF = EEprom_Read(  i+(4*channel))  ' Read Lo to high values


  next i


  end sub


  sub procedure AffichageLCD

   LongWordToStr(freq_32,Freq_str) ' conversion to string

   LCD_CHR(ligne,1,Freq_str[0])'position 1


  for I=1 to 3                ' position 3 to 6


  next i

    LCD_CHR_CP(".")           'position 6

  for I=4 to 6

    LCD_CHR(ligne,i+3,Freq_str[i])'position de 7 to 9

  next i

    LCD_CHR_CP(".")           'position 10

  for I=7 to 9

    LCD_CHR(ligne,i+4,Freq_str[i]) 'position 11 to 13

  next i

   ' LCD_Out(ligne,14," Hz")

  end sub


  sub procedure AffichageFreq

   LongWordToStr(Freq,Freq_str) ' conversion to string

   LCD_CHR(ligne,1," ")'position 1

  for I=1 to 2                ' position 3 to 6


  next i

    LCD_CHR_CP(".")           'position 6

  for I=3 to 5

    LCD_CHR(ligne,i+2,Freq_str[i])'position de 6 to 8

  next i

    LCD_CHR_CP(".")           'position 9

  for I=6 to 8

    LCD_CHR(ligne,i+3,Freq_str[i]) 'position 10 to 12

  next i

    LCD_CHR_CP(".")           'position 13

    LCD_CHR(ligne,13,Freq_str[9]) 'position 14

    LCD_OUT(ligne,15,"Hz" )          'position 15 et 16

  end sub



' Send 16 bits via SPI bus  avec inversion des bits


sub procedure SPI_tx16(dim donnee as word)

dim don_H,don_L as byte

  don_L = donnee  ' byte de poids faible  du word

  donnee=donnee >> 8  'shift droit de 8 bit sur le word

  don_H = donnee ' byte de poids forts du word

  For i=0 to 7

  Reverse.i= don_L.(7-i)

  next i


  For i=0 to 7

  Reverse.i= Don_H.(7-i)

  next i


end sub



' Envoie 32 bits via SPI bus


sub procedure SPI_tx32(dim donnee as longword)

 dim donnee_WH, donnee_WL as word

 donnee_WL=Loword(donnee)     'Isolement du word de poids faibles




end sub


'sub procedure SPI_tx32(dim donnee as longword)

'Dim Don_Lo,Don_Hi,Don_higher,Don_highest as byte


'For i=0 to 7

'  Reverse.i= don_Lo.(7-i)

'  next i

' SPI1_write(Reverse)

' Don_Hi=Hi(Donnee)

'For i=0 to 7

'  Reverse.i= don_Hi.(7-i)

'  next i

' SPI1_write(Reverse)


'For i=0 to 7

'  Reverse.i= don_Higher.(7-i)

'  next i

' SPI1_write(Reverse)

'Don_Highest = Highest(Donnee)

'For i=0 to 7

'  Reverse.i= don_Highest.(7-i)

'  next i

' SPI1_write(Reverse)

'end sub


 sub procedure cursor_underline


  'IF counter_cursor = 250  then         'si le curseurr is OFF

  counter_cursor=0                       ' RAZ temporisation curseur

  'LCD_CMD(_LCD_BLINK_CURSOR_ON)          ' curseur blinking

  'end if

  end sub



  ' Init Interrupt

  T1CON = 1                  ' Timer1 settings

  PIR1.TMR1IF = 0            ' clear TMR1IF

  TMR1H = 0x80               ' Initialize Timer1 register

  TMR1L = 0x00

  PIE1.TMR1IE  = 1           ' enable Timer1 interrupt

  INTCON = 0xC0              ' Set GIE, PEIE

  ' Fin Init interrupt



  TRISC = 0 'sortie

  PORTC = 0 'sorties

  PORTC.2 = 0

  TRISB = 0xFF

  TRISA = 0x3f              ' entrée boutons

  PORTA = 0x00



  SPI1_init_advanced(_SPI_MASTER_OSC_DIV4,_SPI_DATA_SAMPLE_MIDDLE,_SPI_CLK_IDLE_LOW,_SPI_LOW_2_high)   'sortie SPI CLK sur RC3 / Data sur RC5




  FQ_UD=1            'Mise de AD9850 en mode liaison série





  ' Frequences and LCD inits par lecture du "channel 0" in EEPROM




  LCD_OUT(1,1,"  DDS  AD9850")



  LCD_out(2,cursor_pos," ")





  Ecrit= EEPROM_read($02)    'Read "Already writed" EEPROM

  If Ecrit = $55 then       ' EEPROM already writed of not ?

  Channel=1  ' pour le calcul adresse EEPROM   Par défaut canal 1

  Read_EEPROM 'Read  frequency

  End If





   while true      ' boucle principale



'  Quadrature encoder decoding   PORTB.0 et PORTB.1          '

' '''''''''''''''''''''''''''''''''''''''''''''''''''



    If ((encoderlast=0) and (N=1 )) then



    counter_cursor=0    ' raz tempo curseur  des que encoder bouge

       if (Old_FF=0) then   ' Cas incrément de fréquence

       'LCD_CMD(_LCD_UNDERLINE_ON)     'curseur souligné  mias provoque des pbs

          if (PORTB.1=0)then' and (PORTB.2=0)) then   'encoder vers le +

              Freq= Freq + Pow10[cursor_pos-3]

            If Freq>300000000 then Freq = 300000000 End if     '28 MHz  au delà ca déborde !!!


              Freq= Freq - Pow10[cursor_pos-3]

              if Freq>freq_old then Freq= 100 End if ' dépassement négatif

              If Freq<100 then Freq = 100 End if    '10 Hz

          end if


        else     ' déplacement curseur

        LCD_CMD(_LCD_BLINK_CURSOR_ON) 'curseur clignotant

        if (PORTB.1=1)then 'encoder vers le +


           If  Cursor_pos< 3 then Cursor_pos=13   End if

           If  ((Cursor_pos=4) or (Cursor_pos=8) or (Cursor_pos=12)) then Cursor_pos=cursor_pos-1   End if



           If  Cursor_pos> 13 then Cursor_pos=3  End if

           If  ((Cursor_pos=4) or (Cursor_pos=8) or (Cursor_pos=12)) then Cursor_pos=cursor_pos+1   End if

         End if

       end if

    end if



    if counter_aff>10 then      'Affichage via les interruptions TMR1





      'Constante = 3.4359738368 = 2xy32 / 1250000000 avec Fref=125MHZ du module

      'Constante = 4.0328331417 = 2xy32 / 1065000000 avec Fref=106,5MHZ PLVCXO

      'Freq_32= 927.712.935

      'Calcul de Freq_32 = mot de commande en 32 bits


      Freq_32=(Freq*3)        ‘Strange calculations to follow to reduce the error !!!!

      Freq_32=Freq_32+((Freq*2)/5)   '2/5=4/10


      Freq_32=Freq_32+(Freq/200)     '1/200 = 5/1000

      Freq_32=Freq_32+(Freq/2000) +(Freq/2500)  ' 9/10000



      Freq_32=Freq_32+(Freq/1250000)      '****




      'AffichageLCD  ' Ligne 1



      SPI_TX32(Freq_32)       'Envoie 32 bits sur le bus SPI


      FQ_UD=1                 ' Validation des données





       end if


      If counter_cursor=250  then    'Cursor OFF after some time


      end if


    if PORTB.2=1 then

    counter_cursor=0    'permet allumage curseur et raz tempo curseur

    counter_EEPROM =0   ' raz tempo pour memorisation

      while   PORTB.2=1        'Attente relachement bouton

    if counter_EEPROM=70 then  LCD_OUT(1,1,"  MEMORISATION  ") 'tempo avant mémo



      LCD_OUT(1,1,"  DDS  AD9850")

    end if

    wend   'relachement bouton


     if old_FF=0 then old_FF=1       'Mode ajustage fréquence


      LCD_CMD(_LCD_BLINK_CURSOR_ON)  'Allumage curseur

      else old_FF=0                  'Mode déplacement curseur




    end if

    End if