Clearing pending interrupts

Go To Last Post
18 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If an interrupt is requested while inside another interrupthandler and the interrupt is disabled at eh moment, how to prevent it from triggering as soon as the interrupt is reenabled?
Ex: INT1 triggers an interrupt. When inside the handler INT0 also triggers. Since INT0 is disabled, the program won't jump to the INT0-handler at once, but it does as soon as INT0 is enabled again. And I don't want it to do that...

I've tried to clear the INTF0 interrupt flag. The datasheet says that setting it to one will clear it, but that doesn't work either.

Any ideas?

By the way, I'm using an ATMega8.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Please post your code!
It is right, that you clear interrupt flags by writing a logical "1" to it!
May be it is a simulator problem! Try using the latest version of the avr studio!

*greetz*

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
I've tried to clear the INTF0 interrupt flag.

Are you certain that INTF0 was set? The reason I ask is that, unless you
fiddled the ISC0x bits, INT0 is level-triggered, so INTF0 won't come on
(and you can't turn it off).

Sight unseen, my recommendation is to disable the interrupt (turn off INT0
in GICR) rather than trying to clear it. This also has the virtue of avoiding
a race.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Do you have a SEI Global Interrupt Enable in the interrupt so that the interrupts are enable
( beware for stack overflow if the interrupt will take some time )

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Sight unseen, my recommendation is to disable the interrupt (turn off INT0
in GICR) rather than trying to clear it. This also has the virtue of avoiding
a race.

I do disable it. It is when I reenable it the problem comes if it have been triggered while disabled.

I'm using no simulation, this is real and live:(

This is my code and I think I just found the bug. The sbi(GIFR, 6); comes AFTER the sbi(GICR, 6); Stupid misstake...

// Enable trigger interrupt
void TriggerEnable(uint8_t mode)	// mode=KEEPLAST, SEMI, AUTO
{
	if(mode)					// If mode!=KEEPLAST
	{
		if(mode==SEMI)			// Semiautomatic mode
			sbi(MCUCR, 1);		// Trigger on falling edge only
		else 					// Full automatic mode
			cbi(MCUCR, 1);		// Trigger as long as held low
	}
	sbi(GICR, 6);				// Enable interrupt INT0
	sbi(GIFR, 6);				// Clear INT0-flag
}

Thanks for helping me see the problem!

Btw, how many clockcycles do the MCU run when an interrupt is triggered and before it jumps to the handler?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Btw, how many clockcycles do the MCU run when an interrupt is triggered and before it jumps to the handler?

The information is in the datasheet. The summary is that it needs to finish the current instruction plus the time to save the program counter and then get to your routine. For example, there is a section called:

Interrupt Response Time 

The interrupt execution response for all the enabled AVR interrupts is four clock cycles minimum. After four clock cycles, the Program Vector address for the actual interrupt handling routine is executed. During this 4-clock cycle period, the Program Counter is pushed onto the Stack. The Vector is normally a jump to the interrupt routine, and this jump takes three clock cycles. If an interrupt occurs during execution of a multi-cycle
instruction, this instruction is completed before the interrupt is served. If an interrupt occurs when the MCU is in sleep mode, the interrupt execution response time is increased by four clock cycles. This increase comes in addition to the start-up time from the selected sleep mode.
A return from an interrupt handling routine takes four clock cycles. During these four clock cycles, the Program Counter (2 bytes) is popped back from the Stack, the Stack Pointer is incremented by 2, and the I-bit in SREG is set.

that may be pertinent.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm back again...:(
Changing the order didn't solve the problem. And I've tried setting the bit and resetting it and nothing works...

Quote:
Are you certain that INTF0 was set? The reason I ask is that, unless you fiddled the ISC0x bits, INT0 is level-triggered, so INTF0 won't come on (and you can't turn it off).

I've set ISC01 and I know by the behavior of the program that it does indeed trigger on the falling edge.

I have a workaround for it but I don't like it that way. I want to know what my program does...

Any idea?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
I've set ISC01 and I know by the behavior of the program that it does indeed trigger on the falling edge

From the code above it looks as though you're switching between the
two modes(?). Are you certain which mode you were in?

By the way,

   sbi(GIFR, 6);            // Clear INT0-flag 

This is fairly dangerous, since it can inadvertantly clear other pending
interrupts (try "GIFR=1<<6" instead). This is unlikely to be your problem,
though.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm rather sure I'm not switching between the modes. Except for the first initialization I only call it with KEEPLAST.

Quote:
By the way,

sbi(GIFR, 6);            // Clear INT0-flag

This is fairly dangerous, since it can inadvertantly clear other pending
interrupts (try "GIFR=1<<6" instead). This is unlikely to be your problem,
though.

I thought sbi() and cbi() just was direct counterparts to the asm functions and that they changed just one bit. If I use GIFR=1<<6 then it will affect the other bits to. Did you mean GIFR |= 1<<6?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Are you certain that INT0 triggers before you try to clear it (and not,
say, immediately after)? This is the race I mentioned.

Quote:
I thought sbi() and cbi() ... changed just one bit.

sbi/cbi do a read-modify-write sequence on the register -- logically
equivalent to "reg |= 1<<bit", but not interruptible -- and will thus
clear any Other write-clear flags that happen to be set. (For your case --
GIFR on the mega8 -- this Other set consists solely of INTF1, but the sets
are different for different registers and different AVR models.)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

mckenney wrote:
Are you certain that INT0 triggers before you try to clear it (and not,
say, immediately after)? This is the race I mentioned.

I'm certain. The program plays a sound before it enables the interrupt.
And I press the INT0-button before the sound.

mckenney wrote:

Quote:
I thought sbi() and cbi() ... changed just one bit.

sbi/cbi do a read-modify-write sequence on the register -- logically
equivalent to "reg |= 1<<bit", but not interruptible -- and will thus
clear any Other write-clear flags that happen to be set. (For your case --
GIFR on the mega8 -- this Other set consists solely of INTF1, but the sets
are different for different registers and different AVR models.)

Now you turn my world upside down. Can you please tell me exactly what sbi/cbi and |= does? And what do you mean by 'not interruptible'? Why would it affect the other flags?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

McKenney wrote:

Quote:
sbi/cbi do a read-modify-write sequence on the register -- logically
equivalent to "reg |= 1<<bit", but not interruptible -- and will thus
clear any Other write-clear flags that happen to be set. (For your case --
GIFR on the mega8 -- this Other set consists solely of INTF1, but the sets
are different for different registers and different AVR models.)

which is completely wrong. These instructions modify only the single bit.
Your problem is that GICR and GIFR are outside the range of this instruction. From that the solution should be obvious.
Dave Raymond

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

draymond wrote:

Your problem is that GICR and GIFR are outside the range of this instruction. From that the solution should be obvious.

Forgive me for not seeing it, but what is the obvious solution?
Besides, I don't think they are out of range, because I succesfully use sbi and cbi when enabling and disabling interrupts. (in GICR)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wileur wrote:

Besides, I don't think they are out of range, because I succesfully use sbi and cbi when enabling and disabling interrupts. (in GICR)

From the Instruction Set manual:

Quote:

CBI - Clear Bit in I/O Register

Description:
Clears a specified bit in an I/O register. This instruction operates on the lower 32 I/O registers - addresses 0-31.

From the Mega8 datasheet:

Quote:

0x3B (0x5B) GICR INT1 INT0 – – – – IVSEL IVCE 47, 65
0x3A (0x5A) GIFR INTF1 INTF0 – – – – – –

0x3a is >>not<< less than 32.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

to clear the interrupt flags, do not use the read-modify-write operands like |=. instead do an absolute write to the IFR writing ones only to the bits you need to clear, and zeros to all others.

If you use:

GIFR |= (1<<INTF0);

The above will clear INTF0, as well as any other interrupt flags have been set, since you will be writing back a '1' in those locations as well.

instead change it to:

GIFR = (1<<INTF0);

this will now only clear INTF0, and leave all others untouched. I know it seems odd, but it's how the IFR (Interrupt flag registers) work. You write a 1 to the bit you want to clear, and a zero to any bit you want to remain unchanged.

Here's the math behind it all

So let's use the following (for example), the address of GIFR is not important here
GIFR is defined as the following bits x x x x x x INTF1 INTF0
Thus the labels INTF0, and INTF1 would be defined as
INTF0=0
INTF1=1

Now let's assume the following on entry to our line of C
GIFR=0b00000011

Case 1:
GIFR |= (1<<INTF0);
will take the current contents of GIFR OR it with the value (0b00000001) and then write it back to GIFR.

thus
GIFR |= (1<<INTF0) will translate to: GIFR = GIFR | (1<<0) = (0b00000011 | 0b00000001) = 0b00000011
Since GIFR is write one to clear we are left with
GIFR=0b00000000
since we wrote 0b00000011 back to GIFR, both bits were cleared

Case 2:
GIFR = (1<<INTF0);
will write the value (0b00000001) to GIFR.

thus
GIFR = (1<<INTF0) will translate to: GIFR = (1<<0) = 0b00000001
Since GIFR is write one to clear we are left with
GIFR=0b00000010
since we wrote 0b00000001 to GIFR, only the desired bit was cleared

Note that this only applies to the IFR's and not the control registers, for those you should use the read-modify-write routines.

As for the SBI/CBI macros in C.. they are jsut that, macros. They will likely translate into the REG |= (1<<BIT) sequence we are talking about. The compiler will then replace it with a SBI/CBI as is appripriate during compilation, it will also use the correct sequence of instructions when the desired register is out of range for SBI/CBI. So while it's intended to be related to the assembly instruction, it is not directly tied to it. So use the macros with extreme caution, they may not behave as you would expect.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thankyou very much, glitch!
I didn't see the difference between writing a value to and setting a registervalue.
And a lot of thanks to everybody that helped me. But while reading and thinking and testing I suddenly realized that the best way in this case was to simply use polling instead of interrupts. And now it works even better and is more flexible than before.

And you, glitch, were also right about cbi translating into Register |= Value.
I searched for a while in my AVRGCC headers and i found this in sfr_defs.h (with more text in between):

#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

Just out of curiousity, wouldn't it be possible to do something like this found in interrupt.h?

    Enables interrupts by clearing the global interrupt mask. This function
    actually compiles into a single line of assembly, so there is no function
    call overhead. */

#define sei()  __asm__ __volatile__ ("sei" ::)

And please don't tell me to program in asm...:)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

draymond wrote:
McKenney wrote:
Quote:
sbi/cbi do a read-modify-write sequence on the register -- logically
equivalent to "reg |= 1<<bit",

which is completely wrong.

The sbi and cbi instructions do a RMW sequence to set/clear the bit. This
behavior is explicitly described in the datasheet(s), e.g.:
Quote:
Note that the CBI and SBI instructions will operate on all bits in
the I/O Register, writing a one back into any flag read as set, thus
clearing the flag.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My apologies. Thanks for pointing that out. Now please excuse me while I go repair all of my code.
Dave Raymond