Passing a composite data type by value

FreeRTOS queues (and most other FreeRTOS API functions) take in void* as arguments for the individual data types that are being operated on. This is done to provide flexibility for the application writer as efficiently as possible. Since void* is simply a pointer to anything and the sizes of the elements in the queue is defined when it is created, queues can be used to pass anything between tasks.

The use of void* for interacting with queues acts as a double-edged sword. It provides the ultimate amount of flexibility, but also provides the very real possibility for you to pass the wrong data type into the queue, potentially without a warning from the compiler. You must keep track of the data type that is being stored in each queue!

We'll use this flexibility to pass in a composite data type created from a struct of instances of uint8_t (each of which is only one bit wide) to describe the state of all three LEDs:

Excerpt from mainQueueCompositePassByValue.c:

typedef struct
{
uint8_t redLEDState : 1; //specify this variable as 1 bit wide
uint8_t blueLEDState : 1; //specify this variable as 1 bit wide
uint8_t greenLEDState : 1; //specify this variable as 1 bit wide
uint32_t msDelayTime; //min number of mS to remain in this state
}LedStates_t;

We'll also create a queue that is able to hold eight copies of the entire LedStates_t struct:

ledCmdQueue = xQueueCreate(8, sizeof(LedStates_t));

Like the last example, recvTask waits until an item is available from the ledCmdQueue queue and then operates on it (turning LEDs on/off as required):

mainQueueCompositePassByValue.c recvTask:

if(xQueueReceive(ledCmdQueue, &nextCmd, portMAX_DELAY) == pdTRUE)
{
if(nextCmd.redLEDState == 1)
RedLed.On();
else
RedLed.Off();
if(nextCmd.blueLEDState == 1)
BlueLed.On();
else
BlueLed.Off();
if(nextCmd.greenLEDState == 1)
GreenLed.On();
else
GreenLed.Off();
}
vTaskDelay(nextCmd.msDelayTime/portTICK_PERIOD_MS);

Here are the responsibilities of the primary loop of recvTask:

  • Each time an element is available from the queue, each field of the struct is evaluated and the appropriate action is taken.  All three LEDs are updated with a single command, sent to the queue. 
  • The newly created msDelayTime field is also evaluated (it is used to add a delay before the task attempts to receive from the queue again). This is what slows down the system enough so that the LED states are visible.

mainQueueCompositePassByValue.c sendingTask:

while(1)
{
nextStates.redLEDState = 1;
nextStates.greenLEDState = 1;
nextStates.blueLEDState = 1;
nextStates.msDelayTime = 100;

xQueueSend(ledCmdQueue, &nextStates, portMAX_DELAY);

nextStates.blueLEDState = 0; //turn off just the blue LED
nextStates.msDelayTime = 1500;
xQueueSend(ledCmdQueue, &nextStates, portMAX_DELAY);

nextStates.greenLEDState = 0;//turn off just the green LED
nextStates.msDelayTime = 200;
xQueueSend(ledCmdQueue, &nextStates, portMAX_DELAY);

nextStates.redLEDState = 0;
xQueueSend(ledCmdQueue, &nextStates, portMAX_DELAY);
}

The loop of sendingTask sends a few commands to ledCmdQueue – here are the details:

  • sendingTask looks a bit different from before. Now, since a struct is being passed, we can access each field, setting multiple fields before sending nextStates to the queue.
  • Each time xQueueSend is called, the contents of nextStates is copied into the queue before moving on. As soon as xQueueSend() returns successfully, the value of nextStates is copied into the queue storage; nextStates does not need to be preserved.

To drive home the point that the value of nextStates is copied into the queue, this example changes the priorities of tasks so that the queue is filled completely by sendingTask before being emptied by recvTask. This is accomplished by giving sendingTask a higher priority than revcTask. Here's what our task definitions look like (asserts are present in the code but are not shown here to reduce clutter):

xTaskCreate(recvTask, "recvTask", STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, 
NULL);
xTaskCreate(sendingTask, "sendingTask", STACK_SIZE, NULL, 
configMAX_PRIORITIES – 1, NULL);

sendingTask is configured to have the highest priority in the system. configMAX_PRIORITIES is defined in Chapter9/Inc/FreeRTOSConfig.h and is the number of priorities available. FreeRTOS task priorities are set up so that 0 is the lowest priority task in the system and the highest priority available in the system is configMAX_PRIORITIES - 1.  

This prioritization setup allows sendingTask to repeatedly send data to the queue until it is full (because sendingTask has a higher priority). After the queue has filled, sendingTask will block and allow recvTask to remove an item from the queue.  Let's take a look at how this plays out in more detail.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.119.199