Why another driver layer? Why not CMSIS-RTOS?
Maybe the IDriver interface and all the Low Level API layer seems something redundant on top of the HAL because every IDriver subclass, at the end, uses the HAL API to operate a peripheral. While this is an interesting point of view, I never saw things in this way (that is why I like to share my work and discuss about the ideas behind it), so I want to talk about the reasons of this design.
Let’s start by a simple note: STM32 HAL has been developed regardless of FreeRTOS, to be independent of any RTOS. We can say that the HAL doesn’t know anything about FreeRTOS. On the other hand, an ODeV application is multitasking by design.
Now, if we give a look at typical driver implementation, for example an I2C slave driver (the full driver source code is available in the Download page):
sys_error_code_t I2CDrv_vtblWrite(IDriver *this, uint8_t *pDataBuffer, uint16_t nDataSize, uint16_t nChannel) {
assert_param(this);
sys_error_code_t xRes = SYS_NO_ERROR_CODE;
I2CDriver *pObj = (I2CDriver*)this;
if (s_pxHwResouces[pObj->m_nHRIndex].m_pxListener != NULL) {
xRes = SYS_I2C_IO_PENDING_ERROR_CODE;
}
else {
while (HAL_I2C_GetState(&pObj->m_xHi2c) != HAL_I2C_STATE_READY) {
__asm volatile( "NOP" );
}
if (HAL_I2C_Slave_Transmit_IT(&pObj->m_xHi2c, pDataBuffer, nDataSize)!= HAL_OK) {
sys_error_handler();
}
// Suspend the calling task until the operation is completed.
if (pdTRUE != xSemaphoreTake(s_pxHwResouces[pObj->m_nHRIndex].m_xSyncObj, pObj->m_nTimeout)) {
SYS_SET_LOW_LEVEL_ERROR_CODE(SYS_I2C_TIMEOUT_ERROR_CODE);
xRes = SYS_I2C_TIMEOUT_ERROR_CODE;
}
}
return xRes;
}
We see that the driver is able to suspend the calling task. The m_xSyncObj is a FreeRTOS binary semaphore given from the I2C ISR when the transmit operation is completed. This is a passive delay, that means another task can do something, because the task waiting for the IO operation to complete releases the MCU.
So there are at least two reason to design a IDriver hierarchy inside the Low Level API layer, on top of both FreeRTOS and the HAL (kernel layer):
And now it is time to introduce the other question of this post, why don’t use CMSIS-RTOS?
What do these two questions have in common?
The answer, my answer, come from a common root, that is the goal of the ODeV framework, and it is different from the goal of the HAL and CMSIS-OS.
The purpose of the STM32 ecosystem is to allow the user to do everything with STM32 (that is a general purpose MCU). Doesn’t matter what you want to do and how you want to do, ST will support you with STM32 and its ecosystem. This is a very ambitious goal and you can find a lot of examples about almost everything, and you can also chose you favorite IDE, your favorite cloud service (Azure, AWS, etc.), your favorite RTOS, etc.
So the HAL provides compatibility with a lot of firmware, framework and services. It must be the starting point for a lot of different way to make firmware for STM32.
CMSIS-OS goal, as reported in ARM website, is similar:
"The CMSIS-OS is a common API for Real-Time operating systems. It provides a standardized programming interface that is portable to many RTOS and enables therefore software templates, middleware, libraries, and other components that can work across supported the RTOS systems. "
Again, portability to many RTOS.
This goal come with a cost: compromise.
To be able to work with everything, then, the firmware design supports a minimum common set of features.
So in a HAL driver you cannot have a passive delay because there is not concept of task in the HAL. CMSIS-OS does’t support, for example, the RTOS task notification that is a FreeRTOS specific feature. The folder structure of all the HAL examples has been designed to make easy to develop and test the code with different IDE, but the final user uses, at the end, only one IDE to make his product. The implementation of CMSIS-OS is just a wrapper on top of FreeRTOS, that means a bigger memory footprint to have less features (because the CMSIS-OS API specification doesn’t cover all the FreeRTOS API).
The ODeV framework has been designed with a different goal: to make the best firmware for STM32.
So I don’t need to (and I am not interested into) support many different IDE, I chose the one with the best potential, that for me is an Eclipse based IDE, so Atollic TrueSTUDIO was a natural choice.
The project structure can be optimized and take advantage of Eclipse specific features, and it is very easy to export and import an ODeV_f based project.
I don’t need to make firmware that support many different RTOS and also bare metal embedded application. FreeRTOS was (since I started to look for an RTOS with the version v4) the best open source RTOS available, so I focused to optimize the framework for FreeRTOS. It take no sense to use CMSIS-OS as another layer on top of FreeRTOS. Moreover every firmware complex enough to make a real embedded application has to schedule different activity in some way, so it need an real time scheduler.
Let me say the same goal in a different way. The ODeV framework has not been designed to be used with everything in order to do everything, but whatever it does, it tries to do in the best way for STM32.
Of course it is not perfect, there are many idea about how to improve the framework coming from the everyday works on products. But the vision is clear as well as the path to keep improving the framework.
I hope that this post help to clarify the important point about the ODeV design and to answer the initial questions.