View previous topic :: View next topic |
Author |
Message |
JBM
Joined: 12 May 2004 Posts: 54 Location: edinburgh, Scotland
|
Interrupt re-entrany problems |
Posted: Tue Apr 04, 2006 10:22 am |
|
|
I'm currently writing a program which has a loop which takes a temperature reading, does a delay_ms(200), writes out to a MMC, and continutes looping.
Since MMCs need to be written to in 512 byte blocks, I wait until I have a full 512 bytes to write. Having a power failure before the buffer has been written to memory reults in data loss, and a file not being properly closed, both of which are bad.
I'm using a PIC18LF6720, which allows me to use a low voltage interrupt. My isr looks something like
Code: | #INT_LOWVOLT
void lowvolt_isr(void)
{
int8 i;
for(i=0; i < MAXFILES; i++);
fclose(i);
} |
The problem is that fclose() is aslo called from outside the isr (when the user wants to stop logging). Since I'm calling the same funciton from inside and outisde an isr, it has disabled interrupts all over the place, (most notably, during the 200ms delay) meaning the isr stands very little chance of being called.
My question is this: Bearing in mind that I never need to return from the low voltage isr, is there a way for me to be able to stop all the interrupts being automatically disabled by the compilimacator?
-JBM |
|
|
newguy
Joined: 24 Jun 2004 Posts: 1909
|
|
Posted: Tue Apr 04, 2006 10:52 am |
|
|
Try this.
In the low voltage interrupt, simply set a flag:
Down in the main loop of your program have this:
Code: | if (power_fail) {
power_fail = FALSE;
// save & close everything here
} |
That will solve the reentrancy problem. Whether or not you'll have enough time to save & close everything is another issue. |
|
|
JBM
Joined: 12 May 2004 Posts: 54 Location: edinburgh, Scotland
|
|
Posted: Tue Apr 04, 2006 10:56 am |
|
|
Nice idea, but i have a statement
which rather prohibits that - checking for a set bit every 200ms just isn't fast enough when power is going down.
I know i could call the 200ms delay as 200 separate 1ms delays, but that's really not addressing the fundamental problem.
Thanks for the reply
Any suggestions for a cheap-and-dirty hack? I like those....
-JBM |
|
|
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Tue Apr 04, 2006 11:01 am |
|
|
It seems to me that your issue is that interrupts are being disabled which delays your ability to do an fclose() in time?? However, this only occurs during the fclose(). Since the lowvolt isr is actually calling the fclose function, what is the problem? You will still have to call fclose from the ISR, so what if it is called from main? Once the "main" fclose() is completed, the ISR will trigger and the rest of the files will be closed. You won't miss the interrupt, it will just be delayed which shouldn't hurt since the amount of the delay is still required when the function is called from an ISR. |
|
|
newguy
Joined: 24 Jun 2004 Posts: 1909
|
|
Posted: Tue Apr 04, 2006 11:04 am |
|
|
JBM wrote: | Nice idea, but i have a statement
which rather prohibits that - checking for a set bit every 200ms just isn't fast enough when power is going down.
I know i could call the 200ms delay as 200 separate 1ms delays, but that's really not addressing the fundamental problem.
Thanks for the reply
Any suggestions for a cheap-and-dirty hack? I like those....
-JBM |
Change the delay to something like this:
Instead of:
Change to:
Code: | int16 i = 0;
while (i < 2000 && okay_to_delay) {
delay_us(100);
i++;
} |
Your low voltage interrupt routine now also includes:
Code: | okay_to_delay = FALSE; |
Quick & dirty yes, elegant no! |
|
|
JBM
Joined: 12 May 2004 Posts: 54 Location: edinburgh, Scotland
|
|
Posted: Tue Apr 04, 2006 11:04 am |
|
|
Well, yes and no.
fclose() calls other functions, one of which is delay_ms(). So any time delay_ms() is called (like during the 200ms delay in the main loop) , interrupts are disabled.
Any other ideas?
-JBM |
|
|
dyeatman
Joined: 06 Sep 2003 Posts: 1934 Location: Norman, OK
|
|
Posted: Tue Apr 04, 2006 3:02 pm |
|
|
I never use the delay() routines unless they are for very short intervals (1ms or less). Instead I have a 5ms or 10ms (depending on how fine a control I need) timer running that counts a variable down when the timer interrupts. This can control any number of counters and, by testing the counters inside the interrupt, set a flag at any special count or time. If you need precise timing you have to do some cycle counting to get the 10ms interval perfect (I cheat and use a scope with an output bit toggle) but most cases I don't need that much precision.
**************************************************
EXAMPLE:
Code: | #INT_TIMER1
Timer1_int()
// first we must start the counter for the next 10ms interval
set_timer1(TIMER1START);
//
// ***** Then we can do whatever we need to do here....
// count the 10ms intervals for a seconds or heartbeat counter
if (--_10mscntr < 1)
{
_10mscntr = 100; // reset for one second counter
// decrement a seconds counter
if (seconds > 0)
seconds--; // you can have up or down counters
if (seconds==0) // you can have up or down counters
{
//reset seconds to 60 etc. at this point
seconds-60;
mins++;
}
} // if --10mscntr < 1
// *****
if (_delay1 > 0) // count 10ms intervals
_delay1--;
if (_delay2 > 0) // another timer I can use for something
_delay2--;
if (_delay3 > 0) // another timer I can use for something
_delay3--;
}
|
With this, I simply set a variable (e.g. delay1) for the total time in 10ms intervals that I need to delay then wait for the counter to reach zero and set a flag to trigger an action in the background loop. I do a lot of real time state machine control using this type of routine. This was just an example. There are many possible variations of this type of timing that would accomplish the same thing. |
|
|
|