Queues¶
The TI-RTOS Queue module provides a thread-safe unidirectional message passing module operating in a first in, first out (FIFO) basis. Queues are commonly used to allow high priority threads to pass messages to lower priority tasks for deferred processing; therefore allowing low priority tasks to block until necessary to run.
In Figure 59. a queue is configured for unidirectional communication from task A to task B. Task A “puts” messages into the queue and task B “gets” messages from the queue.
In BLE5-Stack, TI-RTOS Queue functions have been abstracted into functions
in util.c
See the Queue module documentation in the
TI-RTOS Kernel (SYS/BIOS) User’s Guide. for the underlying functions. The functions in
util.c
combine a queue from the Queue module with an event from the
Event module to pass messages between threads.
In CC13xx or CC26xx software, ICall uses queues and events from their respective
modules to pass messages between the application and stack tasks.
An example of this can be seen in SimpleCentral_enqueueMsg()
. A high
priority Task, Swi, or Hwi queues a message to the application task. The
application task will then process this message in its own context when no
other high priority threads are running.
The util
module contains a set of abstracted TI-RTOS Queue functions as
shown here:
Util_constructQueue() creates a queue.
Util_enqueueMsg() puts items into the queue.
Util_dequeueMsg() gets items from the queue.
Functional Example¶
Figure 60. and Figure 61. illustrate how a queue is used to enqueue a button press message from a Hwi (to a Swi in the Board Key module) to be post-processed within a task context. This example is taken from the from the simple_central project in BLE5-Stack.
With interrupts enabled, a pin interrupt can occur asynchronously within a
Hwi context. To keep interrupts as short as possible, the work
associated to the interrupt is deferred to tasks for processing. In the
simple_central example found in BLE5-Stack, pin interrupts are abstracted
via the Board Key module. This module notifies registered functions via a
Swi callback. In this case, SimpleCentral_keyChangeHandler
is the registered callback function.
Step 1 in Figure 60. shows the callback to
SimpleCentral_keyChangeHandler
when a key is pressed. This event is
placed into the application’s queue for processing.
1void SimpleCentral_keyChangeHandler(uint8 keys)
2{
3 SimpleCentral_enqueueMsg(SC_KEY_CHANGE_EVT, keys, NULL);
4}
Step 2 in Figure 60. shows how this key press is enqueued
for simple_central task. Here, memory is allocated via ICall_malloc()
so the message can be added to the queue. Once added, Util_enqueueMsg()
will generate a UTIL_QUEUE_EVENT_ID
event to signal the application
for processing.
1static uint8_t SimpleCentral_enqueueMsg(uint8_t event, uint8_t state, uint8_t *pData)
2{
3 scEvt_t *pMsg = ICall_malloc(sizeof(scEvt_t));
4
5 // Create dynamic pointer to message.
6 if (pMsg)
7 {
8 pMsg->hdr.event = event;
9 pMsg->hdr.state = state;
10 pMsg->pData = pData;
11
12 // Enqueue the message.
13 return Util_enqueueMsg(appMsgQueue, syncEvent, (uint8_t *)pMsg);
14 }
15
16 return (false);
17}
Step 3 in Figure 61., the simple_central application is
unblocked by the posted UTIL_QUEUE_EVENT_ID
event where it proceeds
to check if messages have been placed in the queue for processing.
1// If RTOS queue is not empty, process app message
2if (events & SC_QUEUE_EVT)
3{
4 scEvt_t *pMsg;
5 while (pMsg = (scEvt_t *)Util_dequeueMsg(appMsgQueue))
6 {
7 // Process message
8 SimpleCentral_processAppMsg(pMsg);
9
10 // Free the space from the message
11 ICall_free(pMsg);
12 }
13}
Step 4 in Figure 61., the simple_central application takes the dequeued message and processes it.
1static void SimpleCentral_processAppMsg(sbcEvt_t *pMsg)
2{
3 switch (pMsg->hdr.event)
4 {
5 case SC_KEY_CHANGE_EVT:
6 SimpleCentral_handleKeys(pMsg->hdr.state);
7 break;
8 //...
9 }
10}
Step 5 in Figure 61., the simple_central application can now free the memory allocated in Step 2.