;----------------------------------------------------------------------;
; TMR04M.ASM     Show TMR0 in decimal on LCD after counting 1 second   ;
;----------------------------------------------------------------------;

;              555              V+         PIC16F84
;         .----------.          |       .-----------.
;  gnd ---|gnd    Vcc|--- V+   [R]     -|RA2     RA1|----[LCD E]       
;       .-|trig   dis|----------|      -|RA3     RA0|----[LCD RS]      
; RA4<--|-|out  thres|--.--{r}--'   in>-|RA4    OSC1|--|X|__ gnd    
;  V+ --|-|rst   cont|  |--|<---'   V+--|MCLR   OSC2|--|X|  X = 4 MHz
;       | '----------'  |          gnd--|Vss     Vdd|--- V+    cer. res. 
;       '---------------'   [LCD D4]----|RB0     RB7|-   
;       |    C              [LCD D5]----|RB1     RB6|-   V+ = 4.5 or 5V
;       '---||---gnd        [LCD D6]----|RB2     RB5|---[PB]--- gnd       
;   C =  0.001-0.1 mfd      [LCD D7]----|RB3     RB4|---[PB]--- gnd        
;    -{r}- = 33K                        '-----------'         
;    -|<- = 1n4148  [R] = 10K variable resistor     -[PB]- = pushbutton

;     LCD pin connections:
;      1 - gnd     ground    2 - Vcc V+    3 - cont. 10K pot V+ & gnd
;      4 - RS  RA0 PIC(17)   5 - RW  gnd   6 - E   RA1 PIC(18) 
;      7 - D0   gnd      8 - D1  gnd       9 - D2 gnd        10 - D3 gnd       
;      11 - D4  RB0 (6)  12 - D5 RB1 (7)   13 - D6 RB2 (8)   14 - D7 (9)

           LIST P=16F84           ;  16F84 Runs at 4 MHz
           INCLUDE "p16f84.inc"
           __CONFIG _PWRTE_ON & _XT_OSC & _WDT_OFF  
           ERRORLEVEL -224        ;  suppress annoying message from tris
           ERRORLEVEL -302        ;  suppress message from page change

;  Define Information
     #DEFINE RS PORTA, 0          ;  RA0 is RS line of LCD
     #DEFINE E  PORTA, 1          ;  RA1 is E line of LCD
                                  ;  RB0-RB3 are D4-D7 of LCD
     #DEFINE upbutton PORTB, 4    ;  RB4 is the up button
     #DEFINE dnbutton PORTB, 5    ;  RB5 is the down button


;  Macro

EStrobe MACRO                   ;  Strobe the "E" Bit
  bsf    E
  bcf    E
 ENDM

            CBLOCK     0CH
                Temp            ; a temporary variable
                count           ; counter
                precount        ; holds value for prescalar
                bin             ; binary number to be converted to BCD
                hundreds        ; BCD hundreds result
                tens_and_ones   ; BCD tens and ones result
                cntmsec         ; used in counting milliseconds
            ENDC
          
            ORG 0               ; start at location 0

            goto main           ; jump over to main routine       

;----------------------------------------------------------------------;
;                    Data for message to be output                     ;
;----------------------------------------------------------------------;
shomsg:                         ;  Message to Output
  addwf  PCL, f                 ;  Output the Characters
m0  dt     "TMR0 Value:", 0
m1  dt     "Multiply by:", 0

;----------------------------------------------------------------------;
;                        Ratios of Prescaler                           ;
;----------------------------------------------------------------------;
preratio:                          
  addwf  PCL, f                 ;  select offset using W
    dt     D'2',D'4',D'8',D'16',D'32',D'64',D'128'

;----------------------------------------------------------------------;
;                       Initialize the ports                           ;
;----------------------------------------------------------------------;
Init:
            clrf   PORTA
            clrf   PORTB

            movlw B'00010000'          ; RA4 input, others outputs
            tris PORTA
            movlw B'00110000'          ; RB4, RB5 input, others outputs
            tris PORTB
            movlw B'00100001'          ; pull-ups enabled                                    
                                       ; prescaler assigned to RA4
                                       ; prescaler set to 1:4
            option
            clrf precount
            return

;----------------------------------------------------------------------;
;                         Initialize the LCD                           ;
;----------------------------------------------------------------------;
initlcd:
  movlw D'40'
  call   nmsec                  ;  Wait 40 msecs before Reset
  bcf    RS                     ;  send an 8 bit instruction
  movlw  0x03                   ;  Reset Command
  call   NybbleOut              ;  Send the Nybble
  call   Dlay5                  ;  Wait 5 msecs before Sending Again
  EStrobe
  call Dlay160                  ;  Wait 160 usecs before Sending 2nd Time
  EStrobe
  call Dlay160                  ;  Wait 160 usecs before Sending 3rd Time
  bcf    RS                     ;  send an 8 bit instruction
  movlw  0x02                   ;  Set 4 Bit Mode
  call   NybbleOut              
  call Dlay160
  movlw  0x028                  ;  4 bit, 2 Line, 5x7 font
  call   SendINS
  movlw  0x010                  ;  display shift off
  call   SendINS
  movlw  0x001                  ;  Clear the Display RAM
  call   SendINS
  call   Dlay5                  ;  Note, Can take up to 4.1 msecs
  movlw  0x006                  ;  increment cursor
  call   SendINS
  movlw  0x00C                  ;  display on cursor off
  call   SendINS
  return

;----------------------------------------------------------------------;
;              Send the character in W out to the LCD                  ;
;----------------------------------------------------------------------;
SendASCII
  addlw '0'                     ;  Send nbr as ASCII character
SendCHAR                        ;  Send the Character to the LCD
  movwf  Temp                   ;  Save the Temporary Value
  swapf  Temp, w                ;  Send the High Nybble
  bsf    RS                     ;  RS = 1
  call   NybbleOut
  movf   Temp, w                ;  Send the Low Nybble
  bsf    RS
  call   NybbleOut
  return

;----------------------------------------------------------------------;
;              Send an instruction in W out to the LCD                 ;
;----------------------------------------------------------------------;
SendINS                         ;  Send the Instruction to the LCD
  movwf  Temp                   ;  Save the Temporary Value
  swapf  Temp, w                ;  Send the High Nybble
  bcf    RS                     ;  RS = 0
  call   NybbleOut
  movf   Temp, w                ;  Send the Low Nybble
  bcf    RS
  call   NybbleOut
  return

;----------------------------------------------------------------------;
;              Send the nibble in W out to the LCD                     ;
;----------------------------------------------------------------------;
NybbleOut                       ;  Send a Nybble to the LCD
  movwf  PORTB                            
  EStrobe                       ;  Strobe out the LCD Data
  call Dlay160                  ;  delay for 160 msec
  return

;----------------------------------------------------------------------;
;                   Output the message on the LCD                      ;
;----------------------------------------------------------------------;
OutMessage:
  movwf  FSR                    ;  Point at first letter
OutLoop:
  movf   FSR, w                 ;  Get pointer into W
  incf   FSR, f                 ;  Set up for next letter
  call   shomsg                 ;  Get character to output
  iorlw  0                      ;  At the End of the Message?
  btfsc  STATUS, Z              ;  Skip if not at end
  return                        ;  Yes - Equal to Zero
  call   SendCHAR               ;  Output the ASCII Character
  goto   OutLoop                ;  Get the next character

;----------------------------------------------------------------------;
;                  Change binary nbr in bin to BCD                     ;
;----------------------------------------------------------------------;
binary_to_bcd                     ; by Scott Dattalo
  clrf hundreds                   ; found at http://www.piclist.com
  swapf bin, W
  addwf bin, W
  andlw B'00001111'
  skpndc
    addlw 0x16
  skpndc
    addlw 0x06
  addlw 0x06
  skpdc
  addlw -0x06
  btfsc bin,4
    addlw 0x16 - 1 + 0x6
  skpdc
    addlw -0x06
  btfsc bin,5
    addlw 0x30
  btfsc bin, 6
    addlw 0x60
  btfsc bin,7
    addlw 0x20
  addlw 0x60
  rlf hundreds, f
  btfss hundreds, 0
    addlw -0x60
  movwf tens_and_ones
  btfsc bin,7
    incf hundreds, f
  return

;----------------------------------------------------------------------;
;                        time delay routines                           ;
;----------------------------------------------------------------------;

Dlay160     movlw D'40'                ;  160 usec delay entry point
micro4      addlw H'FF'                ;  subtract 1 from 'W'
            btfss STATUS,Z             ;  skip when you reach zero
            goto micro4                ;  more loops
            return                     


Dlay5:      movlw D'5'                ; 5 msec delay entry point
            goto nmsec
msec250:                               ; a subroutine to delay 250 msec
            movlw D'250'          
                ;*** N millisecond delay routine ***
nmsec:      movwf cntmsec              ;  delay for N (in W) millisec
msecloop:   movlw D'248'               ;  1 usec for load
            call micro4                ;  this instruction is 995 usec
            nop                        ;  1 usec 
            decfsz cntmsec,f           ;  1 usec, (2 if skip taken)
            goto msecloop              ;  2 usec here 995+5 = 1 msec
            return 

;----------------------------------------------------------------------;
;                Display binary value in W in decimal                  ;             ;
;----------------------------------------------------------------------;
DispDec
              movwf bin
              call binary_to_bcd
              movf hundreds, W
              call SendASCII
              swapf tens_and_ones, W
              andlw H'F'
              call SendASCII
              movf tens_and_ones, W
              andlw H'F'
              call SendASCII
              return

;----------------------------------------------------------------------;
;             Return the prescalar ratio setting in W                  ;
;----------------------------------------------------------------------;
getpre:
       bsf STATUS, RP0         ; change to page 1
       movf OPTION_REG, W      ; get OPTION register
       bcf STATUS, RP0         ; back to page 0
       andlw 7                 ; get 1st 3 bits in W
       return

;----------------------------------------------------------------------;
;        Check pushbuttons and increment or decrement prescalar        ;
;----------------------------------------------------------------------;
ckbuttons:
              call getpre           ; get prescalar ratio # in Temp
              movwf Temp
              btfss upbutton        ; Skip if up button not pressed
              goto preup            ; increment prescalar
              btfss dnbutton        ; Skip if down button not pressed
              goto predown          ; decrement prescalar
              return                ; no buttons pressed, return
preup:
              incf Temp, f          ; up one
              btfsc Temp, 3         ; if reach 8, go back to zero
              clrf Temp
              goto newpre           ; and fix new ratio
predown:
              decf Temp, f          ; down one
              movlw H'FF'           ; check for underflow
              subwf Temp, W 
              btfss STATUS, Z       ; skip if underflow
              goto newpre           ; and fix new ratio
              movlw 7               ; reset to 7
              movwf Temp
newpre:
              bsf STATUS, RP0      ; change to page 1
              movlw B'11111000'    ; mask out low three bits
              andwf OPTION_REG, W  ; result in W
              bcf STATUS, RP0      ; back to page 0
              iorwf Temp, W        ; add in new prescalar ratio #
              option               ; update OPTION
              return               ; no debounce necessary because it
                                   ; will be one second till you get
                                   ; baclk to this routine, even if the
                                   ; button is still pressed.

;----------------------------------------------------------------------;
;                           The Main routine                           ;
;----------------------------------------------------------------------;
main:
       call Init               ; initialize ports, set up timer
       call initlcd            ; initialize the LCD
       movlw H'80'             ; position at 1st line column 0
       call SendINS
       movlw m0 -2             ; send 'TMR0 Value:' message
       call OutMessage
       movlw H'C0'
       call SendINS            ; position at 2nd line column 0
       movlw m1 -2             ; send 'Multiply by:' message
       call OutMessage
sholoop:
       call ckbuttons          ; update prescalar if buttons pushed
       movlw H'CD'             ; position at  2nd line column 13
       call SendINS
       call getpre             ; get prescalar ratio # in binary
       call preratio           ; change to prescalar ratio
       call DispDec            ; put it on LCD
       movlw H'8C'             ; position at 1st line column 12
       call SendINS
       bcf INTCON, T0IF        ; clear timer zero interrupt flag
       clrf TMR0               ; zero TMR0
       call msec250            ; wait a total of one second
       call msec250
       call msec250
       call msec250
       movf TMR0, W            ; retrieve timer zero
       btfsc INTCON, T0IF      ; check if timer overflowed
       goto overload           ; yes, display 'OVR'
       call DispDec            ; display TMR0 value
       goto sholoop            ; repeat forever
overload
       movlw 'O'               ; output 'OVR'
       call SendCHAR
       movlw 'V'
       call SendCHAR
       movlw 'R'
       call SendCHAR
       goto sholoop            ; and continue

       end