Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

ASM - Enhanced 16F and long calls - how?

augustinetez

Active Member
PIC = 16F1827 and language = ASSEMBLY.

I've been working on a PIC project for months now and it is starting to give me the screaming ad-dabs - I'm about ready to through this @#^% PC out the window.

I'll post the code once I tidy it a bit, but basically it all works fine until I added the code for the last set of functions I need to do.

Now I'm 100% sure (but as usual I could be wrong) that I'm running in to the 256 byte boundary problem, as shifting some of the code about to those block boundaries sorted some of the problems.

But I have one call that has to jump 510 locations and I think this is where the problems start.

So, how do I jump there and back, I tried using 'callw' but that just totally screws the whole thing - nothing works.

As per usual, all the seperate functions of this program, if run on their own, work as they should.

Beginning to think I should be using 18F parts - any recommendation for a drop in replacement for the 16F1827 (through hole part)?
 

Pommie

Well-Known Member
Most Helpful Member
I suspect you're running into the 2K page limit.

You can move code into the upper bank (org 2048) and place a stub in bank 0 that loads PCLATH with the appropriate value (can't remember ATM) and jumps to the code in the high bank. Note that the return contains the full address so will return to bank 0.

Mike.
 

augustinetez

Active Member
Program is only 1200 words at this stage - there is a small part of that on the second page with the correct page bits set for both the call and return and all the processing is completed there before the return.
 

Pommie

Well-Known Member
Most Helpful Member
In that case, why don't you move all your tables onto bank 2 page boundaries as that must be what's causing your problem. Or, use the FSR register to access all your tables without page boundary limits - see section 3.1.1.2 in the datasheet. Note, the MOVIW can have pre/post increment/decrement - see page 334.

Mike.
Edit, there are no bits that need setting/clearing before a return as all 15 address bits are placed on the stack.
 

augustinetez

Active Member
Let me tidy up the code and I'll post it in a little while - there's no tables other than one in program space of page 2 - you'll recognise it as soon as I post it, it's from my ADC scaling thread.
 

augustinetez

Active Member
OK, code listing below.

It's function is to set to an AD9850 DDS chip to a frequency between 5 & 5.5MHz using a rotary encoder as well as add an offset depending on the incoming voltage as read by the ADC (value between ~0.1 & 3V).

The offset is limited to +/-2500Hz with a dead band in the center (equivalent to 0Hz).

What is it actually doing:-

On start, if RIT (the ADC offset) = 0Hz, 5MHz is generated - so far so good.

If the RIT is adjusted to any value between 0Hz and +2500Hz it will add that value to the base 5MHz and it can be continuously varied between those limits OK so far.

If the RIT is adjusted to any negative value (ie 0 to -2500Hz), the generated frequency jumps to 5.032x.xxxHz (the x.xxx being the negative setting of the RIT) and when returning the RIT to just before the 0HZ point, the frequency drops back to 4.98MHz. Turning RIT negative at this point should have no effect on the 5MHz - the generated frequency should stay at 5MHz - there are limit controls further down in the program.

Turning the RIT positive again from the above commented point (4.98MHz), sometimes it will correct it's self in the + direction other times it will give the same 5.032x.xxxHz result in the + direction.

At this point in time, I am only testing the startup phase of this code and trying to get the RIT part working properly there before moving on to the remainder of the program - there is a comment section where the start up part ends that I am working on.

Code:
    list n=0    ; Turn off page breaks in the List file
;
#DEFINE    RIT
;
;********************************************************************************
;                                        *
;                COPYRIGHT NOTICE                *
;                                        *
;        Copyright T Mowles VK5TM 2022                    *
;        AD9850_VFO-16F1827.asm                        *
;                                        *
;                                        *
; *******************************************************************************
;
;********************************************************************************
;                                                                             
; Target Controller        PIC16F1827
;                __________                                         
;        RIT IN-----RA2 |1       18| RA1---SPARE
;        PWR DETECT-RA3 |2       17| RA0---DDS_LOAD
;        SPARE------RA4 |3       16| RA7---DDS_DATA
;        SPARE------RA5 |4       15| RA6---DDS_CLK
;        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
;        CAL--------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_OFF & _IESO_OFF & _FCMEN_OFF
 __CONFIG _CONFIG2, _WRT_OFF & _PLLEN_OFF & _STVREN_OFF & _BORV_LO & _LVP_OFF
;
; *******************************************************************************
;
; *******************************************************************************
; *           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
    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
    encold            ; for encoder routine
    count
    byte_count
    flags             ; RIT Flags
                ; flags,0 = 0 - RIT = 0
                ; flags,1 = 0 - RIT = same as last time
                ; flags,1 = 1 - RIT = changed
    NumH_temp
    NumL_temp
    temp
    rxf_0            ; VFO frequency if NO RIT
    rxf_1            ;     "
    rxf_2            ;     "
    rxf_3            ;    "
    ritf_0            ; VFO frequency if RIT active
    ritf_1            ;    "
    ritf_2            ;    "
    ritf_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
    NumH            ; Used in RIT routine
    NumL            ;     "         
    ENDC            ; End of Data Block
;
; *******************************************************************************
; * General equates.  These may be changed to accommodate the reference clock    *
; * frequency, the desired upper frequency limit, and the default startup    *
; * frequency.                                    *
; *******************************************************************************
;
;    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
;
;    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
;
; 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
;
#IFDEF RIT
#define center 486        ; RIT pot center value///////////////////////////
#ENDIF
;
; *******************************************************************************
; * 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        LATA,6    ; AD9850/AD9851 write clock
#DEFINE DDS_dat        LATA,7    ; AD9850/AD9851 serial data input
#DEFINE DDS_load    LATA,0    ; Update pin on AD9850/AD9851
#DEFINE    CAL_sw        PORTB,3    ; Cal switch input
#DEFINE    STEP_sw        PORTB,2    ; Step switch input
;
;
;    16F1827 Oscillator setup
    ; OSCCON - Oscillator control reg.
    ; ---------------------------------
    ; SPLLEN bit 7 enable PLL x4
    ; 1 = enabled 0 = disabled
    ; IRCF | bits 6-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 bit 2 reserved, 0
    ; SCS      bits 1-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
;
; *******************************************************************************
; * Purpose:  This is the start of the program.                    *
; *******************************************************************************
;
;
    ORG     0x0000               
    BRA    start        ; Jump to main program

    ORG     0x0004        ; interrupt routine data save
;    BRA    start
    bcf    INTCON,GIE    ; Clear Global Interrupt Enable bit
;
    BANKSEL    PIR2
    bcf    PIR2,C2IF    ; Clear comparator flag
    movlb    0
;
    clrf    PORTA
    clrf    PORTB
;
    retfie             ; Enable general interrupts and return
;
start
    clrf    INTCON        ; clear INTCON
    clrf    PORTA
    clrf    PORTB
    clrf    step_size
    clrf    NumH_temp
    clrf    NumL_temp

; Set PIC oscillator frequency
    banksel    OSCCON        ; Select OSCCON
    movlw    OSCCONVAL    ;
    movwf    OSCCON        ; Loads the wanted oscillator value

; Configures I/O as analog/digital
    Banksel    ANSELA
;    movlw    b'00001000'    ; RA3 analog all others digital
    movlw    b'00001100'    ; RA2:3 analog all others digital
    movwf    ANSELA
    clrf    ANSELB        ; PORTB all digital

; Disable all wakeup pull-ups
    Banksel    WPUA
    clrf    WPUA
    clrf    WPUB

    banksel    OPTION_REG
    movlw    b'10000000'    ; Pull-ups disabled
    movwf    OPTION_REG    ;
;
;    movlw    b'00111110'    ; PORTA (RA0 & 6:7 outputs RA1:5 inputs)
    movlw    b'00111110'    ; ##TEST TEMP## PORTA (RA0 & 6:7 outputs RA1:5 inputs)
    movwf    TRISA        ;
    movlw    b'10001111'    ; PORTB 0:3 & 7 inputs 4:6 outputs
    movwf    TRISB        ; NOTE: Pull-up via 10k resistor all unused pins
;
    BANKSEL    FVRCON
;    movlw    b'10001100'    ; FVR on, Comparator ref 4.096V
    movlw    b'10001000'    ; FVR on, Comparator ref 2.048V
    movwf    FVRCON
;
    movlb    0
    movlw    40        ; Delay for FVR to stabilise
    movwf    temp
    decfsz    temp,f
    BRA    $-1

    BANKSEL CM1CON0
    clrf    CM1CON0
    clrf    CM1CON1

    movlw    b'10100011'
    movwf    CM2CON1        ; Positive interrupt on
                ; Negative interrupt off
                ; C2VP connects to FVR
                ; C2VP connects to FVR
                ; Unimplemented
                ; Unimplemented
                ; C2VN connects to C12IN3- pin - RA3
                ; C2VN connects to C12IN3- pin - RA3

    movlw    b'10000110'
    movwf    CM2CON0        ; Comparator enabled
                ; Comparator Output bit polarity
                ; Comparator Output internal
                ; Comparator output is not inverted
                ; Unimplemented
                ; Comparator operates in normal power, higher speed mode
                ; Comparator hysteresis enabled
                ; Comparator output to Timer1 and I/O pin is asynchronous


    movlb    0
;
;    Initialize DDS Module with zero freq
;
    clrf    AD9851_0    ; AD9850/51 control words
    clrf    AD9851_1    ; (5 bytes)
    clrf    AD9851_2
    clrf    AD9851_3
    clrf    AD9851_4
    call    send_dds_word    ; Send it to the DDS twice to be sure
    call    send_dds_word    ;
;
;       Enter Calibrate Mode if PORTB,3 is low when turning the power on

    btfss    CAL_sw        ; 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
;
#IFDEF RIT
    call    freq_to_rxf    ; Load rxf registers with start-up frequency
;    call    freq_to_ritf    ; Load ritf registers with start-up frequency

    BANKSEL    ADCON1        ; Set up ADC
    movlw    b'11000000'    ; right justify, FOSC/4 vdd and vss vref
    movwf    ADCON1        ;

    BANKSEL    ADCON0        ;
    movlw    b'00001001'    ; select channel an2 & turn adc on
    movwf    ADCON0        ;
    movlb    0

rit_test
    movlw    50        ; Delay for ADC to stabilise
    movwf    count
    decfsz    count,f
    BRA    $-1

    PAGESEL    rit
    call    rit        ; Get RIT value
    PAGESEL    $

    btfss    flags,0        ; RIT = 0 if flags,0 = 0
    BRA    exit_start    ; No RIT shift, start with saved frequency
    call    stepdir        ; Else add/subtract RIT to start frequency

#ENDIF
;
exit_start
    call    update_dds

;PIC Time Delay = 0.09999800 s with Osc = 4000000 Hz
    movlw    D'130'        ;///Testing RIT function
    movwf    timer2        ;///Testing RIT function
    movlw    D'221'        ;///Testing RIT function
    movwf    timer1        ;///Testing RIT function
loop
    decfsz    timer1,1    ;///Testing RIT function
    goto    loop        ;///Testing RIT function
    decfsz    timer2,1    ;///Testing RIT function
    goto    loop

    call    rxf_to_freq    ;///Testing RIT function
    BRA    rit_test       ;///Testing RIT function
;
;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
;    Currently doesn't progress past this point as testing RIT function
;    operation during the start up phase but it does use routines past this point.
;||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

    movf    PORTB,w        ; Get the power on encoder value
    movwf   encold        ; Save it
    movlw    b'00000011'    ; Get encoder mask (PORTB,0 and PORTB,1)
    andwf   encold,f    ; Get encoder bits and zero all other bits
    clrf    dir        ; Clear the knob direction indicator

    clrf    PIR2        ; Clear interrupt flags in PIR2
    BANKSEL    PIE2
    bsf    PIE2,C2IE    ; Turn on Comparator 2 interrupt
    bsf    INTCON,PEIE    ; Turn on Periperal interrupts
    bsf    INTCON,GIE    ; Enable general interrupts
    movlb    0
;
; *******************************************************************************
; *                                        *
; * 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 '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.    *
; *                                        *
; *******************************************************************************
;
main
    call    poll_encoder    ; Check for knob movement (wait there!)     
                ; Return here when encoder or RIT change detected
#IFDEF RIT
    btfsc    flags,1        ; Has RIT changed?
    BRA    rit_adjust    ; Yes
#ENDIF
    call    step        ; No
    call    stepdir
    call    update_dds
    BRA    main

#IFDEF RIT
rit_adjust
    call    rxf_to_freq    ; move base frequency minus old RIT to freq_0:3
    btfsc    flags,0        ; If RIT = 0 send base frequency to DDS
    call    stepdir
    call    update_dds
    BRA    main
#ENDIF
;
; *******************************************************************************
;    Code for pushbutton selection of step size                *
;    Step size 10Hz/1kHz/10kHz                        *
; *******************************************************************************
step                ;
    clrf    fstep_3        ;
    clrf    fstep_2        ;
    clrf    fstep_1        ;
    clrf    fstep_0        ;

step1
    movfw    step_size    ; If step_size = 0
    xorlw    0
    btfsc    STATUS,Z
    BRA    step_10hz    ; Set step to 10Hz

    movfw   step_size    ; If step_size = 1
    xorlw   1
    btfsc   STATUS,Z
    BRA    step_1khz    ; Set step to 1kHz

                ; Else step_size = 10kHz

    movlw    0x10        ; 10kHz steps
    movwf    fstep_0        ;
    movlw    0x27        ;
    movwf    fstep_1        ;
    return

step_10hz            ;
    movlw    0x0A        ; 10Hz steps
    movwf    fstep_0        ;
    return

step_1khz
    movlw    0xE8        ; 1kHz steps
    movwf    fstep_0        ;
    movlw    0x03        ;
    movwf    fstep_1        ;
    return
;
;    Based on the knob direction, either add or subtract the increment,
;    then update DDS.
;
stepdir
    btfsc    dir,0        ; Is the knob going up?
    BRA    up        ; Yes, then add the increment
down
    call    sub_step    ; Subtract fstep from freq
    return
up
    call    add_step    ; Add fstep to freq
    call    check_add    ; Make sure we did not exceed the maximum

    return

update_dds
    call    calc_dds_word    ; Calculate the control word for the DDS chip
    call    send_dds_word    ; Send the control word to the DDS chip

    return
;
; *******************************************************************************
; *                                        *
; * Purpose:    Poll encoder                            *
; *                                        *
; *******************************************************************************
;
poll_encoder
;
    btfss    CAL_sw        ; Test if in calibrate mode
    BRA    read_encoder
;
; *******************************************************************************
; *    Code for pushbutton selection of step size                *
; *******************************************************************************
;
    btfsc    STEP_sw        ; Is Step switch pushed?

#IFDEF RIT
    BRA    read_rit
#ELSE
    BRA    read_encoder    ;
#ENDIF

    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    STEP_sw
    BRA    step_exit    ; Wait for Step switch to be released
    call    step_led

#IFDEF RIT
read_rit
    PAGESEL    rit        ;
    call    rit
    PAGESEL    $
;    TEST FLAG TO SEE IF RIT VALUE CHANGED - IF YES RETURN ELSE CARRY ON TO READ ENCODER
    btfsc    flags,0
    return
#ENDIF

;
read_encoder
    movf    PORTB,w        ; load switch data
    andlw    b'00000011'    ; mask encoder B and A switches
    xorwf    encold,w    ; same as last reading?
    btfsc    STATUS,Z
    BRA    poll_encoder    ; yes, branch (no change), else
    xorwf    encold,w    ; restore encoder bits in W
    rrf    encold,f    ; prep for B-old ^ A-new
    xorwf    encold,f    ; encold bit 0 = direction
    rrf    encold,f    ; now Carry bit = direction
    movwf    encold        ; update encold (new BA bits)
 ;
 ;  encoder position has changed but we only act on a change
 ;  that occurs when the encoder falls into one of the detent
 ;  positions (we ignore the changes between detents)
 ;
    xorlw    b'00000011'    ; detent position?
    btfss    STATUS,Z
    BRA    read_encoder    ; No, go test again     
    bcf    dir,0        ; Set direction bit based on value in STATUS,C
    btfsc    STATUS,C    ; 1 = UP
    bsf    dir,0
    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
    BRA    step_led10hz    ; Set 10Hz LED

    movfw   step_size    ; If step_size = 1
    xorlw   1
    btfsc   STATUS,Z
    BRA    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.                                                    *
; *                                         *
; *******************************************************************************
;
    org    0x100
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?
    BRA    add1        ; No, add next byte
    incfsz    freq_1,f    ; Ripple carry up to the next byte
    BRA    add1        ; No new carry, add next byte
    incfsz    freq_2,f    ; Ripple carry up to the next byte
    BRA    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?
    BRA    add2        ; No, add next byte
    incfsz    freq_2,f    ; Ripple carry up to the next byte
    BRA    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?
    BRA    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.                *
; *          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?
    BRA    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?
    BRA    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?
    BRA    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?
    BRA    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?
    BRA    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?
    BRA    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?
    BRA    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
;
; *******************************************************************************
; *                                        *
; * Purpose:    Subtract the increment step from freq.                *
; *                                        *
; *******************************************************************************
;
sub_step
;
    call    invert_fstep    ; Invert fstep_3..0 to perform the subtraction
    call    add_step    ; Add the complement to do the subtraction
;
; *******************************************************************************
; *                                        *
; * Purpose:    Test the new frequency to see if it is above the lower limit.    *
; *        If not, the frequency is set to the lower limit.        *
; *                                        *
; *******************************************************************************
;
low_limit_chk
;       Check the most significant byte.
;
    btfsc    freq_3,7
    BRA    set_low        ; Yes, set to lower frequency limit
    movf    freq_3,w
    sublw    limit_low_3
    btfss     STATUS,C    ; Are we at the limit for the byte?
    return            ; No, above.
    btfss    STATUS,Z    ; Are the bytes equal?
    BRA    set_low        ; No, so vfo_X_3 > limit_3.
;
;       Check the second most significant byte when MSB equals limit_3
;
    movf    freq_2,w
    sublw    limit_low_2
    btfss    STATUS,C    ; Are we at the limit for the byte?
    return            ; No, above.
    btfss    STATUS,Z    ; Might they be equal?
    BRA    set_low        ; Nope, so vfo_X_2 > limit_2
;
;       Check the third most significant byte.
;
    movf    freq_1,w
    sublw    limit_low_1
    btfss    STATUS,C    ; Are we at the limit for the byte?
    return            ; No, above.
    btfss    STATUS,Z    ; Check if the bytes are equal
    BRA    set_low        ; No, so vfo_X_1 > limit_1
;
;       Check the least significant byte.
;
    movf    freq_0,w
    sublw    limit_low_0
    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
    movlw    limit_low_0
    movwf    freq_0
    movlw    limit_low_1
    movwf    freq_1
    movlw    limit_low_2
    movwf    freq_2
    movlw    limit_low_3
    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
    BRA    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
    BRA    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
    BRA    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
    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.
;
    BANKSEL    EEADRL
    clrf    EEADRL        ; Reset the EEPROM read address
    call    read_EEPROM    ; Read first byte from EEPROM (all in bank 1)
    movf    EEDATL,w    ; Get the first osc byte
    movwf    osc_0        ; Save osc frequency
    call    read_EEPROM    ; Read second byte from EEPROM
    movf    EEDATL,w    ;
    movwf    osc_1        ; Save it
    call    read_EEPROM    ; Read third byte from EEPROM
    movf    EEDATL,w    ;
    movwf    osc_2        ; Save it
    call    read_EEPROM    ; Read fourth byte from EEPROM
    movf    EEDATL,w    ;
    movwf    osc_3        ; Save it
    movlb    0        ; Back to bank 0
;
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    CAL_sw        ; Calibrate switch/jumper still set?
    BRA    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?
    BRA    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
    BRA    faster        ; Non-zero, continue
    incfsz    fstep_1,f    ; Increment next byte
    BRA    faster        ; Non-zero, continue
    incfsz    fstep_2,f    ; Increment next byte
    BRA    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?
    BRA    add4        ; No, add the next bytes
    incfsz    osc_1,f        ; Ripple carry up to the next byte
    BRA    add4        ; No new carry, add the next bytes
    incfsz    osc_2,f        ; Ripple carry up to the next byte
    BRA    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?
    BRA    add5        ; No, add the third bytes
    incfsz    osc_2,f        ; Ripple carry up to the next byte
    BRA    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?
    BRA    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
    BRA    cal_loop    ; Yes, stay in calibrate mode
;
;    Write final value to EPROM
;
cal_out
    BANKSEL    EEADRL
    movf    osc_0,w        ; Get the first osc byte to record
    clrf    EEADRL        ; osc bytes start at EEPROM address 0
    movwf    EEDATL        ; Put byte in EEPROM write location
    call    write_EEPROM    ;
    movf    osc_1,w        ; Get the second byte to record
    movwf    EEDATL        ; Put byte in EEPROM write location
    call    write_EEPROM    ;
    movf    osc_2,w        ; Get the third byte to record
    movwf    EEDATL        ; Put byte in EEPROM write location
    call    write_EEPROM    ;
    movf    osc_3,w        ; Get the fourth byte to record
    movwf    EEDATL        ; Put byte in EEPROM write location
    call    write_EEPROM    ;
    movlb    0        ; Back to bank 0
    return            ; Return to the caller
;                     
; *******************************************************************************
; *                                        *
; * Purpose:    This routine will save the current frequency and step size in     *
; *        EEPROM on power down.                        *
; *                                        *
; *******************************************************************************
;
update_EEPROM ;
    bcf    INTCON,GIE    ; turn interrupts off
    btfsc     INTCON,GIE     ; See AN576 - make sure interrupts are off
    BRA     $-2

    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            ;
;
; *******************************************************************************
; *                                        *
; *        Required sequence to write to EEPROM                *
; *        Used by update_EEPROM and calibrate routines            *
; *                                        *
; *******************************************************************************
;
write_EEPROM
    bcf    EECON1,CFGS    ; Deselect Configuration space
    bcf    EECON1,EEPGD    ; Point to DATA memory
    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?
    BRA    bit_check    ; No, keep checking
    bcf    EECON1,WREN    ; Clear the EEPROM write enable bit
    incf    EEADRL,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.            *
; *                                        *
; *******************************************************************************
;
    org    0x200
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?
    BRA    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?
    BRA    add7        ; No, continue with next freq term
    incfsz    AD9851_2,f    ; Yes, add one and check for another carry
    BRA    add7        ; No, continue with next freq term
    incfsz    AD9851_3,f    ; Yes, add one and check for another carry
    BRA    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?
    BRA    add8        ; No, continue with next freq term
    incfsz    AD9851_3,f    ; Yes, add one and check for another carry
    BRA    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?
    BRA    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?
    BRA    mult_loop    ; No, go back and do next

#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?
        BRA    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
                    
        BRA    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?
        BRA    next_bit    ; No, keep going.
        incf    FSR0L,f
    decfsz    byte_count,f    ;
        BRA    next_byte    ;

    BANKSEL LATA
        bsf     DDS_load    ; Send load signal to the AD9850/AD9851             
        bcf     DDS_load    ;
    movlb    0        ; Bank 0

        return            ;
;
#IFDEF RIT
freq_to_rxf
    movfw    freq_0
    movwf    rxf_0
    movfw    freq_1
    movwf    rxf_1
    movfw    freq_2
    movwf    rxf_2
    movfw    freq_3
    movwf    rxf_3
    return

freq_to_ritf
    movfw    freq_0
    movwf    ritf_0
    movfw    freq_1
    movwf    ritf_1
    movfw    freq_2
    movwf    ritf_2
    movfw    freq_3
    movwf    ritf_3
    return

rxf_to_freq
    movfw    rxf_0
    movwf    freq_0
    movfw    rxf_1
    movwf    freq_1
    movfw    rxf_2
    movwf    freq_2
    movfw    rxf_3
    movwf    freq_3
    return

ritf_to_freq
    movfw    ritf_0
    movwf    freq_0
    movfw    ritf_1
    movwf    freq_1
    movfw    ritf_2
    movwf    freq_2
    movfw    ritf_3
    movwf    freq_3
    return

rxf_to_ritf
    movfw    rxf_0
    movwf    ritf_0
    movfw    rxf_1
    movwf    ritf_1
    movfw    rxf_2
    movwf    ritf_2
    movfw    rxf_3
    movwf    ritf_3
    return
#ENDIF

#IFDEF RIT
    org 0x800
rit
;    bcf    flags,0        ; Clear RIT changed flag
    clrf    flags        ; Clear RIT flag bits
    BANKSEL ADCON0
    bsf    ADCON0,ADGO    ; start conversion
    btfsc    ADCON0,ADGO    ; is conversion done?
    BRA    $-1        ; no, test again

    movf    ADRESH,w    ; read upper 2 bits
    movwf    NumH        ; store in gpr space

    movf    ADRESL,w    ; read lower 8 bits
    movwf    NumL        ; store in gpr space
    movlb    0

getValue
;first do ADC-center value to discover direction
    bsf    dir,0        ; set direction, 1 = up
    movlw    high(center)
    subwf    NumH,f
    movlw    low(center)
    subwf    NumL,f
    btfss    STATUS,C
    decf    NumH,f
    btfss    NumH,7        ; is it negative
    BRA    notNeg
;need to negate acc
    bcf    dir,0        ; set direction, 1 = up
    movlw    0xff
    xorwf    NumL,f
    xorwf    NumH,f
    incf    NumL,f
    btfsc    STATUS,Z
    incf    NumH,f
notNeg
; have now got a valid lookup value
    BANKSEL    EEADRL        ; Select Bank for EEPROM registers
    movfw    NumL        ;
    movwf    EEADRL
    movfw    NumH
    addlw    0x0e
    movwf    EEADRH
    bcf    EECON1,CFGS    ; Do not select Configuration Space
    bsf    EECON1,EEPGD    ; Select Program Memory
    bsf    EECON1,RD    ; Initiate read
    NOP            ; required 2 x NOPs
    NOP
        ; EEDAT will now contain 0 - 2500
        ; and direction bit will be set
    movf    EEDATH,w
    movlb    0
    movwf    fstep_1        ; for DDS frequency change
    BANKSEL    EEDATL
    movf    EEDATL,w
    movlb    0
    movwf    fstep_0        ; for DDS frequency change

    movf    fstep_1,f    ; Test if fstep_1 = 0
    btfss    STATUS,Z
    BRA    RIT_exit    ; RIT is greater than 0
    movf    fstep_0,f    ; Test if fstep_0 = 0
    btfss    STATUS,Z
    BRA    RIT_exit
    bcf    flags,0        ; RIT = 0 & flags bit 0 = 0
    return


RIT_exit
;    Test if same as last time
;    test for value change and return if no change
    bsf    flags,0
    movfw    NumH
    subwf    NumH_temp,w    ; Test if both registers the same. If yes test
    btfss    STATUS,Z    ; next set of registers. else get value from table
    BRA    RIT_exit2
    movfw    NumL
    subwf    NumL_temp,w    ; Test if both registers the same. If yes then
    btfsc    STATUS,Z    ; nothing changed so return from here else get
    return            ; value from table

RIT_exit2
    movfw    NumH
    movwf    NumH_temp
    movfw    NumL
    movwf    NumL_temp

    bsf    flags,1        ; 0 = same as last time 1 = different
     return

;       
    org 0xe00
        ;note, dead band incorporated into table
    DW    0,0,0,0,0,0,0,0,0,0,5,11,16,22,28,33,39,45,50,56 ; 20
    DW    62,67,73,79,84,90,96,101,107,113,118,124,130,135,141,147,152,158,164,169 ; 40
    DW    175,180,186,192,197,203,209,214,220,226,231,237,243,248,254,260,265,271,277,282 ; 60
    DW    288,294,299,305,311,316,322,328,333,339,345,350,356,361,367,373,378,384,390,395 ; 80
    DW    401,407,412,418,424,429,435,441,446,452,458,463,469,475,480,486,492,497,503,509 ; 100
    DW    514,520,526,531,537,542,548,554,559,565,571,576,582,588,593,599,605,610,616,622 ; 120
    DW    627,633,639,644,650,656,661,667,673,678,684,690,695,701,707,712,718,723,729,735    ; 140
    DW    740,746,752,757,763,769,774,780,786,791,797,803,808,814,820,825,831,837,842,848 ; 160
    DW    854,859,865,871,876,882,888,893,899,904,910,916,921,927,933,938,944,950,955,961 ; 180
    DW    967,972,978,984,989,995,1001,1006,1012,1018,1023,1029,1035,1040,1046,1052,1057,1063,1069,1074 ; 200
    DW    1080,1085,1091,1097,1102,1108,1114,1119,1125,1131,1136,1142,1148,1153,1159,1165,1170,1176,1182,1187 ; 220
    DW    1193,1199,1204,1210,1216,1221,1227,1233,1238,1244,1250,1255,1261,1266,1272,1278,1283,1289,1295,1300 ; 240
    DW    1306,1312,1317,1323,1329,1334,1340,1346,1351,1357,1363,1368,1374,1380,1385,1391,1397,1402,1408,1414 ; 260
    DW    1419,1425,1430,1436,1442,1447,1453,1459,1464,1470,1476,1481,1487,1493,1498,1504,1510,1515,1521,1527 ; 280
    DW    1532,1538,1544,1549,1555,1561,1566,1572,1578,1583,1589,1595,1600,1606,1611,1617,1623,1628,1634,1640 ; 300
    DW    1645,1651,1657,1662,1668,1674,1679,1685,1691,1696,1702,1708,1713,1719,1725,1730,1736,1742,1747,1753 ; 320
    DW    1759,1764,1770,1776,1781,1787,1792,1798,1804,1809,1815,1821,1826,1832,1838,1843,1849,1855,1860,1866 ; 340
    DW    1872,1877,1883,1889,1894,1900,1906,1911,1917,1923,1928,1934,1940,1945,1951,1957,1962,1968,1973,1979 ; 360
    DW    1985,1990,1996,2002,2007,2013,2019,2024,2030,2036,2041,2047,2053,2058,2064,2070,2075,2081,2087,2092 ; 380
    DW    2098,2104,2109,2115,2121,2126,2132,2138,2143,2149,2154,2160,2166,2171,2177,2183,2188,2194,2200,2205 ; 400
    DW    2211,2217,2222,2228,2234,2239,2245,2251,2256,2262,2268,2273,2279,2285,2290,2296,2302,2307,2313,2319 ; 420
    DW    2324,2330,2335,2341,2347,2352,2358,2364,2369,2375,2381,2386,2392,2398,2403,2409,2415,2420,2426,2432 ; 440
    DW    2440,2450,2460,2470,2480,2490,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500 ; 460
    DW    2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500 ; 480
    DW    2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500 ; 500
    DW    2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500 ; 512
#ENDIF
    END

;--------------------------------------------------------------------------------
 

Pommie

Well-Known Member
Most Helpful Member
I only had a quick look at your code (that's a lot of code) and the first thing I noticed was there's no context save in the ISR. I don't think you're using the ISR ATM but if it ever gets fired it will always return in bank 0. I'll try to look a bit more later.

Mike.
 

augustinetez

Active Member
There's no context save in the ISR because it is automatic in the Enhanced 16F PIC's (there's also an error in there but we won't mention it for now :)).

When it's operational, it only comes in to play at power down.
 

rjenkinsgb

Well-Known Member
Most Helpful Member
Beginning to think I should be using 18F parts - any recommendation for a drop in replacement for the 16F1827 (through hole part)?
I've been meaning to figure out how to search pin-compatible alternatives..

It looks like the PIC18F1220 - 1230 - 1320 - 1330 are the only ones with a matching pinout to the 16F1827.
(Or the 18LF equivalents).


Method, for future reference:-
Find the original in the MPLAB packs subdirectories, locate the original device .PIC you want to match, then search all *.PIC for the power and ground pin data copied from the original device file, eg. a string like this:

<!--Pin 5-->
<edc:VirtualPin edc:name="Vss"/>

Then re-search the matches for another power pin

<!--Pin 14-->
<edc:VirtualPin edc:name="Vdd"/>

I use ZTree for a lot of file manipulation & that makes searches like this very easy.
 

Pommie

Well-Known Member
Most Helpful Member
Terry,
Can I ask why you have various ORGs through your code?
I.E.
Code:
;
; *******************************************************************************
; *                                        *
; * 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.                                                    *
; *                                         *
; *******************************************************************************
;
    org    0x100
add_step
At this point, the PC is at 0xf2 so if you add 14 new instructions before it then memory is being overwritten.

I also see an org at 0x200 as well.

If you need these ORGs then it would be prudent to put a check before them such as,
Code:
    if $>0x100
    error "Memory overwritten"
    endif
    org    0x100

Mike.
 

augustinetez

Active Member
One of the original problems was that the RIT code would only offset around +1400Hz before messing up with being way off frequency.

Moving the code so that the various routines started at the 256 block beginnings ie 0x100 and 0x200 solved part of the problem in that it now correctly adds up to 2500Hz in the + direction.

That's the reasoning behind thinking it is a 256 byte block problem.

Thanks RJ, I'll have a look at those 18F PICs tomorrow.
 

Pommie

Well-Known Member
Most Helpful Member
Moving the code so that the various routines started at the 256 block beginnings ie 0x100 and 0x200 solved part of the problem in that it now correctly adds up to 2500Hz in the + direction.

That's the reasoning behind thinking it is a 256 byte block problem.
I don't know what the 256 byte (word) problem is. However, if it fixed other problems then I suspect something else.
The only limit I know that involves 256 is the table read and relative jumps. Is there another one I'm not aware of?

Mike.
Edit, BTW, what does RIT stand for?
 

augustinetez

Active Member
RIT = Receiver Incremental Tuning - it's a radio thing much like bandspread tuning in older radios you had the main tuning and the finer tuning of bandspread, in this case used mostly in two way radios.

The only limit I know that involves 256 is the table read and relative jumps. Is there another one I'm not aware of?
Well, depending on which datasheet you read, supposedly call and goto can only jump a limited distance like +/- 256 bytes and yet in other places they are indicated as being able to reach any point within a 2k page.
 

Pommie

Well-Known Member
Most Helpful Member
Well, depending on which datasheet you read, supposedly call and goto can only jump a limited distance like +/- 256 bytes and yet in other places they are indicated as being able to reach any point within a 2k page.
In any datasheet I've read they can reach anywhere in a 2K page. Look at the instruction and they both have an 11 bit address field, giving them an address space of 2K.

Mike.
 

Nigel Goodwin

Super Moderator
Most Helpful Member
Are there long call macros for the 16F1827?, I've not used assembler on them, only C.

Back when I wrote a simple BASIC compiler as part of WinPicProg (called WinPicProgB) I simply placed all the BASIC routines (multiply, divide etc.) up in a higher page, and called the routines using the long call macro, as far as I recall any tables were kept in their own pages as well. Not the smallest or neatest solution, but it worked perfectly.
 

rjenkinsgb

Well-Known Member
Most Helpful Member
Are there long call macros for the 16F1827

This is from the XC8 assembler manual, if it is any help?

Long Jumps And Calls The assembler recognizes several mnemonics that expand into regular PIC MCU assembly instructions. The mnemonics are fcall and ljmp. On baseline and mid-range parts, these instructions expand into regular call and goto instructions respectively, but also ensure the instructions necessary to set the bits in PCLATH (for mid-range devices) or STATUS (for baseline devices) will be generated, should the destination be in another page of program memory. Page selection instructions can appear immediately before the call or goto, or be generated as part of, and immediately after, a previous fcall/ljmp mnemonic.
 

augustinetez

Active Member
Are there long call macros for the 16F1827?
There's callw where you preload w with the destination, but again, the 16FXXX to 16F1XXX migration document is not that clear on how far it can actually jump.

As I read it, it supposedly can jump across pages, but as my program is currently only 1200 words, paging is not an issue other than the DW read which is at the very end of the second page and I know that is working ok when run on it's own.
 

Latest threads

Top