CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

Long ints and interrupts

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
dbotkin



Joined: 08 Sep 2003
Posts: 197
Location: Omaha NE USA

View user's profile Send private message Send e-mail Visit poster's website

Long ints and interrupts
PostPosted: Fri Nov 03, 2006 1:35 pm     Reply with quote

I'm using the TIMER0 interrupt to increment and decrement a couple of unsigned long int variables. I use those variables in if() statements elsewhere in the program. Once in a while it looks like the ISR is getting called and changing the variable in the middle of a comparison or test, and things are taking an entirely wrong branch. here's an example:

Code:

// seconds, since_id and bcn_time are unsigned long ints
// seconds & since_id get incremented and decremented
// once per second by TIMER0 ISR. 

            if((seconds < 1) || (bcn_time && (since_id >= bcn_time))) {
                if(!seconds) {
                    do_this();
                } else {
                    do_that();
                }
            }


We know bcn_time is always 0, so when seconds reaches 0 we should execute do_this(). However, every once in a while we execute do_that() for no apparent reason. When that happens, the debugging printf() I put in tells me seconds is at 255, and since_id is always the same value (341).

I think what's happening is the ISR decrements seconds from 0x0100 to 0x00FF in the middle of the first if() statement. The main() function tests the low byte of seconds, and it's zero. ISR occurs, decrementing seconds to 0x0100. Now main() tests the high byte of seconds and finds it zero also. Now as far as main() is concerned, seconds is zero - but it's actually 255. Then the second if() sees it's not zero, so we execute do_that().

So now that I know (or think I know) why Im seeing the incorrect behavior, how do I prevent it? I suspect this is something blindingly obvious to a decent programmer, but it's not jumping out at me right now. I suppose I could disable interrupts any time I'm using one of those long ints, but that's way too messy to be right. What am I missing?
newguy



Joined: 24 Jun 2004
Posts: 1903

View user's profile Send private message

PostPosted: Fri Nov 03, 2006 2:08 pm     Reply with quote

Not messy at all. Just put a disable_interrupts(INT_TIMER0); just before the if() and an enable_interrupts(INT_TIMER0) just after. I'm pretty sure that each of these commands boils down to a single instruction, so they're fast. It only looks messy.

I can't think of a better way around this that is "cleaner" without adding a buttload of code for checking this condition. Perhaps one of the gurus can help.
C Turner



Joined: 10 Nov 2003
Posts: 40
Location: Utah

View user's profile Send private message

PostPosted: Fri Nov 03, 2006 4:19 pm     Reply with quote

When checking a variable that could be changed by an ISR whilst using it, I always make a temporary copy of that variable and then use that temporary copy in the if statements, as in:

temp = isr_timer

...
if(temp [condition])

etc.

While this works for a SINGLE byte, there's still the chance that, while working with multi-byte variables (16 bit, 32 bit, floats, etc.) that, while it's being copied to "temp" that an ISR will occur, changing part of "isr_timer" (in our example) in the middle of the copy.

There are two easy ways around this:

- Stop interrupts, copy the variable, and restart them. The interrupts will be less-disrupted by turning them off during the copy than they would be if you did so during the conditional statements. If, however, you need precise timing AND you are preloading the timer in your ISR to obtain a specific rate (as you would with timer0 or timer1) then this could upset the delicate timing.

The other method is to:

do {
temp1 = isr_timer;
temp2 = isr_timer;
} while (temp1 != temp2);

When you are done, temp1 or temp2 will be guaranteed to have a "good" copy of "isr_timer" and one of these "copies" would be used for the comparison. (Of course, this could be done with just a single temporary variable.)

The advantage to this is that it doesn't require stopping the ISRs at all - but it *does* require a tiny bit of extra memory and processor time.

CT


Last edited by C Turner on Wed Nov 08, 2006 6:32 pm; edited 1 time in total
kam



Joined: 15 Jul 2005
Posts: 59

View user's profile Send private message

PostPosted: Fri Nov 03, 2006 7:52 pm     Reply with quote

First of all, congrats on finding this error! You must have gone mad Shocked when this "glitch" happens...Holy crap!

Everyone is right.

The issue comes down to the need of a mutex/sychronization for those variabes.

We can do the
Code:

do {
temp1 = isr_timer;
temp2 = isr_timer;
} while (temp1 != temp2);


But we would need to have a "temp" variable for each logic use of that variable

Code:

if(seconds!=0 && seconds!=0 && seconds!=0); // we could fail on the last seconds


So before each "logic" block, wait for the mutex to go low, flip is high, do what you want, release the Mutex. In the Int, do the same, but instead of waiting for the mutex to go low, just don't touch the variables but continue.

Now this will give us 100% accurate usage of the variables.

What we loose if that sometimes there's a small pause in the "logic" mutex waiting for the Int to finish. Sometimes the variables don't get updated in the Int. But the values will always be true. This is just like the disable ints, but you can use the Ints still.

There could be a total failure (Shocked) if a routine within also needs sychronization, it'll wait forever.

Yes this add's code, but hey.... Very Happy

~Kam (^8*
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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