Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Interrupt on change problem, PORTA

Status
Not open for further replies.

mjdeez

New Member
I have been at this for a few days, and I can't seem to get my PIC 16F690 to go to the ISR via the PORTA IOC on RA0. I'm using MPLAB and a pickit2 with the in-circuit debugger. Just for the heck of it, I was able to get into the ISR by adding some TMR1 code to get the TMR1 to overflow and cause an interrupt.

I do have INTCON, GIE; INTCON, RABIE; and IOCA, 0 all set. The thing I can't wrap my head around is that when I'm my main wait loop at the bottom of my code, I see that the INTCON register reads: 10001000 (GIE and RABIE set). Then when I step through in debug mode, and bring RA0 low, INTCON changes to 10001001, indicating that the RABIF gets set. Why at this point would it not go to ISR? I read that there are different logic levels for the interrupt (schmitt trigger) vs PORT inputs, but if RABIF gets set, I would think that would cause an INT.

Some comments about my code: I want to get the above issue straightened out (or at least understood) before cleaning up the program. This is my first attempt at programming a ucontroller, and now that I'm reading more about it, I realize I need to shorten that ISR and do the setup conditions at startup more efficiently.

Any insight would be very much appreciated.

Code:
;******************************************************************************
;  SOT doubler / tripler / quadrupler with EOT feedback
; *******************************************************************
; * BUGS *
;	1. ISR not working as an interrupt
;	2. Need to put some TMR1F checking to ensure no overflow in EOT / SOT 
;	3. SOT / EOT times are backwards. To be swapped...
; *******************************************************************

#include <p16F690.inc>
     __config (_INTRC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _BOR_OFF & _IESO_OFF & _FCMEN_OFF)

     cblock     0x20
Delay1               ; Assign an address to label Delay1
Delay2     
SOT_Time
EOT_Time
Test_Cnt_i
     endc
     
; Flag Definitions

     cblock 0x70     ; put these up in unbanked RAM
W_Save
STATUS_Save
     endc
     
     org 0
     goto      Start
     nop
     nop
     nop
ISR:
     movwf     W_Save
     movf      STATUS,w
     movwf     STATUS_Save
	
	bcf		STATUS, RP0		; BANK0
	bcf		STATUS, RP1		; BANK0
	btfsc	PORTA, 0		; DETERMINE STATE OF RA0
	goto	ExitISR			; if RB4 is still high, where should we go? Exit, no other INT Enabled

	movf	PORTA, W		; clear IOCA INT
	bcf		INTCON, RABIF	; clr INT flag
	bcf		INTCON, RABIE	; CLR INT EN???????????????????
	clrf	TMR1L			;start TMR1
	clrf	TMR1H			;start TMR1

	btfss	PORTA, 0		;has SOT_IN gone back high?
	goto	$-1				; if not, keep waiting

	movf	TMR1H, W		;when gone back high, save tmr1h only
	movwf	SOT_Time

	movlw	0x03			;7000us / 8 = 875d = 36Bh ~= 0300h = 768d x 8 = 6144us
	subwf	SOT_Time, W		;was SOT time roughly greater than 7ms?
	btfsc	W, 7			; bit 7 of W is set (1) if SOT time too low
	goto 	ExitISR			;pulse too short, return to start

	movlw	0x08			;15000uS /8 = 1875d = 0753h ~= 0800h = 2048d x 8 = 16384us
	subwf	SOT_Time, W		;was SOT time roughly less than 16ms?
	btfss	W, 7			; 
	goto	ExitISR			; if it is clear, go to start, pulse too long
	;add a line here to check that TMR1F still low
	goto	CountDownLoop

	;here we know that we have received a genuine SOT signal, finally
CountDownLoop:	
	decfsz	Test_Cnt_i, F	
	goto	SendEOT			; Count down complete, Send EOT and exit the ISR
	;send 1 SOT to ATE
	bcf		PORTA, 1		; RA1 low 
	bsf		PORTC, 2		; RC2 high (LED, TEST)
	movlw	0x0E			; 11ms passed to time wasting function
	call	TimeWaste_w		; Low for 11ms
	bsf		PORTA, 1		; RA1 high
	bcf		PORTC, 2		; RC2 low (LED, TEST)
	goto	EOT_Detect

EOT_Detect:
	btfsc	PORTA, 3		;CHECK EOT FROM ATE
	goto	$-1				; wait for it...

	clrf	TMR1L			;start TMR1 on EOT going low
	clrf	TMR1H			;start TMR1
	btfss	PORTA, 4		;has EOT_IN gone back high?
	goto	$-1				; if not, keep waiting

	movf	TMR1H, W		;when gone back high, save tmr1h only
	movwf	EOT_Time

	movlw	0xC3			;50,000uS  / 8 =  6250d = 186Ah ~= C300
	subwf	EOT_Time, W		;was EOT time roughly greater than 50ms?
	btfsc	W, 7			; bit 7 of W is set (1) if EOT time too low
	goto 	EOT_Detect		;pulse too short, return to EOT_start -- ok

	movlw	0x62			;200,000uS/8 = 25kd = 61A8h ~= 6200h = 25088d*8 = 200704us	
	subwf	SOT_Time, W		;was SOT time roughly less than 200ms?
	btfss	W, 7			; 
	goto	$+1				; for now, just go to next step anyway
	
	; add code here to verify TMR1F still low, or if high clear it and exit ISR

	;here we know that we have received a genuine EOT signal
	movlw	0x0E			; 11ms passed to time wasting function
	call	TimeWaste_w		; waste time, go back to start of countdown loop
	goto	CountDownLoop	; go back to start of ISR Count Down Loop
	;END ISR

SendEOT:      
	;send 1 EOT back to ATE after the count down is complete, then exit ISR
	bcf		PORTA, 1		; RA1 low
	bsf		PORTC, 3		; RC3 high (LED, TEST)
	movlw	0x49			; about 150ms
	call	TimeWaste_w		; Low for 150ms
	bsf		PORTA, 2		; RA2 high
	bcf		PORTC, 3		; RC3 low (LED, TEST)
	goto	ExitISR

ExitISR:
	bsf		INTCON, RABIE	;Enable Ints
	movf	STATUS_Save,w
	movwf	STATUS
	swapf	W_Save,f
	swapf	W_Save,w
	retfie
; 	return    

TimeWaste_w:
; Delay Subroutine.  Enter delays Wreg * 771uS + 5 uS including call and return
;	movlw	0x0E			; passed prior to calling this routine
	movwf	Delay2         ;
DelayLoop:
     decfsz    Delay1,f       ; Waste time.  Does this need to be initiallized?????????????????
     goto      DelayLoop      ; The Inner loop takes 3 instructions per loop * 256 loopss = 768 instructions
     decfsz    Delay2,f       ; The outer loop takes and additional 3 instructions per lap * 256 loops
     goto      DelayLoop      ; (768+3) * 14 = 10,794 instructions / 1M instructions per second = 10.8mSec.
     return
     
Start:

	bcf		STATUS,RP1		;Bank0
	bcf		STATUS,RP0		;Bank0
	clrf	PORTB
	clrf	PORTC


	bsf		STATUS,RP0     ; select Register Page 1
	clrf	TRISB          ; Make PortA all output
	clrf	TRISC          ; Make PortC all output
	bsf		TRISC, 0		;Make RC0 an input
	bsf		TRISC, 1		;Make RC1 an input
	
;	movlw	0xFF			
;	movwf	TRISA			; MAKE them all inputs first
;	bsf		TRISA, 0		; Make RA0 an input  (SOT_IN)
;	bcf		TRISA, 1		; Make RA1 an output (SOT_OUT)
;	bcf		TRISA, 2		; Make RA2 an output (EOT_OUT)
;	bsf		TRISA, 3		; Make RA3 an input  (EOT_IN)

	;copy from DS on PORTA
	bcf		STATUS,RP0 		;Bank 0
	bcf		STATUS,RP1 		;
	clrf	PORTA 			;Init PORTA
	bsf		PORTA,0			;Init the INT of PortA (RA0 is set)
	bsf		STATUS,RP1 		;Bank 2
	clrf	ANSEL 			;digital I/O
	bsf 	STATUS,RP0 		;Bank 1
	bcf		STATUS,RP1 		;
	movlw	09h 			;Set RA<0, 3> as inputs   00001001
	movwf	TRISA 			;and set RA<1:2,4,5> as outputs
	;end copy

	bcf		STATUS, RP0		;BANK0
	movf	PORTA, 1		;COPY A TO ITSELF

	bsf		STATUS, RP0		;BANK1
	bsf		IOCA, 0			; enable IOCA bit 0 (RA0) for interupt OC

	bcf		STATUS, RP0		;BACK TO BANK 0
	clrf	INTCON
	bsf		INTCON, GIE		; ENABLE INTERUPTS  (default)
	bsf		INTCON, RABIE	; ENABLE IOC ON PORTA, PORTB

	bcf		T1CON, TMR1CS	; SET THE CLOCK SOURCE TO FOSC / 4 (by def anyway)
	bcf		T1CON, T1CKPS0	; SET THE T1 PRESCALER TO 1:8
	bcf		T1CON, T1CKPS1	; SET THE T1 PRESCALER TO 1:8
	bsf		T1CON, TMR1ON	; Turn on T1 

	movf	PORTC, W		; check portC for user input (switches)
	addlw	0x02			; convert the 2-bit (0-3) to (2-5)
	movwf	Test_Cnt_i		; This is the number of pulses sent to ATE
	movf	PORTA, W		; TEST PORTA

MainWaitLoop:
	goto	$1			; WAIT FOR SOT, INTERUPT ON PORTA (RA0)
	
     end
 
I just tried your code in the simulator and it worked as expected. It did do repeated interrupt due to your ISR not clearing INTCON,RABIF but that is easily fixed.

All I added was the stimulus attached. As soon as I pressed the button the interrupt fired.

Mike.
 

Attachments

  • Stimulus.png
    Stimulus.png
    20.3 KB · Views: 357
Thanks Mike. I really appreciate your help. I've spent at least 10 hours trying to track down this one problem. I suspect my issue has something to do with the ICD I'm using. It is the off the shelf ICD for Pickit2 and I don't know the inner workings of it.

I will change my code around to handle interrupts more cleanly, and I'll probably come back and ask for some suggestions for improvements. Thanks again.
-Mike
 
IMO the PK2 is an excellent choice for programming & debugging PICs. BUT the 16F690 does not have the hardware debug (you need the AC162061 ICD header & AC164110 adapter) if you want debug. Another choice would be use the 16F88 or the advanced 18F1320 instead. Both have hardware debug.
 
I am using the header and adapter. Based on Pommie's simulations, the code works (as far as firing the interrupt). That's why I'm gathering it is HW related. When I get some more standard 16F690 devices (non -ICD, outside of debug mode), I will try it again. I bent all the leads getting the only 16F690 DIP I have when I was removing it from the PK2 socket.

I was considering a more advanced micro, but I wanted to start with something manageable. I'm basically starting by reading datasheets, posts, tutorials, etc. My biggest challenge, other than this IOC, has been figuring out what pin functionalities can be enabled depending on what other functions are being used. PORTB limitations with TMR1 enabled, for example.

I have MPLAB version 8.1 installed. Anyone know if it is bundled with a simulator, where I don't need a PIC target connected? I really am very new to this.
 
Thanks. There are so many overviews, user guides, data sheets, product line manuals that it gets a little overwhelming. I didn't realize the SW included a sim tool.

I'll take a look and see if any caps need to be added. I don't recall if they are there.

-Mike
 
Hello there,
To the problem that RABIF bit is not cleared. Please read the paragraph 4.2.3 of 16F690's Datasheet.
In simple words, just read the port A or B, whatever is the target port, like:
Code:
banksel PORTA
movfw  PORTA
; and then ....
bcf      INTCON,RABIF
this should do the trick.
 
Last edited:
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top