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

 

Par Alain Fort F1CJN, le 13 fevrier 2013       alain.fort.f1cjn@sfr.fr     

 

 

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 (27.000.000.0 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.

 

 

 

 

 

 

 

 

 

                                                           Schematic

 

 

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

' GND

' 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

 

 dim

  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

 

dim

  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_cursor)

  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)       '

  delay_ms(5)

  inc(FSR)

  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

  inc(FSR)

  next i

  LCD_CMD(_LCD_BLINK_CURSOR_ON)

  end sub

 

  sub procedure AffichageLCD

   LongWordToStr(freq_32,Freq_str) ' conversion to string

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

   LCD_CHR_CP(".")

  for I=1 to 3                ' position 3 to 6

    LCD_CHR(ligne,i+2,Freq_str[i])

  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

    LCD_CHR(ligne,i+1,Freq_str[i])

  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

  SPI1_write(Reverse)

  For i=0 to 7

  Reverse.i= Don_H.(7-i)

  next i

  SPI1_write(Reverse)

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

 donnee_WH=Hiword(Donnee)

 SPI_tx16(donnee_WL)

 SPI_tx16(donnee_WH)

end sub

 

'sub procedure SPI_tx32(dim donnee as longword)

'Dim Don_Lo,Don_Hi,Don_higher,Don_highest as byte

'Don_lo=Lo(Donnee)

'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)

'Don_Higher=Higher(Donnee)

'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

 LCD_CMD(_LCD_UNDERLINE_ON)

  '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

 

main:

  ' 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

 

  FQ_UD=0

  TRISC = 0 'sortie

  PORTC = 0 'sorties

  PORTC.2 = 0

  TRISB = 0xFF

  TRISA = 0x3f              ' entrée boutons

  PORTA = 0x00

 

  ADCON1=0x06

  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

  LCD_init()

  LCD_cmd(_LCD_CLEAR)

 

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

  delay_us(10)

  FQ_UD=0

 

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

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

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

 

  LCD_CMD(_LCD_CURSOR_OFF)

  LCD_OUT(1,1,"  DDS  AD9850")

 

  cursor_pos=13

  LCD_out(2,cursor_pos," ")

 

  counter_cursor=0

  encoderlast=0

  Freq=270000000

  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

 

 

  OLD_FF=0

 

   while true      ' boucle principale

  

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

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

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

 

    N=PORTB.0

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

    'delay_ms(1)

    Freq_old=Freq

    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 !!!

           else

              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 +

          Cursor_pos=cursor_pos-1

           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

          else

          Cursor_pos=cursor_pos+1

           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

    encoderlast=N

 

    if counter_aff>10 then      'Affichage via les interruptions TMR1

    Ligne=2

    AffichageFreq

    LCD_out(2,cursor_pos,"")

 

      '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*3)/100)

      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*7)/100000)

      Freq_32=Freq_32+((Freq*3)/1000000)

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

      Freq_32=Freq_32+((Freq*3)/100000000)

      Freq_32=Freq_32+((Freq*3)/500000000)

      'Ligne=1

      'AffichageLCD  ' Ligne 1

     

      FQ_UD=0

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

      SPI1_write(0x00)

      FQ_UD=1                 ' Validation des données

      delay_us(2)

      FQ_UD=0

     

       Counter_aff=0

       end if

   

      If counter_cursor=250  then    'Cursor OFF after some time

      LCD_CMD(_LCD_Cursor_OFF)

      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

      Write_EEPROM

      delay_ms(2000)

      LCD_OUT(1,1,"  DDS  AD9850")

    end if

    wend   'relachement bouton

      delay_ms(2)

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

      PortC.2=1

      LCD_CMD(_LCD_BLINK_CURSOR_ON)  'Allumage curseur

      else old_FF=0                  'Mode déplacement curseur

      LCD_CMD(_LCD_UNDERLINE_ON)

     PortC.2=0

 

    end if

    End if

   

  wend

end.