Consider this instruction sequence:
Code: Select all
void serialize8(uint8_t a) {
headTX++;
if (headTX == TX_BUFFER_SIZE) {
headTX = 0;
}
bufTX[headTX] = a;
checksum ^= a;
#if !defined(PROMICRO)
UCSR0B |= (1<<UDRIE0);
#endif
}
For a brief moment, headTX will have the value of TX_BUFFER_SIZE; this would not pose a problem, but during this time, the serial ISR could fire:
Code: Select all
ISR_UART {
if (headTX != tailTX) {
tailTX++;
if (tailTX == TX_BUFFER_SIZE) {
tailTX = 0;
}
since headTX is outside of the allowed range [0, TX_BUFFER_SIZE-1], tailTX will "overtake" headTX and crush the entire buffer content.
In my opinion, there are two solutions to counter this issue:
a) restore the previous if/else construct, which will always atomically leave the variables in a well defined state
b) disable interrupts (cli()/sei()) while inside the serializer function (obviously not an option)