2008-01-02 4:54 AM
critical section example
2011-05-17 3:20 AM
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 Trevor2011-05-17 3:20 AM
Your code looks like it was ripped from Freertos, why not just have a look at the cortexm3 port.
Cheers sjo2025-02-27 4:37 PM - edited 2025-02-27 4:38 PM
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.
2025-02-27 7:42 PM
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
}
2025-02-27 8:27 PM
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.