Message Queues no FreeRTOS: Transporte de Dados e Desacoplamento entre Tasks
As Message Queues (filas de mensagens) são o principal mecanismo do FreeRTOS para transferência segura de dados entre threads, permitindo desacoplar completamente quem produz a informação de quem consome. Diferentemente de semáforos e notificações, que sinalizam eventos, as filas transportam dados estruturados, preservando ordem e integridade.
Arquiteturalmente, uma fila implementa o padrão Produtor–Consumidor, sendo especialmente útil em sistemas onde sensores, ISRs ou protocolos geram dados em uma cadência diferente da lógica de processamento ou armazenamento.
Modelo Conceitual
Uma fila no FreeRTOS é definida por:
- Tamanho máximo (número de itens).
- Tamanho de cada item (em bytes).
- Política FIFO (First-In, First-Out).
Cada envio ou recebimento pode bloquear a task até que haja espaço ou dados disponíveis, eliminando polling e desperdício de CPU.
Criação de uma Fila
#include "FreeRTOS.h"
#include "queue.h"
typedef struct
{
uint16_t adc_value;
uint32_t timestamp;
} adc_sample_t;
QueueHandle_t xAdcQueue;
void vInit(void)
{
xAdcQueue = xQueueCreate(10, sizeof(adc_sample_t));
}
Task Produtora (ou ISR)
void vAdcProducerTask(void *pvParameters)
{
adc_sample_t sample;
for (;;)
{
sample.adc_value = read_adc();
sample.timestamp = get_time();
xQueueSend(xAdcQueue, &sample, portMAX_DELAY);
}
}
Também é possível enviar dados a partir de uma ISR usando xQueueSendFromISR().
Task Consumidora
void vProcessingTask(void *pvParameters)
{
adc_sample_t received;
for (;;)
{
if (xQueueReceive(xAdcQueue, &received, portMAX_DELAY) == pdTRUE)
{
process_sample(&received);
}
}
}
Quando usar Message Queues
Use filas quando:
- Dados precisam ser transferidos entre tasks.
- A ordem dos dados é importante.
- Há desacoplamento entre produção e consumo.
- Múltiplos produtores ou consumidores existem.
Evite filas quando:
- Apenas um evento simples precisa ser sinalizado.
- Comunicação é estritamente ponto-a-ponto e leve (use notifications).
- O dado pode ser acessado via buffer protegido por mutex.
Vantagens Técnicas
- Comunicação robusta e segura.
- Isolamento entre módulos.
- Facilita escalabilidade.
- Ideal para pipelines de processamento.
Impacto em Memória
Filas consomem RAM proporcional a:
RAM ≈ (tamanho_do_item × quantidade) + overhead
Em microcontroladores com RAM limitada, esse custo deve ser cuidadosamente planejado.
Armadilhas comuns
- Criar filas grandes “por segurança”.
- Enviar ponteiros para dados voláteis.
- Bloquear tarefas críticas em filas longas.
- Usar fila como mecanismo de sincronização simples.
Na próxima seção, entraremos nos Software Timers, explicando como executar funções temporizadas sem criar novas tasks e quando eles são preferíveis a vTaskDelay().