View previous topic :: View next topic |
Author |
Message |
sandy wilson
Joined: 25 Feb 2004 Posts: 28
|
Ext, Timer1 spurious interrupts |
Posted: Fri Oct 30, 2009 9:57 am |
|
|
Can anybody help me with this,
I'm currently using CCS compiler v 4.042 on the pic18F8722. What I want to do is catch IR pulses on the interrupt pin using timer1 to time between the pulses. A general shell for the code is shown
Code: |
#include <18F8722.h>
#device adc=8
#FUSES HS //High speed Osc (> 4mhz)
#FUSES NOPROTECT //Code not protected from reading
#FUSES BROWNOUT //Reset when brownout detected
#FUSES BORV25 //Brownout reset at 2.5V
#FUSES PUT //No Power Up Timer
#FUSES NOLVP //Low Voltage Programming on B3(PIC16) or B5(PIC18)
#FUSES LPT1OSC //Timer1 configured for low-power operation
#FUSES MCLR //Master Clear pin enabled
#FUSES MCU //Microcontroller Mode
#use delay(clock=20000000)
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)
#byte highbyte = 0xFCF
#byte lowbyte = 0xFCE
#int_TIMER1
void TIMER1_isr(void)
{
int16 time = 0;
int8 inputb;
static int count = 0;
static int first = true;
disable_interrupts(int_timer1);
if(first)
{
highbyte = 0xF7;
lowbyte = 0x53;
first = false;
count = 0;
enable_interrupts(int_timer1);
}
else
{
highbyte = 0xF7;
lowbyte = 0x53;
count++;
}
if(count >13)
{
count = 0;
first = true;
enable_interrupts(int_ext);
}
else
enable_interrupts(int_timer1);
}
#int_EXT
void EXT_isr(void)
{
disable_interrupts(int_ext);
highbyte = 0xFF;
lowbyte = 0xFA;
enable_interrupts(int_timer1);
}
void main()
{
setup_adc_ports(NO_ANALOGS|VSS_VDD);
setup_adc(ADC_OFF|ADC_TAD_MUL_0);
setup_psp(PSP_DISABLED);
setup_spi(SPI_SS_DISABLED);
setup_wdt(WDT_OFF);
setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
setup_timer_2(T2_DISABLED,0,1);
setup_timer_4(T4_DISABLED,0,1);
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
ext_int_edge( H_TO_L );
disable_interrupts(int_timer1);
enable_interrupts(INT_EXT);
enable_interrupts(GLOBAL);
//Setup_Oscillator parameter not selected from Intr Oscillotar Config tab
// TODO: USER CODE!!
while(1)
{
delay_ms(1);
}
}
|
HighByte and low byte are the register addresses for timer 1 counts and I'm using this to set the value for timer1 to count FROM. The general trend of how the code should work is:
An interrupt hits RB0 and triggers int_EXT. The external interrupt is then disabled and timer 1 count is set to the point of just triggering. Timer 1 is then enabled. When timer 1 is triggered it checks to see if it is the first interrupt bit. If it is it sets up timer 1 to a set value and then re-enables timer1. It also says that the next timer1 interrupt will not be the first and sets abit count to zero. If the bit count goes to more than 13 interrupts it resets every thing.
My problem is that after the interrupt in RB0 the code works fine, counts up to 13 disables everything etc and then seems to retrigger the ext interrupt or timer1 interrupt on its own with no second external interrupt arriving. After this second spurious interrupt it sits quite happy until I put another interrupt onto RB0. Why the spurious second interrupt?
Any idea why??
This is killing me!!!
Thanks for any help
Sandy Wilson |
|
|
Wayne_
Joined: 10 Oct 2007 Posts: 681
|
|
Posted: Fri Oct 30, 2009 10:06 am |
|
|
You have to read the port in order to clear the #int_EXT interrupt.
As soon as you re-enable the #int_EXT interrupt it fires again, because it has not been cleared. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Oct 30, 2009 12:57 pm |
|
|
Quote: |
You have to read the port in order to clear the #int_EXT interrupt. |
That's true for #int_rb, but not for #int_ext.
Quote: |
My problem is that after the interrupt in RB0 the code works fine, counts
up to 13 disables everything etc and then seems to retrigger the ext
interrupt or timer1 interrupt on its own with no second external interrupt
arriving.
|
Create a cleaner test environment. Get rid of the IR device as an input
to your INT_EXT pin. Use a 2nd PIC board to generate test pulses and
send those pulses to the RB0 pin on the 1st board.
The 2nd board will wait for a keypress in a terminal window (using getc).
Upon receiving a keypress, it will send one pulse to the first board.
These will be clean pulses, with no "bounce" on the edges. There won't
be any unknown pulses generated by the 2nd board.
Or, instead of sending single pulses, you could send a stream of 13 or
more pulses upon each keypress. You can make the pulse generator
board do whatever you want it to do. You now control the test
environment. This will allow you to debug the problem.
The pulse generator board should be connected to the first board with
two short wires: A signal wire and a ground wire. Turn on power to
the pulse generator board first, and have it keep the output pin in a
benign state (either high or low level, based on your design). Then
turn on power to the board under test (the first board). Then start
the tests. |
|
|
sandy wilson
Joined: 25 Feb 2004 Posts: 28
|
|
Posted: Mon Nov 02, 2009 3:18 am |
|
|
Hello Wayne_ and PCM Programmer,
Problem solved.
I had assumed, wrongly it appears, that the compiler generates the code automatically for reseting the interrupt flags in INTCON for the external interrupt and in PIR for timer 1 interrupt. The compiler doesn't.
After I reset them manually, problem solved so now its onwards and upwards.
Thanks again for your help. It pointed me in the right direction and I found the fault fairly quickly after theat.
SandyW |
|
|
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Mon Nov 02, 2009 4:16 am |
|
|
Quote: | I had assumed, wrongly it appears, that the compiler generates the code automatically for reseting the interrupt flags in INTCON for the external interrupt and in PIR for timer 1 interrupt. The compiler doesn't. | It actually does (in a recent version), but possibly not in rather outdated PCH V4.042. Unfortunately I can't check. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon Nov 02, 2009 5:07 pm |
|
|
Quote: |
I had assumed, wrongly it appears, that the compiler generates the code
automatically for reseting the interrupt flags in INTCON for the external
interrupt and in PIR for timer 1 interrupt. The compiler doesn't.
|
The compiler does in fact clear the interrupt flags at the end of each isr.
I installed vs. 4.042 and compiled your program. See the .LST file code
shown below. The line that clears the interrupt flag is at the end of the
code in each case. I've add a comment to mark it.
Code: |
CCS PCH C Compiler, Version 4.042, xxxxx 02-Nov-09 14:40
.................... #int_EXT
.................... void EXT_isr(void)
.................... {
.................... disable_interrupts(int_ext);
000EE: BCF FF2.4
.................... highbyte = 0xFF;
000F0: MOVLW FF
000F2: MOVWF FCF
.................... lowbyte = 0xFA;
000F4: MOVLW FA
000F6: MOVWF FCE
.................... enable_interrupts(int_timer1);
000F8: BSF F9D.0
.................... }
....................
....................
....................
000FA: BCF FF2.1 // *** Clear the INTCON.INT0IF bit ***
000FC: GOTO 0068
|
Code: |
.................... #int_TIMER1
.................... void TIMER1_isr(void)
.................... {
.................... int16 time = 0;
*
000B2: CLRF 1C
000B4: CLRF 1D
.................... int8 inputb;
.................... static int count = 0;
.................... static int first = true;
....................
.................... disable_interrupts(int_timer1);
000B6: BCF F9D.0
....................
.................... if(first)
000B8: MOVF 1A,F
000BA: BZ 00CC
.................... {
.................... highbyte = 0xF7;
000BC: MOVLW F7
000BE: MOVWF FCF
.................... lowbyte = 0x53;
000C0: MOVLW 53
000C2: MOVWF FCE
.................... first = false;
000C4: CLRF 1A
.................... count = 0;
000C6: CLRF 19
.................... enable_interrupts(int_timer1);
000C8: BSF F9D.0
.................... }
.................... else
000CA: BRA 00D6
.................... {
.................... highbyte = 0xF7;
000CC: MOVLW F7
000CE: MOVWF FCF
.................... lowbyte = 0x53;
000D0: MOVLW 53
000D2: MOVWF FCE
.................... count++;
000D4: INCF 19,F
.................... }
....................
.................... if(count >13)
000D6: MOVF 19,W
000D8: SUBLW 0D
000DA: BC 00E6
.................... {
.................... count = 0;
000DC: CLRF 19
.................... first = true;
000DE: MOVLW 01
000E0: MOVWF 1A
.................... enable_interrupts(int_ext);
000E2: BSF FF2.4
.................... }
.................... else
000E4: BRA 00E8
.................... enable_interrupts(int_timer1);
000E6: BSF F9D.0
....................
.................... }
....................
000E8: BCF F9E.0 // *** Clear the PIR1.TMR1IF bit ***
000EA: GOTO 0068
|
|
|
|
sandy wilson
Joined: 25 Feb 2004 Posts: 28
|
|
Posted: Tue Nov 03, 2009 10:40 am |
|
|
Hello PCM Programmer,
I can see in my listing the two instructions for clearing the timer1 and ext interrupts. It still doesn't explain how, when I clear them manually, the code works ok.
Is there anything that you know about in the compiler/assembler/projects options that can affect this?
thanks for any help
sandy wilson |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Nov 03, 2009 10:50 am |
|
|
Post the source code for those two routines (not the .LST files), that
contains your modifications to clear the interrupts manually. |
|
|
sandy wilson
Joined: 25 Feb 2004 Posts: 28
|
|
Posted: Wed Nov 04, 2009 4:00 am |
|
|
Hello PCM Programmer,
Here's the listing of the program with the resets in it. I've put in a couple of pointers to show where. I've also shown where I set a break/run to point.
Code: | #include <18F8722.h>
#device adc=8
#FUSES HS //High speed Osc (> 4mhz)
#FUSES NOPROTECT //Code not protected from reading
#FUSES BROWNOUT //Reset when brownout detected
#FUSES BORV25 //Brownout reset at 2.5V
#FUSES PUT //No Power Up Timer
#FUSES NOLVP //Low Voltage Programming on B3(PIC16) or B5(PIC18)
#FUSES LPT1OSC //Timer1 configured for low-power operation
#FUSES MCLR //Master Clear pin enabled
#FUSES MCU //Microcontroller Mode
#use delay(clock=20000000)
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)
#byte highbyte = 0xFCF
#byte lowbyte = 0xFCE
#bit EXT_RESET = 0xFF2.1 <======= Declaration of the reset bit
#bit TIMER_RESET = 0xF9E.0 <======= Declaration of the reset bit
#int_TIMER1
void TIMER1_isr(void)
{
int16 time = 0;
int8 inputb;
static int count = 0;
static int first = true;
disable_interrupts(int_timer1);
if(first)
{
highbyte = 0xF7;
lowbyte = 0x53;
first = false;
count = 0;
enable_interrupts(int_timer1);
}
else
{
highbyte = 0xF7;
lowbyte = 0x53;
count++;
}
if(count >13)
{
count = 0;
first = true;
EXT_RESET = 0; <==== Use of reset
TIMER_RESET = 0; <==== Use of reset
enable_interrupts(int_ext); , <===== Break Point
}
else
enable_interrupts(int_timer1);
}
#int_EXT
void EXT_isr(void)
{
disable_interrupts(int_ext);
highbyte = 0xFF;
lowbyte = 0xFA;
enable_interrupts(int_timer1);
}
void main()
{
setup_adc_ports(NO_ANALOGS|VSS_VDD);
setup_adc(ADC_OFF|ADC_TAD_MUL_0);
setup_psp(PSP_DISABLED);
setup_spi(SPI_SS_DISABLED);
setup_wdt(WDT_OFF);
setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
setup_timer_2(T2_DISABLED,0,1);
setup_timer_4(T4_DISABLED,0,1);
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
ext_int_edge( H_TO_L );
disable_interrupts(int_timer1);
enable_interrupts(INT_EXT);
enable_interrupts(GLOBAL);
//Setup_Oscillator parameter not selected from Intr Oscillotar Config tab
// TODO: USER CODE!!
while(1)
{
delay_ms(1);
}
}
|
What happens is that with NO interrupt resets in the code and the program running to the break point it receives all thirteen bits and stops at the break point as expected. If I then tell it to run again to the break point it will and then stop at the same point with NO bits being received on ext interrupt. If I then restart it again it runs quite happily until the next thirteen bits arrive. This implies that the ext interrupt clear bit is being toggled.
If I then clear the interrupt flags as shown the program runs as expected. It receives thirteen bits and then waits for the next thirteen bits.
Again I've checked the lst file and I am clearing the correct bits in the registers.
After a bit more digging it appears that it is the ext interrupt not clearing. If I step through it looking at the interrupt registers in the IDE even after it passes the "enable_interrupts(int_ext)" line the external interrupt never clears. On stepping through the assembler it seems to reach line "000EA: GOTO 0068" and then jumps of to address 0068 steps through what appears to be (to the ignorant eye) a delay routine and then jumps back into the external interrupt routine. It never gets to the line that clears the external interrupt until its gone through this process again.
Still no clue why
I'm pretty sure there's no external interrupts arriving when they aren't supposed to as I'm looking at them on the scope. The pulses that do arrive when required are clean and sharp with no slow rising/falling edges or anything funny like that.
Thanks for the help
Sandy Wilson |
|
|
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Wed Nov 04, 2009 5:14 am |
|
|
Obviously, resetting the EXT interrupt flag before re-enabling the EXT interrupt is an additional action in your code, that's not provided by the self-reset in EXT ISR. If it's necessary for intended operation, simply assume that you have received additional H_TO_L edges at the EXT pin in the meantime. Why are you so sure that it can't happen? |
|
|
|