I am building an app in a micro controller. The question is, I'm receiving data in the serial port. It is written using interruptions, what I guess is the same as a thread. So, how could I get this data in the buffer and guarantee some integrity if I can't use lock?
-
interrupt modifies the write pointer, app the read pointer? are you worried about the (head/tail read/write) pointers or the data itself or the ring buffer getting full (head passing tail)? – old_timer Jun 05 '13 at 18:32
-
Even if this is for an embedded system, it is not specifically an embedded issue - tagging it as such may narrow your audience. For example http://stackoverflow.com/questions/871234/circular-lock-free-buffer seems to answer your question. – Clifford Jun 05 '13 at 19:23
4 Answers
Just disable the receive interrupt when you access protected variables (like the ringbuffer, the read and write position) outside the interrupt, so in your case when you need the number of bytes in the input buffer or you need to pop a byte:
int GetBytesAvailable()
{
int result;
DisableReceiveInterrupt();
result = writePos - readPos;
EnableReceiveInterrupt();
if (result < 0)
result += RINGBUFFER_SIZE;
return result;
}
int GetNextByte()
{
int result = -1;
DisableReceiveInterrupt();
if (readPos != writePos)
{
result = RingBuffer[readPos++];
readPos %= RINGBUFFER_SIZE;
}
EnableReceiveInterrupt();
return result;
}
When the microcontroller receives a byte while the interrupt is disabled. The interrupt handler will be called as soon as you re-enable the interrupt.

- 27,376
- 9
- 90
- 133
-
1This way lies the safest approach. However, in many microcontrollers you can know enough about the memory to know which operations are atomic such that in many cases as long as the interrupt only modifies `writepos` (and the matching character in the buffer) and the reader only ever modifies `readpos`, and only reads `writepos` to verify that the buffer is not empty. The reader might miss a byte that is in the process of arriving, but that doesn't matter because that is what the buffer is for and it will pick it up on the next call. The key is to use atomic types for `readpos` and `writepos`. – RBerteig Jun 05 '13 at 17:51
-
1Beware of disabling interrupts though. It can have side effects such as lost serial data, video glitching, etc. Double check your system information to see if there is an approved way of doing this. Also, leave them disabled for as little time as possible. – Michael Dorgan Jun 05 '13 at 17:52
-
It would be a rare serial engine which did not have at least one holding register for unclaimed received data separate from the working register into which the next byte is being received. In terms of other time-critical interrupts, an option to consider would be masking just the relevant serial interrupt but not disabling interrupts globally. But such specific masking may require manually checking for reception upon re-enabling. – Chris Stratton Jun 05 '13 at 18:04
-
Great! Thanks fellows, it will be very useful to me! About disable interrupts, I'll do it just to get if buffer is empty, I believe it's ok. Also, the microcontroller is good, a lpc1769. – Leandro Lima Jun 05 '13 at 18:48
-
@MichaelDorgan: there will be no video glitching if you just disable the serial receive interrupt, he should not mask all interrupts. And you will not lose serial data, unless the code inside the disable/enable block takes longer then the arrival of a character. All microcontrollers I know have a register to store at least 1 byte while receiving the next one. – huysentruitw Jun 05 '13 at 19:33
-
@Leandro: I would do it always, just like you would use a mutex or lock. Disabling/enabling an interrupt results in 1 assembly instruction on most microcontrollers, so there's almost no overhead. – huysentruitw Jun 06 '13 at 16:53
You can accomplish this by disabling interrupts around the instructions in the consumer code which access the buffer and update it's head/tail or whatever pointers.
Pretty much every useful serial peripheral can buffer a received word while receiving the next, so you can get away with disabling interrupts for a small fraction of a word time.
If writing in a language such as C, you will need to use the volatile keyword on shared variables to prevent the compiler from optimizing the actual access in ways which could break sharing between normal and interrupt contexts.

- 39,853
- 6
- 84
- 117
If you have three variables controlling your queue: put_index
, get_index
, and count
, put_index
is only used by the producer thread, get_index
by the consumer thread, and count
by both.
For your particular platform, updating a particular data type will be atomic; if you use such a data type for count
, then if the put()
operation checks count
and if it is not full adds the data at the put_index
, updates put_index
then increments count
as the last operation. The the get()
operation checks count
and if it is non zero, gets the data from the get_index
, updates get_index
and decrements it as the last operation.
By ensuring that count
is volatile
and atomic, and ensuring that it is only incremented after the data written and index are valid, and decremented only after the data is read then no lock is required.
The critical thing is to ensure a reliance on only a single atomic shared variable rather than determining buffer state through the separate put and get indices.

- 88,407
- 13
- 85
- 165
-
2The real challenge comes in if the desired buffer length is greater than the platform's largest natively atomic type. Most likely the 8-bit type is atomic, so if a 256 element buffer is sufficient, it's portably easy. But beyond that it may be tricky depending on the platform. Atomically updating a type larger than is natively atomic would seem to either require constructing some sort of locking mechanism to protect it, or building in asynchronous update detection/compensation logic. Fortunately 256 characters is usually enough buffer for a serial interface. – Chris Stratton Jun 05 '13 at 20:18
-
@ChrisStratton: Good point. Even for non-atomic data types, having a single discrete integer variable needing protection rather than the entire data structure can minimise the length of any critical section where interrupts are disabled. – Clifford Jun 06 '13 at 09:01
If you can make your buffer size a power of two, the simplest approach is to simply use two suitably-sized unsigned values which are capable of handling numbers up to the buffer size, along with the buffer. One value says how many bytes have been put into the buffer; the other says how many bytes have been removed. Do not peg these values to the size of the buffer; just let them increase and let them wrap around the integer size in question.
unsigned short fetch_inx, stuff_inx;
unsigned char buff[1024];
void stuff_byte(uint8_t dat)
{
if ((unsigned short)(stuff_inx - fetch_inx) >= 1024)
// buffer is full
else
{
buff[stuff_inx & 1023] = dat;
stuff_inx++;
}
}
int fetch_byte(void)
{
uint8_t result;
if (fetch_inx == stuff_inx)
return -1;
result = buff[fetch_inx & 1023];
fetch_inx++; // Must be done after previous statement--see text
return result;
}
If the buffer size is exactly 256 or 65,536 bytes, one may use 8-bit or 16-bit "index" values if one doesn't allow more than 255 or 65,535 bytes to be placed into the buffer. Note also that if one doesn't allow the buffer to be completely filled, the fetch_byte routine may use return buff[(fetch_inx++) & 1023];
, but that would be unsafe if the buffer could be filled completely (since the buffer slot which was about to be read would become eligible for recycling before it actually gets read).
Provided that stuff_byte doesn't write [stuff_inx]
until after data is in the buffer, and fetch_byte
doesn't write [fetch_inx]
until after data is read, both routines should be able to execute independently in different interrupt or main-line contexts without interference.

- 5,639
- 2
- 31
- 30

- 77,689
- 9
- 166
- 211
-
Looks like fun... but are they safe (or safely failing) if the update of the tracking variables is non-atomic? It looks to me like the buffer-is-full logic could false trigger if it catches a 16-bit fetch_ptr halfway through a bytewise update which involves 8-bit overflow, when the buffer is more than 3/4 full... which is a situation just unlikely but plausible enough to cause really frustrating errors. – Chris Stratton Jun 05 '13 at 20:58
-
@ChrisStratton: The only times I've used this logic on 8-bit micros the buffer size was 256 or less, so non-atomic updates weren't an issue. Because the pointers are only modified via increment, it would be possible to split each pointer into two pieces and define its value as being ((B1+B2) and 255)+256*B2. If an increment would cause (B1+B2) to equal 256, increment B2; else increment B1. Each increment only affects either B1 or B2, so they're naturally atomic with respect to any read operations. – supercat Jun 05 '13 at 21:03
-
So @supercat, I didn't understand. Isn't unsigned short range from 0 to 255? Why are you doing this mask with 1023? – Leandro Lima Jun 05 '13 at 21:41
-
@Leandro: The mask with 1023 is because the buffer size is 1024. If both the head and tail "pointers" wrapped at 1024, it would not be possible to distinguish the buffer-full case from the buffer-empty case. Letting the pointers count to the full `int32` size means that when the queue holds 1024 entries, the "ptr" values will be unequal even though the lower 10 bits match. – supercat Jun 05 '13 at 21:44