Program #9 - LCD display of TMR0 Count

Also:

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


           LIST P=16F84           ;  16F84 Runs at 4 MHz
           INCLUDE "p16f84.inc"
           __CONFIG _PWRTE_ON & _LP_OSC & _WDT_OFF  ; uses 32.768 kHz crystal
           ERRORLEVEL -224        ;  supress annoying message because of tris
           ERRORLEVEL -302        ;  supress message because of 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
;  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
             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
  nop
  nop                           ;  Wait 244 usecs before Sending the Second Time
  EStrobe
  nop
  nop                           ;  Wait 244 usecs before Sending the Third Time
  bcf    RS                     ; send an 8 bit instruction
  movlw  0x02                   ;  Set 4 Bit Mode
  call   NybbleOut
  nop
  nop
  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
  nop
  nop
  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
  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

;-----------------------------------------------------------------------;
;                           Delay routine                               ;
;-----------------------------------------------------------------------;
msec250  movlw 0               ; 250 msec delay (adjusted to try and
                               ; allow for 2.5% low loop time)
         goto $+2
Dlay5    movlw 5               ; delay for 5 milliseconds
nmsec:                         ; delay for # msec in W on entry
         nop                   ; each nop is 0.122 milliseconds
         nop
         nop                   ; each total loop is 8 X 0.122 = 0.976 msec
         nop
         addlw H'FF'           ; same as subtracting 1 from W
         btfss STATUS, Z       ; skip if result is zero
         goto nmsec            ; this is  2 X 0.122 msec
         return                ; back to calling point

;-------------------------------------------------------------------------;
;                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 PORTB, 4        ; Skip if button on RB4 up
              goto preup            ; increment prescalar
              btfss PORTB, 5        ; Skip if button on RB5 up
              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

;-------------------------------------------------------------------------;
;                           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

TMR0 and RA4

Timer 0 can be set up to count pulses entering the pic on RA4. You can make a simple 555 circuit as shown below to generate the pulses.

Frequency Problem

TMR0 can hold values only up to 255. What if the frequency is higher than this? The answer lies in the prescalar which is inserted between RA4 and TMR0 and provides division of the count by 2,4,8...256 depending on the lowest three bits in the OPTION register, (PS0-PS2). Changing this to 1:256 allows us to count frequency up through 64K. Pushbuttons are used to change the prescalar and the multiplying factor is shown on the display along with TMR0.

Accuracy

Since TMR0 is used to do the counting, it can't be used to provide a one second interval at the same time. We must rely on instruction time counting to get one second. The routine nmsec is not accurate. 1 millisecond turns out to be closer to 975 microseconds, 2.5 % low. We try to allow for this when calling msec250 by counting more than 250 loops. The maximum we can count using one byte is 256 by setting the intial value to 0. Actually this turns out to be pretty close to the value we need, (maybe a little long because of the calls, returns and other overhead). You might also try D'255'.

Pushbuttons

If the message 'OVR' is displayed instead of a value for TMR0, it means that timer 0 has overflowed and the prescalar must be set to a higher ratio. The button on RB4 moves the ratio up and the one on RB5 moves it down. No debouncing is necessary for these buttons because they are checked only once each second. It also means you have to hold the button down for a while to make it work. Multiply the value of TMR0 by the multiplier indicated to get the actual frequency.

Higher Precision

There is a method to get higher precision, but it is a little involved. The residual count actually appears in the prescalar when counting is complete. The problem is that the prescalar can't be read directly as a register. What is usually done is to use another pin to pulse RA4, keeping track of the number of pulses until TMR0 overflows. Subtract this number from 256 and the result is the contents of the prescalar when counting was first terminated. The total count is then the ratio times TMR0 plus this residual. For an example of this, go to http://www.piclist.com/faq and click on 'PICList projects' and the 'Weeder Frequency Counter PIC16F84 port'.

Your turn

The frequency of the 555 oscillator is determined by the resistors and capacitor used. This suggests that since we can measure frequency, we can measure an unknown capacitance if the reistance is known or vice-versa. Also, any property which varies one of these can be measured if we know how this property makes the frequency change. Think of some properties you might like to measure with this setup.

Interested:

Comments: