Olá, caro leitor. Tudo bem? Esse é o segundo artigo da série sobre o FreeRTOS. No primeiro post, foi passado de maneira bem sucinta os conceitos de kernel, escalonador e processo. O artigo tinha como objetivo apresentar os principais recursos oferecidos pelo FreeRTOS para manipular tarefas. Neste artigo, será mostrado o mecanismo para troca de informação entre tarefas, a fila (queue).
Filas, ou queues, em inglês, basicamente, são estruturas de dados do tipo FIFO (first-in first-out). Ou seja, o primeiro elemento adicionado à fila será o primeiro a ser removido. No FreeRTOS, filas são o principal meio de comunicação entre tarefas e interrupções. Além de possibilitar a troca de mensagem entre tarefas, o FreeRTOS permite que as queue sejam utilizadas como mecanismo de sincronização entre elas. A seguir, temos uma figura ilustrando o compartilhamento de dados entre duas tarefas.

Diagrama de troca de mensagem entre tarefas
A criação de queue é feita por meio da função xQueueCreate(). Basicamente, o sistema operacional cria um “buffer” na memória RAM. Para utilizar a fila no FreeRTOS, é necessário criar variável do tipo QueueHandle_t. Essa variável é utilizada como identificador da queue.
Nota: A criação de queue no FreeRTOS pode ser feita utilizando duas abordagens, a primeira, utilizando o método de alocação na memória RAM dinâmica, a partir do Heap. O segundo modo é por alocação de memória RAM estática em tempo de compilação. Para entender melhor a diferença entre alocação de memória estática vs dinâmica, consulte o link.
Protótipo da função:
QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize);
Parâmetros:
Retorno:
Para adicionar elementos em uma fila no FreeRTOS, existem duas tratativas. Quando a troca de informação ocorre entre tarefas, é recomendado o uso da função genérica para inserção de dados na fila. Quando a comunicação é feita entre interrupção ou rotina de “callback” para tarefa, deve ser utilizada uma função especializada.
xQueueSend é função genérica para inserir os dados na fila.
Protótipo da função:
BaseType_t xQueueSend(QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait);
Parâmetro:
Retorno:
A função especializada é xQueueSendFromISR. É recomendado utilizar essa função quando o dado é inserido a partir de uma interrupção ou uma função de “callback”.
Protótipo da função:
BaseType_t xQueueSendFromISR(QueueHandle_t xQueue, const void *pvItemToQueue, BaseType_t *pxHigherPriorityTaskWoken);
Parâmetros:
Retorno:
Semelhante com o que ocorre para inserir dados na fila, onde temos função genérica e especializada. Para remover dados da queue, é necessário obedecer a mesma regra. Quando a troca de informação ocorre entre tarefas, é recomendado o uso da função genérica para remoção de dados na fila. Quando a comunicação é feita entre interrupção ou “callback” para tarefa, deve ser utilizada uma função especializada.
xQueueReceive é função genérica para remover elementos da fila.
Protótipo da função:
BaseType_t xQueueReceive( QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait);
Parâmetros:
Retorno:
A função especializada que remove elementos da fila é xQueueReceiveFromISR.
Protótipo da função:
BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, void *pvBuffer, BaseType_t *pxHigherPriorityTaskWoken);
Parâmetros:
Para deletar uma fila, o FreeRTOS fornece a função vQueueDelete. Quando é excluída uma queue, é liberada toda a memória RAM alocada.
Protótipo da função:
void vQueueDelete( QueueHandle_t xQueue );
Parâmetros:
A aplicação desenvolvida para o projeto de demonstração executa a leitura de um botão do tipo “push button”, aciona o LED “on board” e publica no barramento serial o LOG de mensagem. O projeto consta com 03 tarefas e uma função trata da interrupção gerada pelo pressionar do botão. Para a troca de informação entre as tarefas e a função de interrupção, foram criadas duas filas.
A seguir, temos o código fonte desenvolvido para o projeto de demonstração, trazendo as funções apresentadas para o manuseio de queue no FreeRTOS.
Materiais necessários para o projeto Trocando Informação entre Tarefas com Filas

Circuito elétrico do projeto de demonstração
/**
* @file main.cpp
* @author Evandro Teixeira
* @brief
* @version 0.1
* @date 14-01-2022
*
* @copyright Copyright (c) 2022
*
*/
#include <Arduino.h>
#include <freertos/queue.h>
#include <freertos/task.h>
#define COLOR_BLACK "\e[0;30m"
#define COLOR_RED "\e[0;31m"
#define COLOR_GREEN "\e[0;32m"
#define COLOR_YELLOW "\e[0;33m"
#define COLOR_BLUE "\e[0;34m"
#define COLOR_PURPLE "\e[0;35m"
#define COLOR_CYAN "\e[0;36m"
#define COLOR_WRITE "\e[0;37m"
#define COLOR_RESET "\e[0m"
#define MESSAGE_SIZE 32 // Tamanho da mensagem
#define NUMBER_OF_ELEMENTS 8 // Número de elementos na fila
#define BUTTON 15 // Pino do botão
#define LED_BOARD 2 // Pino do LED
#define DEBOUNCE_BUTTON 1000 // Tempo do debounce do botão
// Estruta dados mensagem
typedef struct
{
uint8_t Sts;
char Txt[MESSAGE_SIZE];
}DataMsg_t;
// Prototipo das tarefas
void Tarefa_A(void *parameters);
void Tarefa_B(void *parameters);
void Tarefa_LED(void *parameters);
// Cria Handle da Mensagem de tarefa de A para tarefa B
QueueHandle_t Msg_A_para_B;
// Cria Handle da Mensagem da interrupção do botão para a tarefa LED
QueueHandle_t Button_para_LED;
/**
* @brief Função da interrupção botão
*/
void IRAM_ATTR Button_ISR()
{
// tempo da ultima leitura do botão
static uint32_t last_time = 0;
static DataMsg_t Msg = {
.Sts = 0,
.Txt = {0}
};
// Algoritmo de debounce do botão
if( (millis() - last_time) >= DEBOUNCE_BUTTON)
{
last_time = millis();
if(Msg.Sts == 1)
{
// Prepara dado para ser publicado na fila
Msg.Sts = 0;
sprintf(Msg.Txt,"LED BOARD: OFF");
}
else
{
// Prepara dado para ser publicado na fila
Msg.Sts = 1;
sprintf(Msg.Txt,"LED BOARD: ON");
}
// Publica dado na fila
xQueueSendFromISR(Button_para_LED,&Msg,pdFALSE);
}
}
/**
* @brief
*/
void setup()
{
// Inicializa a Serial
Serial.begin(115200);
Serial.printf("\n\rFreeRTOS - Fila\n\r");
// Inicializa pino 15 como entra e inicializa interrupção do botão
pinMode(BUTTON, INPUT);
attachInterrupt(BUTTON, Button_ISR, RISING);
// Inicializa pino do LED on Board
pinMode(LED_BOARD,OUTPUT);
digitalWrite(LED_BOARD,LOW);
// Cria Fila de mensagem para comunicação entre as Tarefas A e B
Msg_A_para_B = xQueueCreate( NUMBER_OF_ELEMENTS, (MESSAGE_SIZE * sizeof(char)) );
if(Msg_A_para_B == NULL)
{
Serial.printf("\n\rFalha em criar a fila Msg_A_para_B");
}
// Cria Fila de mensagem para função da inturrupção do botão para a Tarefa LED
Button_para_LED = xQueueCreate( NUMBER_OF_ELEMENTS, sizeof(DataMsg_t) );
if(Button_para_LED == NULL)
{
Serial.printf("\n\rFalha em criar a fila Button_para_LED");
}
// Cria as tarefas da aplicação
xTaskCreate(Tarefa_A, "Tarefa_A", configMINIMAL_STACK_SIZE * 2, NULL, tskIDLE_PRIORITY + 1, NULL);
xTaskCreate(Tarefa_B, "Tarefa_B", configMINIMAL_STACK_SIZE * 2, NULL, tskIDLE_PRIORITY + 1, NULL);
xTaskCreate(Tarefa_LED, "Tarefa_LED", configMINIMAL_STACK_SIZE * 2, NULL, tskIDLE_PRIORITY + 2, NULL);
}
/**
* @brief
*/
void loop()
{
Serial.printf("\n\rDeleta tarefa LOOP");
vTaskSuspend(NULL);
}
/**
* @brief Tarefa A
*
* @param parameters
*/
void Tarefa_A(void *parameters)
{
// Variavel locais
char txt[MESSAGE_SIZE] = {0};
uint8_t msg_counter = 0;
// Imprime informação da tarefa no barramento serial
Serial.print(COLOR_GREEN);
Serial.printf("\n\r%s", pcTaskGetTaskName(NULL) );
Serial.print(COLOR_RESET);
while (1)
{
// Prepara dado para ser publicado na fila
sprintf(txt,"Msg number: %3d, task A to B",msg_counter++);
// Imprime o conteudo a ser publicado no barramento serial
Serial.print(COLOR_GREEN);
Serial.printf("\n\r%s Envia -> %s", pcTaskGetTaskName(NULL), txt);
Serial.print(COLOR_RESET);
// Publica dado na fila
if( xQueueSend( Msg_A_para_B, (void*)&txt, (TickType_t)1000 ) != pdTRUE)
{
Serial.print(COLOR_GREEN);
Serial.printf("\n\rFalha em enviar os dados da fila");
Serial.print(COLOR_RESET);
}
// Pausa a Tarefa A por 05 segundos
vTaskDelay(5000/portTICK_PERIOD_MS);
}
}
/**
* @brief Tarefa B
*
* @param parameters
*/
void Tarefa_B(void *parameters)
{
// Variavel locais
char txt[MESSAGE_SIZE] = {0};
// Imprime informação da tarefa
Serial.print(COLOR_YELLOW);
Serial.printf("\n\r%s", pcTaskGetTaskName(NULL) );
Serial.print(COLOR_RESET);
while (1)
{
// Checa se há dados na fila - e pausa a tarefa por 01 segundo
if( xQueueReceive(Msg_A_para_B,&txt,(TickType_t)1000 ) == pdPASS)
{
// Imprime conteudo da fila no barramento serial
Serial.print(COLOR_YELLOW);
Serial.printf("\n\r%s Recebe -> %s", pcTaskGetTaskName(NULL),txt );
Serial.print(COLOR_RESET);
}
// Imprime informação da tarefa no barramento serial
Serial.print(COLOR_YELLOW);
Serial.printf("\n\r%s - time: %d s", pcTaskGetTaskName(NULL),(uint)(millis()/1000) );
Serial.print(COLOR_RESET);
}
}
/**
* @brief Tarefa LED
*
* @param parameters
*/
void Tarefa_LED(void *parameters)
{
DataMsg_t Msg;
// Imprime informação da tarefa
Serial.print(COLOR_RED);
Serial.printf("\n\r%s", pcTaskGetTaskName(NULL) );
Serial.print(COLOR_RESET);
while (1)
{
// Checa se há dados na fila - e suspende a tarefa enquato a fila estiver vazia
if( xQueueReceive(Button_para_LED, &Msg, portMAX_DELAY) == pdPASS)
{
// Imprime conteudo da fila no barramento serial
Serial.print(COLOR_RED);
Serial.printf("\n\r%s Recebe -> %s | Sts: %d", pcTaskGetTaskName(NULL),Msg.Txt,Msg.Sts);
Serial.print(COLOR_RESET);
// Altera o valor do pino do LED on Board
digitalWrite(LED_BOARD,Msg.Sts);
}
// Imprime informação da tarefa no barramento serial
Serial.print(COLOR_RED);
Serial.printf("\n\r%s - time: %d s", pcTaskGetTaskName(NULL),(uint)(millis()/1000) );
Serial.print(COLOR_RESET);
}
}A seguir, temos a imagem com as mensagem de cada tarefa imprimindo no terminal serial.

LOG de mensagem
As mensagem na figura foram enumeradas para detalhar o comportamento do algoritmo implementado no projeto de demonstração:
Neste segundo artigo da série sobre o FreeRTOS, foi apresentado o recurso de Fila (em inglês queue). Esse recurso é o principal mecanismo de troca de informação entre tarefas e processo (interrupções e funções de callback). No FreeRTOS, esse recurso além de permitir o compartilhamento de informação, funciona, também, como um instrumento para o sincronização de tarefas do sistema.
Após detalhar as principais funções para o manuseio de fila oferecida pelo o FreeRTOS, foi apresentado o projeto simples de demonstração. O projeto desenvolvido contém 03 tarefas, sendo que as tarefas A e B trocam mensagens entre si por meio de fila. A terceira tarefa é responsável pelo o acionamento do LED “on board”, que possui uma queue que recebe informação da função de interrupção que trata o evento do botão. Ao final, analisamos o LOG de mensagem impressa no barramento serial para o melhor entendimento do algoritmo.
Para os próximos artigos, serão apresentados mais recursos do FreeRTOS. O que você achou? Você já utiliza o FreeRTOS em seus projetos? Deixe o seu comentário abaixo.
FreeRTOS Queues
https://www.freertos.org/Embedded-RTOS-Queues.html
Queue Management
https://www.freertos.org/a00018.html
Princípios básicos do kernel do FreeRTOS
https://docs.aws.amazon.com/pt_br/freertos/latest/userguide/dev-guide-freertos-kernel.html
Tenha a Metodologia Eletrogate na sua Escola! Conheça nosso Programa de Robótica Educacional.
|
A Eletrogate é uma loja virtual de componentes eletrônicos do Brasil e possui diversos produtos relacionados à Arduino, Automação, Robótica e Eletrônica em geral.
Tenha a Metodologia Eletrogate dentro da sua Escola! Conheça nosso Programa de Robótica nas Escolas!