Need help with PIC Assembly for PIC16F72 Interrupt service routine

Papabravo

Joined Feb 24, 2006
22,083
Then in that case didn't my code already have a periodic interrupt, where I preloaded the timer where the roll over was 30 ticks, and this would cause the interrupt and in the interrupt i reloaded the timer.
Only if everything else was happening correctly. IMHO your code had more than 1 problem as it was originally presented.
 

Thread Starter

seifullaah73

Joined Dec 13, 2019
51
after some research I found that it is supposed to be DECFSZ INTDELAY, 1
so that the actual F register is decremented and not the w register
same with XORWF PORTB, 1 so result is stored in PORTB but how does it know which pin.
 

jpanhalt

Joined Jan 18, 2008
11,087
After your suggestion my amended program is below. so the interrupt toggles the output LED. I was hoping the interrupt to stop what is currently executing, which is output is high and change it low for a certain amount of time and then change it back high again after the interrupt, which indirectly creates a square wave. But here the interrupts toggles the led from on to off or off to on. Can you check below.
Quite the opposite. The interrupt interrupts before the next step, but does not change anything, unless you do it. You try to write code that is stable to being interrupted, but that is not always possible to do. So, you can toggle GIE during those portions of code.

Also in regards to equ variable
WF - operates on the contents of the register
LW - operates on the direct value mentioned by equ COUNTA EQU 100

This part confuses me a bit, i was taught to assign a register address to the variable and that movlw places the value in that variable via movlw .50 movwf countA but xorlw doesn't use 50 but rather 100.
movfw is recognized by most Assemblers, but, if you want to operate on a register, you move it to W first (movf reg,w). There are some instances where you do operate on the register RAM location (e.g., indirect addressing) , but with what you are doing, you will always be operating on the contents. I do not consider it good practice to define register locations, unless there is a purpose in doing that. There is the directive "CBLOCK 0x20" (or wherever the RAM you want to use begins) which starts all of the register names at 0x20 and assigns them sequentially. At the end, "ENDC" directive ends it.

shouldn't the code be xorwf portb,0 instead of xorwf portb, f
NO. These PIC's cannot do that. XORWF PORTB only affects the bit(s) in PORTB that are in W with a non-zero value. So, if W contains 0x01, the only bit in the operand affected is bit<0>.

EDIT: Actually, xorwf portb,0 is a legitimate instruction, but it doesn't do what you intended to do. It xor's W with PORTB and stored the result in W, not PORTB.

Code:
#include <p16f72.inc>
__config (_HS_OSC & _WDT_OFF & _PWRTE_OFF & _CP_OFF)
        
W_SAVE        EQU         25
STATUS_SAVE    EQU         26
INTDELAY    EQU            27

;******************************************************************

list    P=16F72   

ORG 000H
GOTO MAIN

ORG 004H
GOTO INTFUNCT

; /////////////////////////////////////////////////////////////////////////

;             Initialize all variables and port pins here!

; /////////////////////////////////////////////////////////////////////////
MAIN:
    BSF    STATUS, RP0
    MOVLW     B'00000000'
    MOVWF    TRISB

    MOVLW    B'10000110'
    MOVWF    OPTION_REG
    BCF    STATUS, RP0
    CLRF    PORTB
    MOVLW    .178
    MOVWF    TMR0
    BCF    INTCON, TMR0IF
    BSF    INTCON, TMR0IE
    BSF    INTCON, GIE
    BSF    PORTB, 0
    MOVLW    .100
    MOVWF    INTDELAY

PROGRAM:
    NOP
    GOTO PROGRAM

INTFUNCT:
    MOVWF W_SAVE        ; save W
    SWAPF STATUS, W     ; save STATUS
    MOVWF STATUS_SAVE

    BCF    STATUS, RP0

    BTFSS    TMR0IF
    GOTO ENDIR

    BCF    INTCON, TMR0IF
    DECFSZ    INTDELAY
    GOTO ENDIR        ;once it reaches 0, then do the main part of the interrupt and then reset intdelay

    MOVLW    .1
    XORWF    PORTB,F

    MOVLW    .178
    MOVWF    TMR0
    MOVLW    .100
    MOVWF    INTDELAY
    GOTO ENDIR

ENDIR:
    SWAPF STATUS_SAVE, W
    MOVWF STATUS
    SWAPF W_SAVE, F
    SWAPF W_SAVE, W
    RETFIE

    END
 
Last edited:

jpanhalt

Joined Jan 18, 2008
11,087
after some research I found that it is supposed to be DECFSZ INTDELAY, 1
so that the actual F register is decremented and not the w register
same with XORWF PORTB, 1 so result is stored in PORTB but how does it know which pin.
It is always wise to use the destination when needed and not rely on the default. Standardize on using 0,1 or W,F, respectively. I prefer W,F.
 

Thread Starter

seifullaah73

Joined Dec 13, 2019
51
so if W was .10 binary for 1010, then XORWF PORTB, f would effect bit 1 and 3.
EDIT: lol i just figured out what you mean 0,1 or w,f respectively. meaning either use w or 0 / f or 1

Here is the final code hopefully. I will do research on Cblock and see how I can implement that.

Code:
#include <p16f72.inc>
__config (_HS_OSC & _WDT_OFF & _PWRTE_OFF & _CP_OFF)

W_SAVE        EQU         25
STATUS_SAVE    EQU         26
INTDELAY    EQU        27

;******************************************************************

list    P=16F72          

ORG 000H
GOTO MAIN

ORG 004H
GOTO INTFUNCT

; /////////////////////////////////////////////////////////////////////////

;             Initialize all variables and port pins here!

; /////////////////////////////////////////////////////////////////////////
MAIN:
    BSF    STATUS, RP0
    MOVLW     B'00000000'
    MOVWF    TRISB  

    MOVLW    B'10000110'
    MOVWF    OPTION_REG
    BCF    STATUS, RP0
    CLRF    PORTB
    MOVLW    .178
    MOVWF    TMR0
    BCF    INTCON, TMR0IF
    BSF    INTCON, TMR0IE
    BSF    INTCON, GIE
    BSF    PORTB, 0
    MOVLW    .100
    MOVWF    INTDELAY

PROGRAM:
    NOP
    GOTO PROGRAM
   
INTFUNCT:
    MOVWF W_SAVE        ; save W
    SWAPF STATUS, W     ; save STATUS
    MOVWF STATUS_SAVE
   
    BCF    STATUS, RP0
   
    BTFSS    TMR0IF      
    GOTO ENDIR
   
    BCF    INTCON, TMR0IF
    DECFSZ    INTDELAY,f
    GOTO ENDIR        ;once it reaches 0, then do the main part of the interrupt and then reset intdelay
   
    MOVLW    .1
    XORWF    PORTB,f
   
    MOVLW    .178
    MOVWF    TMR0
    MOVLW    .100
    MOVWF    INTDELAY
    GOTO ENDIR

ENDIR:
    SWAPF STATUS_SAVE, W
    MOVWF STATUS
    SWAPF W_SAVE, F
    SWAPF W_SAVE, W
    RETFIE

    END
Hard to for me to test it until I am back in college as I don't have my own pic board.
 

jpanhalt

Joined Jan 18, 2008
11,087
That will not assemble.
1) BTFSS TMR0IF is missing the full argument. It needs to be BTFSS INTCON,TMR0IF
2) There are also several messages related to the directives you have misplaced in column 1, e.g., List, ORG, GOTO etc. before your "MAIN"

EDIT: With those changes, it assembles without error or messages and simulates with MPLAB SIM. PORTB,0 toggles every 3 seconds or so. If you want 1-second toggles (2 second period), change INTDELAY to .33 or so.
 
Last edited:

Thread Starter

seifullaah73

Joined Dec 13, 2019
51
That will not assemble.
1) BTFSS TMR0IF is missing the full argument. It needs to be BTFSS INTCON,TMR0IF
2) There are also several messages related to the directives you have misplaced in column 1, e.g., List, ORG, GOTO etc. before your "MAIN"

EDIT: With those changes, it assembles without error or messages and simulates with MPLAB SIM. PORTB,0 toggles every 3 seconds or so. If you want 1-second toggles (2 second period), change INTDELAY to .33 or so.
1) My mistake. I forgot about the intcon thing. Thanks

2) How shall I fix this. I'm not sure. So all directives before the Main program should be indented to second column?

3) whats the calculation for the delay.
I have it as tmr rollover is 77 and each tick will take 128 cycles, 1 micro seconds, which is a total of 9856us or 9.8ms x 100 = 0.9 seconds. but maybe because of the delay of the other instructions adds to it. so will change it to .33

As for CBLOCK this is what I have come up with. not that hard.

CBLOCK 0x025H
W_SAVE
STATUS_SAVE
INTDELAY
ENDC

But i am assuming that the same applies with this that if i want to operate on the contents of the register first I have to move it to the w register with (MOVF INTDELAY, W then i can do xorlw or perform operation on w register).
 

Thread Starter

seifullaah73

Joined Dec 13, 2019
51
Regarding collumns.

Most assembly language programs are written in lines of 80 characters organized into four columns. The first column is for optional labels. The second column is for assembly instructions or assembler directives. The third column is for specifying operands, and the fourth column is for comments. Traditionally, the first two columns are 8 characters wide, the third column is 16 characters wide, and the last column is 48 characters wide. However, most modern assemblers (including GAS) do not require a fixed column widths.

So this is what you mean?
 

Thread Starter

seifullaah73

Joined Dec 13, 2019
51
Hopefully this should be correct with the instructions in the correct columns. I didn't want to shift the comments that are above to the 4th column so ignore that.

Code:
;******************************************************************
;Processor:            PIC 16F72
;Clock Speed:         4 MHz (Oscillator Xtal)
; /////////////////////////////////////////////////////////////////////////

    #include <p16f72.inc>
     __config (_HS_OSC & _WDT_OFF & _PWRTE_OFF & _CP_OFF)

    CBLOCK    0x025H
        W_SAVE       
        STATUS_SAVE   
        INTDELAY
    ENDC   

;******************************************************************

    list    P=16F72           

    ORG 000H
    GOTO MAIN

    ORG 004H
    GOTO INTFUNCT

; /////////////////////////////////////////////////////////////////////////

;             Initialize all variables and port pins here!

; /////////////////////////////////////////////////////////////////////////
MAIN:
    BSF    STATUS, RP0
    MOVLW    B'00000000'
    MOVWF    TRISB   

    MOVLW    B'10000110'
    MOVWF    OPTION_REG
    BCF    STATUS, RP0
    CLRF    PORTB
    MOVLW    .178
    MOVWF    TMR0
    BCF    INTCON, TMR0IF
    BSF    INTCON, TMR0IE
    BSF    INTCON, GIE
    BSF    PORTB, 0
    MOVLW    .33
    MOVWF    INTDELAY

PROGRAM:
    NOP
    GOTO PROGRAM
    
INTFUNCT:
    MOVWF W_SAVE    ; save W
    SWAPF STATUS, W    ; save STATUS
    MOVWF STATUS_SAVE
    
    BCF    STATUS, RP0
    
    BTFSS    INTCON, TMR0IF       
    GOTO ENDIR
    
    BCF    INTCON, TMR0IF
    DECFSZ    INTDELAY,f
    GOTO ENDIR    ;once it reaches 0, then do the main part of the interrupt and then reset intdelay
    
    MOVLW    .1
    XORWF    PORTB,f
    
    MOVLW    .178
    MOVWF    TMR0
    MOVLW    .33
    MOVWF    INTDELAY
    GOTO ENDIR

ENDIR: 
    SWAPF STATUS_SAVE, W
    MOVWF STATUS
    SWAPF W_SAVE, F
    SWAPF W_SAVE, W 
    RETFIE

    END
 

jpanhalt

Joined Jan 18, 2008
11,087
1) @post #30
It looks like you have the idea about columns. As for how many spaces to use, that is pretty arbitrary. 80 character wide is about right. In MPLab 8.92, those settings are in "Editor Properties" , I set a "gutter" (space before anything) of 4 spaces, and tabs to 5 spaces. Of course, you need to convert tabs to spaces. So, labels are far left but not at the very edge of the screen, directives /instructions are 5 spaces (1 tab) in, then 2 tabs (10 spaces from start, which is often just 1 tab after the instruction) for the operand, and comments generally start at about 31 spaces in total. I also use all caps for SFR's (e.g, STATUS), all lower case for instructions/directives, camel for for labels, and lower case for RAM registers that I name with exceptions.

Those are simply preferences. You can make up your own rules, but if you are consistent, it is easier to read. All caps for SFR's is pretty common.

2) The timings I gave were based on using the "stopwatch" in the simulator at 4 MHz. It is usually pretty accurate, as it counts actual cycles, A branch or skip (anything that goes to a non-sequential program counter) counts as 2 cycles. A lot of btfss/c's, goto's, calls, returns can drive the count up.
 

Thread Starter

seifullaah73

Joined Dec 13, 2019
51
Ok that was quick. I have another small challenge for myself to add to this.

I want to add a check to see if a button is pressed and change frequency of the led flashing.

This will be added to the main program that has the NOP inside it and will be using BTFSS. Code below is just to give brief idea. I decided to do calls to functions because changing value of register takes 2 lines of code.

Code:
MAIN:
    MOVLW    b'11111111'    ;make all ports input
    MOVWF    TRISA    ;Switches are on port A reconfigured by the teacher

PROGRAM:
    BTFSS    PORTB,0
    CALL FREQUENCY1
    BTFSS    PORTB,1
    CALL FREQUENCY2
    .
    .
    BTFSS    PORTB,7
    CALL FREQUENCY8
    GOTO    PROGRAM
    
FREQUENCY1:
    MOVLW    .28
    MOVWF    INTDELAY
    RETURN

FREQUENCY2:
    MOVLW    .24
    MOVWF    INTDELAY
    RETURN
    
    .
    .
    .
FREQUENCY8:
    MOVLW    .4
    MOVWF    INTDELAY
    RETURN
I was thinking of adding delay when button is pressed but not sure before the check or after.

what do you think
 

jpanhalt

Joined Jan 18, 2008
11,087
Unless the button switch is "debounced" you need either software or hardware debouncing. Switch contacts are very noisy electrically and can bounce up to 40 ms. Consider what would happen if your signal was given repeatedly in rapid succession X3.

Can we assume that your LED flasher program worked in real life?
 

Thread Starter

seifullaah73

Joined Dec 13, 2019
51
Unless the button switch is "debounced" you need either software or hardware debouncing. Switch contacts are very noisy electrically and can bounce up to 40 ms. Consider what would happen if your signal was given repeatedly in rapid succession X3.

Can we assume that your LED flasher program worked in real life?
Yes we can assume that. Till I get it tested on the board.

The switch are those ones where it bounces back after you click it
 

jpanhalt

Joined Jan 18, 2008
11,087
Yes, I am familiar with push button switches as are most others here. This is clearly "homework," but I do not object to it being here so long as you show your solution first. Basically, you detect a change in state and then wait and check again whether the state is the same. That time can be anywhere form 10 ms to 40 ms. Most of the hardware devices I have seen use 40 ms.

The problem is, if one detects a change, say 0 -->1, how fast is the program loop that on the next check it reads a 0, then another 1. That can wreak havoc in a program. There are many discussions of "debouncing"on the web. This one has become a classic: http://www.ganssle.com/debouncing.pdf I would also suggest that you read the datasheets on some hardware debouncers.
 

Thread Starter

seifullaah73

Joined Dec 13, 2019
51
Yes, I am familiar with push button switches as are most others here. This is clearly "homework," but I do not object to it being here so long as you show your solution first. Basically, you detect a change in state and then wait and check again whether the state is the same. That time can be anywhere form 10 ms to 40 ms. Most of the hardware devices I have seen use 40 ms.

The problem is, if one detects a change, say 0 -->1, how fast is the program loop that on the next check it reads a 0, then another 1. That can wreak havoc in a program. There are many discussions of "debouncing"on the web. This one has become a classic: http://www.ganssle.com/debouncing.pdf I would also suggest that you read the datasheets on some hardware debouncers.
When you said debouncers, I wasn't sure if you were talking about the other type of switches that don't bounce back.

I'll be honest. This isn't homework but because my teacher sees how determined I am and a quick learner, this was his other challenge for me if I get past understanding ISR, which I did it was just the implementation part that didn't work.

The main program is already a loop.
so check button then delay 40ms from a separate delay function then check again.

the interrupt also has a delay of it's own. 30ms in total. which multiplied by 33 to get 1 second.

why not a delay of 20ms extra so as to not detect multiple button presses and let the switch go back to it's normal state. but problem with that is will it miss the button being pressed. a possibility that I press the button while code is in middle of interrupt.
 
Last edited:

jpanhalt

Joined Jan 18, 2008
11,087
There is one condition you need to avoid: Never put the program into a loop that cannot be exited waiting for a response. WDT can help. @JohnInTX has written more eloquently about that than I can.

Like I said, I do not feel this dialog is misplaced. But, we need to see your effort to resolve it. Search on "debouncing chip." Here is just one: MC14990 (https://www.onsemi.com/products/standard-logic/arithmetic-logic-functions/mc14490 ). It describes the problem.
 

Thread Starter

seifullaah73

Joined Dec 13, 2019
51
So I was reading into switches which bounce causing spikes of high when button is pressed and some spikes
when button is released.

Good article I found.
http://www.talkingelectronics.com/projects/Elektor/Gooligum PIC Tutorials/PIC_Base_A_4.pdf

Talks about hardware and software solutions of debouncing switches.
EDIT: output is low when button is pressed. my mistake.

So to implement this in code
you would need to detect if button is pressed
do something
delay for debounce effect to wear off about 40ms
wait for it to be released
delay for bounce effect to wear off 40ms
loop

Code:
PROGRAM:
    BTFSC    PORTA,0    ;once button pressed skip goto and trigger event
    GOTO PROGRAM 
    CALL CHANGEFREQUENCY
    CALL DELAY20MS    ;wait for signal to settle at 0
    WAITRELEASE    BTFSS    PORTA,0    ;check if button depressed
    GOTO    WAITRELEASE    ; reduce time instead of looping whole thing wait here till button released
    CALL DELAY20MS    ;add delay to allow signal to settle back to 0

    REPEAT ABOVE COMMANDS FOR OTHER BUTTONS
  
    GOTO PROGRAM
 
Last edited:

Thread Starter

seifullaah73

Joined Dec 13, 2019
51
Only problem I am thinking of is the delay of the interrupt, which is already 30ms periodic interrupt. then add that to the delay 20ms. you get a 50ms delay.
 
Top