;************************************************************************
;                                                                       *
;   Filename:      L8-Count_7seg_BCD.asm                                *
;   Date:          19/11/09                                             *
;   File Version:  1.1                                                  *
;                                                                       *
;   Author:        David Meiklejohn                                     *
;   Company:       Gooligum Electronics                                 *
;                                                                       *
;************************************************************************
;                                                                       *
;   Architecture:  Baseline PIC                                         *
;   Processor:     16F505                                               *
;                                                                       *
;************************************************************************
;                                                                       *
;   Files required: none                                                *
;                                                                       *
;************************************************************************
;                                                                       *
;   Description:    Lesson 8, example 3                                 *
;                                                                       *
;   Demonstrates BCD storage and counting                               *
;                                                                       *
;   3 digit 7-segment LED display: 1 digit minutes, 2 digit seconds     *
;   counts in seconds 0:00 to 9:59 then repeats,                        *
;   with timing derived from int 4 MHz oscillator                       *
;   (seconds count stored in BCD format)                                *
;                                                                       *
;************************************************************************
;                                                                       *
;   Pin assignments:                                                    *
;       RB2, RC0-5 - 7-segment display bus (common cathode)             *
;       RB4 - minutes enable (active high)                              *
;       RB1 - tens enable                                               *
;       RB0 - ones enable                                               *
;                                                                       *
;************************************************************************

    list        p=16F505 
    #include    <p16F505.inc>

    radix       dec


;***** CONFIGURATION
                ; ext reset, no code protect, no watchdog, 4MHz int clock
    __CONFIG    _MCLRE_ON & _CP_OFF & _WDT_OFF & _IntRC_OSC_RB4EN

; pin assignments
    #define MINUTES PORTB,4     ; minutes enable
    #define TENS    PORTB,1     ; tens enable
    #define ONES    PORTB,0     ; ones enable


;***** VARIABLE DEFINITIONS
        UDATA_SHR
digit   res 1                   ; digit to be displayed

        UDATA
mpx_cnt res 1                   ; multiplex counter
mins    res 1                   ; time count: minutes
secs    res 1                   ;   seconds (BCD)


;***** 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  
        clrw                    ; configure PORTB and PORTC as all outputs
        tris    PORTB
        tris    PORTC
        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)

        banksel mins            ; start with count = 0
        clrf    mins
        clrf    secs

;***** Main loop
main_loop

; multiplex display for 1 sec
        movlw   1000000/2048/3  ; display each of 3 digits for 2.048 ms each
        movwf   mpx_cnt         ;   repeat multiplex loop for approx 1 second

mplex_loop
        ; display minutes for 2.048 ms
w60_hi  btfss   TMR0,2          ; wait for TMR0<2> to go high
        goto    w60_hi
        movf    mins,w          ; get minutes digit
        pagesel set7seg
        call    set7seg         ;   then output it
        pagesel $      
        bsf     MINUTES         ; enable minutes display
w60_lo  btfsc   TMR0,2          ; wait for TMR<2> to go low
        goto    w60_lo

        ; display tens for 2.048 ms
w10_hi  btfss   TMR0,2          ; wait for TMR0<2> to go high
        goto    w10_hi
        swapf   secs,w          ; get tens digit
        andlw   0x0F            ;   from high nybble of seconds
        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    secs,w          ; get ones digit
        andlw   0x0F            ;   from low nybble of seconds
        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

        decfsz  mpx_cnt,f       ; continue to multiplex display
        goto    mplex_loop      ;   until 1 sec has elapsed

; increment counters
        incf    secs,f          ; increment seconds
        movf    secs,w          ; if ones overflow,
        andlw   0x0F
        xorlw   .10
        btfss   STATUS,Z 
        goto    end_inc 
        movlw   .6              ;   BCD adjust seconds
        addwf   secs,f 
        movlw   0x60
        xorwf   secs,w          ;   if seconds = 60,
        btfss   STATUS,Z
        goto    end_inc  
        clrf    secs            ;       reset seconds to 0
        incf    mins,f          ;       and increment minutes
        movlw   .10
        xorwf   mins,w          ;       if minutes overflow,
        btfsc   STATUS,Z
        clrf    mins            ;           reset minutes to 0
end_inc 
        
        goto    main_loop       ; repeat forever


;***** LOOKUP TABLES ****************************************************
TABLES  CODE    0x200           ; locate at beginning of a page

; Lookup pattern for 7 segment display on port B
get7sB  addwf   PCL,f
        retlw   b'000000'       ; 0
        retlw   b'000000'       ; 1
        retlw   b'000100'       ; 2
        retlw   b'000100'       ; 3
        retlw   b'000100'       ; 4
        retlw   b'000100'       ; 5
        retlw   b'000100'       ; 6
        retlw   b'000000'       ; 7
        retlw   b'000100'       ; 8
        retlw   b'000100'       ; 9

; Lookup pattern for 7 segment display on port C
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
        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

