; 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