Friday, 21 August 2009

PIC DDS

Starting to put together code for DDS using a rotary controller. Using AD9951, PIC 16F877A, a cheap £1.09 ALPS encoder from Farnell, a 2x16LCD and a keypad £3 from Maplin.

The spec is:

1) DUAL VFO, that is VFO A, VFO B and A=B facilities.

2) UP to 50 spot frequency stored in MEMORY.

3) Three modes of operation

a) DIRECT where the frequency output has no offset displayed.

b) VFO MODE, where a frequency offset (IF) is applied VFO+IF, VFO-IF, IF-VFO).

While in this mode, VFO A or B is displayed on the top line of the LCD.

b) MEMORY MODE, where the encoder is used to select a previously saved frequency. While in this mode, MEM is displayed on the top line of the LCD.

4) KEYPAD Frequency entry and additional 7 Press buttons for other functions.

The remaining Functions included in the KEYPAD itself.

5) UP/DOWN fast search in three different steps using KEYPAD which is user selective.

6) SCAN and quick UP/DOWN search in three different steps with auto Repeat on PTT.

7) Frequencies are entered in MHz, the * is used to enter a decimal point,

and the # is used to ENTER the completed frequency.

If the first digit entered is the #, the number entered will be negative.

8) S- Meter reading from S0 to S9+40db in logarithmic db range.

9) Variable rate tuning to use cheap mechanical encoders.

10) Step size can be changed from 1Hz to MHz range.

11) RIT facility.

12) VFO copy to MEMORY and from MEMORY to any VFO (A/B) facility.

13) AM/LSB/USB/CW modes Operation with PIC pin outputs for switching.

14) Allows -Ve and +Ve IF OFFESTS. Offset frequency = any value from min RX frequency to Max 999MHz

15) 10 RX BANDPASS filter selection for RX coverage from 1.5MHz to 30MHz. Usefull to set up an All band RX.

16) BAND PASS filter switching selectivity 1Hz and 1ms switching speed, which allows cross band Operation.

17) CW OFFSET adds to TX Frequency when transmitting thus avoids modulated CW.

18) Save to MEMORY and Copy from MEMORY functions.

19) Scan VFO frequency and Scan MEMORY facility

20) Time out function for ignoring any wrong entry in system settings.

21) Auto repeat function for respective keys.

22) Display Multiplier Option to use with VHF rigs

23) Menu screen allows user to change any settings in the setup screen.

24) All settings can be changed in the SETUP screen at any time even when the DDS is in working mode including DDS chip type and Reference Clock.

25) SPLIT operation

26) CW OFFSET adds to TX Frequency when transmitting thus avoids modulated CW.

Coding PIC with MikroBasic from www.mikroe.com which creates asm which I then can compile with MPASM to HEX and burn to PIC.

Some code started out life as examples from them.

First play...to confirm can work out direction:


'PortA.0 is "A" Input
'PortA.1 is "B" Input
'"A" is direction indicator based on rising-edge of "B"; 1 = CW, 0 = CCW

dim b_old as integer
dim b_new as integer
dim counter as integer

TRISA = 000011
PORTA = 0
counter = 0

IF (PORTA AND 2) XOR b_old <> 0 THEN 'test for encoder change on "B"
b_new = PORTA AND 2
IF b_new > b_old THEN 'test if "B" is rising edge
IF (PORTA AND 1) = 1 THEN '"A" value is direction...1=CW, 0=CCW
inc(counter) 'increment counter
ELSE
dec(counter) 'decrement counter
END IF
b_old = b_new 'update b_old after changes
END IF


This little example uses the 7 seg display and one of the cheap encoders from farnell...


' Declare Variables
Dim NewRead, OldRead, TempRead, i As BYTE
Dim Rotation As Boolean

' Procedure to figure out what is the rotation direction
Sub Procedure Direction(Dim Rotation As Boolean)
If Rotation = 1 Then
Inc(i)
Else
Dec(i)
End If
End Sub

Main:

' Use PortA <1,0> as the rotary encoder inputs on PIC
TRISA = 3
TRISB = 0

' Init PIC ports as you want, except for PortA <1,0> to 0
PORTA = 0
PORTB = 0

' PortA <1,0> only required
' Read rotary encoder when the PIC is first powered on
TempRead = PORTA
NewRead = TempRead And 3

While (True)

Do

' Init both byte variables for each new reading
OldRead = NewRead
' Now start reading
TempRead = PORTA ' Read the NewRead value to see if changed
NewRead = TempRead And 3
' If not changed, keep reading
Loop Until (NewRead <> OldRead)

' Xor left bit of NewRead and right bit of OldRead
Direction (NewRead.1 Xor OldRead.0)

Wend

End.



Then DDS control of AD9834 like this:

program AD9834

dim hmsb, hlsb, lmsb, llsb as byte

sub procedure ddswrite
TRISC = TRISC and $FD
Spi_Init
ClearBit(PORTC,1) 'Sync low enable write
Spi_Write($F8) 'Setup AD9834 for writing
Spi_Write($00) 'Just padding.......
Spi_Write($33) 'Write High MSB into register $33
Spi_Write(hmsb)
Spi_Write($22) 'Write Low MSB into register $22
Spi_Write(hlsb)
Spi_Write($31) 'Write High LSB into register $31
Spi_Write(lmsb)
Spi_Write($20) 'Write Low LSB into register $20
Spi_Write(llsb)
Spi_Write($C0) 'Enable AD9834 for transmit
Spi_Write($00)' 'Just padding.......
SetBit(PORTC,1) 'Sync high disable write
end sub
main:
' while true

hmsb = 4
hlsb = 0
lmsb = 0
llsb = 0

ddswrite

' wend
end.


Using another routine called Print_Dec_16 which handles all the LCD figure justification and then putting it all together a starting place based on code by kuhng@vodamail.co.za and a couple others, will provide proper references, I get:


program AD9851
' Author M1KTA, dombaines@yahoo.com
' A simple program to enable Rotary Encoder control of an AD9851 DDS
' based on a simple program to enable KeyPad control of an AD9851 DDS
' DDS circuit from www.amqrp.org/kits/dds60
' Generate any frequency up to about 80 MHz, 1Hz steps ! (Specs 60 MHz)
' 16X2 Lcd on Port D, DDS on Port C, KeyPad on Port B
' Default startup values in eeprom, 1 MHz out, for 180 MHz Clock
' Standard Pic 16F877A, EasyPic4 connections apply
' RA0 switches display to frequency entry, RA1 changes clock value
' Both pins are normally pulled high, switches when pulled low
' KeyPad "*" deletes last keypress only, "#" writes new values to eeprom
' Port B KeyPad, pins pulled LOW !

include "Print_Dec_16" ' Xor's lcd print utility
symbol delay50 = Delay_ms(50)
symbol delay500 = Delay_ms(500)
Dim frequency, xtal, cfreq, KeyInput,KeyInputOld as longint
dim Key,Key1, byte_s as byte
dim myvar as string[11]

const KeyPad as byte[16] = (49, 50, 51, 65, 'Map 4*4 KeyPad to ascii
52, 53, 54, 66,
55, 56, 57, 67,
42, 48, 35, 68)

sub procedure Welcome ' Welcome Message
dim I as byte
lcd_cmd(LCD_CURSOR_OFF)
lcd_cmd(lcd_clear)
lcd_out(1, 4, "M1KTA")
lcd_out(2,4,"AD-9851 DDS")
For I = 0 to 3
Delay500
Next I
lcd_cmd(lcd_clear) ' Message Ends
end sub

' Read frequency value stored in eeprom
sub function eread01(dim eeprom_addr, offset as byte) as longint
For offset = 0 to 3
FSR = @ frequency + offset
INDF = EEPROM_READ(eeprom_addr + offset)
Next offset
result = frequency
delay50
end sub

' Read clock value stored in eeprom
sub function eread02(dim eeprom_addr, offset as byte) as longint
For offset = 0 to 3
FSR = @ xtal + offset
INDF = EEPROM_READ(eeprom_addr + offset)
Next offset
result = xtal
delay50
end sub

' Write new frequency to LCD
sub procedure lcdwrite01(dim yy as longint)
Lcd_Cmd(LCD_CLEAR) ' Clear display
Lcd_Cmd(LCD_CURSOR_OFF) ' Cursor off
print_dec(yy,"##.######",RJZ,myvar)
Lcd_Out(1, 4, "M1KTA")
Lcd_Out(2,3,myvar)
Lcd_Out(2,13,"MHz")
delay500
end sub

' LvZ maths, calculate divisor value
sub function CalcFreq(dim f, x as longint) as longint
dim g as float
g = ( f / x ) * $7FFFFFFF
result = longint(g) * 2
delay50
end sub

' Florin Medrea's bit swap routine, forces SPI to TX LSB first
sub function swap_bits(dim data_b as byte) as byte
dim i,j as byte
result = 0
i = 0
j = 7
while i < 8
if TestBit(data_b,i) = 1 then SetBit(result,j) end if
inc(i)
dec(j)
wend
end sub

' Reset and fill DDS data register with zero values
sub procedure ddsclear
ClearBit(PORTC,1)
Soft_Spi_Write($00)
Soft_Spi_Write($00)
Soft_Spi_Write($00)
Soft_Spi_Write($00)
Soft_Spi_Write($00)
SetBit(PortC,1)
end sub

' Write divisor and config values to DDS
sub procedure ddswrite(dim f as longint) ' Soft SPI
eread01(0, 0) ' Read eeprom for Freq
delay50
eread02(16, 0) ' Read eeprom for Clock
delay50
cfreq = CalcFreq(frequency, xtal) ' Calculate divisor
delay50
TRISC = TRISC and $FD
ClearBit(PORTC,1) 'Sync low enable write
byte_s = swap_bits(lo(f)) ' Swap over and write LSB
Soft_Spi_Write(byte_s)
byte_s = swap_bits(hi(f)) ' Swap over and write next byte
Soft_Spi_Write(byte_s)
byte_s = swap_bits(higher(f)) ' Swap over and write next byte
Soft_Spi_Write(byte_s)
byte_s = swap_bits(highest(f)) ' Swap over and write next byte
Soft_Spi_Write(byte_s)
byte_s = swap_bits($01) ' Swap over and write config byte
Soft_Spi_Write(byte_s)
SetBit(PORTC,1) ' Sync high disable write
delay50
lcdwrite01(frequency) ' Write new frequency to LCD
delay50
end sub

' Write Freq value to eeprom
sub procedure ewrite(dim xx as longint)
Eeprom_Write(0, lo(xx))
delay50
Eeprom_Write(1, hi(xx))
delay50
Eeprom_Write(2, higher(xx))
delay50
Eeprom_Write(3, highest(xx))
delay50
end sub

' Write Clock value to eeprom
sub procedure ewrite2(dim xx as longint)
Eeprom_Write(16, lo(xx))
delay50
Eeprom_Write(17, hi(xx))
delay50
Eeprom_Write(18, higher(xx))
delay50
Eeprom_Write(19, highest(xx))
delay50
end sub

'Original routine from Sorcerer, read Freq from keypad
sub Procedure Frequency_Input
Lcd_cmd(lcd_clear)
Lcd_Out(1, 4, "Freq in MHz")
Lcd_Out(2, 3, "Enter with #")
Key1 = 0
KeyInput=0
KeyInputOld=0
While Key1<>35 ' Exit on #
Key=0
while Key = 0
Key = Keypad_Released
Wend
Key1=KeyPad[Key-1]
If Key1=42 Then '* = delete (last input only!)
KeyInput=KeyInputOld
End If
If (Key1>47) and (Key1<58) then '0-9 are added in, anything else ignored
KeyInputOld = KeyInput
KeyInput = KeyInput * 10
KeyInput = KeyInput + Key1 - 48
End If
print_dec(keyinput,"##.######",RJZ,myvar)
Lcd_cmd(LCD_CURSOR_OFF)
Lcd_Out(2, 1," ")
Lcd_Out(2, 4, myvar)
delay50
Wend
ewrite(Keyinput) ' Store frequency value in eeprom
delay50
ddswrite(cfreq) ' Two writes to set registers ???
ddswrite(cfreq)
end sub

' Original routine from Sorcerer, read Clock from KeyPad
' Adjust for different Clock, or Fine Tune
sub Procedure Clock_Input
Lcd_cmd(lcd_clear)
Lcd_Out(1, 1, "Xtal Freq in MHz")
Lcd_Out(2, 3, "Enter with #")
Key1 = 0
KeyInput=0
KeyInputOld=0
While Key1<>35 ' Exit on #
Key=0
while Key = 0
Key = Keypad_Released
Wend
Key1=KeyPad[Key-1]
If Key1=42 Then '* = delete (last input only!)
KeyInput=KeyInputOld
End If
If (Key1>47) and (Key1<58) then '0-9 are added in, anything else ignored
KeyInputOld = KeyInput
KeyInput = KeyInput * 10
KeyInput = KeyInput + Key1 - 48
End If
print_dec(keyinput,"###.######",RJZ,myvar)
Lcd_cmd(LCD_CURSOR_OFF)
Lcd_Out(2, 1," ") ' Cleans Line for next display
Lcd_Out(2, 4, myvar)
delay50
Wend
ewrite2(Keyinput) ' Store clock value in eeprom
delay50
ddswrite(cfreq) ' Two writes to set registers ???
ddswrite(cfreq)
end sub

main:
ADCON1 = 7
trisa = %11111111
trisc = 0
Soft_Spi_Config(PortC,4, 2, 3) ' Required for Soft_SPI
Lcd_Init(PORTD) ' Init LCD on port D
Keypad_Init(PORTB) ' Init KeyPad port B
Welcome ' Display Welcome message
ddsclear ' Write zero values to DDS buffer
ddswrite(cfreq) ' Read stored values, write to DDS
ddswrite(cfreq) ' Two writes to set registers ????
eloop:
if porta.0 = 0 then ' Key for frequency change
Frequency_Input ' Jump to frequency keypad routine
end if
if porta.1 = 0 then ' Key for Clock change
Clock_Input ' Jump to clock keypad routine
end if
goto eloop
end.

No comments:

Post a Comment