A simple ring buffer for microcontrollers

The simplest thing that could possibly work:

#define CB_CAP 128 // capacity
uint32_t cb_data[CB_CAP];
volatile int cb_read_index;
volatile int cb_write_index;
volatile int cb_size;

bool cb_write(uint32_t value)
{
	if(cb_size == CB_CAP) return false;
	cb_data[cb_write_index] = value;
	if(++cb_write_index == CB_CAP) cb_write_index = 0;
	cb_size++;
	return true;
}

bool cb_read(uint32_t* value)
{
	*value = 0;
	if(cb_size == 0) return false;
	*value = cb_data[cb_read_index];
	if(++cb_read_index == CB_CAP) cb_read_index = 0;
	cb_size--;
	return true;
}

void cb_init(void)
{
	cb_read_index = 0;
	cb_write_index = 0;
	cb_size = 0;
	memset(cb_data, 0, sizeof(cb_data));
}

It assumes that data is of type uint32_t. Adjust it to suit your taste. Call cb_init() in your startup code.

A discussion of circular (aka ring) buffers can be found here. I am not going to go into the theory.

What about data races? This may or may not be a problem. You’ll have to decide. Bear in mind that, by default, interrupts on an STM32 have the same group priority. That means that they do not pre-empt each other, so you don’t have to worry about data races where that assumption holds true.

However, interrupts will pre-empt the idle polling loop or interrupts with lower priority. What should you do in that case? Well, you can disable interrupts temporarily in the lower-priority interrupt or idle loop.Take care to keep this critical section as short as possible.

Rather than disable all interrupts, the lower-priority “thread” (but I really mean interrupt or the idle polling loop) can temporarily disable just the higher-priority interrupt that accesses the queue.

So, suppose Timer 2 is a high-priority interrupt that reads from the ring buffer. A lower-priority “thread” can write to the queue like so:

NVIC_DisableIRQ(TIM2_IRQn);
cb_write(value);
NVIC_EnableIRQ(TIM2_IRQn);

Two points to note:

  • TIM2 does not need to employ this trick of disabling IRQ because it’s a high-priority thread, so won’t be pre-empted
  • NVIC_EnableIRQ() and and NVIC_DisableIRQ() are provided by CMSIS that incorporates code to ensure that compiler instructions, memory cache barriers and whatnot are handled correctly, so you don’t have to worry about those issues.

I hope you found that interesting and useful.

About mcturra2000

Computer programmer living in Scotland.
This entry was posted in Uncategorized. Bookmark the permalink.

1 Response to A simple ring buffer for microcontrollers

  1. Pingback: A buffering DAC for #raspberrypi using #stm32 | Mark Carter's blog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s