From Silicon Labs: Making Peripheral Drivers Thread-safe
If you are running in a multi-threaded environment in which you have more than one task making use of a peripheral driver such as USART, SPI, I2C, etc., you should consider making it thread-safe.
The Micrium OS kernel offers a variety of services designed to protect shared resources. In our case, let’s make use of the Mutual Exclusion Semaphore also known as mutex. Why a mutex? Because we want our resource (peripheral driver) to be accessed only by one task at a time. Regular semaphores present a vulnerability with priority-inversion. Mutexes, on the other hand, are implemented in a way that priority inversions are prevented by using priority inheritance.
For this exercise, you will be editing files from the Gecko SDK, which is not recommended therefore do this with caution.
In the peripheral driver file that you want to protect, include the following file:
#include <kernel/include/os.h> |
Now we have to declare a global variable for our mutex, for example, if you want to protect the SPI you could declare it as follows:
OS_MUTEX SPI_Mutex; |
With the mutex now declared, you need to invoke the kernel call to create it. My recommendation is to make this in the initialization function of the peripheral driver that you are using. You create the mutex by calling:
OSMutexCreate(&SPI_Mutex, “SPI Mutex”, &err); |
Notice how the function requires an error argument, just declare RTOS_ERR err locally and pass it on.
Always make sure to check the error returned, if it’s not RTOS_ERR_NONE then something went wrong.
The mutex is now created and registered with the kernel. Now you will need to wrap around the driver calls that your application is using with:
void foo () { RTOS_ERR err; OSMutexPend(&SPI_Mutex, /* Pointer to the mutex */ 0, /* No timeout */ OS_OPT_PEND_BLOCKING, /* Block if not available */ DEF_NULL, /*Timestamp not used */ &err); if (err.Code != RTOS_ERR_NONE) { /* handle error */ } /* peripheral driver function code */ ... ... ... OSMutexPost(&SPI_Mutex, OS_OPT_POST_NONE, &err); if (err.Code != RTOS_ERR_NONE) { /* handle error */ } } |
Please make sure to check the returned errors. A common one is RTOS_ERR_NOT_READY, this happens when the pend or post calls are made before the kernel is in its running state (after the call to OSStart()).
If the driver initialization function can potentially be called multiple times from more than one task, my recommendation is to also protect it.
With this setup, you can be sure that your peripheral is only being accessed by one task at a time.
Beware also that some drivers have abort functions, you have to be careful with this and not lock your system on a mutex pend. For more information on how to protect a resource using Micrium OS please visit https://doc.micrium.com/display/OSUM50600/Resource+Management+Using+the+Kernel
Source: https://www.silabs.com/community/blog.entry.html/2019/06/26/making_peripheraldriversthread-safe-5AjD