; DDSPC4F.ASM Version for 5MHz clock G4JNT Aug 2000
;Controls AD9850 DDS with ASCII Hex commands via 19200 Baud serial interface
;D0-D4 bug fixed 2000-09-17
;Monitors state of x4 switch via PORTB change interrupt and generates
;modified control word for Freq * 4 (=0) (nb. no overflow checking)
;Clock freq info stored for reference.
;~~PC4 version adds precise timing control input with related "T" command
LIST P=16C84
; Command Protocol
; Address byte ASCII 0-9 / A - F
; followed by
; Qxxxxxxxx [cr] 32 bit frequency word
; Pxx [cr] 8 bit phase word
; U Update, do not store to EEprom
; W Update and write to EEProm
; T Update on next external timing pulse
; Y Change and store new Address
; R Readback user data info
; Kxxxxxxxx [cr] Store new user data info. in EEPROM
INCLUDE "p16c84.inc"
cblock 0x0C
BitCount
DelCount
DelCount1
DelCount2
Temp
SD ;Used in sendback routine
D0
D1
D2
D3
D4 ;Input data, final frequency / phase word
R0
R1 ;Holding reg For D0-3 sent to AD9850
R2 ;Need to be separated for x4 function
R3
C0 ;Holding registers for Clock freq info
C1
C2
C3
C4
InstAddr ;Instrument address
Counter
Flags
endc
DDSPort equ PORTA
#define RXD PORTB , 0 ;Serial data in from PC
#define TXD PORTB , 3 ;Echoed data / status out to PC
#define CTS PORTB , 4 ;Flag etc. echoed back
#define ExtTim PORTB , 1 ;External timing input
#define HexFlag Flags , 0
FData equ 0 ;DDS Data on port A
WClk equ 2
FQud equ 1
Reset equ 3
;code starts here
org 0
boot nop
clrw
movwf INTCON ;disable interrupts
goto startup ;jump to main code
;.....................................................
ints
btfss INTCON , RBIF ;test for Port B change interrupt
retfie ;any other interrupt
movlw d'92'
movwf DelCount1
SwBounce
call Delay
decfsz DelCount1 ;approx 10ms debounce delay
goto SwBounce
call SendDDS ;polarity checked in SendDDS
movlw "X"
call Send232
movlw "1"
btfss PORTB , 7 ;echo back x4 state "1" = x4
movlw "4"
call Send232
movlw 0x0D
call Send232
movlw 0x0A
call Send232
movf PORTB , W ;End mismatch
bcf INTCON , RBIF
retfie
;.....................................................
startup
bsf STATUS,RP0 ;ram page 1
movlw b'11100000' ;
movwf PORTA ;set port A0 as outputs
movlw b'10000111' ;PC interface B0-RXD, B3-TXD B4-CTS
movwf PORTB ; B1- External timing input B7-X4 switch
bcf OPTION_REG , NOT_RBPU ;enable pull ups
bcf STATUS,RP0 ;ram page 0
movlw b'10001000' ;PortB Change, enable interrupts
movwf INTCON ;GIE + RBIE
clrf DDSPort
movlw 0x0a ;initial start up delay to stabilise levels
movwf DelCount2
StartDel1
movlw 0
movwf DelCount1
StartDel2
call Delay
decfsz DelCount1 ;256 calls to delay ...
goto StartDel2
decfsz DelCount2 ;delcount2 times
goto StartDel1
call SendDDS ;send some rubbish to initialise
call Delay
call ReadEeprom ;Want this irrespective of any prog info
call SendDDS ;Boot with stored EEPROM data
call Delay
movlw 0x0A
call GetEE
andlw b'00001111' ;Mask to 0 - 15 only
movwf InstAddr ;Instrument address stored in EE loc $0A
call SendInit
call SendBack ;Send back start up values
call SendClkData
;---------------------------------------------------------------
MainLoop
call WaitData ;Look for address - ASCII digit 0 - 9
call DecodeHex
subwf InstAddr , W
btfss STATUS , Z
goto MainLoop
call Ack
call WaitData ;Get first character
bcf Temp , 5 ;Upper case conversion
movf Temp , W
sublw "Q"
btfsc STATUS , Z
goto GetQ ;Freq word, up to 4 hex digits
movf Temp , W
sublw "P"
btfsc STATUS , Z
goto GetP ;Phase word
movf Temp , W
sublw "U"
btfsc STATUS , Z
goto Update ;"U"pdates DDS chip
movf Temp , W
sublw "W"
btfsc STATUS , Z
goto WriteIt ;"W"rites EE and updates DDS chip
movf Temp , W
sublw "T"
btfsc STATUS , Z ;Updates DDS chip on low-high of external
goto TimeWrite ;timing pulse.
movf Temp , W
sublw "Y"
btfsc STATUS , Z
goto ChangeAddr
movf Temp , W
sublw "R"
btfsc STATUS , Z
goto GetR
movf Temp , W
sublw "K"
btfsc STATUS , Z
goto GetK
goto MainLoop ;Any other character, just ignore
;...............................
GetQ ;last 8 bytes sent before CR are used
clrf D0
clrf D1
clrf D2
clrf D3
QLoop
call WaitData
movf Temp , W
sublw 0x0D
btfsc STATUS , Z ;look for [cr] to terminate
goto GetOutQ
movf Temp , W
call DecodeHex ;generate binary equivalent
movwf Temp
bcf STATUS , C
rlf Temp
rlf Temp
rlf Temp
rlf Temp ; into 4 MSBs of Temp
movlw 4
movwf Counter
bcf STATUS , C ;Shouldn't be necessary, but.....
ShiftQLoop
rlf Temp
rlf D0
rlf D1 ;into 32 bit Freq register 4 bits at a time
rlf D2
rlf D3
decfsz Counter ;any less than 4 chars are right justified
goto ShiftQLoop ;any more, last 4 are taken
goto QLoop ;Loop until a [cr] is received
GetOutQ
call SendBack
goto MainLoop
;...............................
GetK ;last 8 bytes sent before CR are used
clrf C0
clrf C1
clrf C2
clrf C3
clrf C4
KLoop
call WaitData
movf Temp , W
sublw 0x0D
btfsc STATUS , Z ;look for [cr] to terminate
goto GetOutK
movf Temp , W
call DecodeHex ;generate binary equivalent
movwf Temp
bcf STATUS , C
rlf Temp
rlf Temp
rlf Temp
rlf Temp ; into 4 MSBs of Temp
movlw 4
movwf Counter
bcf STATUS , C ;Shouldn't be necessary, but.....
ShiftKLoop
rlf Temp
rlf C0
rlf C1 ;into 40 bit clock register 4 bits at a time
rlf C2
rlf C3
rlf C4
decfsz Counter ;any less than 4 chars are right justified
goto ShiftKLoop ;any more, last 4 are taken
goto KLoop ;Loop until a [cr] is received
GetOutK
call SendClkData
call WriteClockEE
goto MainLoop
;...............................
GetP ;
clrf D4 ; last byte sent is used
PLoop
call WaitData
movf Temp , W
sublw 0x0D
btfsc STATUS , Z ;look for [cr] to terminate
goto GetOutP
movf Temp , W ;generate binary equivalent
call DecodeHex
movwf Temp
bcf STATUS , C
rlf Temp
rlf Temp
rlf Temp
rlf Temp ;initially into 4 MSBs of Temp
movlw 4
movwf Counter
bcf STATUS , C
ShiftPLoop
rlf Temp
rlf D4
decfsz Counter
goto ShiftPLoop
goto PLoop
GetOutP
movlw b'11111000'
andwf D4 ;Mask out control bits in case these get sent
call SendBack
goto MainLoop
;...............................
GetR ;Readback internal info - not necessarily EEprom
call SendClkData
call SendBack
movlw "A"
call Send232
movlw "d"
call Send232
movlw "d"
call Send232
movlw "r"
call Send232
movlw "."
call Send232
movlw " "
call Send232
movf InstAddr , W ;Transfer register contents to SD so it
movwf SD ;doesn't get corrupted
swapf SD , W ;Send High nibble first
andlw 0x0F
call HexToAscii
call Send232
movf SD , W ;Now do the low nibble
andlw 0x0F
call HexToAscii
call Send232
movlw 0x0D
call Send232
movlw 0x0A
call Send232
goto MainLoop
;...............................
WriteIt
call WriteEeprom
Update
call SendDDS
call SendBack ;Echo back register contents to controller
goto MainLoop
;...............................
ChangeAddr
call WaitData ;Look for new address - ASCII digit 0 - 9
call DecodeHex
movwf InstAddr
movlw 0x0A ;Save new address
movwf EEADR
movf InstAddr , W
movwf EEDATA
call StoreEE
call SendInit ;Echoes device address
goto MainLoop
;--------------------------------------
BitDelay ;total incl call and return, (3N+5) = 44us
movlw d'17' ;19200 = 52us, -8us used in Tx/Rx routines
movwf DelCount ;baud period delay
LoopBit
decfsz DelCount
goto LoopBit
nop
return
;-------------------------
WaitData
btfss RXD ;look for start transition, low/high = 1/0
goto WaitData ;
movlw d'9' ; ( may need to adjust this)
movwf DelCount ;
nop
LoopStartBit ;this loop 3.N - 1 long = 17
decfsz DelCount
goto LoopStartBit
btfss RXD ;make sure it's still high, now centre of bit
goto WaitData ;
movlw 8 ;
movwf Counter
clrf Temp ;26us to here from transition = half bit
ByteLoop
call BitDelay
nop
bcf STATUS , C
btfss RXD
bsf STATUS , C
rrf Temp
decfsz Counter
goto ByteLoop ;this loop BitDelay + 8
movf Temp , w ;Returns with stop bit+ slack period
return ;received byte in Temp and W
;-------------------------
SendBack
movlw "Q"
call Send232
movlw " "
call Send232
movf D3 , W ;Transfer register contents to SD so they
movwf SD ;don't get corrupted
swapf SD , W ;Send High nibble first
andlw 0x0F
call HexToAscii
call Send232
movf SD , W ;Now do the low nibble
andlw 0x0F
call HexToAscii
call Send232
movf D2 , W
movwf SD
swapf SD , W
andlw 0x0F
call HexToAscii
call Send232
movf SD , W
andlw 0x0F
call HexToAscii
call Send232
movf D1 , W
movwf SD
swapf SD , W
andlw 0x0F
call HexToAscii
call Send232
movf SD , W
andlw 0x0F
call HexToAscii
call Send232
movf D0 , W
movwf SD
swapf SD , W
andlw 0x0F
call HexToAscii
call Send232
movf SD , W
andlw 0x0F
call HexToAscii
call Send232
movlw " "
call Send232
movlw " "
call Send232
movlw "P"
call Send232
movlw " "
call Send232
movf D4 , W
movwf SD
swapf SD , W
andlw 0x0F
call HexToAscii
call Send232
movf SD , W
andlw 0x0F
call HexToAscii
call Send232
movlw 0x0D ;Final [CR][LF]
call Send232
movlw 0x0A ;
call Send232
return
;----------------------------
SendClkData ;Send contents of C0-C4
movlw "K"
call Send232
movlw " "
call Send232
movf C4 , W ;Transfer register contents to SD so they
movwf SD ;don't get corrupted
swapf SD , W ;Send High nibble first
andlw 0x0F
call HexToAscii
call Send232
movf SD , W ;Now do the low nibble
andlw 0x0F
call HexToAscii
call Send232
movf C3 , W
movwf SD
swapf SD , W
andlw 0x0F
call HexToAscii
call Send232
movf SD , W
andlw 0x0F
call HexToAscii
call Send232
movf C2 , W
movwf SD
swapf SD , W
andlw 0x0F
call HexToAscii
call Send232
movf SD , W
andlw 0x0F
call HexToAscii
call Send232
movf C1 , W
movwf SD
swapf SD , W
andlw 0x0F
call HexToAscii
call Send232
movf SD , W
andlw 0x0F
call HexToAscii
call Send232
movf C0 , W
movwf SD
swapf SD , W
andlw 0x0F
call HexToAscii
call Send232
movf SD , W
andlw 0x0F
call HexToAscii
call Send232
movlw 0x0D ;Final [CR][LF]
call Send232
movlw 0x0A ;
call Send232
return
;----------------------------
Send232 ;Enter with data in W, send it to PC
movwf Temp
bsf TXD ;start bit
call BitDelay ;44us, need 52 total
movlw 8
movwf Counter
nop
nop
nop
SendDatLoop
rrf Temp ;bit into C
btfsc STATUS , C
bcf TXD
btfss STATUS , C
bsf TXD ;could have 2us jitter depending on 1/0
call BitDelay
decfsz Counter
goto SendDatLoop ;loop 8 + BitDelay long
bcf TXD ;stop bit
call BitDelay
nop
nop
nop
nop
nop
nop
return
;-------------------------
DecodeHex ;Single ASCII character to binary with error checking
;Untrapped characters remaining ...........
;at the moment Upper Case letters G-Z plus characters
; : ; < = > ? @ [ \ ] ^ _ ` will be let through.
;Chars 0 - $2F and letters "g" up
;are detected.
bsf HexFlag ;Flag for valid hex character
movwf Temp ;
movlw 0x30
subwf Temp ;subtract $30 and leave in Temp
btfss STATUS , C ;if < $30, result -ve so C=0, error, set=0
bcf HexFlag
movf Temp , W ;Test for residual from above > d54 ie > "g"
sublw d'54' ;W = 54 - W, if < 54 invalid char, -ve, C=0
btfss STATUS , C ;
bcf HexFlag
movf Temp , W
andlw 0x1F ;mask to 5 bits, resolves upper / lower case
movwf Temp ;if 0 - 9 then Temp is correct, else 7 high
movf Temp , W
sublw d'9' ;W = 9 - W
btfsc STATUS , C ;if -ve, C=0, char > 9, so subtract 7
goto ConvDone
movlw d'7'
subwf Temp ;
ConvDone
movf Temp , W ;
andlw 0x0F ;mask out any rubbish left in
return ;flag set if partly valid
;-------------------------
HexToAscii
addlw 0x30
movwf Temp
sublw 0x39
btfsc STATUS , C
goto AsciiDone
movlw 7
addwf Temp
AsciiDone
movf Temp , W
return
;-------------------------
WriteEeprom ;save registers in EEPROM
movlw 5
movwf EEADR
movf D0 , W
movwf EEDATA
call StoreEE
movlw 6
movwf EEADR
movf D1 , W
movwf EEDATA
call StoreEE
movlw 7
movwf EEADR
movf D2 , W
movwf EEDATA
call StoreEE
movlw 8
movwf EEADR
movf D3 , W
movwf EEDATA
call StoreEE
movlw 9
movwf EEADR
movf D4 , W
movwf EEDATA
call StoreEE
return
;-------------------------
WriteClockEE
movlw 0
movwf EEADR
movf C0 , W
movwf EEDATA
call StoreEE
movlw 1
movwf EEADR
movf C1 , W
movwf EEDATA
call StoreEE
movlw 2
movwf EEADR
movf C2 , W
movwf EEDATA
call StoreEE
movlw 3
movwf EEADR
movf C3 , W
movwf EEDATA
call StoreEE
movlw 4
movwf EEADR
movf C4 , W
movwf EEDATA
call StoreEE
return
;-------------------------
StoreEE ;writes the data byte in Data reg to EEprom EEADR
bsf STATUS , RP0
bsf EECON1 , WREN ;Enable EEprom writing
movlw 0x55
movwf EECON2
movlw 0xAA
movwf EECON2
bsf EECON1 , WR
EEwaitW
btfss EECON1 , EEIF
goto EEwaitW
bcf EECON1 , EEIF ;has to be cleared manually
bcf EECON1 , WR
bcf STATUS , RP0
return
;-------------------------
ReadEeprom ;read stored data and update registers
movlw 5
call GetEE
movwf D0
movlw 6
call GetEE
movwf D1
movlw 7
call GetEE
movwf D2
movlw 8
call GetEE
movwf D3
movlw 9
call GetEE
movwf D4
movlw 0
call GetEE
movwf C0
movlw 1
call GetEE
movwf C1
movlw 2
call GetEE
movwf C2
movlw 3
call GetEE
movwf C3
movlw 4
call GetEE
movwf C4
return
;-------------------------
GetEE
movwf EEADR
bsf STATUS,RP0 ;ram page 1
bsf EECON1 , RD
bcf STATUS , RP0 ;ram page 0
movf EEDATA , W
return
;-------------------------
Delay
movlw 0x20 ;delay = 5N + 5 = 105us
movwf DelCount
DelLoop
nop
nop
decfsz DelCount
goto DelLoop
return
;-------------------------
SendDDS
call DataTo9850 ;Load data into 9850
bsf DDSPort , FQud ;+ve edge of FQud sets 9850 internal reg
call Delay
bcf DDSPort , FQud
bcf DDSPort , FData
call Delay ;total of 43 calls to Delay
return
;-------------------------
TimeWrite
call DataTo9850 ;Load data into 9850
WaitTim
btfss ExtTim ;Wait for a high on external input
goto WaitTim ;before updating DDS chip
bsf DDSPort , FQud ;+ve edge of FQud sets 9850 internal reg
call Delay ;approx 3 clocks after external pulse
bcf DDSPort , FQud
bcf DDSPort , FData
movlw "T" ;Echo back T[cr][lf] to show update done
call Send232
movlw 0x0D
call Send232
movlw 0x0A
call Send232
goto MainLoop
;--------------------------
DataTo9850
movf D0 , W ;Load data into holding register for D0-3
movwf R0 ;needed so D0-3 don't get destroyed
movf D1 , W
movwf R1
movf D2 , W
movwf R2
movf D3 , W
movwf R3
btfsc PORTB , 7 ;B7=0 > Freq * 4 B7=1 > Freq
goto NoShift
bcf STATUS , C
rlf R0
rlf R1
rlf R2
rlf R3
bcf STATUS , C
rlf R0
rlf R1
rlf R2
rlf R3 ;Multiply freq word by 4
NoShift
movf R0 , W
movwf Temp
call Write9850
movf R1 , W
movwf Temp
call Write9850
movf R2 , W
movwf Temp
call Write9850
movf R3 , W
movwf Temp
call Write9850
movf D4 , W ;Phase word
movwf Temp
call Write9850
bcf DDSPort , WClk
return
;--------------------------
Write9850
movlw 8
movwf BitCount
Loop9850
bcf DDSPort , WClk ;data changes with -ve edge of clock
rrf Temp
btfss STATUS , C
bcf DDSPort , FData
btfsc STATUS , C
bsf DDSPort , FData
call Delay
bsf DDSPort , WClk
call Delay
decfsz BitCount
goto Loop9850
return
;-------------------------
SendInit
movlw 0x0D
call Send232
movlw 0x0A
call Send232 ;Startup message
movlw "9"
call Send232
movlw "8"
call Send232
movlw "5"
call Send232
movlw "0"
call Send232
movlw " "
call Send232
movlw "D"
call Send232
movlw "D"
call Send232
movlw "S"
call Send232
movlw " "
call Send232
movlw "C"
call Send232
movlw "o"
call Send232
movlw "n"
call Send232
movlw "t"
call Send232
movlw "r"
call Send232
movlw "o"
call Send232
movlw "l"
call Send232
movlw "l"
call Send232
movlw "e"
call Send232
movlw "r"
call Send232
movlw " " ;Instrument address
call Send232
movlw "A"
call Send232
movlw "d"
call Send232
movlw "d"
call Send232
movlw "r"
call Send232
movlw "."
call Send232
movlw " "
call Send232
movf InstAddr , W ;Transfer register contents to SD so it
movwf SD ;doesn't get corrupted
swapf SD , W ;Send High nibble first
andlw 0x0F
call HexToAscii
call Send232
movf SD , W ;Now do the low nibble
andlw 0x0F
call HexToAscii
call Send232
movlw 0x0D
call Send232
movlw 0x0A
call Send232
return
;------------------------
Ack ;Send Z[cr][lf] back to PC to acknowledge etc.
movlw "Z"
call Send232
movlw 0x0D
call Send232
movlw 0x0A
call Send232
return
;------------------------
end