Gabriel Staples Gabriel Staples - 2 months ago 15
C++ Question

C++ decrementing an element of a single-byte (volatile) array is not atomic! WHY? (Solved: also answered: how to force atomicity in Atmel AVR mcus)

I just lost days, literally, ~25 hrs of work, due to trying to debug my code over something simple that I didn't know.

It turns out decrementing an element of a single-byte array in C++, on an AVR ATmega328 8-bit microcontroller (Arduino) is not an atomic operation, and requires atomic access guards (namely, turning off interrupts). Why is this??? Also, what are all of the C techniques to ensure atomic access to variables on an Atmel AVR microcontroller?

Here's a dumbed down version of what I did:

//global vars:
const uint8_t NUM_INPUT_PORTS = 3;
volatile uint8_t numElementsInBuf[NUM_INPUT_PORTS];

ISR(PCINT0_vect) //external pin change interrupt service routine on input port 0
{
//do stuff here
for (uint8_t i=0; i<NUM_INPUT_PORTS; i++)
numElementsInBuf[i]++;
}

loop()
{
for (uint8_t i=0; i<NUM_INPUT_PORTS; i++)
{
//do stuff here
numElementsInBuf[i]--; //<--THIS CAUSES ERRORS!!!!! THE COUNTER GETS CORRUPTED.
}
}


Here's the version of loop that's fine:

loop()
{
for (uint8_t i=0; i<NUM_INPUT_PORTS; i++)
{
//do stuff here
noInterrupts(); //globally disable interrupts
numElementsInBuf[i]--; //now it's ok...30 hrs of debugging....
interrupts(); //globally re-enable interrupts
}
}


Notice the "atomic access guards", ie: disabling interrupts before decrementing, then re-enabling them after.

Since I was dealing with a single byte here, I didn't know I'd need atomic access guards. Why do I need them for this case? Is this typical behavior? I know I'd need them if this was an array of 2-byte values, but why for 1-byte values???? Normally for 1-byte values atomic access guards are not required here...




Update: read the "Atomic access" section here: http://www.gammon.com.au/interrupts. This is a great source.





Answer

I don't know much about Arduino and interrupts, so I might not answer your particular question here, but in multithreaded environment decrementing and incrementing using -- and ++ is never atomic. Moreover, volatile also does not mean atomic in C++ in general (proof). Though I know that volatile is meaningful when you program microcontrollers, so I suspect that my answer might not apply to your case.

Does it work if you replace an array of volatile uint8_ts with three separate volatile uint8_ts?

Comments