Program #8 - LCD display of Timer

;-------------------------------------------------------------------------;
; WORKTIME.ASM     Pushbutton toggles clock keeping track of time on LCD  ;
;-------------------------------------------------------------------------;

;  Note: must add additional time digit to run over 99 hrs.

           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

;  Define Information
     #DEFINE RS PORTA, 0
     #DEFINE E  PORTA, 1
     #DEFINE TOGGLESW PORTB, 4

;  Macro

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

            CBLOCK     0CH
                sec             ; seconds digit
                sec10           ; 10's of second digit
                mins            ; minutes digit
                min10           ; 10's of minutes digit
                hr              ; hours digit
                hr10            ; 10's of hours digit
                highlim         ; high limit + 1 of digit
                w_temp          ; holds W during interrupt
                status_temp     ; holds STATUS during interrupt
                fsr_temp        ; holds FSR during interrupt
                Dlay            ; 8 Bit Delay Variable
                working         ; working flag 0 not working, 1 working
                ptr             ; used in displaying message
                Temp            ; a temporary variable
                bin             ; a temporary variable
                oset            ; offset of time register
                oldtime         ; holds last value of sec
               ENDC

            ORG 0               ; start at location 0

            goto main           ; jump over to main routine

            ORG 4

            goto isr            ; jump to interrupt routine

;-------------------------------------------------------------------------;
;                      High limit + 1 of digits at position W             ;
;-------------------------------------------------------------------------;
sethi:
            addwf PCL, f
         dt H'A',H'6',H'A',H'6',H'A',H'A'

;-------------------------------------------------------------------------;
;                    Data for message to be output                        ;
;-------------------------------------------------------------------------;
shomsg                          ;  Message to Output
  addwf  PCL, f                 ;  Output the Characters
  dt     "Working Time:", 0

;-------------------------------------------------------------------------;
;      Interrupt routine, increments time by one second  (BCD)            ;
;-------------------------------------------------------------------------;
isr:
            movwf w_temp             ; save W
            swapf STATUS,W           ; save status
            movwf status_temp        ; without changing flags
            swapf FSR,W              ; save FSR
            movwf fsr_temp           ; without changing flags

            movf working, f          ; check working flag
            btfsc STATUS, Z          ; if not zero then increment time
            goto restore             ; else get out of interrupt routine

            movlw sec                ; point at sec register
            movwf FSR
newdigit:   incf INDF, f             ; current digit up one
            movlw sec                ; get difference between sec and FSR
            subwf FSR, W
            call sethi               ; use to get high limit + 1
            subwf INDF, W            ; reached that number yet?
            btfss STATUS, Z          ; skip over if yes
            goto restore             ; else exit isr
            clrf INDF                ; set current digit to 0
            incf FSR, f               ; point at next digit
            goto newdigit            ; no, increment the next digit
restore:
            swapf status_temp,W      ; get original status back
            movwf STATUS             ; into status register
            swapf fsr_temp,W         ; get original fsr back
            movwf FSR                ; into status register
            swapf w_temp,f           ; old no flags trick again
            swapf w_temp,W           ; to restore W
            bcf INTCON,T0IF          ; clear the TMR0 interrupt flag
            retfie                   ; finished reset GIE

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

            movlw  0                   ;  Port A all outputs
            tris PORTA
            movlw B'00010000'          ; RB4 input, others outputs
            tris PORTB
            movlw B'00000100'          ; pull-ups enabled
                                       ; prescaler assigned to TMR0
                                       ; prescaler set to 1:32
                                       ; rolls over each second
            option

            movlw 0                    ; zero out all registers
            movwf hr10
            movwf hr
            movwf min10
            movwf mins
            movwf sec10
            movwf sec
            clrf working               ; working flag to zero

            movlw B'10100000'          ; GIE set T0IE set, T0IF cleared
            movwf INTCON
   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


;-------------------------------------------------------------------------;
;                Wait until button is released                            ;
;-------------------------------------------------------------------------;
waitup:
            btfss TOGGLESW           ; test toggle switch
            goto $ -1                ; ck again if pressed
            movlw 20                 ; wait 20 msec for debounce
            call nmsec
            btfss TOGGLESW           ; check again, still up?
            goto $ -4                ; no start over
            return                   ; yes, finished

;-----------------------------------------------------------------------;
;                           Delay routine                               ;
;-----------------------------------------------------------------------;
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 the Time                                  ;
;-------------------------------------------------------------------------;
DispTime
              MOVLW H'C0'
              CALL SendINS
              MOVF hr10, W
              CALL SendASCII
              MOVF hr, W
              CALL SendASCII
              MOVLW ":"
              CALL SendCHAR
              MOVF min10, W
              CALL SendASCII
              MOVF mins, W
              CALL SendASCII
              MOVLW ":"
              CALL SendCHAR
              MOVF sec10, W
              CALL SendASCII
              MOVF sec, W
              CALL SendASCII
              RETURN

;-------------------------------------------------------------------------;
;                            Toggle Work Flag                             ;
;-------------------------------------------------------------------------;
togglewk:
            movf working, f   ; set zero flag if zero
            btfss STATUS, Z   ; skip if working is zero
            goto turnoff
            incf working, f   ; set working to 1
            call waitup       ; wait for button release
            return
turnoff:
            clrf working      ; working set to zero
            call waitup       ; wait for button to be released
            return

;-------------------------------------------------------------------------;
;                           The Main routine                              ;
;-------------------------------------------------------------------------;
main:
       call init               ; initialize ports, set up timer
       call initlcd            ; initialize the LCD
       movlw 0                 ; display 'Working Time:'
       call OutMessage
ckbutton:                      ; check for a press of the TOGGLE button
       btfss TOGGLESW          ; skip if not pressed
       call togglewk           ; else change the mode of timer
       movf oldtime, W         ; is oldtime the same as sec?
       subwf sec, W
       btfsc STATUS, Z         ; if not, skip over next instruction
       goto ckbutton           ; else continue checking
       call DispTime           ; sec has changed, display the time
       movf sec, W             ; make sec and oldsec the same
       movwf oldtime
       goto ckbutton           ; and continue checking

       end

Worktime Program

This program is a timer with pushbutton to start and stop accumulating time. The output is displayed on a Liquid Crystal Display, (LCD). An interrupt service routine, (isr), runs in the background. Every second it checks a flag called 'working'. The register 'sec' is incremented if the flag is set. An overflow of sec increments sec10 and so forth through tens of hours.

The display will flicker if the main loop displays the time continuously. Instead, the current seconds values is checked against the last displayed seconds value in 'oldtime'. Only if they are different is the display and oldtime updated.

Notice that there are two entry point to the subroutine that displays characters. At the entry point 'SendASCII', the value of the character '0' is added to W changing a binary number to an ASCII character. Sending instructions differs from sending characters. The RS line is high when sending characters, low for instructions.

Tables are used twice in this program. One table holds overflow values for each time register. The second holds the characters of a message to be displayed on the LCD. In both cases, the value of W on entry determines the value returned in W.

This is the first program to use a macro. The two instructions of the macro 'EStrobe' are inserted at each location in the program where the name of the macro, (EStrobe), appears. The puropose of the macro is to make the program easier to read.

LCD as an Output Device

Surplus LCD units that use the Hitachi 44870 controller can be purchased from B.G. Micro, (http://www.bgmicro.com), for as little as $3. Another supplier is Marlin P. Jones, (http://www.mpja.com). This program requires at least a two line by 16 character unit.

Hooking it up, contrast voltage

The first thing to do when you get your LCD is to confirm that it works. Apply +5 volts to pin 2 and ground pin 1. Also ground pin 3 which is the contrast voltage. You should see a line of squares where the characters appear. You may have to look at a low angle to see these. If you see nothing, the contrast voltage may not be right. Put a 10K, (or so), pot between +5 and ground and run the wiper to pin 3. Adjust the pot and see if you can make the squares appear and disappear. Some units require a negative contrast voltage.

Initialization

We must go through a complex procedure just to initialize the display. We also put it into 4 bit mode so only six control 6 wires are needed instead of ten. If you want to read more about the 44780, here are some URL's:

Your Turn

The purpose of this program was to demonstrate a simple application that uses a LCD display. It does contain many elements you would use in any program with a LCD display unit. Among these are:

Try to use subroutines from this program to change some of the previous programs to use a LCD display as an output device.

Comments:

Questions:

Christian Hultqvist Says: " how do i get this to work with 10mhz external reference clock/xtal " James Newton of Massmind replies: With great difficulty. The pre-scaler in the uC can't divide by a multiple of 10 (just 1/32, 1/64, 1/128, etc) so although you start with a nice 10Mhz clock, you end up with an ISR that runs every 10/32 or so on cycles. You might want to look at the page on timers.