|
|
View previous topic :: View next topic |
Author |
Message |
Herbert
Joined: 20 Jul 2008 Posts: 32 Location: Brisbane, Australia
|
Unexpected Interrupts delays |
Posted: Sat Feb 07, 2009 10:30 pm |
|
|
Hi, there is something happening here that I can't get my head around. Using PCWH vers 4.079.
I have a simple ISR routine at the beginning of a lot of other code -
Code: |
#include <18F2620.h>
#device ICD=TRUE //When TRUE, debugging code is inserted
#device adc=10
int32 Timer1_Rollovers = 0; //Timer based on counting Timer1 rollovers
#device HIGH_INTS=TRUE //Define fast interrupt
#int_TIMER1 fast //2us main interval timer used for timing events
/* Use it as a high priority interrupt.
It happens every 131.07ms.
*/
void TIMER1_isr(void)
{
Timer1_Rollovers++; //Increment the rollover counter. Don't worry about a rollover, it will never happen.
}
|
Timer1 ticks every 2us. I then combine (elsewhere) the Timer1_Rollovers with the current timer1 value (16bits) to give me a 48 bit, 2us resolution timer, whenever I want a current chip time. This of course involves reading and combining timer1 and the Timer1_Rollovers values.
I have noticed there are some delays between the timer1 rolling over and Timer1_Rollovers being updated which can lead to race conditions. To deal with this I introduce a blank out period around the rollover period.
Code: |
void Read_2us_Timer( int8 *Start )
//Store the current timer value at Start, a 6 byte area
// LSB is at start of memory storage area and MSB at highest memory location
// First 2 bytes are the Timer1 value
// Next 4 bytes are the rollover counter.
{
int8 *Addr;
int16 Temp;
Do
{
}
While ( get_timer1() < 0x50 ); //loop to allow counter to properly update
Temp_I16 = get_timer1();
memcpy( Start, &Temp_I16, 2 );
Addr = Start + 2;
memcpy( Addr, &Timer1_Rollovers, 4 );
}
|
It seems to me the 0x50 delay is already too long and I have now seen an event where the delay would have to be out at 0xB0 to cover that event.
Looking at the assembler code the interrupt used is at address 0x0008 and the number of instructions from there to where the Timer1_Rollovers is incremented is not very long at all ( about 10). I have also searched for disable_interrupts in the code and there are none in inappropriate positions, ie. in the execution loop.
I do have two other ISRs on CCP2 and timer3 that are low priority interrupts (not specified as "fast") and are not being actioned during this testing phase.
I can't see why the delay is so long. Would anybody have any suggestions what could be happening here?
Last edited by Herbert on Sat Feb 07, 2009 11:24 pm; edited 1 time in total |
|
|
Herbert
Joined: 20 Jul 2008 Posts: 32 Location: Brisbane, Australia
|
|
Posted: Sat Feb 07, 2009 10:35 pm |
|
|
Did forget one thing, the chip is running on a 16MHz clock. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sat Feb 07, 2009 11:18 pm |
|
|
Quote: | void TIMER1_isr(void)
{
Timer1_Rollovers++;
}
memcpy( Addr, &Timer1_Rollovers, 4 ); |
Quote: | .................... memcpy( Addr, &Timer1_Rollovers, 4 );
002A: CLRF FSR0H
002C: MOVLW Addr
002E: MOVWF FSR0L
0030: CLRF FSR1H
0032: MOVLW Timer1_Rollovers
0034: MOVWF FSR1L
0036: MOVLW 04
0038: MOVWF @01
003A: MOVFF POSTINC1,POSTINC0
003E: DECFSZ @01,F
0040: BRA 003A |
The memcpy has to run through that loop 4 times to do the copy.
What happens if you get an interrupt somewhere in the middle of that ?
What will happen to your Timer1_Rollovers variable while it's being copied ? |
|
|
Herbert
Joined: 20 Jul 2008 Posts: 32 Location: Brisbane, Australia
|
|
Posted: Sat Feb 07, 2009 11:30 pm |
|
|
Ahh yes. I didn't tell you about the interrupt disabled version that showed similar issues. The trouble there was that timer1 was still incrementing during the interrupt disabled period and I would get timer1 rollovers that were not resulting in being counted in time, so once again my read back 6 byte value was incorrect. By removing the interrupt disable and having a dead period around rollover I was hoping to minimise these issues. |
|
|
Herbert
Joined: 20 Jul 2008 Posts: 32 Location: Brisbane, Australia
|
|
Posted: Sun Feb 08, 2009 12:06 am |
|
|
The following is an improved version, as it uses the first captured timer1 value and then waits for the Timer1_Rollovers to be updated, if it needs to be.
Code: |
void Read_2us_Timer( int8 *Start )
{
#define Update_Period 0x50
int8 *Addr;
int16 Temp;
Temp_I16 = get_timer1();
memcpy( Start, &Temp_I16, 2 );
If ( Temp_I16 < Update_period )
{
Do
{
}
While ( get_timer1() < Update_Period ); //loop to allow counter to properly update
}
Addr = Start + 2;
memcpy( Addr, &Timer1_Rollovers, 4 );
}
|
My problem is I can't understand why the Update_Period has to be so large. |
|
|
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Sun Feb 08, 2009 3:11 am |
|
|
Provided, you have no interrupt locks active during timer reads, the observed update delay can be caused only by regular processing time within the interrupt routine and in your timer read. The problem reduces to detect all cases, where an interrupt has occured between reading the partial timer results (timer1 and overflow counter) and correcting the result respectively.
Generally a good approach is to read one result twice. E.g. read overflows, read timer1, read overflows. If the readings are different, an interrupt has occured in between. You should be able to calculate the correct timer reading then. |
|
|
Herbert
Joined: 20 Jul 2008 Posts: 32 Location: Brisbane, Australia
|
|
Posted: Sun Feb 08, 2009 4:35 am |
|
|
Yes, thank you, I have pushed the code along and have come up with a solution along the lines you suggested -
Code: |
void Read_2us_Timer( int8 *Start )
{
#define Update_Period 0x10
int8 *Addr;
int16 Temp;
disable_interrupts( INT_TIMER1 ); //Prevent counter from changing during read
Addr = Start + 2;
memcpy( Addr, &Timer1_Rollovers, 4 );
enable_interrupts( INT_TIMER1 ); //Finished so enable interrupts again
// Now get timer1 value
Temp_I16 = get_timer1();
memcpy( Start, &Temp_I16, 2 );
// If we are within the time it takes to update the rollover counter, wait
// till rollover counter is updated and then sample it again.
If ( Temp_I16 < Update_period )
{
Do
{
}
While ( get_timer1() < Update_Period ); //loop to allow counter to properly update
Addr = Start + 2;
memcpy( Addr, &Timer1_Rollovers, 4 );
}
}
|
That now seems to work without causing many of the problems I had seen previously. I guess I could also add a condition that if the new Timer1_Rollovers is not equal to the one obtained at the beginning of the routine, then I can consider that I have captured the correct value on the subsequent read.
I have also returned the timer1 isr back to a normal level, ie. not fast or high priority, and all seems well. Even with the reduced "#define Update_Period 0x10".
There however still appears to be a problem when I activate the part of the code that uses the timer3 interrupt. It looks very much like the timer1 isr gets cleared without being actioned, i.e. it misses a rollover. I am a bit surprised, as I thought the compiler took care of these details, ie. dealing with multiple interrupts. |
|
|
|
|
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
|