;************************************************************************
;                                                                       *
;   Filename:      L10-ADC_dec-out.asm                                  *
;   Date:          1/12/07                                              *
;   File Version:  1.2                                                  *
;                                                                       *
;   Author:        David Meiklejohn                                     *
;   Company:       Gooligum Electronics                                 *
;                                                                       *
;************************************************************************
;                                                                       *
;   Architecture:  Baseline PIC                                         *
;   Processor:     16F506                                               *
;                                                                       *
;************************************************************************
;                                                                       *
;   Files required: none                                                *
;                                                                       *
;************************************************************************
;                                                                       *
;   Description:    Tutorial 10, exercise 1                             *
;                                                                       *
;   Displays ADC output in decimal on 2x7-segment LED display           *
;                                                                       *
;   Continuously samples analog input, scales result to 0 - 99          *
;   and displays as 2 x dec digits on multiplexed 7-seg displays        *
;                                                                       *
;************************************************************************
;                                                                       *
;   Pin assignments:                                                    *
;       AN0 - voltage to be measured (e.g. pot or LDR)                  *
;       RB5, RC0-5 - 7-segment display bus (common cathode)             *
;       RB4 - tens enable (active high)                                 *
;       RB1 - ones enable                                               *
;                                                                       *
;************************************************************************

    list        p=16F506 
    #include    <p16F506.inc>

    radix       dec


;***** CONFIGURATION
                ; ext reset, no code protect, no watchdog, 4MHz int clock
    __CONFIG    _MCLRE_ON & _CP_OFF & _WDT_OFF & _IOSCFS_OFF & _IntRC_OSC_RB4EN

; pin assignments
    #define TENS_EN     PORTB,4     ; tens enable
    #define ONES_EN     PORTB,1     ; ones enable


;***** VARIABLE DEFINITIONS
VARS1   UDATA
digit   res 1                   ; digit to be displayed (used by set7seg)
adc_out res 1                   ; raw ADC output
adc_dec res 2                   ; scaled ADC output (LE 16 bit, 0-99 in MSB)
mpy_cnt res 1                   ; multiplier count
tens    res 1                   ; digits of scaled output: tens
ones    res 1                   ;   ones


;**********************************************************************
RCCAL   CODE    0x3FF           ; processor reset vector
        res     1               ; holds movlw with factory RC cal value

RESET   CODE    0x000           ; effective reset vector
        movwf   OSCCAL          ; update OSCCAL with factory cal value 
        pagesel start
        goto    start           ; jump to main program

;***** SUBROUTINE VECTORS
set7seg                         ; display digit on 7-segment display
        pagesel set7seg_R       
        goto    set7seg_R


;***** MAIN PROGRAM
MAIN    CODE
start   ; initialisation  
        clrw                    ; configure PORTB and PORTC as all outputs
        tris    PORTB
        tris    PORTC
        movlw   1<<NOT_C1OUTEN  ; disable Comparator 1 and C1OUT
        movwf   CM1CON0         ;   (RB0, RB1, RB2 usable)
        movlw   1<<NOT_C2OUTEN  ; disable Comparator 2 and C2OUT
        movwf   CM2CON0         ;   (RC0, RC1, RC4 usable)
        clrf    VRCON           ; disable CVref (RC2 usable)
        movlw   b'10110001'     ; configure ADC: AN0, AN2 analog (RB1 usable)
        movwf   ADCON0          ;   INTOSC/4 clock, AN0 selected
        movlw   b'111'          ; set Timer0: timer mode, prescale = 256
        option                  ;   ( -> TMR0<2> cycles every 2.048ms)

;***** Main loop
main_loop
        ; sample input
        bsf     ADCON0,GO       ; start conversion
w_adc   btfsc   ADCON0,NOT_DONE ; wait until conversion complete
        goto    w_adc
        movf    ADRES,w         ; save ADC result
        banksel adc_out
        movwf   adc_out

        ; scale to 0-99: adc_dec = adc_out * 100
        ;   -> MSB of adc_dec = adc+out * 100 / 256
        clrf    adc_dec         ; start with adcdec = 0
        clrf    adc_dec+1
        movlw   .8              ;   count = 8
        movwf   mpy_cnt
        movlw   .100            ;   multiplicand (100) in W
        bcf     STATUS,C        ;   and carry clear
l_mpy   rrf     adc_out,f       ; right shift multiplier
        btfsc   STATUS,C        ; if low-order bit of multiplier was set
        addwf   adc_dec+1,f     ;   add multiplicand (100) to MSB of result
        rrf     adc_dec+1,f     ; right shift result
        rrf     adc_dec,f
        decfsz  mpy_cnt,f       ; repeat for all 8 bits
        goto    l_mpy

        ; extract digits of result
        movf    adc_dec+1,w     ; start with scaled result
        movwf   ones            ;   in ones digit
        clrf    tens            ; and tens clear
l_bcd   movlw   .10             ; subtract 10 from ones
        subwf   ones,w
        btfss   STATUS,C        ; (finish if < 10)
        goto    end_bcd
        movwf   ones 
        incf    tens,f          ; increment tens
        goto    l_bcd           ; repeat until ones < 10
end_bcd

        ; display tens digit for 2.048ms
w10_hi  btfss   TMR0,2          ; wait for TMR0<2> to go high
        goto    w10_hi
        movf    tens,w          ; output tens digit
        pagesel set7seg
        call    set7seg 
        pagesel $   
        bsf     TENS_EN         ; enable "tens" display
w10_lo  btfsc   TMR0,2          ; wait for TMR<2> to go low
        goto    w10_lo

        ; display ones digit for 2.048ms
w1_hi   btfss   TMR0,2          ; wait for TMR0<2> to go high
        goto    w1_hi
        banksel ones            ; output ones digit
        movf    ones,w
        pagesel set7seg
        call    set7seg  
        pagesel $    
        bsf     ONES_EN         ; enable ones display
w1_lo   btfsc   TMR0,2          ; wait for TMR<2> to go low
        goto    w1_lo

        ; repeat forever
        goto    main_loop


;***** LOOKUP TABLES
TABLES  CODE    0x200           ; locate at beginning of a page

; Lookup pattern for 7 segment display on port B
; RB5 = G
get7sB  addwf   PCL,f
        retlw   b'000000'       ; 0
        retlw   b'000000'       ; 1
        retlw   b'100000'       ; 2
        retlw   b'100000'       ; 3
        retlw   b'100000'       ; 4
        retlw   b'100000'       ; 5
        retlw   b'100000'       ; 6
        retlw   b'000000'       ; 7
        retlw   b'100000'       ; 8
        retlw   b'100000'       ; 9

; Lookup pattern for 7 segment display on port C
; RC5:0 = ABCDEF 
get7sC  addwf   PCL,f
        retlw   b'111111'       ; 0
        retlw   b'011000'       ; 1
        retlw   b'110110'       ; 2
        retlw   b'111100'       ; 3
        retlw   b'011001'       ; 4
        retlw   b'101101'       ; 5
        retlw   b'101111'       ; 6
        retlw   b'111000'       ; 7
        retlw   b'111111'       ; 8
        retlw   b'111101'       ; 9

; Display digit passed in W on 7-segment display
set7seg_R
        banksel digit
        movwf   digit           ; save digit
        call    get7sB          ; lookup pattern for port B
        movwf   PORTB           ;   then output it
        movf    digit,w         ; get digit 
        call    get7sC          ;   then repeat for port C
        movwf   PORTC
        retlw   0


        END

