MCU & FPGA RTOS Padrões de Projeto Aplicados a RTOS (FreeRTOS)

Padrões de Projeto Aplicados a RTOS (FreeRTOS)

Padrões de Comunicação e Sincronização em RTOS

Em FreeRTOS, concorrência mal projetada é a principal fonte de bugs: deadlocks, starvation, jitter temporal e corrupção de dados. Os padrões desta seção existem para substituir variáveis globais, flags soltas e volatile mal utilizados por mecanismos determinísticos e auditáveis.

Aqui falamos de comunicação, não apenas de sincronização.


2.1 Event Queue (Fila de Eventos)

Problema resolvido:
Múltiplos produtores gerando eventos assíncronos para um único consumidor.

Ideia do padrão:
Eventos são encapsulados em estruturas e enviados por uma fila RTOS. O consumidor processa eventos em ordem temporal.

Quando usar:

  • Interface homem-máquina
  • Eventos de sensores
  • Drivers desacoplados da lógica de aplicação

Estrutura típica do evento:

typedef enum {
    EVT_BUTTON,
    EVT_SENSOR,
    EVT_TIMEOUT
} EventType;

typedef struct {
    EventType type;
    uint32_t  data;
} AppEvent;

Fila global (criada na inicialização):

QueueHandle_t EventQueue;

EventQueue = xQueueCreate(10, sizeof(AppEvent));

Produtor:

void ButtonISR(void)
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;

    AppEvent evt = {
        .type = EVT_BUTTON,
        .data = 1
    };

    xQueueSendFromISR(EventQueue, &evt, &xHigherPriorityTaskWoken);
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

Consumidor:

void EventTask(void *pvParameters)
{
    AppEvent evt;

    for (;;)
    {
        if (xQueueReceive(EventQueue, &evt, portMAX_DELAY))
        {
            switch (evt.type)
            {
                case EVT_BUTTON:
                    HandleButton(evt.data);
                    break;

                case EVT_SENSOR:
                    HandleSensor(evt.data);
                    break;

                default:
                    break;
            }
        }
    }
}

Vantagens:

  • Desacoplamento total
  • Ordem garantida
  • Fácil instrumentação

Erro comum:
Usar fila para stream contínuo de dados (para isso existem buffers).


2.2 Mailbox (Fila de Mensagem Única)

Problema resolvido:
Apenas o último valor importa, não o histórico.

Ideia do padrão:
Uma fila de tamanho 1 funciona como mailbox sobrescrevível.

QueueHandle_t Mailbox;

Mailbox = xQueueCreate(1, sizeof(uint32_t));

Produtor:

uint32_t temperature = ReadTemperature();
xQueueOverwrite(Mailbox, &temperature);

Consumidor:

uint32_t temp;

if (xQueuePeek(Mailbox, &temp, 0))
{
    UseTemperature(temp);
}

Quando usar:

  • Telemetria
  • Estados globais observáveis
  • Última leitura válida

Vantagem-chave:
Evita backlog artificial.


2.3 Publish–Subscribe (Pub-Sub) com FreeRTOS

Problema resolvido:
Múltiplos consumidores interessados no mesmo evento.

Ideia do padrão:
Uma tarefa “dispatcher” recebe eventos e os redistribui para múltiplas filas.

Arquitetura:

ISR / Producers

  Event Queue

  Dispatcher Task
   ↓     ↓     ↓
 Task A Task B Task C

Dispatcher:

void DispatcherTask(void *pvParameters)
{
    AppEvent evt;

    for (;;)
    {
        if (xQueueReceive(EventQueue, &evt, portMAX_DELAY))
        {
            xQueueSend(QueueA, &evt, 0);
            xQueueSend(QueueB, &evt, 0);
        }
    }
}

Vantagens:

  • Extensível
  • Isola produtores de consumidores
  • Facilita testes

Custo:
Mais RAM e latência — aceitável em troca de clareza.


2.4 Task Notification como Padrão de Sinalização Rápida

Problema resolvido:
Overhead de filas quando apenas sinalização simples é necessária.

Ideia do padrão:
Usar Task Notifications como semáforos ultraleves.

Produtor:

xTaskNotify(TaskHandle, 0x01, eSetBits);

Consumidor:

uint32_t flags;

xTaskNotifyWait(0, 0xFFFFFFFF, &flags, portMAX_DELAY);

if (flags & 0x01)
{
    ProcessEvent();
}

Vantagens:

  • Zero alocação dinâmica
  • Extremamente rápido
  • Ideal para ISR → Task

Limitação:
Apenas 32 bits por tarefa → não serve para dados complexos.


2.5 Stream Buffer e Message Buffer

Problema resolvido:
Troca eficiente de fluxos contínuos de dados.

Stream Buffer

  • Dados binários contínuos
  • Sem fronteira de mensagem
StreamBufferHandle_t sb;
sb = xStreamBufferCreate(128, 1);

Message Buffer

  • Mensagens delimitadas
  • Ideal para pacotes
MessageBufferHandle_t mb;
mb = xMessageBufferCreate(128);

Uso típico:

  • UART
  • SPI
  • Áudio
  • Protocolos binários

Boa prática:
Preferir buffers a filas para dados “em volume”.

0 0 votos
Classificação do artigo
Inscrever-se
Notificar de
guest
0 Comentários
mais antigos
mais recentes Mais votado
Feedbacks embutidos
Ver todos os comentários

Related Post

Debug, Tracing e Análise Temporal no FreeRTOS: Monitoramento Avançado de Tasks, Watermark e Confiabilidade em Tempo RealDebug, Tracing e Análise Temporal no FreeRTOS: Monitoramento Avançado de Tasks, Watermark e Confiabilidade em Tempo Real

Aprenda como aplicar debug estruturado, tracing, análise temporal e watermark no FreeRTOS para monitorar tasks, medir WCET, detectar jitter, prevenir stack overflow e aumentar a confiabilidade de sistemas embarcados em

0
Adoraria saber sua opinião, comente.x