# How to use PIC16F84A TMR0 Interrupt as a substitute to the Sleep instruction in Assembly

I have this circuit:

So my problem is that I'm having trouble using the TMR0 interrupt as a delay or substitute to the SLEEP instruction in the PIC16F84A. I can't quite get the concept of using the overflows to generate a timeout.

What I have in mind is that, I have to put the overflow counter loop inside the TMR0 subroutine, and then loop until it reaches the number of overflows required to generate a 1s timeout. Is this correct? If so, how could I implement it in my code?

Side Question: How can I initialize variables in PIC assembly? What is the equivalent of

LABEL DB 00H


in PIC assembly?

Here is my code so far:

LIST P=16F84A, F=INHX8M          ; MCU is PIC16F84A, output is Intel Hex
INCLUDE<P16F84A.INC>             ; include this file to use register names instead of addresses
__CONFIG _CP_OFF & _WDT_ON & _XT_OSC ; code protection off, Watchdog Tiemr on, XTAL osc used

ORG 000H
GOTO MAIN
ORG 004H               ; interrupt vector
GOTO INT_RTN           ; jump to label INT_RTN or the interrupt service routine

**MAIN**
; initialize PIC
BSF STATUS, RP0            ; set register bank to 1
CLRF TRISA                 ; clear register TRISA (bank 1)
; sets all bits in PORTA as "output"

MOVLW 0FFH                 ; load literal value 01H to Wreg
MOVWF TRISB                ; move data in Wreg to register TRISB (bank 1)
; set ports in PORTB as "input"

MOVLW 004H                 ; load literal value 04H to Wreg
; prescaler 1:32 assigned to Watchdog Timer (WDT)
MOVWF OPTION_REG           ; move data in Wreg to register OPTION_REG (bank 0/1)

BSF OPTION_REG, 6          ; interrupt event during rising edge
BCF INTCON, INTF           ; clear the RB0/INT interrupt flag
BSF INTCON, INTE           ; unmask (enable) RB0/INT interrupt source
BSF INTCON, GIE            ; enable all unmasked interrupt
BCF INTCON, T0IF           ; clears the TMR0 overflow interrupt flag
BSF INTCON, T0IE           ; unmasked (enable) TMR0 overflow interrupt
BCF STATUS, RP0            ; set register bank to 0

CLRF PORTA
CLRF PORTB
CLRF TMR0
GOTO START
; program start

**INT_RTN**
BCF INTCON, GIE             ; disables all unmasked interrupt (no interrupt overriding)
BTFSC INTCON, INTF          ; check the RB0/INT flag
GOTO RB0_INT                ; jump to label RB0_INT (routine for RB0_INT)
BTFSC INTCON, T0IF          ; check the TMR0 interrupt flag
GOTO TMR0_INT               ; jump to label TMR0_INT (routine for TMR0 interrupt)
GOTO EXIT                   ; exit INT_RTN

; hardware interrupt
**RB0_INT**
BCF INTCON, INTF            ; reset RB0/INT flag
;code for RB0
GOTO EXIT

; TMR0 Interrupt
**TMR0_INT**
BCF INTCON, T0IF
;code for TMR

**EXIT**
BSF INTCON, GIE               ; enable all unmasked interrupt
BCF INTCON, INTF
RETFIE                    ; return from interrupt routine

**START**
CLRF TMR0
MOVLW 09H                 ; load literal value 09H to Wreg
MOVWF PORTA

**COUNTDOWN**  //Countdown timer from 9-0
MOVF TMR0, W
BTFSS STATUS, Z
DECFSZ PORTA, 1                 ; decrement register COUNT, skip next line if result is 0
GOTO COUNTDOWN                  ; jump to label INC
GOTO START                      ; jump to label START

;====================================================================
END


## 1 Answer

In your main loop, when you want to have a delay and wait for a while, clear tmr0 and the counter variable and then enable tmr0 interrupts. Immediatly enter a little loop which repetitively compares the value of the counter variable against the limit value (number of required tmr0 interrupts). The counter variable would be incremented in the tmr0 ISR every time the ISR is executed (every time tmr0 overflows). When the counter variable value matches the limit value jump out of the loop, disable tmr0 interrupts and continue on the main loop. The code to achieve an exact 1 second delay would require a little complexity because tmr0 has a count of 256 and this figure combined with the available pre-scaler values can make life a little more complicated.

To compare the counter value against the limit value subtract one from the other and test the zero flag.

Alternatively you could start with the counter value at the required count and decrement it once every tmr0 interrupt. Then test the counter's value for when it reaches zero by repetitively moving it into itself in the repeating loop (MOVF Counter,F) and then testing the zero flag. The MOVF instruction sets the zero flag if the result is zero. This method is a little simpler than the first because no subtraction is necessary.

In the PIC 16F84A there are 68 general purpose registers (SRAM) for holding variables. They start at address 0x0C. So, before your ORG 0x0000 you would initialise your variables like this

Variablename1 equ 0x0C

Variablename2 equ 0x0D

Variablename3 equ 0x0E

and so on...

• THANK YOU SO MUCH – German III Felisarta Dec 17 '20 at 12:52
• Umm one last question though, do variables assigned in the general-purpose registers, really behave like variables like in higher level languages? – German III Felisarta Dec 17 '20 at 12:54
• The variables declared in the general purpose registers are variables in the true sense and can have there values incremented, decremented, added, subtracted and processed in just the same way as any high level variable, obviously limited by the capabilities of the particular PIC's instruction set. However they are only 8 bits wide and so would be equivalent to a variable defined as type "char" in C. – James Dec 17 '20 at 14:21
• Again, thanks a lot for the very in-depth explanations! – German III Felisarta Dec 17 '20 at 14:28