|
|
View previous topic :: View next topic |
Author |
Message |
eyewonder300
Joined: 09 Jun 2004 Posts: 52
|
Problem understanding why my interrupt isn't working |
Posted: Thu Jan 13, 2011 9:50 pm |
|
|
I want to be able to enable/disable interrupt for Timer2 at different times in the program, to get a required delay (yes, I could use delay_us(), but I am having timing problems with that).
When Timer two overflows & interrupts, I want it to set a flag for me to wait on. What is supposed to happen is that I clear the flag I use to monitor the interrupt, enable the interrupt, start waiting, it overflows in 3.3mSec, enters the isr, and then sets the flag, and I do the other things I need.
My 'Set_Strobe' & 'Clr_Strobe' are lines that I can monitor with my scope.
What does appear to be happening is that as soon as I enable the interrupt, the isr fires, asserts my flag & does a 'Set_Strobe' without doing the 3.3mSec delay that I expected. The 'Set_Strobe ' signal gets asserted for only 15uSec, looking at my scope. And the other 'output_bit' pins are showing a time between interrupts of 20mSec, not the expected delay_ms(20) PLUS the 3.3mSec expected for the Timer2 overflow isr.
The chip is PIC16F876a, and compiler is 4.069.
Help is appreciated.
Cheers,
Steve
Code: |
/////////////////////////////////////////////////////////////////////////
#include "Y:\CCS_Projects\GE_LOAD_876a\GE_LOAD_876a.h"
#int_Timer2
void Timer2_ISR()
{
set_strobe;
T2_Delay_Complete = True;
if (input(pin_b2))
output_bit(pin_b2,0);
else
output_bit(pin_b2,1);
}
void main()
{
Clr_Strobe; //clear sprocket signal
setup_adc_ports(NO_ANALOGS);
setup_adc(ADC_OFF);
setup_spi(SPI_SS_DISABLED);
setup_timer_0(RTCC_Internal);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DIV_BY_16,37,16); //gives delay of 3.3mSec
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
set_tris_A(0x20); //lower 4 bits output for 'lo_nibble', bit 4 output
//for 'strobe', bit 5 input for 'Read Tape' from GE
set_tris_C(0x80); //lower 4 bits outputs for 'hi_nibble', bit 4 'strobe',
//bit 5 output for 'pic_rts', bit 6 output for RS232,
//bit 7 input for RS232
enable_interrupts(GLOBAL);
while(true)
{
if(run_fwd)
{
clr_strobe;
T2_Delay_Complete = False;
enable_interrupts(int_timer2); //start the 3.3mS delay
while(T2_Delay_Complete == False); //wait for flag to be set by isr
disable_interrupts(int_timer2);
delay_ms(20);
}
else
disable_interrupts(int_timer2);
}
}
#include <16F876A.h>
#device *=16
//#device ICD=TRUE
#device adc=8
#FUSES NOWDT //No Watch Dog Timer
#FUSES HS //High speed Osc (> 4mhz for PCM/PCH) (>10mhz for PCD)
#FUSES NOPUT //No Power Up Timer
#FUSES NOPROTECT //Code not protected from reading
//#FUSES DEBUG //Debug mode for use with ICD
#FUSES BROWNOUT //Reset when brownout detected
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD //No EE protection
#FUSES NOWRT //Program memory not write protected
#use delay(clock=12000000)
#use rs232(baud=38400,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8,errors)
Boolean T2_Delay_Complete;
//------------------------------------------------------------
#define Set_Strobe output_bit(PIN_C4,1) //simulates the 'sprocket' signal
//generated by tape reader when
//sprocket hole gets over detector
#define Clr_Strobe output_bit(PIN_C4,0)
#define Set_RB2 output_bit(pin_b2,1)
#define Clr_RB2 output_bit(pin_b2,0)
#define Run_Fwd input(PIN_A5) //signal into PIC, which came from
//GE PERI board to read next char.
//Logic '1' for true
|
|
|
|
asmallri
Joined: 12 Aug 2004 Posts: 1634 Location: Perth, Australia
|
|
Posted: Fri Jan 14, 2011 1:54 am |
|
|
T2_Delay_Complete does not get initialized.
T2_Delay_Complete should be declared as volatile however I do not know if CCS requires this as it may internally treat variables as volatile by default.
Timer 2 is free running and you do not initialize timer 2 value before enabling the interrupt which means the delay is random.
You did not need to use interrupts. You could simply initialize the timer count and clear the timer interrupt flag then poll the timer interrupt flag until it is set. _________________ Regards, Andrew
http://www.brushelectronics.com/software
Home of Ethernet, SD card and Encrypted Serial Bootloaders for PICs!! |
|
|
eyewonder300
Joined: 09 Jun 2004 Posts: 52
|
Volatile? |
Posted: Fri Jan 14, 2011 7:09 am |
|
|
I guess I am confused - wouldn't volatile mean that the value was lost after exiting an isr (or subroutine)?
T2_Delay_Complete initialization - it was there in the full code which didn't work, but inadvertently stripped it out for the test. I'll put it back in this evening, and try it, along with declaring "Volatile T2_Delay_Complete as Boolean".
Thanks,
Steve |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19504
|
|
Posted: Fri Jan 14, 2011 10:14 am |
|
|
Volatile has no effect at all in CCS. It is used in some compilers to say that a variable _may_ be altered by something else, such as an interrupt handler. In CCS, you need to provide protection for this yourself. If you use a variable longer than a byte, and access it both in an interrupt, and in external code, you need to disable interrupts in the external code, while the variable is accessed, or you could get 'half the value coming from before an interrupt, and the other half from after......
Best Wishes |
|
|
gpsmikey
Joined: 16 Nov 2010 Posts: 588 Location: Kirkland, WA
|
|
Posted: Fri Jan 14, 2011 11:37 am |
|
|
To expand a bit on what Ttelmah was saying - I got bitten with this one some years back using an older version of the CCS compiler with an 876a part. My ISR set a flag in memory. My "Main" sat in a tight loop looking at that variable and when it changed, it did something. Unfortunately, the compiler decided that since nothing in my main code modified that variable, it "helped" me by optimizing the variable into a register variable -- which never changed. The ISR changed the real one, but since the compiler had "helped" me by making it a register variable for the loop, it never saw it. Declaring the variable as "volatile" forced the compiler to re-read the variable from memory each time around the loop and hey, wonderful, now my code ran as expected !!! (every time the ISR went off, it would read some bits from the port and stuff them into the variable that Main() was supposed to be watching.
mikey _________________ mikey
-- you can't have too many gadgets or too much disk space !
old engineering saying: 1+1 = 3 for sufficiently large values of 1 or small values of 3 |
|
|
John P
Joined: 17 Sep 2003 Posts: 331
|
|
Posted: Fri Jan 14, 2011 1:34 pm |
|
|
One thing to beware of if you're using interrupts which are disabled and then enabled again, is that you must clear the interrupt flag before the enable operation (for TMR2 on a PIC16F877A, that's TMR2IF, PIR1.1). If you don't, and if the interrupt would have occurred earlier if you hadn't disabled it, then you'll see an immediate interrupt as soon as you do enable it.
This is mentioned in the manual but I have failed to take note of it quite a few times. |
|
|
eyewonder300
Joined: 09 Jun 2004 Posts: 52
|
|
Posted: Fri Jan 14, 2011 2:28 pm |
|
|
Good ideas, which I will be trying this weekend.
But a (simple) question - how do I clear the interrupt flag? I don't remember seeing anything specific like that in the compiler manual.
Thanks for the good suggestions. I will update upon success.
Cheers,
Steve |
|
|
gpsmikey
Joined: 16 Nov 2010 Posts: 588 Location: Kirkland, WA
|
|
Posted: Fri Jan 14, 2011 2:39 pm |
|
|
Another reason not to have any delays inside an ISR.
One thing I have done in the past that works well is to set an unused bit high at the start of the ISR then clear it at the end of the ISR - makes it very easy to see just how much of your total time is being spend in the ISR with a scope probe.
mikey _________________ mikey
-- you can't have too many gadgets or too much disk space !
old engineering saying: 1+1 = 3 for sufficiently large values of 1 or small values of 3 |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19504
|
|
Posted: Fri Jan 14, 2011 3:51 pm |
|
|
John P wrote: | One thing to beware of if you're using interrupts which are disabled and then enabled again, is that you must clear the interrupt flag before the enable operation (for TMR2 on a PIC16F877A, that's TMR2IF, PIR1.1). If you don't, and if the interrupt would have occurred earlier if you hadn't disabled it, then you'll see an immediate interrupt as soon as you do enable it.
This is mentioned in the manual but I have failed to take note of it quite a few times. |
Except you really _do_ want this, if you are simply disabling the interrupt to read a number. The sequence wants to be something like:
Code: |
int16 local_value;
disable_interrupts(GLOBAL);
local_value=global_value_updated_in_interrupt;
enable_interrupts(GLOBAL);
//Then use 'local value' for your operations.
|
This way you only delay the interrupt response by a very few machine cycles while the value is copied. Obviously similar code for putting numbers back as well.
Best Wishes |
|
|
eyewonder300
Joined: 09 Jun 2004 Posts: 52
|
Finally got it to work |
Posted: Fri Jan 14, 2011 7:55 pm |
|
|
It seems to have been a sequencing issue. The code posted below works as expected. The commented out interrupt enable in Main at that position would immediately clear the 'Strobe' signal, resulting in the 1.5uSec Strobe signal I was seeing with the scope.
Enabling the interrupt, then setting strobe & clearing my flag made everything work as expected.
Thanks to all for the help.
Cheers,
Steve
Code: |
#include "Y:\CCS_Projects\GE_LOAD_876a\GE_LOAD_876a.h"
Short T2_Delay_Complete;
#int_Timer2
void Timer2_ISR()
{
clr_strobe;
T2_Delay_Complete = True;
}
void main()
{
Clr_Strobe; //clear sprocket signal
T2_Delay_Complete = 0;
setup_adc_ports(NO_ANALOGS);
setup_adc(ADC_OFF);
setup_spi(SPI_SS_DISABLED);
setup_timer_0(RTCC_Internal);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DIV_BY_16,39,16); //gives delay of 3.2mSec
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
set_tris_A(0x20); //lower 4 bits output for 'lo_nibble', bit 4 output
//for 'strobe', bit 5 input for 'Read Tape' from GE
set_tris_C(0x80); //lower 4 bits outputs for 'hi_nibble', bit 4 'strobe',
//bit 5 output for 'pic_rts', bit 6 output for RS232,
//bit 7 input for RS232
enable_interrupts(GLOBAL);
while(true)
{
if(run_fwd == True)
{
enable_interrupts(int_timer2); //start the 3.3mS delay
T2_Delay_Complete = False;
set_strobe;
// enable_interrupts(int_timer2); //start the 3.3mS delay
while (T2_Delay_Complete == False);
disable_interrupts(int_timer2);
delay_ms(7);
}
}
}
|
|
|
|
asmallri
Joined: 12 Aug 2004 Posts: 1634 Location: Perth, Australia
|
Re: Finally got it to work |
Posted: Fri Jan 14, 2011 11:39 pm |
|
|
eyewonder300 wrote: | It seems to have been a sequencing issue. The code posted below works as expected. The commented out interrupt enable in Main at that position would immediately clear the 'Strobe' signal, resulting in the 1.5uSec Strobe signal I was seeing with the scope.
Enabling the interrupt, then setting strobe & clearing my flag made everything work as expected.
Thanks to all for the help.
Cheers,
Steve
|
It is still flawed for all the reasons already given. _________________ Regards, Andrew
http://www.brushelectronics.com/software
Home of Ethernet, SD card and Encrypted Serial Bootloaders for PICs!! |
|
|
Ken Johnson
Joined: 23 Mar 2006 Posts: 197 Location: Lewisburg, WV
|
|
Posted: Mon Jan 17, 2011 8:26 am |
|
|
One thing I have done in the past that works well is to set an unused bit high at the start of the ISR then clear it at the end of the ISR - makes it very easy to see just how much of your total time is being spend in the ISR with a scope probe.
mikey
Caveat: this shows how much time is spent in **your** code in the isr, but not the isr "overhead" of saving/restoring registers, which can easily be much more when your code is short and sweet . . .
Ken |
|
|
gpsmikey
Joined: 16 Nov 2010 Posts: 588 Location: Kirkland, WA
|
|
Posted: Mon Jan 17, 2011 8:57 am |
|
|
Good point on the overhead - but if that pulse is at 80%, you probably should look at your ISR code
I was used to writing my own ISR in assembly language for things like the Z-80 where I did all the overhead as well. Anybody remember the old TI 9900 processor ? One of the fastest context switches in it's day - all registers except the acc and workspace pointer were in RAM - you simply changed the workspace pointer to a new area of RAM and hey, presto 16 new registers There were two versions of the chip - TMS9900 that was a MOS device and the SBP9900 that was injector logic (and rad hard) which made it interesting to anybody doing military stuff back in the 70's.
mikey _________________ mikey
-- you can't have too many gadgets or too much disk space !
old engineering saying: 1+1 = 3 for sufficiently large values of 1 or small values of 3 |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19504
|
|
Posted: Mon Jan 17, 2011 3:39 pm |
|
|
Yes, in fact a lot of the older processors were 'better' for fast ISR responses.
The Z80 itself had a duplicate register set, and a couple of the Motorola chips also allowed multiple register sets to be held 'in RAM'. Downside of that is of course that it makes the speed relatively slower than using internal registers that can be accessed at the same time as performing other memory operations, but it does seem 'daft', when old processors running at a very few MHz, can easily outperform 'modern' chips internally running hundreds of times faster for ISR responses.....
Generally, 'embedded' chips, are more likely to find systems allowing really fast ISR responses, especially those aimed at the RTOS market. Texas still produce embedded chips for which this is true.
Unfortunately, the PIC is particularly 'bad' in this regard, and CCS's implementation makes it worse....
Campaign for a bit of smartness in their interrupt handler, with it only saving registers that _are_ used in the interrupt code!. It shouldn't be that hard to actually do, and would make a massive difference.
Best Wishes |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Tue Jan 18, 2011 12:57 am |
|
|
eyewonder300 wrote: | But a (simple) question - how do I clear the interrupt flag? I don't remember seeing anything specific like that in the compiler manual. | How about: Code: | clear_interrupt(INT_TIMER2(); | It is in the most recent manual (June 2010).
Code: | setup_spi(SPI_SS_DISABLED); | This is an error in the CCS code wizard. To disable the SPI module it should be:
Another suggestion is to make your program case sensitive by adding the '#case' directive. You now are mixing lower and upper casing in a way that is not according to the C standards... |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|