Critical sections are used to provide a task with unshared access to a resource when this resource is used by other tasks. For example: a task uses EEPROM and enters a critical section to avoid EEPROM data modification by other tasks.
In fact, this is a rare situation for a cooperative RTOS because the programmer switches context manually, but it is still possible. So OSA has services to protect critical sections of the program.
To allow usage of critical sections you must define the OS_ENABLE_CRITICAL_SECTION constant in OSAcfg.h.
There are two services to protect program's critical sections:
When entering a critical section, all interrupts are disabled (their current value is stored in internal system flags). Only the task that called the service OS_EnterCriticalSection can get control independently of priority, and all other tasks will be blocked until OS_LeaveCriticalSection is called. When in the critical section it still possible to return to the scheduler.
The service OS_EnterCriticalSection saves current GIEx values, and then clears them. Thus if you call this service twice in a row, the GIEx value saved by the first call will be overwritten with zeroes by the second call, since after the first call GIEx has been cleared. In this situation OS_LeaveCriticalSection will restore the zeroed GIEx saved by the second call.
Avoid calling service OS_EnterCriticalSection twice in a row.
You will be able to use task delays (OS_Delay) and waiting with timeout services only if OS_Timer is not called from an interrupt (since interrupts are disabled while in a critical section). There are two ways of solving this problem:
void interrupt int_routine (void) { OS_EnterInt(); if (TMR2IF) // This code will not work in critical section { TMR2IF = 0; OS_Timer(); // This service will be called only outside // critical section } OS_LeaveInt(); } void Task1 (void) { for (;;) { . . . OS_EnterCriticalSection(); OS_Delay(100); OS_LeaveCriticalSection(); . . . } } void main (void) { OS_Init(); OS_Task_Create(0, Task1); for (;;) { OS_Sched(); if (OS_IsInCriticalSection()) { if (TMR2IF) { TMR2IF = 0; OS_Timer(); // This service will be called within critical section } } } }
All task except one (the one that called OS_EnterCriticalSection) are blocked even if their priority is higher and they are ready to run. Thus if a task enters a critical section and then waits for an event that occurs in another task, the system will loop. To avoid this you can first wait for the event and then enter the critical section.
void Task1 (void) { for (;;) { . . . // Incorrect OS_EnterCriticalSection(); OS_Bsem_Wait(BS_USART_FREE); . . . // Correct OS_Bsem_Wait(BS_USART_FREE); OS_EnterCriticalSection(); . . . } }
Service | Arguments | Description | Properties |
---|---|---|---|
bool OS_IsInCriticalSection | | Return true if any task is in critical section | |
OS_EnterCriticalSection | | Enter critical section | |
OS_LeaveCriticalSection | | Leave critical section |