Explorando o FreeRTOS no ESP32: O que é e como funciona


Introdução🔗

O ESP32 é um microcontrolador poderoso e versátil, amplamente utilizado em projetos de automação e Internet das Coisas (IoT). Uma das características que o destacam é a integração com o FreeRTOS, um sistema operacional de tempo real (RTOS) que permite o gerenciamento eficiente de múltiplas tarefas. Neste artigo, vamos explorar em profundidade o FreeRTOS no ESP32, entendendo o que é, como funciona e como aproveitar seus recursos em projetos práticos.

A tabletop view of hands discussing open source software, featuring a clipboard with the text 'OPEN SOURCE SOFTWARE' and various graphs and documents.

O que é o FreeRTOS?🔗

O FreeRTOS é um sistema operacional de código aberto, projetado para microcontroladores e microprocessadores de pequeno porte. Ele fornece um ambiente multitarefa, permitindo que diferentes partes de um programa sejam executadas como tarefas independentes, gerenciadas de forma eficiente e determinística.

Importância em Sistemas Embarcados🔗

Em sistemas embarcados, como os baseados no ESP32, o gerenciamento de recursos é crucial. O FreeRTOS auxilia no controle preciso do tempo de execução, permitindo que aplicações respondam a eventos em tempo real. Isso é especialmente importante em aplicações críticas, onde atrasos não são toleráveis.

Por que usar o FreeRTOS no ESP32?🔗

Utilizar o FreeRTOS no ESP32 traz diversas vantagens:

  • Multitarefa Eficiente: Permite executar várias tarefas simultaneamente, aproveitando os dois núcleos do ESP32.
  • Controle de Prioridades: Tarefas podem ser priorizadas, garantindo que as mais importantes tenham preferência na execução.
  • Sincronização e Comunicação: Fornece mecanismos como semáforos, filas e mutexes para sincronizar tarefas e compartilhar recursos.
  • Determinismo: Garante que eventos críticos sejam tratados dentro de um período previsível.

Conceitos Básicos de Sistemas Operacionais em Tempo Real (RTOS)🔗

Antes de mergulharmos no FreeRTOS, é fundamental compreender alguns conceitos-chave.

Multitarefa🔗

A multitarefa permite que um sistema execute várias tarefas "ao mesmo tempo". Em microcontroladores, isso é alcançado por meio de um escalonador que alterna rapidamente entre as tarefas, dando a impressão de simultaneidade.

Escalonamento de Tarefas🔗

O escalonador é responsável por decidir qual tarefa será executada em determinado momento, baseado em prioridades e estados das tarefas (pronta, em execução, bloqueada).

Priorização🔗

Cada tarefa recebe uma prioridade. Tarefas com prioridades mais altas são selecionadas pelo escalonador antes das de prioridade inferior.

Sincronização🔗

Mecanismos para coordenar a execução de tarefas, garantindo que acessos a recursos compartilhados sejam seguros e que as tarefas possam se comunicar de forma eficiente.

Arquitetura do FreeRTOS no ESP32🔗

O FreeRTOS está integrado ao ESP-IDF (Espressif IoT Development Framework), o ambiente de desenvolvimento oficial do ESP32. Isso permite um acesso simplificado aos recursos do RTOS e uma interação direta com o hardware.

Dual Core do ESP32 e o FreeRTOS🔗

O ESP32 possui dois núcleos de processamento: PRO_CPU e APP_CPU. O FreeRTOS pode gerenciar tarefas em ambos os núcleos, permitindo um paralelismo real.

Integração com o Hardware🔗

O FreeRTOS no ESP32 é otimizado para interagir com os periféricos do microcontrolador, como GPIOs, interfaces de comunicação e outros recursos, facilitando o desenvolvimento de aplicações complexas.

Elementos do FreeRTOS🔗

Vamos detalhar os principais componentes que o FreeRTOS oferece:

Tarefas (Tasks)🔗

São funções independentes que o FreeRTOS gerencia. Cada tarefa possui seu próprio contexto de execução, incluindo registradores e pilha.

Criando Tarefas

A criação de uma tarefa é feita usando a função xTaskCreate().

void MinhaTarefa(void *pvParameter)
{
    while(1)
    {
        // Código da tarefa
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}
void app_main()
{
    xTaskCreate(
        &MinhaTarefa,      // Função da tarefa
        "MinhaTarefa",     // Nome da tarefa
        2048,              // Tamanho da pilha
        NULL,              // Parâmetro passado para a tarefa
        5,                 // Prioridade da tarefa
        NULL               // Handle da tarefa
    );
}

Filas (Queues)🔗

Permitem a troca de informações entre tarefas de forma segura, através de um buffer FIFO (First In, First Out).

Exemplo de Uso de Filas

QueueHandle_t xFila;
void TarefaProdutora(void *pvParameter)
{
    int contador = 0;
    while(1)
    {
        contador++;
        xQueueSend(
            xFila,
            (void *)&contador,
            portMAX_DELAY
        );
        vTaskDelay(500 / portTICK_PERIOD_MS);
    }
}
void TarefaConsumidora(void *pvParameter)
{
    int recebido;
    while(1)
    {
        if(xQueueReceive(
            xFila,
            &(recebido),
            portMAX_DELAY
        ))
        {
            printf("Valor recebido: %d\n", recebido);
        }
    }
}
void app_main()
{
    xFila = xQueueCreate(
        10,              // Tamanho da fila
        sizeof(int)      // Tamanho do item
    );
    xTaskCreate(&TarefaProdutora, "Produtora", 2048, NULL, 5, NULL);
    xTaskCreate(&TarefaConsumidora, "Consumidora", 2048, NULL, 5, NULL);
}

Semáforos🔗

Semáforos são usados para controlar o acesso a recursos compartilhados, evitando condições de corrida.

Exemplo de Uso de Semáforos

SemaphoreHandle_t xSemaforo;
void TarefaA(void *pvParameter)
{
    while(1)
    {
        if(xSemaphoreTake(xSemaforo, portMAX_DELAY))
        {
            // Seção crítica
            printf("Tarefa A está acessando o recurso compartilhado.\n");
            xSemaphoreGive(xSemaforo);
            vTaskDelay(1000 / portTICK_PERIOD_MS);
        }
    }
}
void TarefaB(void *pvParameter)
{
    while(1)
    {
        if(xSemaphoreTake(xSemaforo, portMAX_DELAY))
        {
            // Seção crítica
            printf("Tarefa B está acessando o recurso compartilhado.\n");
            xSemaphoreGive(xSemaforo);
            vTaskDelay(2000 / portTICK_PERIOD_MS);
        }
    }
}
void app_main()
{
    xSemaforo = xSemaphoreCreateMutex();
    xTaskCreate(&TarefaA, "TarefaA", 2048, NULL, 5, NULL);
    xTaskCreate(&TarefaB, "TarefaB", 2048, NULL, 5, NULL);
}

Mutexes🔗

Semelhantes aos semáforos, mas especificamente projetados para garantir a exclusão mútua de recursos.

Criando um Projeto Básico com FreeRTOS no ESP32🔗

Vamos desenvolver um projeto simples para consolidar os conceitos aprendidos.

Configurando o Ambiente de Desenvolvimento🔗

Para programar o ESP32 com FreeRTOS, você pode utilizar o ESP-IDF. Certifique-se de que ele está instalado e configurado em sua máquina.

Exemplo Prático: Criando e Executando Tarefas🔗

Neste exemplo, vamos criar duas tarefas que exibem mensagens no console em intervalos diferentes.

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
void Tarefa1(void *pvParameter)
{
    while(1)
    {
        printf("Executando Tarefa 1\n");
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}
void Tarefa2(void *pvParameter)
{
    while(1)
    {
        printf("Executando Tarefa 2\n");
        vTaskDelay(2000 / portTICK_PERIOD_MS);
    }
}
void app_main()
{
    xTaskCreate(
        &Tarefa1,
        "Tarefa1",
        2048,
        NULL,
        5,
        NULL
    );
    xTaskCreate(
        &Tarefa2,
        "Tarefa2",
        2048,
        NULL,
        5,
        NULL
    );
}
Descrição: As duas tarefas serão executadas em paralelo, exibindo mensagens no console em tempos diferentes. Representação gráfica das tarefas sendo executadas em paralelo

Entendendo o Código🔗

  • vTaskDelay(): Suspende a tarefa por um período de tempo, permitindo que outras tarefas sejam executadas.
  • portTICK_PERIOD_MS: Constante que converte milissegundos para ticks do sistema.

Exemplo Prático: Sincronização entre Tarefas🔗

Vamos criar um exemplo onde uma tarefa aguarda por um evento sinalizado por outra tarefa.

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
QueueHandle_t xFilaEventos;
void TarefaSensor(void *pvParameter)
{
    int evento = 0;
    while(1)
    {
        // Simula a leitura de um sensor
        evento++;
        printf("Sensor detectou evento %d\n", evento);
        xQueueSend(xFilaEventos, &evento, portMAX_DELAY);
        vTaskDelay(3000 / portTICK_PERIOD_MS);
    }
}
void TarefaProcessamento(void *pvParameter)
{
    int eventoRecebido;
    while(1)
    {
        if(xQueueReceive(xFilaEventos, &eventoRecebido, portMAX_DELAY))
        {
            // Processa o evento recebido
            printf("Processando evento %d\n", eventoRecebido);
        }
    }
}
void app_main()
{
    xFilaEventos = xQueueCreate(5, sizeof(int));
    xTaskCreate(
        &TarefaSensor,
        "TarefaSensor",
        2048,
        NULL,
        5,
        NULL
    );
    xTaskCreate(
        &TarefaProcessamento,
        "TarefaProcessamento",
        2048,
        NULL,
        5,
        NULL
    );
}
Descrição: A TarefaSensor simula a leitura de um sensor e envia eventos para a TarefaProcessamento através de uma fila.

Conclusão🔗

O FreeRTOS amplia significativamente as capacidades do ESP32, permitindo a criação de aplicações complexas e responsivas. Com o entendimento dos conceitos de multitarefa, sincronização e comunicação entre tarefas, você está preparado para desenvolver projetos que exigem operações em tempo real e gerenciamento eficiente de recursos.


Este artigo faz parte do grupo Introdução ao ESP32: O que é e como funciona
Autor: Marcelo V. Souza - Engenheiro de Sistemas e Entusiasta em IoT e Desenvolvimento de Software, com foco em inovação tecnológica.

Referências🔗

Artigos Relacionados