augustinetez
Active Member
This is probably going to be another one of those "it's staring me in the face" moments and will serve me right for doing three things at once, but could somebody run through this and see if anything twigs.
What I'm doing - converting code from one of my older projects that runs fine on a 12F629 to a 16F1827. Note - done in assembly!
The problem is - sometimes the rotary encoder works straight up and other times it takes quite few seconds to become active - almost like an interrupt is running and timing out.
This happens immediately on power up, once the encoder starts working it is fine from then on. I have changed the encoder, done meter and scope tests and it is all doing the right thing electrically.
The encoder code is something Mike (Pommie) wrote a little while ago, I have an LED on one of the Port pins that I'm using to try and work out where things are going astray and it appears to be in the Poll_encoder routine starting at line 542. When the encoder is not working, the code runs from the beginning of the routine on through a table call, back to the encoder routine but fails to exit in either up or down mode <- reading the code below should explain that better.
The commented out code is either yet to be modified or removed.
What I'm doing - converting code from one of my older projects that runs fine on a 12F629 to a 16F1827. Note - done in assembly!
The problem is - sometimes the rotary encoder works straight up and other times it takes quite few seconds to become active - almost like an interrupt is running and timing out.
This happens immediately on power up, once the encoder starts working it is fine from then on. I have changed the encoder, done meter and scope tests and it is all doing the right thing electrically.
The encoder code is something Mike (Pommie) wrote a little while ago, I have an LED on one of the Port pins that I'm using to try and work out where things are going astray and it appears to be in the Poll_encoder routine starting at line 542. When the encoder is not working, the code runs from the beginning of the routine on through a table call, back to the encoder routine but fails to exit in either up or down mode <- reading the code below should explain that better.
The commented out code is either yet to be modified or removed.
C-like:
;
;********************************************************************************
; *
; COPYRIGHT NOTICE *
; *
; Copyright T Mowles VK5TM September 2021 *
; *
; *
; This software and any derivatives of it are free for personal use only. *
; Commercial use in any form is expressly forbidden including, but not *
; limited to, kits based on this and any derivatives of this software *
; without specific written consent. *
; *
; This software is, in part, based on and modified from, the works of: *
; *
; Curtis W. Preuss - WB2V *
; Bob Okas (SK) - W3CD *
; Bruce Stough - AA0ED *
; Craig Johnson - AA0ZZ *
; *
; *******************************************************************************
;
; ----- SELECT ONE OF THESE DDS TYPES AND TURN THE OTHER OFF !! ---------
#define AD9850 ; Using the AD9850
;#define AD9851 ; Using the AD9851
;
;********************************************************************************
;
;
;********************************************************************************
;********************************************************************************
;
; Target Controller PIC16F1827
; __________
; DDS_LOAD---RA2 |1 18| RA1---DDS_CLK
; DDS_DATA---RA3 |2 17| RA0---SPARE
; CAL SW-----RA4 |3 16| RA7---SPARE
; SPARE------RA5 |4 15| RA6---SPARE
; Ground-----Vss |5 14| VDD---+5 V
; ENCODER----RB0 |6 13| RB7---SPARE
; ENCODER----RB1 |7 12| RB6---LED STEP 10kHz
; STEP SW----RB2 |8 11| RB5---LED STEP 1kHz
; SPARE------RB3 |9 10| RB4---LED STEP 10Hz
; ----------
;
; *******************************************************************************
; * Device type and options *
; *******************************************************************************
;
processor 16F1827
radix dec
errorlevel -207 ; Skip found label after column 1
errorlevel -302 ; Skip out of bank nuisance messages
errorlevel -303 ; Skip program word too large. Truncated to core size
;
; *******************************************************************************
; * Configuration fuse information for 16F1827 *
; *******************************************************************************
;
include <P16F1827.INC>
; __CONFIG _CONFIG1, _FOSC_INTOSC & _WDTE_OFF & _PWRTE_ON & _MCLRE_OFF & _CP_OFF & _CPD_OFF & _BOREN_OFF & _CLKOUTEN_ON & _IESO_OFF & _FCMEN_OFF
__CONFIG _CONFIG1, _FOSC_INTOSC & _WDTE_OFF & _PWRTE_ON & _MCLRE_OFF & _CP_OFF & _CPD_OFF & _BOREN_OFF & _CLKOUTEN_OFF & _IESO_OFF & _FCMEN_OFF
__CONFIG _CONFIG2, _WRT_OFF & _PLLEN_OFF & _STVREN_OFF & _BORV_LO & _LVP_OFF
;
; *******************************************************************************
;
; *******************************************************************************
; * General equates. These may be changed to accommodate the reference clock *
; * frequency, the desired upper frequency limit, and the default startup *
; * frequency. *
; *******************************************************************************
;
#ifdef AD9850
; For 125 MHz Oscillator =======
ref_osc_3 equ 0x22 ; Most significant osc byte
ref_osc_2 equ 0x5C ; Next byte
ref_osc_1 equ 0x17 ; Next byte
ref_osc_0 equ 0xD0 ; Least significant byte
#endif
#ifdef AD9851
; For 180 MHz (30 MHz clock and 6x multiplier)
ref_osc_3 equ 0x17 ; Most significant osc byte
ref_osc_2 equ 0xDC ; Next byte
ref_osc_1 equ 0x65 ; Next byte
ref_osc_0 equ 0xDE ; Least significant byte
#endif
; Change limits to suit
;
; Limit_0...3 contains the upper limit frequency as a 32 bit integer.
;
limit_hi_3 equ 0x00 ; Most significant byte for 5.5 MHz
limit_hi_2 equ 0x53 ; Next byte
limit_hi_1 equ 0xEC ; Next byte
limit_hi_0 equ 0x60 ; Least significant byte
;limit_3 equ 0x00 ; Most significant byte for 5.5 MHz
;limit_2 equ 0x53 ; Next byte
;limit_1 equ 0xEC ; Next byte
;limit_0 equ 0x60 ; Least significant byte
;
; Low_limit_3..0 contains the lower frequency limit as a 32 bit integer
;
limit_low_3 equ 0x00 ; Most significant byte for 5.0 MHz
limit_low_2 equ 0x4C ; Next byte
limit_low_1 equ 0x4B ; Next byte
limit_low_0 equ 0x40 ; Least significant byte
;
; Default contains the default startup frequency as a 32 bit integer.
; This will be overwritten by the frequency save routine in normal use.
;
default_3 equ 0x00 ; Most significant byte for 5.0 MHz
default_2 equ 0x4C ; Next byte
default_1 equ 0x4B ; Next byte
default_0 equ 0x40 ; Least significant byte
;
; Frequency at which Calibration will take place
;
cal_freq_3 equ 0x00 ; Most significant byte for 5.250 MHz
cal_freq_2 equ 0x50 ; Next byte
cal_freq_1 equ 0x1B ; Next byte
cal_freq_0 equ 0xD0 ; Least significant byte
;
EEstartup_adr equ 4 ; Location of startup frequency in EEPROM
;
; *******************************************************************************
; * Setup the initial constant, based on the frequency of the reference *
; * oscillator. This can be tweaked with the calibrate function. *
; *******************************************************************************
;
ORG 0xF000 ; For 16F1827
; ref_osc bytes must be first 4 bytes of EEPROM
DATA ref_osc_0
DATA ref_osc_1
DATA ref_osc_2
DATA ref_osc_3
; startup frequency bytes must be next 4 bytes of EEPROM
DATA default_0 ; startup -> freq_0
DATA default_1 ; startup -> freq_1
DATA default_2 ; startup -> freq_2
DATA default_3 ; startup -> freq_3
DATA 0
;
; *******************************************************************************
; * Assign names to IO pins. *
; *******************************************************************************
#DEFINE DDS_clk PORTA,1 ; AD9850/AD9851 write clock
#DEFINE DDS_dat PORTA,3 ; AD9850/AD9851 serial data input
#DEFINE DDS_load PORTA,2 ; Update pin on AD9850/AD9851
;
; 16F1827 Oscillator setup
; OSCCON - Oscillator control reg.
; ---------------------------------
; SPLLEN b7 enable PLL x4
; 1 = enabled 0 = disabled
; IRCF | b6-3 frequency selection
; 1111 = 16MHz HF
; 1110 = 8 or 32MHz HF
; 1101 = 4MHz HF
; 1100 = 2MHz HF
; 1011 = 1MHz HF
; 1010 = 500kHz HF
; 1001 = 250kHz HF
; 1000 = 125kHz HF
; 0111 = 500kHz MF (default)
; 0110 = 250kHz MF
; 0101 = 125kHz MF
; 0100 = 62.5kHz MF
; 0011 = 31.25kHz HF
; 0010 = 31.25kHz MF
; 000x = 31.25kHz LF
; Reserved B2 reserved, 0
; SCS B1 0-: 1x = int. OSC.
; 01 = Timer1 oscillator
; 00 = determined by FOSC <2:0> in Configuration
; POR default 00111-00 500 kHz (POR = Power On Reset)
OSCCONVAL EQU b'01101000' ; 4MhZ CLOCK
;
; *******************************************************************************
; * Allocate variables in general purpose register space *
; *******************************************************************************
;
CBLOCK 0x20 ; Start Data Block
AD9851_0 ; AD9850/AD9851 control word
AD9851_1 ; (5 bytes)
AD9851_2
AD9851_3
AD9851_4
fstep_0 ; Frequency inc/dec
fstep_1 ; (4 bytes)
fstep_2
fstep_3
low_limit_3 ; Low frequency limit
low_limit_2 ; (4 bytes)
low_limit_1
low_limit_0
mult_count ; Used in calc_dds_word
bit_count ; "
byte2send ;
osc_temp_0 ; Oscillator frequency
osc_temp_1 ; (4 bytes)
osc_temp_2
osc_temp_3
timer1 ; Used in delay routines
timer2 ; "
dir ; for encoder routine
previous
clicks
temp ; for encoder routine
saved ; Flags for frequency save routine
; saved,0 used in read encoder routine to jump flag for frequency save
; =1 calibrate mode active
; =0 calibrate mode not active
; saved,2 used in interupt routine
CNT1 ; Counter for interrupt routine
CNT2 ; " " " "
cal_flag ; Flag to test if CAL done. Bit 0 = 1 if done
; 0=10Hz 1=1kHz 2=10kHz step size
count
byte_count
regb_0
regb_1
regb_2
regb_3
ENDC ; End of Data Block
CBLOCK 0x70 ; Use bank common locations - saves bank switching
freq_0 ; Frequency (hex)
freq_1 ; (4 bytes)
freq_2 ;
freq_3 ;
osc_0 ; Current oscillator
osc_1 ; (4 bytes)
osc_2
osc_3
step_size ; 0=10Hz 1=1kHz 2=10kHz step size
ENDC ; End of Data Block
; *******************************************************************************
; * Purpose: This is the start of the program. *
; *******************************************************************************
;
ORG 0x0000
goto start ; Jump to main program
ORG 0x0004 ; interrupt routine for approx 2 second counter
; movwf w_temp ; save off current W register contents
; movf STATUS,w ; move status register into W register
; movwf status_temp ; save off contents of STATUS register
; bcf INTCON,T0IF ; Clear Timer0 interrupt flag
; decfsz CNT1,F ; Decrease CNT1. If zero, skip next instruction
; goto not_yet ; Not zero goto not yet
; decfsz CNT2,f
; goto not_yet2 ; Not zero goto not yet
; bsf saved,2 ; set interrupt timed out flag
; movlw delay ; Delay x number of seconds
; movwf CNT2
;
;not_yet2
; movlw 18
; movwf CNT1
;not_yet
; movlw 39
; movwf TMR0 ; Reload Timer0
;;
;exit_interrupt
;;
; movf status_temp,w ; retrieve copy of STATUS register
; movwf STATUS ; restore pre-isr STATUS register contents
; swapf w_temp,f
; swapf w_temp,w ; restore pre-isr W register contents
; retfie ; Enable general interrupts and return
;
LED_on macro
bsf PORTB,7
endm
LED_off macro
bcf PORTB,7
endm
enc_table
addwf PCL,f ;index into table
dt 0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0
;--------------------------------------------------------------------------------
start
clrf INTCON ; clear INTCON
clrf PORTA
clrf PORTB
clrf step_size
clrf cal_flag ;
; Set PIC oscillator frequency
banksel OSCCON ; Select OSCCON
movlw OSCCONVAL ;
movwf OSCCON ; Loads the wanted value
; Configures all I / O as digital
Banksel ANSELA
clrf ANSELA
clrf ANSELB
; Disable all wakeup pull-ups
Banksel WPUA
clrf WPUA
clrf WPUB
banksel OPTION_REG
movlw b'10000111' ; Pull-ups disabled, TMR0 clock source internal
; clock, prescaler to TMR0, set TMR0 prescaler
movwf OPTION_REG ; 1:256
;
movlw b'10110000' ; PORTA (RA0:3 & 6 outputs RA4,5,7 input)
movwf TRISA ;
movlw b'00001111' ; PORTB 0:3 & 7 inputs 4:6 outputs
movwf TRISB ; Set port B to all outputs
movlb 0 ; NOTE: Pull-up via 10k resistor all unused pins
;
;
; Initialize DDS Module with zero freq
;
clrf AD9851_0 ; AD9850/51 control word
clrf AD9851_1 ; (5 bytes)
clrf AD9851_2
clrf AD9851_3
clrf AD9851_4
call send_dds_word ; Send it to the DDS
call send_dds_word ; twice to be sure
;
movlw limit_low_3 ; Most significant byte for lower limit
movwf low_limit_3
movlw limit_low_2 ; Next byte
movwf low_limit_2
movlw limit_low_1 ; Next byte
movwf low_limit_1
movlw limit_low_0 ; Least significant byte
movwf low_limit_0
;
; Enter Calibrate Mode if GPIO,5 is low when turning the power on and CAL not already done.
; Test flag to see if CAL already done. Jump over this section if done.
; movlw 9
; bsf STATUS,RP0 ; Switch to bank 1
; movwf EEADR ; Point to flag location in EEprom
; call read_EEPROM ; Read EEprom at address 4
; bcf STATUS,RP0 ; Switch to bank 0
; movwf cal_flag ; Move data to register
; btfsc cal_flag,0 ; Test set_flag,0 - will be 0 if CAL not done
; goto read_EEocs ; or 1 if CAL already done
; btfss PORTA,4 ; Cal pushbutton pressed
; call calibrate ; Yes,call calibration routine
;
; Get the reference oscillator constant from the EEPROM.
;
read_EEocs
;
BANKSEL EEADRL ;
clrf EEADRL ; Data Memory Address to read
call read_EEPROM ; Read EEPROM
movf EEDATL,W ; Get the first osc byte
movwf osc_0 ; Save osc frequency
call read_EEPROM ; Read EEPROM
movf EEDATL,W ;
movwf osc_1 ; Save it
call read_EEPROM ; Read EEPROM
movf EEDATL,W ;
movwf osc_2 ; Save it
call read_EEPROM ; Read EEPROM
movf EEDATL,W ;
movwf osc_3 ; Save it
;
; Set the power on frequency to the defined value.
; (They always follow the osc bytes)
;
call read_EEPROM ; Read EEPROM
movf EEDATL,w ; Get the first default freq byte
movwf freq_0 ; Save it
call read_EEPROM ; Read EEPROM
movf EEDATL,w ; Get the next freq byte
movwf freq_1 ; Save it
call read_EEPROM ; Read EEPROM
movf EEDATL,w ; Get the next freq byte
movwf freq_2 ; Save it
call read_EEPROM ; Read EEPROM
movf EEDATL,w ; Get the last freq byte
movwf freq_3 ; Save it
call read_EEPROM ; Read EEPROM
movf EEDATL,w ; Get step size
movwf step_size ; Save it
movlb 0 ; Back to bank 0
call step_led
;
; Send power on frequency to the DDS chip.
;
call calc_dds_word ; Convert to delta value
call send_dds_word ; Send the power-on frequency to the
; AD9850/AD9851 in serial mode
;
; Get the power on encoder value.
movf PORTB,w
movwf previous ; Save it in ren_old
movlw b'00011000' ; Get encoder mask (GPIO,4 and GPIO,3)
andwf previous,f ; Get encoder bits and zero all other bits
clrf dir ; Clear the knob direction indicator
clrf saved ;
;
;; Setup interupt on change pins
; bsf STATUS,RP0 ; Switch to bank 1
; movlw b'00111000' ; Set GPIO,3,4 & 5 to interupt on change
; movwf IOC ;
; bcf STATUS,RP0 ; Back to bank 0
;
; Fall into the Main Program Loop
;
; *******************************************************************************
; * *
; * Purpose: This is the Main Program Loop. The program's main loop *
; * calls poll_encoder, which continuously polls the rotary shaft *
; * encoder. When the shaft encoder has changed, the direction *
; * it moved is determined and stored in last_dir. The subroutine *
; * then returns to main. *
; * *
; * The variable fstep is added or subtracted from the current *
; * VFO frequency and stored in freq. *
; * Next, the subroutine calc_dds_word is used to calculate the DDS *
; * frequency control word from the values in freq and osc. *
; * The result is stored in AD9850/AD9851. This data is transferred *
; * to the AD9851 DDS chip by calling the subroutine send_dds_word. *
; * *
; * The frequency is saved to EPROM X seconds (as set by 'delay') *
; * after the encoder stops turning and the PIC goes to sleep until *
; * the encoder is turned again *
; * *
; * Input: None. *
; * *
; * Output: None. *
; * *
; *******************************************************************************
;
; bsf INTCON,T0IE ; Enable Timer0 interrupt
; bsf INTCON,GIE ; Enable general interrupts
main
call poll_encoder ; Check for knob movement (wait there!)
; Return here when encoder change detected
; bsf STATUS,RP0 ; Switch BANK1
; bcf OPTION_REG,T0CS ; Start Timer0
; bcf STATUS,RP0 ; Switch to BANK0
; movlw 18 ; Interrupt counter for ~1 second
; movwf CNT1
; movlw delay ; Delay x number of seconds
; movwf CNT2
; movlw 39
; movwf TMR0 ; Writing this will restart Timer0 after two ins. cyc.
;
; *******************************************************************************
; Code for pushbutton selection of step size *
; Step size 10Hz/1kHz/10kHz *
; *******************************************************************************
step ;
clrf fstep_3 ;
clrf fstep_2 ;
clrf fstep_1 ;
step1
movfw step_size ; If step_size = 0
xorlw 0
btfsc STATUS,Z
goto step_10hz ; Set step to 10Hz
movfw step_size ; If step_size = 1
xorlw 1
btfsc STATUS,Z
goto step_1khz ; Set step to 1kHz
; Else step_size = 10kHz
movlw 0x10 ; 10kHz steps
movwf fstep_0 ;
movlw 0x27 ;
movwf fstep_1 ;
goto stepdir
step_10hz ;
movlw 0x0A ; 10Hz steps
movwf fstep_0 ;
goto stepdir
step_1khz
movlw 0xE8 ; 1kHz steps
movwf fstep_0 ;
movlw 0x03 ;
movwf fstep_1 ;
;
; Based on the knob direction, either add or subtract the increment,
; then update DDS.
;
stepdir
btfsc dir,0 ; Is the knob going up?
goto up ; Yes, then add the increment
down
call sub_step ; Subtract fstep from freq
goto update ; Update DDS
up
call add_step ; Add fstep to freq
call check_add ; Make sure we did not exceed the maximum
update
call calc_dds_word ; Calculate the control word for the DDS chip
call send_dds_word ; Send the control word to the DDS chip
;
goto main ; Continue main loop
;
; *******************************************************************************
; * *
; * Purpose: This routine does the following: *
; * *
; * Reads the encoder bits until a change is detected, then *
; * determines the direction the knob was moved. *
; * *
; * Input: Knob input read from GPIO *
; * ren_old -> the last encoder bits read *
; * last_dir -> the last direction moved *
; * *
; * Output: ren_new -> the current encoder bits *
; * last_dir -> the last direction (0 = down, 1 = up) *
; * *
; *******************************************************************************
;
poll_encoder
;
; btfsc saved,0 ; Test if in calibrate mode - ignore interrupt flags
; goto read_encoder
; btfsc saved,2 ; Test interrupt flag, jump over if not set
; call update_EEPROM ; Call to save freq when changed and timer timed out
;
; *******************************************************************************
; * Code for pushbutton selection of step size *
; *******************************************************************************
btfsc PORTB,2 ; Is Step switch pushed?
goto read_encoder ; No
incf step_size,f
movfw step_size ; Keep step_size in range of 0 - 2
xorlw 3
btfsc STATUS,Z
clrf step_size
step_exit
btfss PORTB,2
goto step_exit ; Wait for Step switch to be released
call step_led
read_encoder
rlf previous,f
rlf previous,w
andlw 0x0c ; Keep only bits 2 & 3
btfss PORTB,0 ; Move encoder bits
iorlw 1 ; to bits 1 & 2
btfss PORTB,1
iorlw 2
movwf previous ; Keep for next time
call enc_table
addwf clicks,f
movlw 3
xorwf clicks,w
btfsc STATUS,Z
goto exit_up
movlw 0xfc ;-4
xorwf clicks,w
btfsc STATUS,Z
goto exit_down
goto poll_encoder
exit_down
clrf clicks
bcf dir,0 ; Set "DOWN"
return
exit_up
LED_on
clrf clicks
bsf dir,0 ; Set "UP"
return
;
; *******************************************************************************
; * *
; * Set step size and output pulses to indicate step size *
; * *
; *******************************************************************************
step_led
movfw step_size ; If step_size = 0
xorlw 0
btfsc STATUS,Z
goto step_led10hz ; Set 10Hz LED
movfw step_size ; If step_size = 1
xorlw 1
btfsc STATUS,Z
goto step_led1khz ; Set 1kHz LED
;
; ; Else set 10kHz LED
;
BANKSEL LATB
bsf LATB,6 ; 10kHz LED
nop
bcf LATB,5 ; 1kHz LED
nop
bcf LATB,4 ; 10Hz LED
movlb 0
return
;
step_led10hz
BANKSEL LATB ;
bsf LATB,4
nop
bcf LATB,5
nop
bcf LATB,6
movlb 0
return
;
step_led1khz
BANKSEL LATB
bsf LATB,5
nop
bcf LATB,6
nop
bcf LATB,4
movlb 0
return
;
; *******************************************************************************
; * *
; * Purpose: This routine adds the 32 bit value of fstep to the 32 bit *
; * value in freq. When incrementing, the fstep value is a *
; * positive integer. When decrementing, fstep is the complement *
; * of the value being subtracted. *
; * *
; * Input: The 32 bit values in fstep and freq *
; * *
; * Output: The sum of fstep and freq is stored in freq. When incrementing *
; * this value may exceed the maximum. When decrementing, it may *
; * go negative. *
; * *
; *******************************************************************************
;
add_step
movf fstep_0,w ; Get low byte of the increment
addwf freq_0,f ; Add it to the low byte of freq
btfss STATUS,C ; Any carry?
goto add1 ; No, add next byte
incfsz freq_1,f ; Ripple carry up to the next byte
goto add1 ; No new carry, add next byte
incfsz freq_2,f ; Ripple carry up to the next byte
goto add1 ; No new carry, add next byte
incf freq_3,f ; Ripple carry up to the highest byte
add1
movf fstep_1,w ; Get the next increment byte
addwf freq_1,f ; Add it to the next higher byte
btfss STATUS,C ; Any carry?
goto add2 ; No, add next byte
incfsz freq_2,f ; Ripple carry up to the next byte
goto add2 ; No new carry, add next byte
incf freq_3,f ; Ripple carry up to the highest byte
add2
movf fstep_2,w ; Get the next to most significant increment
addwf freq_2,f ; Add it to the freq byte
btfss STATUS,C ; Any carry?
goto add3 ; No, add last byte
incf freq_3,f ; Ripple carry up to the highest byte
add3
movf fstep_3,w ; Get the most significant increment byte
addwf freq_3,f ; Add it to the most significant freq
return ; Return to the caller
;
; *******************************************************************************
; * *
; * Purpose: Check if freq exceeds the upper limit. *
; * *
; * Input: The 32 bit values in freq *
; * *
; * Output: If freq is below the limit, it is unchanged. Otherwise, it is *
; * set to equal the upper limit. *
; * *
; *******************************************************************************
;
check_add
;
; Check the most significant byte.
;
movlw 0xFF-limit_hi_3 ; Get (FF - limit of high byte)
addwf freq_3,w ; Add it to the current high byte
btfsc STATUS,C ; Was high byte too large?
goto set_max ; Yes, apply limit
movlw limit_hi_3 ; Get high limit value
subwf freq_3,w ; Subtract the limit value
btfss STATUS,C ; Are we at the limit for the byte?
goto exit1 ; No, below. Checks are done.
;
; Check the second most significant byte.
;
movlw 0xFF-limit_hi_2 ; Get (FF - limit of next byte)
addwf freq_2,w ; Add it to the current byte
btfsc STATUS,C ; Is the current value too high?
goto set_max ; Yes, apply the limit
movlw limit_hi_2 ; Second limit byte
subwf freq_2,w ; Subtract limit value
btfss STATUS,C ; Are we at the limit for the byte?
goto exit1 ; No, below. Checks are done.
;
; Check the third most significant byte.
;
movlw 0xFF-limit_hi_1 ; Get (FF - limit of next byte)
addwf freq_1,w ; Add it to the current byte
btfsc STATUS,C ; Is the current value too high?
goto set_max ; Yes, apply the limit
movlw limit_hi_1 ; Third limit byte
subwf freq_1,w ; Subtract limit value
btfss STATUS,C ; Are we at the limit for the byte?
goto exit1 ; No, below. Checks are done.
;
; Check the least significant byte.
;
movlw limit_hi_0 ; Fourth limit byte
subwf freq_0,w ; Subtract limit value
btfss STATUS,C ; Are we at the limit for the byte?
goto exit1 ; No, below. Checks are done.
set_max
movlw limit_hi_0 ; Get least significant limit
movwf freq_0 ; Set it in freq
movlw limit_hi_1 ; Get the next byte limit
movwf freq_1 ; Set it in freq_1
movlw limit_hi_2 ; Get the next byte limit
movwf freq_2 ; Set it in freq_2
movlw limit_hi_3 ; Get the most significant limit
movwf freq_3 ; Set it in freq_3
exit1
return ; Return to the caller
;
; *******************************************************************************
; * *
; * Function: sub_step *
; * *
; * Purpose: Subtract the increment step from freq. *
; * *
; * Input: The values in fstep and freq_3..0. *
; * *
; * Output: None *
; * *
; * Revisions: Modified for limited range VFO. 29-9-13 VK5TM *
; * *
; *******************************************************************************
;
sub_step
;
call invert_fstep ; Invert fstep_3..0 to perform the subtraction
call add_step ; Add the complement to do the subtraction
;
; *******************************************************************************
; * *
; * Function: low_limit_chk *
; * *
; * Purpose: Test the new frequency to see if it is above the lower band *
; * limit. If not, the frequency is set to the band lower limit. *
; * *
; * Input: Freq_0...3 and Low_limit_0...3 *
; * *
; * Output: Original frequency if above low limit or low_limit_0..3 in *
; * Freq_0...3 *
; * *
; *******************************************************************************
;
low_limit_chk
; Check the most significant byte.
;
btfsc freq_3,7
goto set_low ; Yes, set to lower frequency limit
movf freq_3,w
subwf low_limit_3,w
btfss STATUS,C ; Are we at the limit for the byte?
return ; No, above.
btfss STATUS,Z ; Are the bytes equal?
goto set_low ; No, so vfo_X_3 > limit_3.
;
; Check the second most significant byte when MSB equals limit_3
;
movf freq_2,w
subwf low_limit_2,w
btfss STATUS,C ; Are we at the limit for the byte?
return ; No, above.
btfss STATUS,Z ; Might they be equal?
goto set_low ; Nope, so vfo_X_2 > limit_2
;
; Check the third most significant byte.
;
movf freq_1,w
subwf low_limit_1,w
btfss STATUS,C ; Are we at the limit for the byte?
return ; No, above.
btfss STATUS,Z ; Check if the bytes are equal
goto set_low ; No, so vfo_X_1 > limit_1
;
; Check the least significant byte.
;
movf freq_0,w
subwf low_limit_0,w
btfss STATUS,C ; Are we at the limit for the byte?
return ; No, above.
;
; The frequency is below the band lower limit. Set frequency to the
; band starting frequency.
set_low
movf low_limit_0,w
movwf freq_0
movf low_limit_1,w
movwf freq_1
movf low_limit_2,w
movwf freq_2
movf low_limit_3,w
movwf freq_3
;
return ; Return to caller
;
; *******************************************************************************
; * *
; * Function: invert_fstep *
; * *
; * Purpose: Support function for sub_step and calibrate. This function *
; * negates the value of fstep_3..0 to assist in the frequency *
; * decrement. This operation is performed twice by sub_step, and *
; * is also used by calibrate. *
; * *
; * Input: fstep_3, fstep_2, fstep_1, fstep_0 *
; * *
; * Output: fstep_3..0 contain the 2's complement of their original value *
; * *
; *******************************************************************************
;
invert_fstep
; Invert the bits in
comf fstep_0,f ; fstep_0
comf fstep_1,f ; fstep_1
comf fstep_2,f ; fstep_2
comf fstep_3,f ; fstep_3
incfsz fstep_0,f ; Now incremnt fstep_0 to get 2's complement
goto invert_done ; If fstep_0 > 0, then done
; Else, there was a carry out of fstep_0
incfsz fstep_1,f ; Add 1 to fstep_1
goto invert_done ; If fstep_1 > 0, then done
; Else, there was a carry out of fstep_1
incfsz fstep_2,f ; Add 1 to fstep_2
goto invert_done ; if fstep_2 > 0, then done
; Else, there was a carry out of fstep_2
incf fstep_3,f ; Increment the high byte
;
invert_done
return ; Back to caller
;
;
; *******************************************************************************
; * *
; * Purpose: This routine is entered at start up if the calibrate pads are *
; * shorted at power-on time. *
; * *
; * The DDS chip is programmed to produce a frequency, based on the *
; * osc value stored in the EEPROM and the calibration frequency as *
; * at the head of the code. As long as the pads are shorted, the *
; * osc value is slowly altered to allow the output to be trimmed. *
; * Once the encoder is turned after the short is removed from the *
; * Cal pads, the new osc value is stored in the EEPROM and normal *
; * operation begins. *
; * *
; * Input: The original osc constant in EEPROM *
; * *
; * Output: The corrected osc constant in EEPROM *
; * *
; *******************************************************************************
;
;calibrate
; bsf saved,0 ; set flag for poll encoder routine
; bcf INTCON,GIE ; Turn off interupts to be sure
;;
; movlw cal_freq_0 ; Move Calibration frequency constants
; movwf freq_0 ; to freq_0...3 for Calibration routine.
; movlw cal_freq_1 ;
; movwf freq_1 ;
; movlw cal_freq_2 ;
; movwf freq_2 ;
; movlw cal_freq_3 ;
; movwf freq_3 ;
;;
;; Read the starting reference oscillator value from EEPROM.
;;
; bsf STATUS,RP0 ; Switch to bank 1
; clrf EEADR ; Reset the EEPROM read address
; call read_EEPROM ; Read first byte from EEPROM (all in bank 1)
; movf EEDATA,w ; Get the first osc byte
; movwf osc_0 ; Save osc frequency
; call read_EEPROM ; Read second byte from EEPROM
; movf EEDATA,w ;
; movwf osc_1 ; Save it
; call read_EEPROM ; Read third byte from EEPROM
; movf EEDATA,w ;
; movwf osc_2 ; Save it
; call read_EEPROM ; Read fourth byte from EEPROM
; movf EEDATA,w ;
; movwf osc_3 ; Save it
; bcf STATUS,RP0 ; Back to bank 0 for store
;;
;cal_loop
; call calc_dds_word ; Calculate DDS value based on current osc
; call send_dds_word ; Update the DDS chip
; call poll_encoder ; Wait until the encoder has moved.
; btfsc GPIO,5 ; Calibrate switch/jumper still set?
; goto cal_out ; No, go to exit and save values to EEPROM
; clrf fstep_3 ; Clear the three most significant
; clrf fstep_2 ; bytes of fstep
; clrf fstep_1 ;
; movlw 0x10 ;
; movwf fstep_0 ; Use small increment
; nop ; Wait a cycle
; btfsc dir,0 ; Are we moving down?
; goto faster ; No, increase the osc value
;;
;; slower
;;
; comf fstep_0,f ; Subtraction of fstep is done by
; comf fstep_1,f ; adding the twos compliment of fsetp
; comf fstep_2,f ; to osc
; comf fstep_3,f ;
; incfsz fstep_0,f ; Increment last byte
; goto faster ; Non-zero, continue
; incfsz fstep_1,f ; Increment next byte
; goto faster ; Non-zero, continue
; incfsz fstep_2,f ; Increment next byte
; goto faster ; Non-zero, continue
; incf fstep_3,f ; Increment the high byte
;faster
; movf fstep_0,w ; Get the low byte increment
; addwf osc_0,f ; Add it to the low osc byte
; btfss STATUS,C ; Was there a carry?
; goto add4 ; No, add the next bytes
; incfsz osc_1,f ; Ripple carry up to the next byte
; goto add4 ; No new carry, add the next bytes
; incfsz osc_2,f ; Ripple carry up to the next byte
; goto add4 ; No new carry, add the next bytes
; incf osc_3,f ; Ripple carry up to the highest byte
;add4
; movf fstep_1,w ; Get the second byte increment
; addwf osc_1,f ; Add it to the second osc byte
; btfss STATUS,C ; Was there a carry?
; goto add5 ; No, add the third bytes
; incfsz osc_2,f ; Ripple carry up to the next byte
; goto add5 ; No new carry, add the third bytes
; incf osc_3,f ; Ripple carry up to the highest byte
;add5
; movf fstep_2,w ; Get the third byte increment
; addwf osc_2,f ; Add it to the third osc byte
; btfss STATUS,C ; Was there a carry?
; goto add6 ; No, add the fourth bytes
; incf osc_3,f ; Ripple carry up to the highest byte
;add6
; movf fstep_3,w ; Get the fourth byte increment
; addwf osc_3,f ; Add it to the fourth byte
; goto cal_loop ; Yes, stay in calibrate mode
;;
;; Write final value to EPROM
;;
;cal_out
; bsf STATUS,RP0 ; Switch to bank 1
; movf osc_0,w ; Get the first osc byte to record
; clrf EEADR ; osc bytes start at EEPROM address 0
; movwf EEDATA ; Put byte in EEPROM write location
; call write_EEPROM ;
; movf osc_1,w ; Get the second byte to record
; movwf EEDATA ; Put byte in EEPROM write location
; call write_EEPROM ;
; movf osc_2,w ; Get the third byte to record
; movwf EEDATA ; Put byte in EEPROM write location
; call write_EEPROM ;
; movf osc_3,w ; Get the fourth byte to record
; movwf EEDATA ; Put byte in EEPROM write location
; call write_EEPROM ;
; movlw 9
; movwf EEADR ; Move to EEPROM write location
; movlw 1 ; Set cal_flag
; movwf EEDATA ; Put byte in EEprom write location
; call write_EEPROM ;
; bcf STATUS,RP0 ; Back to bank 0
; bcf saved,0 ; clear flag used poll encoder routine
; return ; Return to the caller
;
; *******************************************************************************
; * *
; * Purpose: This routine will save the current frequency in EEPROM. This *
; * frequency will then be used as the initial frequency upon start *
; * up. Frequency is automatically saved 2 seconds after encoder *
; * stops moving *
; * *
; * Input: The constants in freq_0...3 *
; * *
; * Output: None *
; * *
; *******************************************************************************
;
update_EEPROM ;
bcf INTCON,GIE ; turn interrupts off
btfsc INTCON,GIE ; See AN576 - make sure interrupts are off
goto $-2
bcf saved,2 ; clear interrupt timed out flag
banksel OPTION_REG
bsf OPTION_REG,T0CS ; Turn off Timer0
BANKSEL EEADRL ;
movlw EEstartup_adr ; Get startup address location
movwf EEADRL ; and set up for start of EEPROM writes
movf freq_0,w ; Get the first freq byte to write
movwf EEDATL ; First freq byte to EEPROM Write register
call write_EEPROM ; Write it
movf freq_1,w ; Get the second freq byte to write
movwf EEDATL ; Second freq byte to EEPROM Write register
call write_EEPROM ; Write it
movf freq_2,w ; Get the third freq byte to write
movwf EEDATL ; Third freq byte to EEPROM Write register
call write_EEPROM ; Write it
movf freq_3,w ; Get the fourth freq byte to write
movwf EEDATL ; Fourth freq byte to EEPROM Write register
call write_EEPROM ; Write it
movf step_size,w ; Move step size value to w to save in to EEprom
movwf EEDATL ;
call write_EEPROM ; Write it
movlb 0 ; Back to bank 0
bsf INTCON,GIE ; turn interrupts on
return ;
;
; *******************************************************************************
; * *
; * Sleep routine. *
; * Puts PIC to sleep and waits for encoder to move. *
; * *
; * *
; *******************************************************************************
;
;nod_off
;; Setup interupt on change pins
; bsf INTCON,GPIE ; Enable Port Change Interupt
; movf GPIO,w ; clear the change condition (see 12F629 data sheet)
; bcf INTCON,GPIF ; clear the interrupt flag
; sleep ; Night,night. Sweet dreams.
; nop ; Wake from sleep (may still be a bit drowsy)
; bcf INTCON,GPIE ; Disable Port Change Interupt
; return ; Back to work
;
; *******************************************************************************
; * *
; * Required sequence to write to EEPROM *
; * Used by update_EEPROM and calibrate routines *
; * *
; *******************************************************************************
;
write_EEPROM
bsf EECON1,WREN ; Set the EEPROM write enable bit
; Start of required sequence
movlw 0x55 ; Write 0x55 and 0xAA to EECON2
movwf EECON2 ; control register, as required
movlw 0xAA ;
movwf EECON2 ;
bsf EECON1,WR ; Set WR bit to begin write
; End of required sequence
bit_check
btfsc EECON1,WR ; Has the write completed?
goto bit_check ; No, keep checking
bcf EECON1,WREN ; Clear the EEPROM write enable bit
incf EEADR,f ; Increment the EE write address
return ; Return to the caller
;
; *******************************************************************************
; * *
; * Purpose: Read a byte of EEPROM data at address EEADR into EEDATA. *
; * *
; * Input: The address EEADR. *
; * *
; * Output: The value in EEDATA. *
; * *
; * NOTE: All in BANK 1 *
; * *
; *******************************************************************************
;
read_EEPROM
bcf EECON1,CFGS ; Deselect Config space
bcf EECON1,EEPGD ; Point to DATA memory
bsf EECON1,RD ; EE Read
incf EEADRL,f ; Increment the read address
return
;
; *******************************************************************************
; * *
; * Purpose: Multiply the 32 bit number for oscillator frequency times the *
; * 32 bit number for the displayed frequency. *
; * *
; * Input: The reference oscillator value in osc_3 ... osc_0 and the *
; * current frequency stored in freq_3 ... freq_0. The reference *
; * oscillator value is treated as a fixed point real, with a 24 *
; * bit mantissa. *
; * *
; * Output: The result is stored in AD9851_3 ... AD9851_0. *
; * *
; *******************************************************************************
;
calc_dds_word
;
clrf AD9851_0 ; Clear the AD9850/AD9851 control word bytes
clrf AD9851_1 ;
clrf AD9851_2 ;
clrf AD9851_3 ;
clrf AD9851_4 ;
movlw 0x20 ; Set count to 32 (4 osc bytes of 8 bits)
movwf mult_count ; Keep running count
movf osc_0,w ; Move the four osc bytes
movwf osc_temp_0 ; to temporary storage for this multiply
movf osc_1,w ; (Don't disturb original osc bytes)
movwf osc_temp_1 ;
movf osc_2,w ;
movwf osc_temp_2 ;
movf osc_3,w ;
movwf osc_temp_3 ;
mult_loop
bcf STATUS,C ; Start with Carry clear
btfss osc_temp_0,0 ; Is bit 0 (Least Significant bit) set?
goto noAdd ; No, don't need to add freq term to total
movf freq_0,w ; Get the "normal" freq_0 term
addwf AD9851_1,f ; Add it in to total
btfss STATUS,C ; Does this addition result in a carry?
goto add7 ; No, continue with next freq term
incfsz AD9851_2,f ; Yes, add one and check for another carry
goto add7 ; No, continue with next freq term
incfsz AD9851_3,f ; Yes, add one and check for another carry
goto add7 ; No, continue with next freq term
incf AD9851_4,f ; Yes, add one and continue
add7
movf freq_1,w ; Get the "normal" freq_0 term
addwf AD9851_2,f ; Add freq term to total in correct position
btfss STATUS,C ; Does this addition result in a carry?
goto add8 ; No, continue with next freq term
incfsz AD9851_3,f ; Yes, add one and check for another carry
goto add8 ; No, continue with next freq term
incf AD9851_4,f ; Yes, add one and continue
add8
movf freq_2,w ; Get the "normal" freq_2 term
addwf AD9851_3,f ; Add freq term to total in correct position
btfss STATUS,C ; Does this addition result in a carry?
goto add9 ; No, continue with next freq term
incf AD9851_4,f ; Yes, add one and continue
add9
movf freq_3,w ; Get the "normal" freq_3 term
addwf AD9851_4,f ; Add freq term to total in correct position
noAdd
rrf AD9851_4,f ; Shift next multiplier bit into position
rrf AD9851_3,f ; Rotate bits to right from byte to byte
rrf AD9851_2,f ;
rrf AD9851_1,f ;
rrf AD9851_0,f ;
rrf osc_temp_3,f ; Shift next multiplicand bit into position
rrf osc_temp_2,f ; Rotate bits to right from byte to byte
rrf osc_temp_1,f ;
rrf osc_temp_0,f ;
decfsz mult_count,f ; One more bit has been done. Are we done?
goto mult_loop ; No, go back to use this bit
#ifdef AD9850
movlw 0x00 ; No clock multiplier (AD9850)
#endif
#ifdef AD9851
movlw 0x01 ; Turn on 6x clock multiplier (AD9851)
#endif
movwf AD9851_4 ; Last byte to be sent
; Mult answer is in bytes _3 .. _0
return ; Done.
;
; *******************************************************************************
; * *
; * Purpose: This routine sends the AD9850/AD9851 control word to the DDS *
; * using a serial data transfer. *
; * *
; * Input: AD9851_4 ... AD9851_0 *
; * *
; * Output: The DDS chip register is updated. *
; * *
; *******************************************************************************
;
send_dds_word
movlw 5 ; Set number of control bytes to send
movwf byte_count ;
movlw LOW AD9851_0
movwf FSR0L
movlw HIGH AD9851_0
movwf FSR0H
next_byte
movf INDF0,w ;
movwf byte2send ;
movlw 0x08 ; Set counter to 8
movwf bit_count ;
next_bit
rrf byte2send,f ; Test if next bit is 1 or 0
btfss STATUS,C ; Was it zero?
goto send0 ; Yes, send zero
; BANKSEL LATA
bsf DDS_dat ; No, send one
bsf DDS_clk ; Toggle write clock
bcf DDS_clk ;
; movlb 0 ; Bank 0
goto break ;
send0
; BANKSEL LATA
bcf DDS_dat ; Send zero
bsf DDS_clk ; Toggle write clock
bcf DDS_clk ;
; movlb 0 ; Bank 0
break
decfsz bit_count,f ; Has the whole byte been sent?
goto next_bit ; No, keep going.
incf FSR0L,f
decfsz byte_count,f ;
goto next_byte ;
; BANKSEL LATA
bsf DDS_load ; Send load signal to the AD9850/AD9851
bcf DDS_load ;
; movlb 0 ; Bank 0
return ;
;
; *******************************************************************************
; * *
; * Purpose: Wait delays. *
; * *
; * *
; * Input: None *
; * *
; * Output: None *
; * *
; *******************************************************************************
;
wait_100us
movlw D'1'
movwf timer2
movlw D'31'
movwf timer1
goto loop
wait_1ms
movlw D'2'
movwf timer2
movlw D'73'
movwf timer1
goto loop
wait_10ms
movlw D'13'
movwf timer2
movlw D'251'
movwf timer1
loop
decfsz timer1,f
goto loop
decfsz timer2,f
goto loop
return
;
END