How to disable interrupts for a moment to copy volatile variable? And are the interrupts handled independently properly?

216 views
Skip to first unread message

Petr Stehlík

unread,
May 11, 2013, 4:09:31 PM5/11/13
to arduino-pincha...@googlegroups.com
Hi,

I have a volatile long variable counting ticks in an interrupt routine. In order to work with the value in the main loop safely I first copy the volatile long to a local long and then work with the local one. Naturally I have to ensure that the interrupt doesn't increment the volatile long when I am copying it (imagine 0x0000ffff -> 0x00010000) so I need to disable the interrupts for a microsecond. What is the suggested way of temporary disabling the interrupts? Note that I must not lose any other interrupt coming at the time when the interrupts are disabled temporarily.

Currently I use three pins (A0, A1 and A2) for independent counting of three sources of ticks and I feel like sometimes the interrupt routines are called improperly and the ticks say from A0 pin are counted for pin A1 instead. It happens in like one of ten thousands ticks so it's almost impossible to debug. I suppose it might be caused by the way I disable the interrupts now:

volatile long _imp_count[3];

void loop()
{
    long imp_count[3];

    PCintPort::detachInterrupt(A0);
    PCintPort::detachInterrupt(A1);
    PCintPort::detachInterrupt(A2);
        for(int f=0; f<3; f++) {
            imp_count[f] = _imp_count[f];
            imp_delta[f] = _imp_delta[f];
            imp_millis[f] = _imp_millis[f];
        }
    PCintPort::attachInterrupt(A0, Kirq1, FALLING);
    PCintPort::attachInterrupt(A1, Kirq2, FALLING);
    PCintPort::attachInterrupt(A2, Kirq3, FALLING);
 ...
}

Perhaps if there is another interrupt coming at the moment when they are disabled then something bad happens when the interrupts are being re-enabled again and the interrupt is mis-handled?

What do you think? Is there a better way of safe copying the volatile variable to a local one? Also is it safe to assume that the interrupts on pin A0, A1 and A2 are handled independently by this library, even when the interrupts occur almost at the same time or when the interrupt routines are just disabled? Would it perhaps be more reasonable to direct each of the tick sources to its own dedicated Arduino port (B, C, D)?

Thanks,

Petr

Petr Stehlík

unread,
May 12, 2013, 1:52:44 PM5/12/13
to arduino-pincha...@googlegroups.com
What is the suggested way of temporary disabling the interrupts?

Alright, I have replaced the detachInterrupts with noInterrupts() and the subsequent attachInterrupts() with just interrupts(). Let's see if it helps. Originally it was causing freezes so I was searching for a different way. Now it seems to work better (knocking on wood).

Petr

David Cary

unread,
Sep 16, 2013, 4:13:04 PM9/16/13
to arduino-pincha...@googlegroups.com
The "read twice and compare"_ algorithm is a good way to read up-counters from a main loop when the counter is incremented in an interrupt routine.
It is better than the "turn interrupts off, read the variable, then turn interrupts on" approach
because the "read twice and compare" algorithm never turns interrupts off -- so interrupts are never "lost" or even delayed.
It is safe because it always returns a consistent, correct value even if the counter was incremented from 0x0001ffff to 0x00020000 partway through the read.

Would something like this work for you?:

  volatile long _imp_count[3];

  long atomic_read_counter(long *counter){
    do{
        long counter_old = *counter;
        long counter_new = *counter;
    }while( counter_old != counter_new );
    return counter_new
  }

  void loop(){
    long imp_count[3];
    long imp_delta[3];
    long imp_millis[3];
    // don't bother disabling interrupts here
    for(int f=0; f<3; f++) {
        imp_count[f] = atomic_read_counter(&_imp_count[f]);
        imp_delta[f] = atomic_read_counter(&_imp_delta[f]);
        imp_millis[f] = atomic_read_counter(&_imp_millis[f]);
    };
    // also, no need to re-enable interrupts here.
  }

Or, for your application, would it be better to read all 9 variables into two local copies, and then check if an interrupt has changed any of the values?


Petr Stehlík:
 
I have a volatile long variable counting ticks in an interrupt routine. In order to work with the value in the main loop safely I first copy the volatile long to a local long and then work with the local one. Naturally I have to ensure that the interrupt doesn't increment the volatile long when I am copying it (imagine 0x0000ffff -> 0x00010000) so I need to disable the interrupts for a microsecond. What is the suggested way of temporary disabling the interrupts? Note that I must not lose any other interrupt coming at the time when the interrupts are disabled temporarily.
...
What do you think? Is there a better way of safe copying the volatile variable to a local one?
...
Thanks,

Petr

Petr Stehlík

unread,
Sep 4, 2014, 2:47:59 PM9/4/14
to arduino-pincha...@googlegroups.com
That's indeed an elegant solution. Thank you, David!

Reply all
Reply to author
Forward
0 new messages