I have put together a simple timer from code I have downloaded. It runs on a timer0 interrupt and displays elapsed time on a 16 X 2 LCD. I am trying to run the code on a 16f88. It will run for a few seconds, zero up, run for a few seconds, zero up and maybe even run for a few minutes and zero up. Eventually it quits some times leaving numbers in the display other time leaving the display blank. This code with the minor necessary changes will run flawlessly for days on a 16f627a. I am setup on a breadboard using only the PIC, a voltage regulator and a LCD. I am using the internal clock on the PIC. I am inclosing the code. Maybe someone will look at the code and tell me what the problem is.
;******************************************************************************
; ZERO-ERROR ONE SECOND TIMER
; (Roman Black 2001, public domain, use it as you like)
;
;
;******************************************************************************
;==============================================================================
; processor defined ;
LIST p=16F88 ;tell assembler what chip we are using
include "P16F88.inc" ;include the defaults for the chip
ERRORLEVEL 0, -302 ;suppress bank selection messages
;==============================================================================
; MPLAB stuff here
LIST b=5, n=97, t=ON, st=OFF
; absolute listing tabs=5, lines=97, trim long lines=ON, symbol table=OFF
__CONFIG _CONFIG1, _CP_OFF & _CCP1_RB0 & _DEBUG_OFF & _WRT_PROTECT_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _MCLR_OFF & _PWRTE_ON & _WDT_OFF & _INTRC_IO
;Program Configuration Register 2
__CONFIG _CONFIG2, _IESO_OFF & _FCMEN_OFF
LCD_PORT Equ PORTB
LCD_TRIS Equ TRISB
LCD_RS Equ 0x04 ;LCD handshake lines
LCD_RW Equ 0x06
LCD_E Equ 0x07
;==============================================================================
; Variables here
CBLOCK 0x20
sec
sec10
min
min10
hr
hr10
bres_hi ; hi byte of our 24bit variable
bres_mid ; mid byte
bres_lo ; lo byte
; (we only need 3 bytes for this system)
status_temp ; used for interrupt servicing
w_temp ; used for interrupt servicing
templcd ;temp store for 4 bit mode
templcd2
count ;used in looping routines
count1 ;used in delay routine
counta ;used in delay routine
countb ;used in delay routine
oldtime
ENDC
;==============================================================================
; Code here
org 0x000 ; Set program memory base at reset vector 0x000
reset
goto setup ; set up ints and port stuff
org 0x004 ; Interrupt vector, int handler code comes next.
;==============================================================================
;******************************************************************************
; INTERRUPT HANDLER (runs this code each timer0 interrupt)
;******************************************************************************
;
;------------------
int_handler
;
;------------------
;-------------------------------------------------
; first we preserve w and status register
movwf w_temp ; save off current W register contents
movf STATUS,w ; move status register into W register
movwf status_temp ; save off contents of STATUS register
tstf bres_mid ; first test for mid==0
skpnz ; nz = no underflow needed
decf bres_hi,f ; z, so is underflow, so dec the msb
decfsz bres_mid,f ; dec the mid byte (subtract 256)
goto int_exit ; nz, so definitely not one second yet.
tstf bres_hi ; test hi for zero too
skpz ; z = both hi and mid are zero, is one second!
goto int_exit ; nz, so not one second yet.
movlw 0x0F ; get msb value
movwf bres_hi ; load in msb
movlw 0x42 ; get mid value
movwf bres_mid ; load in mid
movlw 0x40 ; lsb value to add
addwf bres_lo,f ; add it to the remainder already in lsb
skpnc ; nc = no overflow, so mid is still ok
incf bres_mid,f ; c, so lsb overflowed, so inc mid
; movlw b'00001000' ; mask for bit 3
; xorwf PORTB,f ; toggle PORTA,bit3 (toggle the led)
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 int_exit ; else exit isr
clrf INDF ; set current digit to 0
incf FSR, f ; point at next digit
goto newdigit ; no, increment the next digit
int_exit
BCF INTCON,2 ; reset the tmr0 interrupt flag
bcf STATUS,Z
movf status_temp,w ; retrieve copy of STATUS register
movwf STATUS ; restore pre-isr STATUS register contents
swapf w_temp,f
swapf w_temp,w ; restore pre-isr W register contents
retfie ; return from interrupt
;------------------------------------------------------------------------------
;-------------------------------------------------------------------------;
; 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'
;******************************************************************************
; SETUP (runs this only once at startup)
;******************************************************************************
;
;------------------
setup ; goto label
;------------------
movlw B'01100000' ; 4Mhz = B'01100000'
banksel OSCCON
movwf OSCCON
banksel 0
;Wait until osc stabilizes
lp:
btfss OSCCON, 2
goto lp
; OPTION setup
movlw b'00001000' ;
; -------x ;
; Note! We set the prescaler to the wdt, so timer0
; has NO prescaler and will overflow every 256
; instructions and make an interrupt.
;
banksel OPTION_REG ; go proper reg bank
movwf OPTION_REG ; load data into OPTION_REG
banksel 0 ; back to normal bank 0
;-------------------------------------------------
; PORTB pins direction setup
; 1=input, 0=output
clrf PORTB ;
;
movlw b'00000000' ; all 8 portb are outputs
;
banksel TRISB ; go proper reg bank
movwf TRISB ; send mask to portb
banksel 0 ; back to normal reg bank
;-------------------------------------------------
; PORTA pins direction setup
; 1=input, 0=output
clrf PORTA ;
;
movlw b'00000000' ; all 5 porta are outputs,
; (with 16F84 porta only has lower 5 bits)
;
banksel TRISA ; go proper reg bank
movwf TRISA ; send mask to porta
banksel 0 ; back to normal reg bank
;-------------------------------------------------
movlw b'11100000' ; GIE=on TOIE=on (timer0 overflow int)
; bsf INTCON,7
; bsf INTCON,6
; bsf INTCON,5
banksel INTCON
movwf INTCON
banksel 0
clrf sec
clrf sec10
clrf min
clrf min10
clrf hr
clrf hr10
movlw 0x0F ; get msb value
movwf bres_hi ; put in hi
movlw 0x42 +1 ; get mid value (note we added 1 to it)
movwf bres_mid ; put in mid
movlw 0x40 ; get lsb value
movwf bres_lo ; put in mid
; now setup is complete, we can start execution.
;-------------------------------------------------
goto main ; start main program
;------------------------------------------------------------------------------
;Initialise LCD
LCD_Init call Delay100 ;wait for LCD to settle
movlw 0x20 ;Set 4 bit mode
call LCD_Cmd
movlw 0x28 ;Set display shift
call LCD_Cmd
movlw 0x06 ;Set display character mode
call LCD_Cmd
movlw 0x0c ;Set display on/off and cursor command
call LCD_Cmd ;Set cursor off
call LCD_Clr ;clear display
retlw 0x00
; command set routine
LCD_Cmd movwf templcd
swapf templcd, w ;send upper nibble
andlw 0x0f ;clear upper 4 bits of W
movwf LCD_PORT
bcf LCD_PORT, LCD_RS ;RS line to 1
call Pulse_e ;Pulse the E line high
movf templcd, w ;send lower nibble
andlw 0x0f ;clear upper 4 bits of W
movwf LCD_PORT
bcf LCD_PORT, LCD_RS ;RS line to 1
call Pulse_e ;Pulse the E line high
call Delay5
retlw 0x00
LCD_CharD addlw 0x30 ;add 0x30 to convert to ASCII
LCD_Char movwf templcd
swapf templcd, w ;send upper nibble
andlw 0x0f ;clear upper 4 bits of W
movwf LCD_PORT
bsf LCD_PORT, LCD_RS ;RS line to 1
call Pulse_e ;Pulse the E line high
movf templcd, w ;send lower nibble
andlw 0x0f ;clear upper 4 bits of W
movwf LCD_PORT
bsf LCD_PORT, LCD_RS ;RS line to 1
call Pulse_e ;Pulse the E line high
call Delay5
retlw 0x00
LCD_Line1 movlw 0x80 ;move to 1st row, first column
call LCD_Cmd
retlw 0x00
LCD_Line2 movlw 0xc0 ;move to 2nd row, first column
call LCD_Cmd
retlw 0x00
LCD_Line1W addlw 0x80 ;move to 1st row, column W
call LCD_Cmd
retlw 0x00
LCD_Line2W addlw 0xc0 ;move to 2nd row, column W
call LCD_Cmd
retlw 0x00
LCD_CurOn movlw 0x0d ;Set display on/off and cursor command
call LCD_Cmd
retlw 0x00
LCD_CurOff movlw 0x0c ;Set display on/off and cursor command
call LCD_Cmd
retlw 0x00
LCD_Clr movlw 0x01 ;Clear display
call LCD_Cmd
retlw 0x00
;LCD_HEX movwf tmp1
; swapf tmp1, w
; andlw 0x0f
; call HEX_Table
; call LCD_Char
; movf tmp1, w
; andlw 0x0f
; call HEX_Table
; call LCD_Char
; retlw 0x00
Delay255 movlw 0xff ;delay 255 mS
goto d0
Delay100 movlw d'100' ;delay 100mS
goto d0
Delay50 movlw d'50' ;delay 50mS
goto d0
Delay20 movlw d'20' ;delay 20mS
goto d0
Delay5 movlw 0x05 ;delay 5.000 ms (4 MHz clock)
d0 movwf count1
d1 movlw 0xC7 ;delay 1mS
movwf counta
movlw 0x01
movwf countb
Delay_0
decfsz counta, f
goto $+2
decfsz countb, f
goto Delay_0
decfsz count1 ,f
goto d1
retlw 0x00
Pulse_e bsf LCD_PORT, LCD_E
nop
bcf LCD_PORT, LCD_E
retlw 0x00
;end of LCD routines
DispTime
MOVLW 0x04
CALL LCD_Line1W
movf hr10,w
CALL LCD_CharD
movf hr,w
CALL LCD_CharD
MOVLW ":"
CALL LCD_Char
movf min10,w
CALL LCD_CharD
movf min,w
CALL LCD_CharD
MOVLW ":"
CALL LCD_Char
movf sec10,10
CALL LCD_CharD
movf sec,w
CALL LCD_CharD
RETURN
;*****************************************************************************
;******************************************************************************
; MAIN (main program loop)
;******************************************************************************
;
;------------------
main ; goto label
;------------------
call LCD_Init
main_loop ;
movf oldtime, W ; is oldtime the same as sec?
subwf sec, W
btfsc STATUS, Z ; if not, skip over next instruction
goto main_loop ; else continue checking
call DispTime ; sec has changed, display the time
movf sec, W ; make sec and oldsec the same
movwf oldtime
; or calls to the main program pieces.
; The interrupt does all the one second timer stuff.
;
goto main_loop ; keep running the main code.
;------------------------------------------------------------------------------
;==============================================================================
end ; no code after this point.
;==============================================================================
;******************************************************************************
; ZERO-ERROR ONE SECOND TIMER
; (Roman Black 2001, public domain, use it as you like)
;
;
;******************************************************************************
;==============================================================================
; processor defined ;
LIST p=16F88 ;tell assembler what chip we are using
include "P16F88.inc" ;include the defaults for the chip
ERRORLEVEL 0, -302 ;suppress bank selection messages
;==============================================================================
; MPLAB stuff here
LIST b=5, n=97, t=ON, st=OFF
; absolute listing tabs=5, lines=97, trim long lines=ON, symbol table=OFF
__CONFIG _CONFIG1, _CP_OFF & _CCP1_RB0 & _DEBUG_OFF & _WRT_PROTECT_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _MCLR_OFF & _PWRTE_ON & _WDT_OFF & _INTRC_IO
;Program Configuration Register 2
__CONFIG _CONFIG2, _IESO_OFF & _FCMEN_OFF
LCD_PORT Equ PORTB
LCD_TRIS Equ TRISB
LCD_RS Equ 0x04 ;LCD handshake lines
LCD_RW Equ 0x06
LCD_E Equ 0x07
;==============================================================================
; Variables here
CBLOCK 0x20
sec
sec10
min
min10
hr
hr10
bres_hi ; hi byte of our 24bit variable
bres_mid ; mid byte
bres_lo ; lo byte
; (we only need 3 bytes for this system)
status_temp ; used for interrupt servicing
w_temp ; used for interrupt servicing
templcd ;temp store for 4 bit mode
templcd2
count ;used in looping routines
count1 ;used in delay routine
counta ;used in delay routine
countb ;used in delay routine
oldtime
ENDC
;==============================================================================
; Code here
org 0x000 ; Set program memory base at reset vector 0x000
reset
goto setup ; set up ints and port stuff
org 0x004 ; Interrupt vector, int handler code comes next.
;==============================================================================
;******************************************************************************
; INTERRUPT HANDLER (runs this code each timer0 interrupt)
;******************************************************************************
;
;------------------
int_handler
;
;------------------
;-------------------------------------------------
; first we preserve w and status register
movwf w_temp ; save off current W register contents
movf STATUS,w ; move status register into W register
movwf status_temp ; save off contents of STATUS register
tstf bres_mid ; first test for mid==0
skpnz ; nz = no underflow needed
decf bres_hi,f ; z, so is underflow, so dec the msb
decfsz bres_mid,f ; dec the mid byte (subtract 256)
goto int_exit ; nz, so definitely not one second yet.
tstf bres_hi ; test hi for zero too
skpz ; z = both hi and mid are zero, is one second!
goto int_exit ; nz, so not one second yet.
movlw 0x0F ; get msb value
movwf bres_hi ; load in msb
movlw 0x42 ; get mid value
movwf bres_mid ; load in mid
movlw 0x40 ; lsb value to add
addwf bres_lo,f ; add it to the remainder already in lsb
skpnc ; nc = no overflow, so mid is still ok
incf bres_mid,f ; c, so lsb overflowed, so inc mid
; movlw b'00001000' ; mask for bit 3
; xorwf PORTB,f ; toggle PORTA,bit3 (toggle the led)
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 int_exit ; else exit isr
clrf INDF ; set current digit to 0
incf FSR, f ; point at next digit
goto newdigit ; no, increment the next digit
int_exit
BCF INTCON,2 ; reset the tmr0 interrupt flag
bcf STATUS,Z
movf status_temp,w ; retrieve copy of STATUS register
movwf STATUS ; restore pre-isr STATUS register contents
swapf w_temp,f
swapf w_temp,w ; restore pre-isr W register contents
retfie ; return from interrupt
;------------------------------------------------------------------------------
;-------------------------------------------------------------------------;
; 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'
;******************************************************************************
; SETUP (runs this only once at startup)
;******************************************************************************
;
;------------------
setup ; goto label
;------------------
movlw B'01100000' ; 4Mhz = B'01100000'
banksel OSCCON
movwf OSCCON
banksel 0
;Wait until osc stabilizes
lp:
btfss OSCCON, 2
goto lp
; OPTION setup
movlw b'00001000' ;
; -------x ;
; Note! We set the prescaler to the wdt, so timer0
; has NO prescaler and will overflow every 256
; instructions and make an interrupt.
;
banksel OPTION_REG ; go proper reg bank
movwf OPTION_REG ; load data into OPTION_REG
banksel 0 ; back to normal bank 0
;-------------------------------------------------
; PORTB pins direction setup
; 1=input, 0=output
clrf PORTB ;
;
movlw b'00000000' ; all 8 portb are outputs
;
banksel TRISB ; go proper reg bank
movwf TRISB ; send mask to portb
banksel 0 ; back to normal reg bank
;-------------------------------------------------
; PORTA pins direction setup
; 1=input, 0=output
clrf PORTA ;
;
movlw b'00000000' ; all 5 porta are outputs,
; (with 16F84 porta only has lower 5 bits)
;
banksel TRISA ; go proper reg bank
movwf TRISA ; send mask to porta
banksel 0 ; back to normal reg bank
;-------------------------------------------------
movlw b'11100000' ; GIE=on TOIE=on (timer0 overflow int)
; bsf INTCON,7
; bsf INTCON,6
; bsf INTCON,5
banksel INTCON
movwf INTCON
banksel 0
clrf sec
clrf sec10
clrf min
clrf min10
clrf hr
clrf hr10
movlw 0x0F ; get msb value
movwf bres_hi ; put in hi
movlw 0x42 +1 ; get mid value (note we added 1 to it)
movwf bres_mid ; put in mid
movlw 0x40 ; get lsb value
movwf bres_lo ; put in mid
; now setup is complete, we can start execution.
;-------------------------------------------------
goto main ; start main program
;------------------------------------------------------------------------------
;Initialise LCD
LCD_Init call Delay100 ;wait for LCD to settle
movlw 0x20 ;Set 4 bit mode
call LCD_Cmd
movlw 0x28 ;Set display shift
call LCD_Cmd
movlw 0x06 ;Set display character mode
call LCD_Cmd
movlw 0x0c ;Set display on/off and cursor command
call LCD_Cmd ;Set cursor off
call LCD_Clr ;clear display
retlw 0x00
; command set routine
LCD_Cmd movwf templcd
swapf templcd, w ;send upper nibble
andlw 0x0f ;clear upper 4 bits of W
movwf LCD_PORT
bcf LCD_PORT, LCD_RS ;RS line to 1
call Pulse_e ;Pulse the E line high
movf templcd, w ;send lower nibble
andlw 0x0f ;clear upper 4 bits of W
movwf LCD_PORT
bcf LCD_PORT, LCD_RS ;RS line to 1
call Pulse_e ;Pulse the E line high
call Delay5
retlw 0x00
LCD_CharD addlw 0x30 ;add 0x30 to convert to ASCII
LCD_Char movwf templcd
swapf templcd, w ;send upper nibble
andlw 0x0f ;clear upper 4 bits of W
movwf LCD_PORT
bsf LCD_PORT, LCD_RS ;RS line to 1
call Pulse_e ;Pulse the E line high
movf templcd, w ;send lower nibble
andlw 0x0f ;clear upper 4 bits of W
movwf LCD_PORT
bsf LCD_PORT, LCD_RS ;RS line to 1
call Pulse_e ;Pulse the E line high
call Delay5
retlw 0x00
LCD_Line1 movlw 0x80 ;move to 1st row, first column
call LCD_Cmd
retlw 0x00
LCD_Line2 movlw 0xc0 ;move to 2nd row, first column
call LCD_Cmd
retlw 0x00
LCD_Line1W addlw 0x80 ;move to 1st row, column W
call LCD_Cmd
retlw 0x00
LCD_Line2W addlw 0xc0 ;move to 2nd row, column W
call LCD_Cmd
retlw 0x00
LCD_CurOn movlw 0x0d ;Set display on/off and cursor command
call LCD_Cmd
retlw 0x00
LCD_CurOff movlw 0x0c ;Set display on/off and cursor command
call LCD_Cmd
retlw 0x00
LCD_Clr movlw 0x01 ;Clear display
call LCD_Cmd
retlw 0x00
;LCD_HEX movwf tmp1
; swapf tmp1, w
; andlw 0x0f
; call HEX_Table
; call LCD_Char
; movf tmp1, w
; andlw 0x0f
; call HEX_Table
; call LCD_Char
; retlw 0x00
Delay255 movlw 0xff ;delay 255 mS
goto d0
Delay100 movlw d'100' ;delay 100mS
goto d0
Delay50 movlw d'50' ;delay 50mS
goto d0
Delay20 movlw d'20' ;delay 20mS
goto d0
Delay5 movlw 0x05 ;delay 5.000 ms (4 MHz clock)
d0 movwf count1
d1 movlw 0xC7 ;delay 1mS
movwf counta
movlw 0x01
movwf countb
Delay_0
decfsz counta, f
goto $+2
decfsz countb, f
goto Delay_0
decfsz count1 ,f
goto d1
retlw 0x00
Pulse_e bsf LCD_PORT, LCD_E
nop
bcf LCD_PORT, LCD_E
retlw 0x00
;end of LCD routines
DispTime
MOVLW 0x04
CALL LCD_Line1W
movf hr10,w
CALL LCD_CharD
movf hr,w
CALL LCD_CharD
MOVLW ":"
CALL LCD_Char
movf min10,w
CALL LCD_CharD
movf min,w
CALL LCD_CharD
MOVLW ":"
CALL LCD_Char
movf sec10,10
CALL LCD_CharD
movf sec,w
CALL LCD_CharD
RETURN
;*****************************************************************************
;******************************************************************************
; MAIN (main program loop)
;******************************************************************************
;
;------------------
main ; goto label
;------------------
call LCD_Init
main_loop ;
movf oldtime, W ; is oldtime the same as sec?
subwf sec, W
btfsc STATUS, Z ; if not, skip over next instruction
goto main_loop ; else continue checking
call DispTime ; sec has changed, display the time
movf sec, W ; make sec and oldsec the same
movwf oldtime
; or calls to the main program pieces.
; The interrupt does all the one second timer stuff.
;
goto main_loop ; keep running the main code.
;------------------------------------------------------------------------------
;==============================================================================
end ; no code after this point.
;==============================================================================