Introdução aos Sistemas Operacionais de Tempo Real (RTOS)

Um RTOS (Real-Time Operating System) é um sistema operacional projetado para garantir que tarefas sejam executadas dentro de prazos definidos. Ele é amplamente utilizado em sistemas embarcados, automação industrial, dispositivos médicos e aplicações críticas como controle de robôs e veículos autônomos.

Entre os RTOS mais utilizados estão:

Exemplo de tarefa simples com FreeRTOS
void vTarefaExemplo(void *pvParameters) {
    for(;;) {
        printf("Executando tarefa!\n");
        vTaskDelay(pdMS_TO_TICKS(1000)); // espera 1 segundo
    }
}

Ao contrário de sistemas tradicionais como GNU/Linux, um RTOS prioriza previsibilidade e baixa latência em vez de throughput. O foco é garantir que cada tarefa crítica seja concluída dentro do seu deadline.

Características de um Sistema em Tempo Real

Um sistema operacional de tempo real (RTOS) se diferencia por sua capacidade de responder a eventos externos dentro de prazos específicos. A previsibilidade é mais importante do que o desempenho bruto.

🎯 Características principais

Exemplo: delay determinístico vs não determinístico

// Delay em um RTOS (preciso)
vTaskDelay(pdMS_TO_TICKS(500)); // 500 ms com precisão

// Delay em um sistema convencional (pode variar)
sleep(0.5); // não garantido em sistemas multitarefa

💡 Exemplo prático: sensor com deadline

Imagine um sistema que lê a temperatura de um motor a cada 100 ms. Se o RTOS atrasar essa leitura, pode causar superaquecimento ou falhas no controle. O sistema precisa garantir que:

Esse controle refinado só é possível com o uso de um sistema operacional de tempo real. A previsibilidade é o elemento chave que torna o RTOS essencial para aplicações críticas.

Tipos de RTOS: Hard vs Soft Real-Time

Nem todos os sistemas de tempo real possuem os mesmos requisitos de precisão. Os RTOS podem ser classificados em duas grandes categorias, com base na severidade de seus prazos (deadlines):

🟥 Hard Real-Time (Tempo Real Rígido)

Hard RTOS – exemplo de deadline crítico
void vTarefaAirbag(void *pvParameters) {
  for (;;) {
    if (sensorColisaoDetectado()) {
      acionarAirbag();
    }
    vTaskDelay(pdMS_TO_TICKS(10)); // leitura a cada 10 ms
  }
}

🟡 Soft Real-Time (Tempo Real Flexível)

Soft RTOS – exemplo tolerante a atrasos
void vTarefaDisplayGPS(void *pvParameters) {
  for (;;) {
    atualizarTelaComLocalizacao();
    vTaskDelay(pdMS_TO_TICKS(500)); // atraso pode ser tolerado
  }
}

📌 Observação importante:

Em um Hard RTOS, o sistema é projetado para garantir que tarefas críticas nunca percam o prazo, mesmo que isso exija sacrificar tarefas menos prioritárias. Já um Soft RTOS busca melhor desempenho geral com menor prioridade aos prazos rígidos.

Escalonamento em Sistemas em Tempo Real

O escalonamento é o mecanismo que determina qual tarefa será executada a cada instante. Em RTOS, o escalonador é projetado para garantir que tarefas críticas sejam executadas dentro de seus prazos.

📌 Conceitos-chave

📋 Algoritmos comuns

1. Rate Monotonic Scheduling (RMS)

Exemplo RMS em pseudocódigo
// Tarefa com período de 100ms = prioridade mais alta
xTaskCreate(sensorAtualiza, "Sensor", 1000, NULL, 3, NULL);
xTaskCreate(enviaDados, "Transmissão", 1000, NULL, 2, NULL);
xTaskCreate(logMemoria, "Log", 1000, NULL, 1, NULL);

2. Earliest Deadline First (EDF)

Ilustração de escalonamento EDF
Tempo 0ms: Tarefa A (deadline 100ms) ativa
Tempo 10ms: Tarefa B (deadline 50ms) chega → preempção!
Resultado: B executa antes de A pois seu prazo é menor

📌 Escalonamento no FreeRTOS

O FreeRTOS utiliza um escalonador baseado em prioridade fixa, com suporte a preempção:

vTaskStartScheduler(); // Inicia o escalonamento
// Tarefas são selecionadas com base em prioridade numérica (maior = mais prioridade)

A escolha do algoritmo de escalonamento depende do tipo de aplicação. Sistemas críticos normalmente usam RMS por sua previsibilidade. Já sistemas mais flexíveis podem usar EDF para melhor aproveitamento da CPU.

Ilustração Comparativa: RMS vs EDF

O gráfico abaixo representa três tarefas (A, B e C) com diferentes períodos e deadlines.

RMS: EDF: A B A C B A C A Tarefa A (período 100ms) Tarefa B (período 200ms) Tarefa C (período 300ms)

Tarefas, Prioridades e Preempção

Em um RTOS, o sistema é composto por várias tarefas que competem por tempo de CPU. Cada tarefa pode ser atribuída a uma prioridade e, com preempção habilitada, o RTOS pode interromper uma tarefa em execução para iniciar outra mais urgente.

📌 Tarefa (Task)

Criando uma tarefa no FreeRTOS
void vTarefaExemplo(void *pvParameters) {
  for (;;) {
    printf("Executando tarefa\n");
    vTaskDelay(pdMS_TO_TICKS(1000)); // espera 1 segundo
  }
}

xTaskCreate(vTarefaExemplo, "Exemplo", 1000, NULL, 1, NULL);

📈 Prioridades

xTaskCreate(tarefaAlta, "Alta", 1000, NULL, 3, NULL);
xTaskCreate(tarefaMedia, "Media", 1000, NULL, 2, NULL);
xTaskCreate(tarefaBaixa, "Baixa", 1000, NULL, 1, NULL);

⚡ Preempção

Simulação: preempção entre tarefas
Tempo 0 ms → Tarefa Baixa executa
Tempo 50 ms → Tarefa Alta torna-se pronta
Resultado: Tarefa Alta preempte imediatamente a Tarefa Baixa

🧠 Boa prática

Evite usar a mesma prioridade para todas as tarefas. Isso pode gerar comportamento imprevisível ou injusto. Além disso, use vTaskDelay() ou vTaskDelayUntil() para liberar a CPU e evitar ocupação contínua.

Sincronização e Comunicação entre Tarefas

Em um sistema de tempo real com múltiplas tarefas, é essencial coordenar o acesso a recursos compartilhados e permitir a troca de informações entre elas. O RTOS fornece primitivas específicas para isso, como semaphores, mutexes e queues.

🔒 Semáforos

Semáforo binário serve para sinalização entre tarefas (ex: uma tarefa sinaliza que um dado está pronto).

Exemplo: sincronização com semáforo
SemaphoreHandle_t xSinal;

// Criação
xSinal = xSemaphoreCreateBinary();

// Tarefa A (produtora)
xSemaphoreGive(xSinal);  // envia o sinal

// Tarefa B (consumidora)
xSemaphoreTake(xSinal, portMAX_DELAY); // espera o sinal

🔁 Mutex (Mutual Exclusion)

Usado para proteger acesso a recursos compartilhados (ex: variável global, periférico). Permite apenas uma tarefa por vez.

SemaphoreHandle_t xMutex;

xMutex = xSemaphoreCreateMutex();

xSemaphoreTake(xMutex, portMAX_DELAY);
  // Seção crítica protegida
xSemaphoreGive(xMutex);

📬 Filas (Queues)

Permitem comunicação entre tarefas enviando dados (inteiros, structs, etc.) com segurança e controle de sincronização.

Exemplo: fila de inteiros entre tarefas
QueueHandle_t xFila;

// Criar fila para 10 inteiros
xFila = xQueueCreate(10, sizeof(int));

// Tarefa A
int dado = 42;
xQueueSend(xFila, &dado, portMAX_DELAY);

// Tarefa B
int recebido;
xQueueReceive(xFila, &recebido, portMAX_DELAY);

⚠️ Problemas comuns

O uso correto dessas ferramentas garante um sistema confiável e eficiente. O FreeRTOS também oferece versões com herança de prioridade para prevenir inversões.

Exemplos Práticos com FreeRTOS

A seguir, veja exemplos reais do uso do FreeRTOS em sistemas embarcados. Os códigos estão em linguagem C, e funcionam em plataformas como ESP32, STM32 ou simuladores de RTOS.

📊 Exemplo 1: Leitura de sensor periódica

Tarefa que lê um sensor a cada 500 ms e envia pela UART.

void vSensorTask(void *pvParameters) {
  for (;;) {
    int valor = lerSensor(); // função fictícia
    printf("Sensor: %d\n", valor);
    vTaskDelay(pdMS_TO_TICKS(500));
  }
}

xTaskCreate(vSensorTask, "Sensor", 1000, NULL, 1, NULL);

🔀 Exemplo 2: Produtor e consumidor com fila

Uma tarefa gera dados e outra os processa usando queue.

QueueHandle_t xFila;

void vProdutor(void *pvParameters) {
  int dado = 0;
  for (;;) {
    xQueueSend(xFila, &dado, portMAX_DELAY);
    dado++;
    vTaskDelay(pdMS_TO_TICKS(200));
  }
}

void vConsumidor(void *pvParameters) {
  int valor;
  for (;;) {
    xQueueReceive(xFila, &valor, portMAX_DELAY);
    printf("Recebido: %d\n", valor);
  }
}

// Criação
xFila = xQueueCreate(5, sizeof(int));
xTaskCreate(vProdutor, "Produtor", 1000, NULL, 2, NULL);
xTaskCreate(vConsumidor, "Consumidor", 1000, NULL, 1, NULL);

🔐 Exemplo 3: Controle de acesso com mutex

Protegendo o uso do display entre duas tarefas.

SemaphoreHandle_t xDisplayMutex;

void vTarefa1(void *pvParameters) {
  for (;;) {
    xSemaphoreTake(xDisplayMutex, portMAX_DELAY);
    printf("Tarefa 1 exibindo\n");
    xSemaphoreGive(xDisplayMutex);
    vTaskDelay(pdMS_TO_TICKS(500));
  }
}

void vTarefa2(void *pvParameters) {
  for (;;) {
    xSemaphoreTake(xDisplayMutex, portMAX_DELAY);
    printf("Tarefa 2 exibindo\n");
    xSemaphoreGive(xDisplayMutex);
    vTaskDelay(pdMS_TO_TICKS(700));
  }
}

xDisplayMutex = xSemaphoreCreateMutex();
xTaskCreate(vTarefa1, "T1", 1000, NULL, 2, NULL);
xTaskCreate(vTarefa2, "T2", 1000, NULL, 2, NULL);

Esses exemplos demonstram o uso típico de tarefas concorrentes, comunicação com filas e exclusão mútua em sistemas embarcados com RTOS.

RTOS vs Sistemas Operacionais Convencionais

Embora ambos gerenciem tarefas, recursos e periféricos, um RTOS é otimizado para previsibilidade e precisão temporal, enquanto um Sistema Operacional Convencional (como Linux ou Windows) foca em desempenho geral, multitarefa e experiência do usuário.

📊 Comparativo lado a lado

Critério RTOS SO Convencional
Foco Tempo real, precisão, controle Desempenho geral, usabilidade
Tempo de resposta Determinístico e previsível Indeterminado, pode variar
Uso de recursos Mínimo, ideal para microcontroladores Elevado, requer processadores potentes
Gerenciamento de tarefas Com prioridade fixa ou EDF Multitarefa com foco em justiça e interatividade
Interface Geralmente sem interface gráfica GUI, suporte multimídia, drivers complexos
Exemplos FreeRTOS, VxWorks, Zephyr GNU/Linux, Windows, macOS

💡 Quando usar cada um?

Em muitos projetos modernos, ambos coexistem: um microcontrolador com RTOS lida com tarefas em tempo real e se comunica com um sistema maior baseado em Linux.

Quiz Interativo

Teste seus conhecimentos sobre GNU/Linux:

FreeRTOS: Visão Geral e Primeiros Passos

O FreeRTOS é um dos sistemas operacionais de tempo real mais utilizados no mundo. Ele é leve, modular, de código aberto e altamente portátil, funcionando em microcontroladores de 8, 16 e 32 bits. É mantido pela AWS (Amazon Web Services) e tem uma comunidade ativa de desenvolvedores.

✅ Características principais

  • Kernel leve (menos de 10 KB)
  • Suporte a tarefas com prioridades e preempção
  • Semáforos, mutexes, filas, timers e notificações
  • Portável para dezenas de arquiteturas (ARM, RISC-V, AVR, etc.)
  • Licença MIT — livre para uso comercial

⬇️ Onde baixar e documentação oficial

⚙️ Como instalar e começar

Via PlatformIO (ESP32/STM32/Arduino):

[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200
build_flags = -D FREERTOS

; FreeRTOS já está incluído no core ESP32/STM32

Manual (via CMSIS ou port próprio):

  • Clone o repositório
  • Inclua os diretórios FreeRTOS/Source e portable no seu projeto
  • Configure FreeRTOSConfig.h
  • Chame vTaskStartScheduler() no main()

🧪 Teste prático: tarefa com e sem RTOS

Este exemplo compara um código com delay bloqueante tradicional com outro usando vTaskDelay() para mostrar o comportamento multitarefa do RTOS.

Sem RTOS: delay bloqueia tudo
void loop() {
  digitalWrite(LED_BUILTIN, HIGH);
  delay(1000); // tudo bloqueia aqui
  digitalWrite(LED_BUILTIN, LOW);
  delay(1000);
}
Com FreeRTOS: múltiplas tarefas em paralelo
void vPiscaLED(void *pvParameters) {
  for (;;) {
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
    vTaskDelay(pdMS_TO_TICKS(1000));
  }
}

void vSerialTask(void *pvParameters) {
  for (;;) {
    Serial.println("Sistema rodando...");
    vTaskDelay(pdMS_TO_TICKS(500));
  }
}

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(115200);

  xTaskCreate(vPiscaLED, "LED", 1000, NULL, 1, NULL);
  xTaskCreate(vSerialTask, "Serial", 1000, NULL, 1, NULL);
  vTaskStartScheduler();
}

Nesse teste, vemos que mesmo com o LED piscando, a mensagem "Sistema rodando..." aparece no meio do intervalo — algo que seria impossível com delay(). Esse comportamento é um dos grandes diferenciais do uso de um RTOS.

Zephyr RTOS: Visão Geral e Primeiros Passos

O Zephyr RTOS é um sistema operacional de tempo real moderno, mantido pela Linux Foundation, focado em aplicações embarcadas e IoT. Ele não é um sistema que você "instala" em seu computador como Linux ou Windows, mas sim um framework embarcado que você compila junto com sua aplicação e grava em microcontroladores.

✅ Características principais

  • Kernel leve com suporte a multitarefa preemptiva
  • Arquitetura modular baseada em Kconfig + CMake
  • Suporte a ARM, RISC-V, x86, ARC, Xtensa e outros
  • Integração com Bluetooth, Wi-Fi, USB, TCP/IP, sensores e mais
  • Licença Apache 2.0 — livre para uso comercial

⬇️ Onde baixar e documentação oficial

⚙️ Como instalar o Zephyr no Linux

O Zephyr é instalado como um conjunto de bibliotecas e ferramentas para serem usadas com placas embarcadas ou em simulação.

1. Instale os pré-requisitos:

sudo apt update
sudo apt install git cmake ninja-build gperf python3-pip \
  python3-setuptools python3-wheel xz-utils file make gcc g++ \
  device-tree-compiler dfu-util ccache

2. Instale o West (gerenciador de projetos do Zephyr):

pip3 install west

3. Baixe o Zephyr e inicialize:

mkdir zephyrproject && cd zephyrproject
west init
west update
west zephyr-export

4. Instale as dependências Python:

pip3 install -r zephyr/scripts/requirements.txt

5. (Opcional) Baixe o Zephyr SDK completo:

Disponível em: Zephyr SDK Releases

💻 Executando um exemplo sem hardware (QEMU)

Se você não tiver uma placa física, pode simular com qemu_x86:

# Compilar o exemplo Hello World
west build -b qemu_x86 zephyr/samples/hello_world

# Executar no simulador
west run

Você verá no terminal:

Hello World! zephyr on qemu_x86

🚀 Primeiro exemplo prático: piscar LED

#include 
#include 
#include 

#define LED0_NODE DT_ALIAS(led0)
#define LED0    DT_GPIO_LABEL(LED0_NODE, gpios)
#define PIN     DT_GPIO_PIN(LED0_NODE, gpios)
#define FLAGS   DT_GPIO_FLAGS(LED0_NODE, gpios)

void main(void) {
  const struct device *dev = device_get_binding(LED0);
  gpio_pin_configure(dev, PIN, GPIO_OUTPUT_ACTIVE | FLAGS);

  while (1) {
    gpio_pin_toggle(dev, PIN);
    k_msleep(1000);
  }
}

🔧 Compilar e gravar (caso tenha uma placa)

west build -b nucleo_f401re zephyr/samples/basic/blinky
west flash

🧪 Multitarefa com Zephyr: criando threads

void tarefa1(void) {
  while (1) {
    printk("Tarefa 1 executando\n");
    k_sleep(K_MSEC(500));
  }
}

void tarefa2(void) {
  while (1) {
    printk("Tarefa 2 executando\n");
    k_sleep(K_MSEC(1000));
  }
}

K_THREAD_DEFINE(t1_id, 512, tarefa1, NULL, NULL, NULL, 1, 0, 0);
K_THREAD_DEFINE(t2_id, 512, tarefa2, NULL, NULL, NULL, 2, 0, 0);

O escalonador do Zephyr intercalará essas tarefas com base em suas prioridades e tempo de espera, demonstrando multitarefa real com precisão temporal.