cancel
Showing results for 
Search instead for 
Did you mean: 

critical section example

togrady
Associate II
Posted on January 02, 2008 at 13:54

critical section example

5 REPLIES 5
togrady
Associate II
Posted on May 17, 2011 at 12:20

Hi,

I have implemented buffered uarts using interupts on the STM32. Can anyone provide a ''critical section'' example to disable interupts when getting data in or out of my circular buffers. I've used the code below in the past with other processors but I'm not sure how to implement the macros DISABLE_INTERRUPTS and ENABLE_INTERRUPTS on the STM32.

Code:

void EnterCriticalSection( void )

{

DISABLE_INTERRUPTS();

CriticalSectionNesting++;

}

void ExitCriticalSection( void )

{

if ((--CriticalSectionNesting) == 0)

ENABLE_INTERRUPTS();

}

Thanks

Trevor

sjo
Associate II
Posted on May 17, 2011 at 12:20

Your code looks like it was ripped from Freertos, why not just have a look at the cortexm3 port.

Cheers

sjo

14 years later, about time someone answered this? Here's what I've come up with:

 

 

#include <assert.h>
//cmsis_compiler includes cmsis_gnu or cmsis_armcc as applicable,
//which is where the `__` functions are defined.
#include <cmsis_compiler.h>



//Support calling CRITICAL_enter() multiple times before calling CRITICAL_exit(), as
//sometimes happens when you call functions within your critical sections that themselves
//enter critical sections. Generally this is a sign of problem, because critical sections
//should be kept short and simple, but is supported just in case there's a worthy use case.
uint8_t gNestingLevel = 0;
//Remember whether interrupts were on or not at the time we disable them.
bool    gInterruptsWereEnabled;


void CRITICAL_enter(void)
{
  assert(gNestingLevel < 100); //don't be silly

  if(gNestingLevel == 0)
  {
    gInterruptsWereEnabled = (__get_PRIMASK() == 0);
    __disable_irq(); //we only need to disable interrupts at the top nesting level
  }
  gNestingLevel++;
}

void CRITICAL_exit(void)
{
  assert(gNestingLevel > 0); //don't exit before entering

  //if we've reached the top nesting level, and interrupts were enabled, re-enable them
  if(--gNestingLevel == 0 && gInterruptsWereEnabled)
    __enable_irq();
}

 

 
The DISABLE_INTERRUPTS and ENABLE_INTERRUPTS bits are just the __disable_irq() and __enable_irq() calls, but I've also stored whether they were enabled in the first place so we can restore the correct state.

If you use RTOS - critical section support is there (e.g. using semaphores).
Even you would not know how to mark a critical section: it is a semaphore which is set to block if "somebody" (another thread/task) enters this piece of code as well. When the owner has done, it releases the semaphore.
So, other threads/tasks have to check this semaphore and block if it is already allocated/in use.

Enabling and disabling INTs might sound "logical" but it is not good practice: you disable also SYSTICK and any other timer or peripheral which is not related to your critical section. If you want to use "preemptive" multi-tasking - disabling all global INTs, including the timer and time based scheduling - is not a nice approach.

Critical Section means: you want to avoid that two parallel processes (tasks/threads) use the same piece of code, which is potentially using a peripheral (e.g. UART) which can be used just once at a time. I one code runs a bit, another code kicks in - it can mess up on the "not-sharable" resource. So, you need to encapsulate this code in a critical section.

In general it looks like this:

if (xSemaphoreTake(xSemaphoreSPI2, portMAX_DELAY) == pdTRUE)
{
  //OK: SPI2 was free - I own it now - exclusivly
  //... do something with it
  //all parties using this code cannot intercept each other = critical section

  xSemaphoreGive(xSemaphoreSPI2);
  //if all done, relaese the semaphore
}

 

Looks like you've got your wires crossed there. Even if the OP were using an RTOS (I can't see any evidence they are), you still need to know how to do this. You might like to have read through the FreeRTOS docs, and in particular portDISABLE_INTERRUPTS and taskENTER_CRITICAL to see why.

In the more probable scenario (I'm assuming) that there's no RTOS at play here, then the pre-emptive multi-tasking requirement rarely applies. In single threaded applications, disabling interrupts has an even more important role to play.

Note the peripherals are not disabled as you state. Their interrupts become pending. That is essential to understanding how to do this safely.