Application that use RTOS are structured as a set of independent task (each task is a mini program in its own right). These autonomous tasks will have to communicate with each other so collectively they can provide useful functionality.
The "queue" is the underlying primitive used by all FreeRTOS communication and synchronization mechanism. This mechanism provide:
- Task to Task communication.
- Interrupt to Task communication.
- Task to Interrupt communication.
A queue can hold a finite number of fixed size data elements. The maximum number of items a queue can hold is called its 'length'. Length and size of each data item are set when the queue is created. A Queue must be explicitly created before it can be used.
QueHandle_t xQueueCreate(
UBaseType_t uxQueueLength /*number of max elements to hold*/
UBaseType_t uxItemSize /*size in bytes of elements to hold*/
);
Queues are used as First In First Out (FIFO) buffer where data is written to the end (tail) and removed from the front (head). Writing data to a queue causes a byte-to-byte copy of the data to be stored in the queue itself. Reading data from a queue causes the copy of the data to be removed from the queue. Queues are objects on its own right that are not owned by or assigned to any particular task. Any number of tasks can write to the same queue and any number of tasks can read from the same queue.
Queue Functionality
- A queue is created to allow Task A and Task B to communicate. The queue can hold a maximum of 5 integers. When the queue is created it does not contain any values so is empty.
- Task A writes (sends) the value of a local variable to the back of the queue. as the queue was previously empty, the value is now the only item in the queue.
- ask A changes the value of its local variable before writing it to the queue again. The queue now contains copies of both values written to the queue.
- Task B reads (receives) from the queue into a different variable. The value received by task B is the value from the head of the queue.
- Task B has removed one item, leaving only the second value written by Task A remaining in the queue. This is the value Task B would be receive next if it read from the queue again.
Blocking on Queue
When a task attempts to read from a queue it can optionally specify a block time. This is the time the task should be kept in the Blocked State to wait for data to be available from the queue.
A task can optionally specify a block time when writing to a queue. The block time will be the maximum time the task should be held in the Blocked state to wait for space to become available on the queue.
BaseType_t xQueueSend( QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait );
BaseType_t xQueueReceive( QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait );
Setting xTicksToWait to portMAX_DELAY will cause the task to wait indefinitely (without timing out), provided.
INCLUDE_vTaskSuspend must set to 1 in FreeRTOSConfig.h
Example Program:
QueueHandle_t xQueue;
int main( void ){
xQueue = xQueueCreate(5, sizeof(long));
xTaskCreate(vSender, "Send1", 240,(void*)100, 1,NULL );
xTaskCreate(vSender, "Send2", 240,(void*)200, 1,NULL );
xTaskCreate(vReceiver, "Receiver", 240, NULL, 2, NULL );
vTaskStartScheduler();
return 0;
}
void vSender(void *pvParameters){
long lValue;
lValue = (long)pvParameters;
for(;;){
(void)xQueueSend(xQueue, &lValue, 0);
taskYIELD();
}
}
void vReceiver(void *pvParameters){
long lValue;
const TickType_t xTicks = 100/portTICK_RATE_MS;
for(;;){
(void)xQueueReceive(xQueue, &lValue, xTicks);
SEGGER_SYSVIEW_PrintfHost("Received: %d", lValue);
}
}
Execution Pattern
Compound Types and Multiple Writers
It is common for a task to receive data from multiple sources on a single queue. Often, the receiver of the data needs to know where the data came from. A simple way to achieve this is to use the queue to transfer structures with the source and the value.
If the size of the data being stored in the queue is large, then it is preferable to use the queue to transfer pointer to the data, rather than copy the data itself.
NOTE:
- The owner of the RAM being pointed to is clearly defined.
- The RAM being pointed to remains valid.
Code Snippets
- QUEUE: Task communication
- QUEUE: Task communication with structs
- QUEUE: save memory data using pointers
- QUEUE: Data lost in Queue buffer
- QUEUE: Avoid data lost when Queue buffer is full
- QUEUE: Messages to control a series of LEDs
- QUEUE: Multiples Queues as communication between task
- QUEUE: SendToBack - SendToFront
Exercises (printf)
- Write a task to send through a Queue each 10s different times ( one at a time ), other task should display a message concurrently each time indicated by the Queue.
- Modify the previous program to send in Queue the time and message that should be displayed.
- Write a program to create a task to send ānā messages to a Queue, after, the task will block 3 seconds. Next, create another task that reads the totality of the messages to be displayed and the Task will block until the first task sends the messages again. Use the function uxQueueMessagesWaiting.
- Create a task to send a whole number different each second to a Queue and create another 2 tasks with different priorities to receive the messages to be displayed. Support you with the function xQueuePeek.
- Investigate and describe the functions xQueueDelete, xQueueOverwrite, xQueueReset and uxQueueSpaceAvailable.
Exercises
- Write a program that turns on an LED when a button is pressed and turns it off when the button is released. (The LED will only turn on when the button is pressed.), use a task to read the button a a second task to control the led state, a queue shall be used to communicate both tasks
- Write a program that rotates an LED on port C, but this time with three speeds and three buttons. Each button will activate a different speed. again one task will control the buttons an second task the leds and a queue to communicate both tasks
- Modify exercise 2 so that pressing the button once turns on the LED, and pressing it again turns it off.
- Modify the previous program using two buttons each button shall be controlled by a single task. Pressing one button will rotate the LEDs from left to right, and pressing the other button will rotate them from right to left.
- Create a program that sends to Queue a struct data, the struct should contain the LED pin and the LED state. Use 2 task, the first task must send a message, and the second process the queue message to change the state of the LED.
- Read example of data lost in Queue Buffer, and search a solution. Modify the program to solve the problem.