Passing one byte by value

In this example, a single uint8_t is set up to pass individual enumerations, (LED_CMDS), defining the state of one LED at a time or all of the LEDs (on/off).  Here's a summary of what is covered in this example:

  • ledCmdQueue: A queue of one-byte values (uint8_t) representing an enumeration defining LED states.
  • recvTask: This task receives a byte from the queue, executes the desired action, and immediately attempts to receive the next byte from the queue.
  • sendingTask: This task sends enumerated values to the queue using a simple loop, with a 200 ms delay between each send (so the LEDs turning on/off are visible).

So, let's begin:

  1. Set up an enum to help us describe the values that are being passed into the queue:

The following is an excerpt from mainQueueSimplePassByValue.c:

typedef enum
{
ALL_OFF = 0,
RED_ON = 1,
RED_OFF = 2,
BLUE_ON = 3,
BLUE_OFF= 4,
GREEN_ON = 5,
GREEN_OFF = 6,
ALL_ON = 7

}LED_CMDS;
  1. Similar to the initialization paradigm of semaphores, queues must first be created and their handles stored so they can be used to access the queue later. Define a handle to be used to point at a queue that is to be used for passing around instances of uint8_t:
static QueueHandle_t ledCmdQueue = NULL;
  1. Create the queue (verifying its successful creation before continuing) using the xQueueCreate() function:
QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength,
                             UBaseType_t uxItemSize );

Let's quickly outline what we see here:

  • uxQueueLength: The maximum number of elements the queue can hold
  • uxItemSize: The size (in bytes) of each element in the queue
  • Return value: A handle to the queue that is created (or NULL upon error)

Our call to xQueueCreate will look like this:

ledCmdQueue = xQueueCreate(2, sizeof(uint8_t));
assert_param(ledCmdQueue != NULL);

Let's outline what we see here:

  • The queue holds up to 2 elements.
  • Each element is sized to hold uint8_t (a single byte is large enough to store the value of any enumeration we have explicitly defined).
  • xQueueCreate returns a handle to the queue created, which is stored in ledCmdQueue.  This "handle" is a global that will be used by various tasks when accessing the queue.

    The beginning of our recvTask() looks like this:

    void recvTask( void* NotUsed )
    {
    uint8_t nextCmd = 0;

    while(1)
    {
    if(xQueueReceive(ledCmdQueue, &nextCmd, portMAX_DELAY) == pdTRUE)
    {
    switch(nextCmd)
    {
    case ALL_OFF:
    RedLed.Off();
    GreenLed.Off();
    BlueLed.Off();
    break;
    case GREEN_ON:
    GreenLed.On();
    break;

    Let's have a close look at the actual queue receive line highlighted in the preceding code:

    if(xQueueReceive(ledCmdQueue, &nextCmd, portMAX_DELAY) == pdTRUE)

    • The handle ledCmdQueue is used to access the queue.
    • A local uint8_tnextCmd, is defined on the stack. The address of this variable (a pointer) is passed. xQueueReceive will copy the next LED_CMD enumeration (stored as a byte in the queue) into nextCmd.
    • An infinite timeout is used for this access—that is, this function will never return if nothing is added to the queue (the same as timeouts for mutex and semaphore API calls).
    The if( <...> == pdTRUE) is redundant since the delay time is infinite; however, it is a good idea to set up error handling ahead of time so that if a noninfinite timeout is later defined, the error state won't be forgotten about down the road. It is also possible for xQueueReceive() to fail for other reasons (such as an invalid queue handle).

    The sendingTask is a simple while loop that uses prior knowledge of the enum values to pass different values of LED_CMDS into ledCmdQueue:

    void sendingTask( void* NotUsed )
    {
    while(1)
    {
    for(int i = 0; i < 8; i++)
    {
    uint8_t ledCmd = (LED_CMDS) i;
    xQueueSend(ledCmdQueue, &ledCmd, portMAX_DELAY);
    vTaskDelay(200/portTICK_PERIOD_MS);
    }
    }
    }

    The arguments for the sending side's xQueueSend() are nearly identical to the receiving side's xQueueReceive(), the only difference being that we're sending data to the queue this time:

    xQueueSend(ledCmdQueue, &ledCmd, portMAX_DELAY);

    • ledCmdQueue: The handle for the queue to send the data to
    • &ledCmd: The address of the data to pass to the queue
    • portMax_DELAY: The number of RTOS ticks to wait for the queue space to become available (if the queue is full)
    Similar to timeouts from xQueueReceive when nothing is in the queue before the timeout value is reached, calls to xQueueSend can time out if the queue remains full beyond the specified timeout and the item isn't added to the queue. If your application has a noninfinite timeout (which in nearly all cases it should), you'll need to consider what should happen in this case. Courses of action could range from simply dropping the data item (it will be lost forever) to throwing an assert and going into some type of emergency/panic state with an emergency shutdown. A reboot is also popular in some contexts. The exact behavior will generally be dictated by the type of project/product you're working on.

    Feel free to build and download queueSimplePassByValue to the Nucleo dev board. You'll notice that the LEDs follow the pattern defined by the definition of the LED_CMDS enum: ALL_OFF, RED_ON, RED_OFF, BLUE_ON, BLUE_OFF, GREEN_ON, GREEN_OFF, ALL_ON, with 200 ms between each transition.

    But what if we decide we'd like to operate on more than one LED at a time? We could add more values to the existing LED_CMDS enum, such as RED_ON_BLUE_ON_GREEN_OFF, but that would be a lot of very error-prone typing, especially if we had more than 3 LEDs (8 LEDs results in 256 enum values to cover all combinations of each LED being on/off). Instead, let's look at how we can use a struct to describe the LED command and pass that through our queue.

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

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