Having problems with external interrupt on PIC16F886

Thread Starter

Barnaby Walters

Joined Mar 2, 2011
102
Hello All,

I'm having problems getting the external interrupt to trigger on my robot. The output of a 38kHz IR detector is fed into RB0 — the signal is high unless a wave is detected, in which case it drops down to low. I have tried to set the interrupt to trigger on a falling edge. I've tried using the weak pull up resistor, I've added context save/restore even though my program doesn't need it, and I'm stuck.

The ASM is below:

Rich (BB code):
;
;	Author: Barnaby Walters
;	Created: 27th March 2011
;	
;	For PIC: 16F886
;	Default Radix: Hex
;
;	Description:	Contains the IR enable/detect loop. Lights different LEDs to indicate which direction an object is seen in
;					Runs at 4MHz	
;	
;	Currently:		The interrupt handler is never called. The rest of the program seems to function fine, but no matter what I do
;					I can't get the handler routine called.
;
; * * * * * * * * * * * * * * * *

list p=16f886
include p16f886.inc
	
	; Set up 16F886 Config Registers
	__CONFIG	_CONFIG1, _MCLRE_OFF & _WDT_OFF & _INTRC_OSC_NOCLKOUT & _BOR_ON & _CP_OFF & _DEBUG_OFF & _LVP_OFF & _FCMEN_OFF & _IESO_OFF & _CPD_OFF
				; For Config Bank 1: No MCLR Reset, no WDT, int. Osc no clkout, BOR On, Code protection, Low voltage protection — essectially, very few features.
	__CONFIG	_CONFIG2, _BOR40V
				; Set the BOR voltage to 4v
	
; Reserve Variables
tmr1	equ	20h
tmr2	equ	tmr1 + 1
tmr3	equ	tmr2 + 1
IRData	equ	tmr3 + 1		; IRData structure: 7:4 = N/A; 3 = Signal right; 2 = Signal Left; 1 = Right Lit; 0 = Left Lit
w_temp	equ	IRData + 1
status_temp	equ	w_temp + 1

;firstAvailableRegister	equ	0x20

	org 0h
	nop						; Allows ICD2 Debugger to work - not that I'm using that.
	goto	main			; Start main program
	nop
	nop
	
; Interrupt Service Routine	
	movwf	w_temp
	movf	STATUS,w
	movwf	status_temp		; Save context
; Actual ISR
	movlw	b'11111001'
	movwf	PORTC			; Light up most of PORTC, including the indicator LED @ RC3
	bcf		INTCON,INTF		; Clear ext. int. flag
	
	movf	status_temp,w	; Restore context
	movwf	STATUS
	swapf	w_temp,f
	swapf	w_temp,w
	retfie					; Return
	
; * * * * * * * * * * *	SUBROUTINES	* * * * * * * * * * * * * * *

; Initalisation Routine
init
	clrf	PORTA
	clrf	PORTB
	clrf	PORTC
	banksel	OSCCON			; switches to bank 1. We need to be here to mess about with TRIS registers
	movlw	b'01100001'		; 
	movwf	OSCCON			; Osc Config: 0 (un) , 110 (4Mhz) , 000 (Rd Only) , 1 (Use internal Clk)
	
	movlw	b'00000000'		;
	movwf	TRISA			; PORTA = all outputs. A0 = Right indicator A1 = Left Indicator
	movlw	b'00000001'		;
	movwf	TRISB			; PORTB 0:3 used. B1 = Right Enable B2 = Left Enable B0 = Sensor Input
	movwf	WPUB			; Weak pull-up on RB0
	movlw	b'00000000'		;
	movwf	TRISC			; PORTC = outputs. C3 = HB LED. Motors not used in this app (yet)
	movlw	b'1000'
	movwf	TRISE			; PORTE 3 (MCLR) = Input
	
	banksel	OPTION_REG
	
	bcf		OPTION_REG,NOT_RBPU	; Enable RB Pull Ups
	bcf		OPTION_REG,INTEDG	; Interrupt triggered on falling edge @ RB0
	
	movlw	b'10010000'
	movwf	INTCON			; Enable unmasked interrupts, Enable interrupt on RB0
	
	banksel	PORTA		; Switch to bank 0 so we can move on with the program
	return					; Return to the program

; Delay Routines:

D30IRCycles					; Delays program flow for 30 cycles @ 38kHz — 'On' time for IREDs
	movlw	d'2'
	movwf	tmr2
	movlw	d'195'			; 30 IR Cycles = 780 instruction cycles. GOTO = 2 instruction cycles
	movwf	tmr1			; This timer will have to be completely decremented two times for the full 780 cycles
D30IR_Delay
	decfsz	tmr1,f
	goto	D30IR_Delay		; Cycle 195 times
	movwf	tmr1
	decfsz	tmr2,f
	goto	D30IR_Delay		; Go back and cycle twice
	return
	
D15IRCycles					; Delays program flow for 15 cycles @ 38kHz — 'Off' time for IREDs
	movlw	d'195'			; GOTO = 2 cycles. 2 * 195 = 390 instructions = 15 IR Cycles
	movwf	tmr1
D15IR_Delay
	decfsz	tmr1,f
	goto	D15IR_Delay
	return	
	
; * * * * * * * * * *	Main Program	* * * * * * * * * *

main
	call 	init			; Set up ports, etc
	; Program structure: Constantly alternate between IR LEDs. Each one on for 30 cycles, the off for 15 cycles as per datasheet
	
sensorPollLoop

	clrf	IRData			; Clear data from last sensor poll loop execution
	
	call	D15IRCycles		;  — — — — — — — Hold LEDs off for the required time — — — — — — —
	
	bsf		PORTB, 2		; Turn on the left IRED (Robot perspective)
	bsf		IRData, 0		; Left IRED currently emitting.
	
	call	D30IRCycles		; Hold it on for a certain amount of time
	
	bcf		IRData, 2		; Left IRED not currently emitting
	bcf		PORTB, 0		; Turn off the left IRED
	
	call	D15IRCycles		;  — — — — — — — Hold LEDs off for the required time — — — — — — —
	
	bsf		PORTB, 1		; Turn on the right IRED (Robot perspective)
	bsf		IRData, 1		; Right IRED currently emitting
	
	call	D30IRCycles		; Hold it on for a certain amount of time
	
	bcf		IRData, 1		; Right IRED not currently emitting
	bcf		PORTB, 1		; Turn off the right IRED
	
	;   — — — — — — — One complete sensor poll finished — — — — — — —
	; Now: See what data we have. For the moment, that means copying the data in IRData 3:2 to PORTA 3:2. How convenient…
	
	movf	IRData, w		; Pop value of IRData into the W. Reg…
	movwf	PORTA			; …and copy it into PORTA. Sorted!
	
	goto	sensorPollLoop
	
	end
I would greatly appreciate it if you could have a look through and see if there's anything obvious that I'm missing. I have verified that the signal is arriving nicely at the pin, and I've even manually applied high/low voltages to the pin, to no avail.

This is the first time I've used a hardware interrupt, I've only used TMR overflows before, so I may well have left out some obvious step.

Just for reference: The program constantly alternates between lighting two IREDs, and the interrupt should fire when a detector receives signal from one of them.

Thanks a lot,
Barnaby
 

ErnieM

Joined Apr 24, 2011
8,377
Add the following into your init routine:

bcf ANSELH,4

Of course, check you are in the correct bank.

RB0 shares function as an A2D analog input, and all inputs get power-on initialized to the analog function if it exists.
 

ErnieM

Joined Apr 24, 2011
8,377
BTW, that just makes the interrupt work, I did not check other I/Os for the same conflict.

But I bet you will now. ;)
 

Markd77

Joined Sep 7, 2009
2,806
I've added context save/restore even though my program doesn't need it
You should keep this because the interrupt modifies W, and part of your main loop uses W.
You wouldn't want the interrupt to occur between these lines if it didn't do context saving.
movlw d'2'
movwf tmr2
 

Thread Starter

Barnaby Walters

Joined Mar 2, 2011
102
Hello,

Thanks for the help — Mark you are absolutely right, I had forgotten that I was changing the wreg in my ISR. It seems to be a good idea to include it anyway.

Ernie — The interrupt fired perfectly after I added in your line! When I was debugging the code, I read through the datasheet pretty thoroughly, and saw no mention of the fact that selecting RB0 as a AD channel was needed for the interrupt to work. How did you you know that it was needed?

Thanks a lot,
Barnaby
 

ErnieM

Joined Apr 24, 2011
8,377
When I was debugging the code, I read through the datasheet pretty thoroughly, and saw no mention of the fact that selecting RB0 as a AD channel was needed for the interrupt to work. How did you you know that it was needed?
Well... I should have thought of it first,as it is a common problem everyone runs into constantly. I myself spent have spent many bad days wondering why my I/O lines were not working only to remember the analog functions.

But this hasn't happened to me since.... last Wednesday.

Some of the data sheets put together all the resisters that can affect the I/O lines, so when you see the ANSEL in there you know to worry. Not all do. The PIC32 sheets may be the worst, only way I've found is to check the pin functions at the front of the data sheet and look each one up.

But I missed all of that to find yours. You posted the entire asm file, so I just wizzed my way into a fresh MPLAB project and pasted your code into it, then built it. That took a minute or two. Then open a Stimulus workbook to simulate hitting RB0. When I attempted to change RB the SIM tab told me:

(5960) SIM-N0001 Note: Asynchronous Stimulus Toggle RB0 fired.
IOPORT-W0001: Pin(s) (0x01) on PORTB can not be stimulated due to being controlled by the A/D converter

It was pretty obvious from that. ;)
 
Top