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