cli sei . When to use them?

Hi

Every example of setting up an interrupt I have seen is always enclosed in "cli sei". Reading https://gammon.com.au/interrupts; Nick says to use cli sei when implementing time sensitive code or to read a multi byte feild 'atomically'.

Why would it be necessary to disable interrupts to write a few registers? I can't see how this is time sensitive. What would be the problem if I set up the following timer interrupt or external interrupt for example without encasing the code in cli sei.

cli();//stop interrupts 
TCCR1A = 0;// set entire TCCR1A register to 0
TCCR1B = 0;// same for TCCR1B
TCNT1  = 0;// initialize counter value to 0
OCR1A = 18749;// set compare match register (max 65536)  = (8,000,000/(256/0.6seconds))-1
TCCR1B |= (1 << WGM12);  // turn on CTC mode
// TCCR1B |= (1 << CS12); starts clock by setting prescaler to 256. TCCR1B &= ~(1 << CS12); stops clock 
TIFR1 |= (1 << OCF1A); // unflag interrupt
TIMSK1 |= (1 << OCIE1A);  // enable timer compare interrupt

attachInterrupt(digitalPinToInterrupt(3), ccaInterrupt, RISING);  // ISR(INT1_vect) already defined in an included library, so we have to redefine it here.
EIMSK &= ~(1 << INT1);  // disable interrupt  
sei();//allow interrupts

Also, where can I find the code called by attachInterupt() or detachInterrupt() ? I want to see if they use cli ? Often I write masks eg:

EIFR = bit (INTF0);  // clear flag
EIMSK |= (1 << INT0); // enable INT0
EIMSK &= ~(1 << INT0);  // disable interrupt

without a preceding cli. Is this bad practice and why? I can't see how it is time sensitive.

Thanks

Matt

Also, where can I find the code called by attachInterupt() or detachInterrupt() ?

For which board? On what operating system?

On my computer, the attachInterrupt() function is in WInterrupts.c in C:\Users\PaulS\Documents\Arduino_182\hardware\arduino\avr\cores\arduino for avr boards.

Why would attaching an interrupt handler need to suspend other interrupts?

The reason for disabling interrupts when setting the timer registers is that you do NOT want an interrupt happening before you have done all the setup.

The reason for disabling interrupts when setting the timer registers is that you do NOT want an interrupt happening before you have done all the setup.

Hi Paul

Do you mean that you don't want the interrupt you are setting up to fire before you have finished setting it up. Or do you mean you don't want some other random interrupt such as timer0 for millis() going off while you are half way through setting up?

Cheers

I found attachInterrupt() in WInterrupts.c, thanks. It doesn't use cli(). So I can only assume that others use cli() before setting up an interrupt to prevent that specific interrupt from firing during the set up. In which case why wouldn't you just clear the flag before enabling the interrupt or timer prescaler in the case of a timer interupt.

Thanks again.

For a more in-depth discussion, you might try Googling Race Conditions and Atomic Operations. Mostly though, the problem arises when you attempt to read/write a value larger that the bus width of the processor. For example, the AVR is an 8-bit processor, so reading/writing a 16-bit value has to be done in 2 instruction cycles. When an interrupt occurs, only the currently executing instruction gets completed before the interrupt is serviced, returning afterwards to complete the interrupted code. It is possible that the values have changed. In cases where this can be an issue, it has been shown to occur about 10% of the time. Any time-sensitive action should be bracketed by cli/sei for safety.

Note: the Arduino functions are: noInterrupts() and interrupts().

There is one place where disabling interrupts is important: When fetching a volatile variable longer than 1 byte. For example, if you have an interrupt incrementing a 16-bit integer you will get problems if the interrupt occurs between fetching the two bytes. If the compiler fetches the high byte first you may get 0x0000 on the transition from 0x00FF to 0x0100. If the low byte is fetched first you may get 0x01FF. Neither is close to 0x00FF or 0x0100.

Note: Library functions should NOT use sei() or interrupts(). They can disable interrupts (cli() or noInterrupts()) but they should first make a copy of the status register and restore it rather than enable interrupts. That way they won't enable interrupts when they are called with interrupts disabled.

boggydew:
I found attachInterrupt() in WInterrupts.c, thanks. It doesn't use cli().

That's a mistake.

johnwasser:
Note: Library functions should NOT use sei() or interrupts(). They can disable interrupts (cli() or noInterrupts()) but they should first make a copy of the status register and restore it rather than enable interrupts.

Yup...

<util/atomic.h>

ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
  // Protected code goes here
}