QP/C++  8.0.0
Real-Time Embedded Framework
Loading...
Searching...
No Matches
FreeRTOS
FreeRTOS

About the QP/C++ Port to FreeRTOS

The ports/freertos/ directory contains a generic platform-independent QP/C++ port to FreeRTOS kernel↑ (version 10). The provided QP port to FreeRTOS has been designed generically to rely exclusively on the existing FreeRTOS API. This means that the port should run without changes on any CPU/compiler platform supported by FreeRTOS.

The QP/C++-FreeRTOS port works as follows:

  • The QP/C++ port uses the static memory allocation of FreeRTOS. This requires the FreeRTOS configuration to define the configSUPPORT_STATIC_ALLOCATION
  • Each QP/C++ active object executes in a separate FreeRTOS task (StaticTask_t) and requires a private stack space.
  • The task-level critical section used in QF and QS is based on the FreeRTOS APIs taskENTER_CRITICAL()/taskEXIT_CRITICAL().
  • The ISR-level critical section used in QF and QS is based on the FreeRTOS APIs taskENTER_CRITICAL_FROM_ISR()/taskEXIT_CRITICAL_FROM_ISR().
  • The QP/C++ port to FreeRTOS provides new "FromISR" APIs, which must be used in the ISRs (but cannot be used at the task-level)
Attention
The design of FreeRTOS requires the use of special "FromISR" API inside ISRs, which imposes the requirement to also provide the "FromISR" variants of the QP/C++ APIs, such as QACTIVE_POST_FROM_ISR(), QF_PUBLISH_FROM_ISR(), etc. These "FromISR" QP/C++ APIs must be used inside ISRs instead of the task-level APIs (QACTIVE_POST(), QF_PUBLISH(), etc.) and conversely, they cannot be used inside tasks and active objects. Unfortunately, FreeRTOS provides no generic way to enforce the proper API use via assertions.
  • The QP/C++ port uses the FreeRTOS message queue (StaticQueue_t) for active object event queues.
  • The QP/C++ port uses the native QF memory pool (QMPool) to implement event pools.
  • The QP/C++ port does not mandate any specific method to manage the QP/C++ time events, but the provided examples use the FreeRTOS "hook" vApplicationTickHook() to periodically invoke the QP/C++ clock tick QF_TICK_X_FROM_ISR(). (NOTE: the vApplicationTickHook() executes in the ISR context and therefore mandates the use of the "FromISR" APIs).

QP/C++ Source Files Needed in this QP/C++ Port
It is important to note that not all QP/C++ source files should be included in the build process. Specifically, the QP/C++ source file qf_actq.c must NOT be included in the build, because this functionality is taken from FreeRTOS. Here is the list of QP/C++ source files needed:

qpcpp/
+---src/
| +---qf/
| | qep_hsm.cpp
| | qep_msm.cpp
| | qf_act.cpp
| | qf_actq.cpp // NOT included (implemented in FreeRTOS)
| | qf_defer.cpp
| | qf_dyn.cpp
| | qf_mem.cpp
| | qf_ps.cpp
| | qf_qeq.cpp
| | qf_qmact.cpp
| | qf_time.cpp
| |
| +---qs/
| | qs.cpp // included only in the Spy build configuration
| | qs_fp.cpp // included only in the Spy build configuration
|
+---ports/
| +---embos/ // QP/C++ port to FreeRTOS
| | qp_port.hpp
| | qf_port.cpp

Example Code

The QP/C++ port to FreeRTOS comes with examples located in the directory qpcpp /examples/freertos. Currently, the examples are provided for the following boards and development toolchains:

  • EK-TM4C123GXL (ARM Cortex-M4F), ARM-KEIL, GNU-ARM, IAR-ARM
  • STM32F746G-Discovery (ARM Cortex-M7), ARM-KEIL, GNU-ARM, IAR-ARM

Writing ISRs for QP/C++-FreeRTOS
The provided examples show how to write regular "kernel-aware" ISRs as well as "kernel-unaware" ISRs for QP/C++-FreeRTOS . (See also the FreeRTOS documentation for configMAX_SYSCALL_INTERRUPT_PRIORITY.

Here is an example of a regular "kernel-aware" ISR (note the use of the FromISR suffix in the QP/C++ APIs):

// NOTE: only the "FromISR" API variants are allowed in the ISRs!
void GPIOPortA_IRQHandler(void) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
// for testing
QACTIVE_POST_FROM_ISR(AO_Table,
Q_NEW_FROM_ISR(QEvt, MAX_PUB_SIG),
&xHigherPriorityTaskWoken,
&l_GPIOPortA_IRQHandler);
// the usual end of FreeRTOS ISR
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
}

Here is an example of a "kernel-unaware" ISR (See also the FreeRTOS documentation for configMAX_SYSCALL_INTERRUPT_PRIORITY ):

//
// ISR for receiving bytes from the QSPY Back-End
// NOTE: This ISR is "kernel-unaware" meaning that it does not interact with
// the FreeRTOS or QP and is not disabled. Such ISRs don't need to call
// portEND_SWITCHING_ISR(() at the end, but they also cannot call any
/ FreeRTOS or QP APIs.
//
void UART0_IRQHandler(void) {
uint32_t status = UART0->RIS; // get the raw interrupt status
UART0->ICR = status; // clear the asserted interrupts
while ((UART0->FR & UART_FR_RXFE) == 0) { // while RX FIFO NOT empty
uint32_t b = UART0->DR;
QS_RX_PUT(b);
}
}
QP/C++ framework.
Definition qequeue.hpp:42

Writing FreeRTOS Hooks Running in ISR Context
FreeRTOS provides "hooks" that are user functions that execute in the ISR context (e.g., vApplicationTickHook()). Such ISR-level functions are closely related to ISRs and should also use exclusively only the "FromISR" APIs. Here is an example of the vApplicationTickHook():

// NOTE: only the "FromISR" API variants are allowed in vApplicationTickHook
void vApplicationTickHook(void) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
. . .
// process time events for rate 0
QF_TICK_X_FROM_ISR(0U, &xHigherPriorityTaskWoken, &l_TickHook);
. . .
// notify FreeRTOS to perform context switch from ISR, if needed
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
}

Starting Active Objects in QP/C++-FreeRTOS
As mentioned in the FreeRTOS port summary, the QP/C++ port to FreeRTOS uses the static memory allocation of FreeRTOS. This means that all memory for an active object, including the private queue buffer and the private stack for the the associated FreeRTOS task must be allocated by the user. Here is an example code that starts an active object:

int main() {
. . .
static QEvt const *tableQueueSto[N_PHILO];
static StackType_t tableStack[configMINIMAL_STACK_SIZE];
. . .
Table_ctor(); // instantiate the Table active object
. . .
QActive_setAttr(AO_Table, TASK_NAME_ATTR, "Table");
QACTIVE_START(AO_Table, // AO to start
N_PHILO + 1U, // QP priority of the AO
tableQueueSto, // event queue storage
Q_DIM(tableQueueSto), // queue length [events]
tableStack, // stack storage
sizeof(tableStack), // stack size [bytes]
(void *)0); // initialization param (not used)
. . .
return QF_run(); // run the QF application
}
#define Q_DIM(array_)
Definition qp.hpp:501

ThreadX