;************************************************************************
;                                                                       *
;   Filename:      BA_L10-ADC_Vdd-measure.asm                           *
;   Date:          25/1/10                                              *
;   File Version:  1.2                                                  *
;                                                                       *
;   Author:        David Meiklejohn                                     *
;   Company:       Gooligum Electronics                                 *
;                                                                       *
;************************************************************************
;                                                                       *
;   Architecture:  Baseline PIC                                         *
;   Processor:     16F506                                               *
;                                                                       *
;************************************************************************
;                                                                       *
;   Files required: none                                                *
;                                                                       *
;************************************************************************
;                                                                       *
;   Description:    Lesson 10, example 3                                *
;                                                                       *
;   Demonstrates use of 0.6V ref with ADC to test supply voltage        *
;                                                                       *
;   Continuously samples 0.6V internal reference,                       *
;   displaying result as 2 x hex digits on multiplexed 7-seg displays   *
;   Turns on warning LED if measurement > threshold                     *
;                                                                       *
;************************************************************************
;                                                                       *
;   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                                       *
;       RB2         = warning LED                                       *
;                                                                       *
;************************************************************************

    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    PORTB,4     ; tens enable
    #define ONES    PORTB,1     ; ones enable
    #define WARN    PORTB,2     ; warning LED


;***** CONSTANTS
    constant MINVDD=3500            ; Minimum Vdd (in mV)
    constant VRMAX=255*600/MINVDD   ; Threshold for 0.6 V ref measurement


;***** VARIABLE DEFINITIONS
        UDATA_SHR
digit   res 1                   ; digit to be displayed


;***** RESET VECTOR *****************************************************
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

;***** Initialisation
start   
        ; configure ports   
        clrw                    ; configure PORTB and PORTC as all outputs
        tris    PORTB
        tris    PORTC
        clrf    CM1CON0         ; disable Comparator 1 (RB0, RB1, RB2 usable)
        clrf    CM2CON0         ; disable Comparator 2 (RC0, RC1, RC4 usable)
        clrf    VRCON           ; disable CVref (RC2 usable)
        ; configure ADC
        movlw   b'00111101'
                ; 00------        no analog inputs (ANS = 00) -> RB0-2 usable
                ; --11----        clock = INTOSC/4 (ADCS = 11)
                ; ----11--        select 0.6 V reference (CHS = 11)
                ; -------1        turn ADC on (ADON = 1)
        movwf   ADCON0            
        ; configure timer
        movlw   b'11010111'     ; configure Timer0:
                ; --0-----          timer mode (T0CS = 0) -> RC5 usable
                ; ----0---          prescaler assigned to Timer0 (PSA = 0)
                ; -----111          prescale = 256 (PS = 111)            
        option                  ;   -> increment every 256 us        
                                ;      (TMR0<2> cycles every 2.048ms)

;***** Main loop
main_loop
        ; sample 0.6 V reference
        bsf     ADCON0,GO       ; start conversion
w_adc   btfsc   ADCON0,NOT_DONE ; wait until conversion complete
        goto    w_adc

        ; test for low Vdd (measured 0.6 V > threshold)
        movlw   VRMAX
        subwf   ADRES,w         ; if ADRES > VRMAX
        btfsc   STATUS,C
        bsf     WARN            ;   turn on warning LED

        ; display high nybble for 2.048 ms
w10_hi  btfss   TMR0,2          ; wait for TMR0<2> to go high
        goto    w10_hi
        swapf   ADRES,w         ; get "tens" digit
        andlw   0x0F            ;   from high nybble of ADC result
        pagesel set7seg
        call    set7seg         ;   then output it
        pagesel $   
        bsf     TENS            ; enable "tens" display
w10_lo  btfsc   TMR0,2          ; wait for TMR<2> to go low
        goto    w10_lo

        ; display ones for 2.048 ms
w1_hi   btfss   TMR0,2          ; wait for TMR0<2> to go high
        goto    w1_hi
        movf    ADRES,w         ; get ones digit
        andlw   0x0F            ;   from low nybble of ADC result
        pagesel set7seg
        call    set7seg         ;   then output it   
        pagesel $    
        bsf     ONES            ; 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
        retlw   b'100000'       ; A
        retlw   b'100000'       ; b
        retlw   b'000000'       ; C
        retlw   b'100000'       ; d
        retlw   b'100000'       ; E
        retlw   b'100000'       ; F

; 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
        retlw   b'111011'       ; A
        retlw   b'001111'       ; b
        retlw   b'100111'       ; C
        retlw   b'011110'       ; d
        retlw   b'100111'       ; E
        retlw   b'100011'       ; F

; Display digit passed in W on 7-segment display
set7seg_R
        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


