; TERRY COTTON ZS1AYJ ; PIC CONTROLLER FOR AVR BASED DDS FUNCTION GENERATOR ; VERSION 1.0 LAST AMENDED NOVEMBER 2005 ;************************************************************** ; Much of the code in this module is original. However, other ; parts such as the LCD & Math routines are based on code ; published in various magazine projects, particularly EPE magazine ; over the last few years. All credit to the original authors. ;************************************************************** ; PIC PORT USAGE ; ; THIS PROJECT WAS GRADUALLY DEVELOPED RATHER THAN DESIGNED. ; A PIC16F876 WAS CHOSEN AT THE OUTSET AS IT HAD ENOUGH PORT ; PINS TO ACCOMODATE WHATEVER THE PROJECT FINALLY TURNED OUT TO BE ! ; THIS IS WHAT TRANSPIRED: ; ; PORTA RA0 WAVEFORM SELECTION BUTTON ; RA1 BEEP SOUND ON BUTTON PRESS ; RA2-5 UNUSED ; PORTB RB0 LCD DATA D4 ; RB1 LCD DATA D5 ; RB2 LCD DATA D6 ; RB3 LCD DATA D7 ; RB4 LCD RS LINE ; RB5 LCD E LINE ; RB6-7 FREE FOR IN-CIRCUIT PROGRAMMING ; PORTC RC0 0.1 HZ BUTTON ; RC1 1 HZ BUTTON ; RC2 10 HZ BUTTON ; RC3 100 HZ BUTTON ; RC4 1000 HZ BUTTON ; RC5 UP/DOWN BUTTON ; RC6 19.2Kb SERIAL DATA TO AT90S2313 PIN 2 ; RC7 UNUSED ;************************************************************** include "P16F876A.inc" list p=16F876, r=dec, st=on ;radix 10, produce symbol table ;code protection off ;power-on timer on ;watchdog timer off ;HS Oscillator ;Low voltage programming off __config _CP_OFF & _PWRTE_ON & _WDT_OFF & _HS_OSC & _LVP_OFF ; BANK SWITCHING MACROS BANK0 macro bcf STATUS,RP1 ;BANK0 bcf STATUS,RP0 endm BANK1 macro bcf STATUS,RP1 ;BANK1 bsf STATUS,RP0 endm BANK2 macro bsf STATUS,RP1 ;BANK2 bcf STATUS,RP0 endm BANK3 macro bsf STATUS,RP1 ;BANK3 bsf STATUS,RP0 endm ;************************************************************** ; General Purpose register definitions ;************************************************************** CBLOCK 020h ;************************************************************** ; REGISTERS A to C. THESE ARE USED BY ; PETER HEMSLEY'S 32-BIT MATHS ROUTINES. FOR THIS ; APPLICATION THEY HAVE BEEN EXTENDED TO 48 BITS ; BY TERRY COTTON - ZS1AYJ ;************************************************************** REGA0 ;lsb REGA1 REGA2 REGA3 REGA4 REGA5 ;msb REGB0 ;lsb REGB1 REGB2 REGB3 REGB4 REGB5 ;msb REGC0 ;lsb REGC1 REGC2 REGC3 REGC4 REGC5 ;msb DSIGN ;Digit Sign. 0=positive,FF(or non zero)=negative DIGIT1 ;MSD DIGIT2 DIGIT3 DIGIT4 DIGIT5 ;Decimal digits DIGIT6 DIGIT7 DIGIT8 DIGIT9 DIGIT10 DIGIT11 DIGIT12 DIGIT13 DIGIT14 DIGIT15 ;LSD MTEMP MCOUNT DCOUNT OVERFLOW POINT ;************************************************************** ; Frequency register. This is scaled by 10 to give frequency to 1 ; decimal place. It is incremented/decremented by the push buttons ; and the result is displayed as the frequency on the LCD. Then the ; phase increment required for the DDS unit to generate this frequency ; is calculated and sent by the serial interface to the DDS unit. ; Although the maximum value in the frequency register cannot exceed ; 1 million (100000.0 Hz) the register is 48 bits wide for compatability ; with the math routines. ;************************************************************** Freq0 ;Freq lsd Freq1 Freq2 Freq3 Freq4 Freq5 ;Freq msd LOOP ; loop counter 1 - general LOOPA ; loop counter 2 - LCD use only MSLOOP ; 1 mS delay routine loop VARLOOP ; Vardelay loop STORE ; store used by lcdout RSLINE ; bit 4 RS line flag for LCD WAVE ; Waveform. 0=Sine 1=Saw ; 2=Triangle 3=Square PHASERES0 ; lsb of phase resolution PHASERES1 PHASERES2 ; msb of phase resolution TEMP ; general temp store BEEPOUTER ; Beep routine loop values BEEPINNER EEPROMDATA ; Data byte for writes to EEPROM ENDC ;************************************************************** ORG 0 ; Reset Vector address GOTO 5 ; go to PIC address location 5 ORG 4 ; Interrupt Vector address GOTO 5 ; go to PIC address location 5 ORG 5 ; Start of Program Memory at location 5 clrf intcon ; disable all interrupts clrf PORTA clrf PORTB clrf PORTC BANK1 movlw 6 ; configure all porta pins movwf adcon1 ; as digital inputs movlw 1 ; Port A0 as input movwf TRISA ; Port A1-A5 as outputs clrf TRISB ; PORTB as output movlw 0ffh movwf TRISC ; Port C as inputs movlw B'10000000' ; move ratio value into W movwf OPTION_REG ; light pull-ups off - bit 7 high BANK0 call SETBAUD ; setup the serial port ; baud rate to 19.2 Kb call LCDSETUP ; setup LCD clrf WAVE ; Initialise to sine wave b main ; bypass tables ;************************************************************** ; LCD initialisation table ;************************************************************** TABLCD addwf PCL,F ; LCD initialisation table retlw B'00110011' ; initialise lcd - first byte retlw B'00110011' ; 2nd byte (repeat of first) retlw B'00110010' ; set for 4-bit operation retlw B'00101100' ; set for 2 lines retlw B'00000110' ; set entry mode to increment each address retlw B'00001100' ; set display on, cursor off, blink off retlw B'00000001' ; clear display retlw B'00000010' ; return home, cursor & RAM to zero retlw 0 ; end initialisation table ;************************************************************** ; LCD SETUP ROUTINE ;************************************************************** LCDSETUP movlw 200 call vardelay ; perform first 1/5th sec delay LCDSET clrf LOOP ; clr LCD set-up loop clrf RSLINE ; clear RS line for command send LCDST2 movf LOOP,W ; get table address call TABLCD ; get set-up instruction iorlw 0 ; test if zero returned to bz LCDST3 ; indicate end of table data call LCDOUT ; not zero. send to lcd incf LOOP,F ; inc loop b LCDST2 LCDST3 movlw 200 call vardelay ; yes, perform second 1/5th sec delay ; to allow final LCD command to occur ; (it takes longer than the rest) return ;end of lcd setup ;************************************************************** ; START OF PROGRAM MAIN CODE ;************************************************************** Main ; The ZS1AYJ unit uses a 11.0592 MHz oscillator for the AT90S2313 ; as per the Jesper Hensen design simply because I also had a number ; of these at hand (recovered from redundant PC boards). Any other ; crystal frequency can be used. Simply change the phase resolution ; value as per the calculation below. Remember to also change the ; nominal value displayed by the ADJUST routine. ; The PIC PIC18F876 uses a 4 MHz clock crystal. The accuracy of the ; PIC clock is not important but if a different frequency is used ; then the SETBAUD routine might require changing to maintain ; accurate 19.2Kb baud rate. ; The nominal 11.059 MHz crystal measured 11.061 Mhz in practice. ; Phase Resolution = 11061000 / 9 / 2^24 = 0.0732541 Hz ; This is scaled by 1 million so that we can use fixed point ; math throughout. ; For our purpose 73254 (11E26 hex) is accurate enough. ; When the PIC is first programmed this value is stored in the first ; 3 bytes of EEPROM. It can be changed by holding the waveform ; button pressed at switch-on ; Take this stored EEPROM value and move into Phaseres0, 1 & 2 movlw 0 ;eeprom address 0 call geteeprom movwf phaseres2 movlw 1 ;eeprom address 1 call geteeprom movwf phaseres1 movlw 2 ;eeprom address 2 call geteeprom movwf phaseres0 ; Check if Waveform button is pressed at switch-on and enter adjust mode ; if so. Adjust mode allows the phase resolution value to be manually ; adjusted if required. i.e the software equivalent of adjusting the ; crystal trimming capacitor on the dds unit. call adjustmode ; The frequency register is scaled by 10 to allow accuracy ; to 1 decimal place. Initialise frequency to 1000.0 ; in the frequency register freq0 to freq5 clrf freq5 clrf freq4 clrf freq3 clrf freq2 movlw 027h ; set freq register to 10000 movwf freq1 ; i.e 1000.0 Hz movlw 010h movwf freq0 call SetFreq ; set dds frequency to that in ; the freq register clrf wave ; init waveform to sine movlw '1' call serialsend ; send sine command to DDS call lcdline2 call displaywave ; display sine on LCD Mainloop btfsc Porta,0 ; Waveform button pressed ? b CheckTenthHz ; branch if not call beep incf wave ; bump waveform number movlw 3 ; Don't allow wave to andwf wave,f ; be > 3 movf wave,w addlw '1' call serialsend ; Send reqd waveform No to DDS call lcdline2 call displaywave ; Display new waveform description CheckTenthHz btfsc Portc,0 ; 0.1 Hz button pressed ? b Check1Hz ; branch if not call beep call INCRTenthHz ; increment by 0.1 Hz b MainLoopEnd Check1Hz btfsc Portc,1 ; 1 Hz button pressed ? b Check10Hz ; branch if not call beep call INCR1Hz ; increment by 1 Hz b MainLoopEnd Check10Hz btfsc Portc,2 ; 10 Hz button pressed ? b Check100Hz ; branch if not call beep call INCR10Hz ; increment by 10 Hz b MainLoopEnd Check100Hz btfsc Portc,3 ; 100 Hz button pressed ? b Check1000Hz ; branch if not call beep call INCR100Hz ; increment by 100 Hz b MainLoopEnd Check1000Hz btfsc Portc,4 ; 1 KHz button pressed ? b MainloopEnd ; branch if not call beep call INCR1000Hz ; increment by 1000 Hz MainLoopEnd movlw 200 ; delay for debounce call vardelay goto mainloop ; Loop checking buttons ;************************************************************** ; OUTPUT CHARACTER IN W TO LCD ;************************************************************** LCDOUT movwf STORE ; temp store data movlw 50 ; set minimum time between sending full bytes to movwf LOOPA ; LCD - value of 50 seems OK for this prog with DELAY decfsz LOOPA,F ; XTAL clk of up to 5MHz, possibly 5.5MHz goto DELAY ; keep decrementing LOOPA until zero call SENDIT ; send MSB call SENDIT ; send LSB return SENDIT swapf STORE,F ; swap data nibbles movf STORE,W ; get data byte andlw 15 ; get nibble from byte (LSB) iorwf RSLINE,W ; OR the RS bit movwf PORTB ; output the byte bsf PORTB,5 ; set E line high bcf PORTB,5 ; set E line low return ;************************************************************** ; LCD COMMANDS LINE1, LINE2, CLEAR ;************************************************************** LCDCLEAR movlw b'00000001' b LCDCMD LCDLINE1 movlw b'10000000' b LCDCMD LCDLINE2 movlw b'11000000' LCDCMD BCF RSLINE,4 ;sets LCD command/line call LCDOUT ;and outputs cmmand code to LCD BSF RSLINE,4 ;set RS flag movlw 20 call vardelay return ;************************************************************** ; OUTPUT THE CHARACTER IN W TO SERIAL PORT ;************************************************************** SERIALSEND btfss PIR1,TXIF ; wait for TXIF bit 4 to go high goto SERIALSEND ; (showing TXREG empty) movwf TXREG ; put val (held in W) in TXREG ready for transmission return ;************************************************************** ; SET SERIAL OUTPUT BAUD RATE ;************************************************************** ; as shown is set for 4 MHz crystal rate as used for the ; PIC16F876 used in this design SETBAUD BANK1 movlw 12 ; BRG for 19.2 Kb from 4 MHz, brgh=1 movwf SPBRG movlw B'00000100' ; set sync=0, brgh=1 + ninth bit not set movwf TXSTA bcf PIE1,TXIE ; clear interrupt bit (bit TXIE) BANK0 movlw B'10000000' ; set SPEN Bit of RCSTA reg movwf RCSTA BANK1 bsf TXSTA,TXEN ; enable transmission (bit TXEN) BANK0 return ;************************************************************** ; Show waveform type on LCD ;************************************************************** Displaywave movf wave,w bz displaysine sublw 1 bz displaysaw movf wave,w sublw 2 bz displaytri b displaysquare displaysine movlw 4 call lcdspaces movlw 'S' call lcdout movlw 'i' call lcdout movlw 'n' call lcdout movlw 'e' call lcdout movlw ' ' call lcdout movlw 'W' call lcdout movlw 'a' call lcdout movlw 'v' call lcdout movlw 'e' call lcdout movlw 3 call lcdspaces return displaysaw movlw 5 call lcdspaces movlw 'S' call lcdout movlw 'a' call lcdout movlw 'w' call lcdout movlw 't' call lcdout movlw 'o' call lcdout movlw 'o' call lcdout movlw 't' call lcdout movlw 'h' call lcdout movlw 3 call lcdspaces return displaytri movlw 5 call lcdspaces movlw 'T' call lcdout movlw 'r' call lcdout movlw 'i' call lcdout movlw 'a' call lcdout movlw 'n' call lcdout movlw 'g' call lcdout movlw 'l' call lcdout movlw 'e' call lcdout movlw 3 call lcdspaces return displaysquare movlw 2 call lcdspaces movlw 'S' call lcdout movlw 'q' call lcdout movlw 'u' call lcdout movlw 'a' call lcdout movlw 'r' call lcdout movlw 'e' call lcdout movlw ' ' call lcdout movlw 'W' call lcdout movlw 'a' call lcdout movlw 'v' call lcdout movlw 'e' call lcdout movlw 3 call lcdspaces return ;************************************************************** ; OUTPUT NUMBER OF SPACES SPECIFIED BY W TO LCD ;************************************************************** LCDspaces movwf temp LCDspaces1 movlw ' ' call lcdout decf temp,f bnz lcdspaces1 return ; *************************************************** ; PETER HEMSLEY'S 32-BIT MATHS ROUTINES. FOR THIS ; APPLICATION THEY HAVE BEEN UPGRADED TO 48 BITS ; BY TERRY COTTON - ZS1AYJ ; *************************************************** ; SIGNED MULTIPLY REGA * REGB -> REGA ; Return carry set if overflow ; *************************************************** multiply clrf MTEMP ;Reset sign flag call chksgna ;Make REGA positive skpc call chksgnb ;Make REGB positive skpnc return ;Overflow call movac ;Move REGA to REGC call cleara ;Clear product movlw D'47' ;Loop counter movwf MCOUNT muloop call slac ;Shift left product and multiplicand rlf REGC5,w ;Test MSB of multiplicand skpnc ;If multiplicand bit is a 1 then call addba ;add multiplier to product skpc ;Check for overflow rlf REGA5,w skpnc return decfsz MCOUNT,f ;Next goto muloop btfsc MTEMP,0 ;Check result sign call negatea ;Negative return ; *************************************************** ; SIGNED DIVIDE REGA / REGB -> REGA ; Remainder in REGC ; Return carry set if overflow or division by zero ; *************************************************** divide clrf MTEMP ;Reset sign flag movf REGB0,w ;Trap division by zero iorwf REGB1,w iorwf REGB2,w iorwf REGB3,w iorwf REGB4,w iorwf REGB5,w sublw 0 skpc call chksgna ;Make dividend (REGA) positive skpc call chksgnb ;Make divisor (REGB) positive skpnc return ;Overflow clrf REGC0 ;Clear remainder clrf REGC1 clrf REGC2 clrf REGC3 clrf REGC4 clrf REGC5 movlw D'48' ;Loop counter movwf MCOUNT dvloop call slac ;Shift dividend (REGA) msb into remainder (REGC) movf REGB5,w ;Test if remainder (REGC) >= divisor (REGB) subwf REGC5,w skpz goto dtstgt movf REGB4,w subwf REGC4,w skpz goto dtstgt movf REGB3,w subwf REGC3,w skpz goto dtstgt movf REGB2,w subwf REGC2,w skpz goto dtstgt movf REGB1,w subwf REGC1,w skpz goto dtstgt movf REGB0,w subwf REGC0,w dtstgt skpc ;Carry set if remainder >= divisor goto dremlt movf REGB0,w ;Subtract divisor (REGB) from remainder (REGC) subwf REGC0,f movf REGB1,w skpc incfsz REGB1,w subwf REGC1,f movf REGB2,w skpc incfsz REGB2,w subwf REGC2,f movf REGB3,w skpc incfsz REGB3,w subwf REGC3,f movf REGB4,w skpc incfsz REGB4,w subwf REGC4,f movf REGB5,w skpc incfsz REGB5,w subwf REGC5,f clrc bsf REGA0,0 ;Set quotient bit dremlt decfsz MCOUNT,f ;Next goto dvloop btfsc MTEMP,0 ;Check result sign call negatea ;Negative return ; *************************************************** ; SIGNED BINARY TO DECIMAL ; REGA -> DIGITS 1 (MSD) TO 15 (LSD) & DSIGN ; DSIGN = 0 if REGA is positive, FF if negative ; Return carry set if overflow ; Uses FSR register ; *************************************************** bin2dec call clrdig ;Clear all digits clrf MTEMP ;Reset sign flag call chksgna ;Make REGA positive skpnc goto BLANKIT ;Overflow movlw D'48' ;Loop counter movwf MCOUNT b2dloop rlf REGA0,f ;Shift msb into carry rlf REGA1,f rlf REGA2,f rlf REGA3,f rlf REGA4,f rlf REGA5,f movlw DIGIT15 movwf FSR ;Pointer to digits movlw D'15' ;15 digits to do movwf DCOUNT adjlp rlf INDF,f ;Shift digit and carry 1 bit left movlw -D'10' addwf INDF,w ;Check and adjust for decimal overflow skpnc movwf INDF decf FSR,f ;Next digit decfsz DCOUNT,f goto adjlp decfsz MCOUNT,f ;Next bit goto b2dloop btfsc MTEMP,0 ;Check sign comf DSIGN,f ;Negative clrc BLANKIT: movlw 48 iorwf DIGIT1,F iorwf DIGIT2,F iorwf DIGIT3,F iorwf DIGIT4,F iorwf DIGIT5,F iorwf DIGIT6,F iorwf DIGIT7,F iorwf DIGIT8,F iorwf DIGIT9,F iorwf DIGIT10,F iorwf DIGIT11,F iorwf DIGIT12,F iorwf DIGIT13,F iorwf DIGIT14,F iorwf DIGIT15,F movlw 15 ; blank leading zeros movwf LOOP movlw DIGIT1 movwf FSR BLANK: movf LOOP,W xorwf POINT,W btfsc STATUS,Z return movf INDF,W andlw 15 btfss STATUS,Z return bcf INDF,4 incf FSR,F decfsz LOOP,F goto BLANK movlw 48 iorwf DIGIT15,F return ; *************************************************** ; Check sign of REGA and convert negative to positive ; Used by multiply, divide, bin2dec ; *************************************************** chksgna rlf REGA5,w skpc return ;Positive call negatea ;Negative so negate REGA return ; *************************************************** ; Negate REGA ; Used by chksgna, multiply, divide, mod, bin2dec, dec2bin ; *************************************************** negatea movf REGA5,w ;Save sign in w andlw 0x80 comf REGA0,f ;2's complement comf REGA1,f comf REGA2,f comf REGA3,f comf REGA4,f comf REGA5,f incfsz REGA0,f goto nega1 incfsz REGA1,f goto nega1 incfsz REGA2,f goto nega1 incfsz REGA3,f goto nega1 incfsz REGA4,f goto nega1 incf REGA5,f nega1 incf MTEMP,f ;flip sign flag addwf REGA5,w ;Return carry set if -2147483648 return ; *************************************************** ; Set all digits of 15 char buffer to 0h characters ; Used by bin2dec ; *************************************************** clrdig clrf DSIGN clrf DIGIT1 clrf DIGIT2 clrf DIGIT3 clrf DIGIT4 clrf DIGIT5 clrf DIGIT6 clrf DIGIT7 clrf DIGIT8 clrf DIGIT9 clrf DIGIT10 clrf DIGIT11 clrf DIGIT12 clrf DIGIT13 clrf DIGIT14 clrf DIGIT15 return ; *************************************************** ; Shift left REGA and REGC ; Used by multiply, divide ; *************************************************** slac rlf REGA0,f rlf REGA1,f rlf REGA2,f rlf REGA3,f rlf REGA4,f rlf REGA5,f rlf REGC0,f rlf REGC1,f rlf REGC2,f rlf REGC3,f rlf REGC4,f rlf REGC5,f return ; *************************************************** ;Check sign of REGB and negative convert to positive ;Used by multiply, divide, mod ; *************************************************** chksgnb rlf REGB5,w skpc return ;Positive call negateb ;negative so negate REGB return ; *************************************************** ; Negate REGB ; Used by chksgnb, subtract, multiply, divide, mod ; *************************************************** negateb movf REGB5,w ;Save sign in w andlw 0x80 comf REGB0,f ;2's complement comf REGB1,f comf REGB2,f comf REGB3,f comf REGB4,f comf REGB5,f incfsz REGB0,f goto negb1 incfsz REGB1,f goto negb1 incfsz REGB2,f goto negb1 incfsz REGB3,f goto negb1 incfsz REGB4,f goto negb1 incf REGB5,f negb1 incf MTEMP,f ;flip sign flag addwf REGB5,w ;Return carry set if -2147483648 return ; *************************************************** ; Move REGA to REGC ; used by multiply ; *************************************************** movac movf REGA0,w movwf REGC0 movf REGA1,w movwf REGC1 movf REGA2,w movwf REGC2 movf REGA3,w movwf REGC3 movf REGA4,w movwf REGC4 movf REGA5,w movwf REGC5 return ; *************************************************** ; Move REGC to REGA ; used by multiply ; *************************************************** movca movf REGC0,w movwf REGA0 movf REGC1,w movwf REGA1 movf REGC2,w movwf REGA2 movf REGC3,w movwf REGA3 movf REGC4,w movwf REGA4 movf REGC5,w movwf REGA5 return ; *************************************************** ; Clear REGA ; Used by multiply, sqrt ; *************************************************** cleara clrf REGA0 clrf REGA1 clrf REGA2 clrf REGA3 clrf REGA4 clrf REGA5 return ; *************************************************** ; Clear REGB ; *************************************************** CLEARB clrf REGB0 clrf REGB1 clrf REGB2 clrf REGB3 clrf REGB4 clrf REGB5 return ; *************************************************** ; Add REGB to REGA (Unsigned) ; Used by add, multiply, ; *************************************************** addba movf REGB0,w ;Add byte 0 addwf REGA0,f movf REGB1,w ;Add byte 1 skpnc ;No carry_in, so just add incfsz REGB1,w ;Add carry_in to REGB addwf REGA1,f ;Add and propagate carry_out movf REGB2,w ;Add byte 2 skpnc incfsz REGB2,w addwf REGA2,f movf REGB3,w ;Add byte 3 skpnc incfsz REGB3,w addwf REGA3,f movf REGB4,w ;Add byte 4 skpnc incfsz REGB4,w addwf REGA4,f movf REGB5,w ;Add byte 5 skpnc incfsz REGB5,w addwf REGA5,f return ; *************************************************** ; SIGNED SUBTRACT REGA - REGB -> REGA ; Return carry set if overflow ; *************************************************** subtract call negateb ;Negate and add skpnc return ;Overflow ; *************************************************** ; SIGNED ADD REGA + REGB -> REGA ;Return carry set if overflow ; *************************************************** add movf REGA5,w ;Compare signs xorwf REGB5,w movwf MTEMP call addba ;Add REGB to REGA clrc ;Check signs movf REGB5,w ;If signs are same xorwf REGA5,w ;so must result sign btfss MTEMP,7 ;else overflow addlw 0x80 return ; *************************************************** ; MULTIPLY REGA BY 1 MILLION ; *************************************************** multiplymillion clrf regb5 clrf regb4 clrf regb3 movlw 0fh movwf regb2 movlw 042h movwf regb1 movlw 040h movwf regb0 call multiply return ; *************************************************** ; MOVE FREQUENCY TO REGA ; *************************************************** freq2rega movf freq0,W movwf REGA0 movf freq1,W movwf REGA1 movf freq2,W movwf REGA2 movf freq3,W movwf REGA3 movf freq4,W movwf REGA4 movf freq5,W movwf REGA5 return ; *************************************************** ; MOVE REGA TO FREQUENCY ; *************************************************** rega2freq movf rega0,W movwf freq0 movf rega1,W movwf freq1 movf rega2,W movwf freq2 movf rega3,W movwf freq3 movf rega4,W movwf freq4 movf rega5,W movwf freq5 return ; *************************************************** ; WORK REG TO REGB ; *************************************************** Work2Regb call CLEARB movwf REGB0 return ; *************************************************** ; MOVE PHASE RESOLUTION VALUE TO REGB ; *************************************************** PhaseRes2RegB clrf regb5 clrf regb4 clrf regb3 movf phaseres2,w movwf regb2 movf phaseres1,w movwf regb1 movf phaseres0,w movwf regb0 return ; *************************************************** ; CALCULATE THE PHASE REGISTER VALUE WHICH CORRESPONDS ; TO THE FREQUENCY IN THE FREQREG. WHEN CALCULATED THIS ; WILL BE SENT TO THE AT90S2313 TO CHANGE FREQUENCY ; *************************************************** SetFreq call freq2rega ; Freq to RegA call multiplymillion ; scale by 1 million call phaseres2regb ; phase resolution to regb call divide ; calculate phase acc value movlw 5 ; to 1 decimal place call work2regb ; add .5 to round call addba call clearb ; clear register b movlw 10 ; divide by 10 movwf regb0 call divide ; phase increment value is now in ; REGA3 to REGA0 movlw 's' ; send s+4 binary digits to call serialsend ; the serial port movf rega3,w call serialsend movf rega2,w call serialsend movf rega1,w call serialsend movf rega0,w call serialsend call displayfreq ; display the new frequency on lcd return ; *************************************************** ; DISPLAY THE FREQUENCY HELD IN THE FREQREG ; ON TOP LINE OF LCD ; *************************************************** DisplayFreq call freq2rega ; convert freq to decimal call bin2dec call lcdline1 movlw 2 ; 2 leading spaces call lcdspaces movf digit9,w ; output digits to lcd call lcdout movf digit10,w call lcdout movf digit11,w call lcdout movf digit12,w call lcdout movf digit13,w call lcdout movlw ' ' ; don't suppress a zero before subwf digit14,w ; the decimal point bz displayfreq1 movf digit14,w b displayfreq2 displayfreq1 movlw '0' displayfreq2 call lcdout movlw '.' call lcdout movf digit15,w call lcdout movlw ' ' call lcdout movlw 'H' call lcdout movlw 'z' call lcdout movlw 3 ; 3 trailing spaces call lcdspaces return ; *************************************************** ; DELAY APPROXIMATELY 1 mS WITH 4 MHZ CLOCK ; *************************************************** msdelay movlw 248 movwf msloop msdelay1 nop decfsz msloop b msdelay1 nop nop nop return ; *************************************************** ; DELAY APPROXIMATELY 1 mS x THE VALUE IN W ; *************************************************** VarDelay movwf varloop Vardelay1 call msdelay decfsz varloop b vardelay1 return ; *************************************************** ; INCREASE FREQ BY 0.1 HZ ; *************************************************** INCRtenthHz call CLEARB ; set up regb with the value movlw 1 ; to be added/subtracted movwf regb0 ; freq reg is scaled by 10 so add 1 call changefreq return ; *************************************************** ; INCREASE OR DECREASE FREQ BY 1 HZ ; *************************************************** INCR1Hz call CLEARB movlw 0ah ; freq reg is scaled by 10 so add movwf regb0 ; 10 (0A hex) call changefreq ; return ; *************************************************** ; INCREASE OR DECREASE FREQ BY 10 HZ ; *************************************************** INCR10Hz call CLEARB movlw 064h ; freq reg is scaled by 10 so add movwf regb0 ; 100 (64 hex) call changefreq ; return ; *************************************************** ; INCREASE OR DECREASE FREQ BY 100 HZ ; *************************************************** INCR100Hz call CLEARB movlw 03h ; freq reg is scaled by 10 so add movwf regb1 ; 1000 (3E8 hex) movlw 0E8h movwf regb0 call changefreq ; return ; *************************************************** ; INCREASE OR DECREASE FREQ BY 1000 HZ ; *************************************************** INCR1000Hz call CLEARB movlw 027h ; freq reg is scaled by 10 so add movwf regb1 ; 10000 (2710 hex) movlw 010h movwf regb0 call changefreq ; return ; *************************************************** ; INCREASE OR DECREASE FREQ BY VALUE IN REG B ; *************************************************** ChangeFreq call freq2rega ; move freq reg to rega btfss portc,5 ; check if up/down button pressed b ChangeFreq1 ; branch if pressed call addba ; up/down button not pressed b changefreq2 ; so increment frequency ChangeFreq1 call subtract ; up/down button pressed b changefreq2 ; so decrement frequency Changefreq2 Call AdjustLimits ; Check if freq outside limits call rega2freq ; and adjust if neccessary call setfreq ; send frequency change to dds return ; *************************************************** ; CHECK IF REGA IS WITHIN FREQUENCY LIMITS ; 0.1 Hz to 100 KHz AND ADJUST IF NECCESSARY ; *************************************************** AdjustLimits call movac ; preserve rega call million2b ; 1 Million (100 KHz) to regb incf regb0,f ; bump by 1 for upper limit comparison call subtract btfss rega5,7 b FreqTooHigh ; branch if above upper limit call movca ; frequency is not above the upper limit call clearb ; check lower limit. frequency must not incf regb0 ; be less than 1. Check this by call subtract ; subtracting 1 and check for negative btfsc rega5,7 b FreqTooLow ; branch if negative (too low) call movca return FreqTooHigh call movca ; restore freq to rega call million2b call subtract return FreqTooLow call movca ; restore freq to rega call million2b ; 1 million (100KHz) to regb call addba return ; *************************************************** ; MOVE 1 MILLION (0F4240h) TO REGB. THIS REPRESENTS ; 100 KHz AND IS USED FOR UPPER LIMIT CHECKING & ADJUSTMENT ; *************************************************** MILLION2B call clearb movlw 0Fh movwf regb2 movlw 42h movwf regb1 movlw 40h movwf regb0 return ; *************************************************** ; BEEP ON EACH BUTTON PUSH. PULSES A SMALL PIEZO ; SOUNDER CONNECTED TO RA1 ; *************************************************** BEEP movlw 255 movwf beepouter beep1 bsf porta,1 call beepinnerdelay bcf porta,1 call beepinnerdelay decf beepouter,f bnz beep1 return beepinnerdelay movlw 45 movwf beepinner beepinnerdelay1 decf beepinner,f bnz beepinnerdelay1 return ; *************************************************** ; READ DATA FROM EEPROM ROUTINE FOR PIC16F87x DEVICES ; ENTER WITH EEPROM ADDRESS IN W ; RETURNS WITH DATA IN W ; *************************************************** GETEEPROM BANK2 movwf EEADR ;copy W into EEADR to set eeprom address BANK3 bcf EECON1,EEPGD ;point to data memory bsf EECON1,RD ;enable read flag BANK2 movf EEDATA,W ;read eeprom data now in EEDATA into W BANK0 return ; *************************************************** ; WRITE DATA TO EEPROM ROUTINE FOR PIC16F87x DEVICES ; ENTER WITH EEPROM ADDRESS IN W AND DATA TO BE ; WRITTEN IN VARIABLE EEPROMDATA ; *************************************************** SETEEPROM BANK2 movwf EEADR ;copy W into EEADR to set eeprom address BANK0 MOVF EEPROMDATA,W ;get data value from STORE1 and hold in W BANK2 movwf EEDATA ;copy W into eeprom data byte register BANK3 bcf EECON1,EEPGD ;point to Data memory bsf EECON1,WREN ;enable write flag MANUAL movlw 55h ;these lines cause the action required by movwf EECON2 ;by the eeprom to store the data in EEDATA movlw 0AAh ;at the address held by EEADR. movwf EECON2 bsf EECON1,WR ;set the ``perform write'' flag BANK0 CHKWRT btfss PIR2,EEIF ;wait until bit 4 of PIR2 is set goto CHKWRT bcf PIR2,EEIF ;clear bit 4 of PIR2 return ;************************************************************** ; Show nominal phase increment value on LCD line 1 ;************************************************************** displaynominal call lcdline1 movlw 'N' call lcdout movlw 'o' call lcdout movlw 'm' call lcdout movlw 'i' call lcdout movlw 'n' call lcdout movlw 'a' call lcdout movlw 'l' call lcdout movlw 4 call lcdspaces movlw '7' call lcdout movlw '3' call lcdout movlw '2' call lcdout movlw '5' call lcdout movlw '4' call lcdout return ;************************************************************** ; Show adjusted phase increment value on LCD line 2 ;************************************************************** displayadjusted call lcdline2 movlw 'A' call lcdout movlw 'd' call lcdout movlw 'j' call lcdout movlw 'u' call lcdout movlw 's' call lcdout movlw 't' call lcdout movlw 'e' call lcdout movlw 'd' call lcdout movlw 3 call lcdspaces call cleara ; move phase incr value to rega movf phaseres2,w movwf rega2 movf phaseres1,w movwf rega1 movf phaseres0,w movwf rega0 call bin2dec ; convert to decimal movf digit11,w call lcdout movf digit12,w call lcdout movf digit13,w call lcdout movf digit14,w call lcdout movf digit15,w call lcdout return ; *************************************************** ; CHECK IF WAVEFORM BUTTON IS PRESSED AT SWITCH-ON ; AND ENTER ADJUST MODE IF SO ; *************************************************** AdjustMode btfsc Porta,0 ; Waveform button pressed ? return ; exit if not call beep call displaynominal ; display nominal phase increment value call displayadjusted ; display adjusted phase increment value Adjust1 btfsc Portc,1 ; 1 Hz button pressed ? b adjust1 ; branch if not call beep call cleara ; clear rega call clearb ; set regb = 1 incf regb0,f btfss portc,5 ; check if up/down button pressed b Adjust2 ; branch if pressed call addba ; up/down button not pressed so add ; regb (1) to rega (0) to give rega = 1 b Adjust3 ; so increment frequency Adjust2 call subtract ; up/down button pressed so subtract ; regb (1) from rega (0) to give rega = -1 Adjust3 call Phaseres2regb call addba ; add the +1 or -1 to phase increment movf rega2,w ; move rega back as the new phase movwf phaseres2 ; increment movf rega1,w movwf phaseres1 movf rega0,w movwf phaseres0 call displayadjusted ; display adjusted phase increment value movf phaseres2,w ; write new phase increment to eeprom movwf eepromdata movlw 0 call seteeprom movf phaseres1,w movwf eepromdata movlw 1 call seteeprom movf phaseres0,w movwf eepromdata movlw 2 call seteeprom movlw 200 call vardelay b Adjust1 ; loop until switch-off org 2100h Phaseincr DE 1h,1Eh,26h ; Nominal value of phase increment 73254 (11E26h) for 11.061 MHz Xtal ; This can be modified for fine tuning by holding the waveform button ; pressed at switch-on. The value is modified with the 1 Hz button ; in conjunction with up/down. The new value is stored in EEPROM ; Switch off & on again for normal operation using the new value end