ESP32

Software

Introdução

ESP32 é um sistema em um chip que integra os seguintes atributos:

    -> Wi-fi(2.4GHz)
    -> Bluetooth 4.2
    -> Dois cores de alta performance
    -> Coprocessador de baixo consumo de energia
    -> Diversos periféricos

    O firmware padrão é o FreeRTOS, que é um software gratuito que opera em tempo real (Real Time Operating System - RTOS). Entretanto, existem outros sistemas que podem ser implementados no ESP32, tais como MicroPython, Espruino, Mongoose-os, entre outros. No FreeRTOS, as threads são chamadas de tarefas.

RTOS

    Um Real Time Operating System (sistema operacional de tempo real) é um sistema operacional projetado para executar multitarefas onde cada evento tem um padrão de tempo pré-definido. Como cada core do processador executa apenas uma tarefa de cada vez, é necessário que o escalonador tenha um padrão previsível.

FreeRTOS

    FreeRTOS é uma classe de RTOS que é projetado para ser leve o suficiente para rodar em um microcontrolador, tal como o ESP32.

Adaptações para o ESP32

    O FreeRTOS é projetado para rodar em um único core do processador. Porém o ESP32 é dual core, contendo uma CPU de protocolo (conhecida como CPU 0 ou PRO_CPU) e uma CPU de aplicação (referida como CPU 1 ou APP_CPU). Na prática, ambos são idênticos e compartilham a mesma memória. O ESP-IDF FreeRTOS é uma versão adaptada do vanilla FreeRTOS que suporta symetric multiprocessing (SMD).

Tarefas

    No sistema FreeRTOS, o usuário pode criar uma tarefa com a função xTaskCreate, e decidir o nível de prioridade da tarefa criada com o parâmetro uxPriority. Dessa forma, o escalonador decide qual tarefa irá usar o processador através de uma fila de prioridades, na qual as tarefas são inseridas. Exemplo de criação da tarefa:

// Tarefa a ser criada.
//A função vTaskCode tem uma série de parâmetros. Para mais informações acesse a
//documentação relacionada ao FreeRTOS.
void vTaskCode( [...], uxPriority, [..] ){
   for( ;; )
   {
    // Código da tarefa vem aqui.
   }
}

// Função que cria a tarefa.
void vOtherFunction( void ){
  static uint8_t ucParameterToPass;
  TaskHandle_t xHandle = NULL;

  // Cria uma tarefa, armazenando o identificador. Note que o parâmetro passado
  //ucParameterToPass deve existir pelo tempo de vida da tarefa, Então nesse caso é
  //declarado estático.
  xTaskCreate( vTaskCode, "NAME", STACK_SIZE, &ucParameterToPass, tskIDLE_PRIORITY, &xHandle);
  configASSERT( xHandle );

  // Use o identificador para excluir a tarefa.
   if( xHandle != NULL ){
    vTaskDelete( xHandle );
   }
}

Escalonamento

    O FreeRTOS implementa o escalonamento através da função vTaskSwitchContext(). Essa função é responsável por selecionar a tarefa de maior prioridade para rodar de uma lista conhecida como Ready task list (lista de tarefas prontas). No ESP-IDF FreeRTOS, cada núcleo vai chamar vTaskSwitchContext() independentemente para rodar da lista, que é compartilhada entre os dois núcleos.

Escalonamento Round Robin

    Quando há uma série de tarefas no estado “pronto”, e todas com a mesma prioridade, o sistema implementa o escalonamento Round Robin. Entretanto, o ESP-IDF FreeRTOS pode pular tarefas quando o Round Robin escalonar múltiplas tarefas prontas. Isso ocorre porque o Vanilla FreeRTOS é adaptado ao ESP32 para rodar em dois cores. A imagem abaixo ilustra a lista de tarefas prontas do ESP-IDF FreeRTOS:


    Quando o core PRO_CPU chama o escalonador, ele considera apenas as tarefas azuis e roxas. Da mesma forma, quando o core APP_CPU chama o escalonador, ele considera apenas as tarefas laranjas e roxas.
    Uma solução para o problema de ignorar tarefas é garantir que todas as tarefas entrem em um estado bloqueado para que sejam removidas da lista de tarefas prontas. Outra solução é distribuir tarefas entre várias prioridades, de modo que uma determinada prioridade não seja atribuída a várias tarefas que são fixadas em diferentes núcleos.

Alocação de memória

     Aplicações ESP-IDF utilizam os padrões comuns de arquitetura de computadores de pilha (memória dinâmica alocada pelo fluxo de controle do programa) e heap (memória dinâmica alocada por chamadas de funções), bem como memória alocada estaticamente (alocada em tempo de compilação).
     Por o ESP-IDF ser um ambiente RTOS de multi-thread, cada tarefa RTOS tem sua própria pilha. Por padrão, cada uma dessas pilhas são alocadas da heap quando a tarefa é criada.
     Um alocador de memória baseado na capacidade da memória permite apps fazerem alocações de heaps para diferentes propósitos. Para a maioria dos casos, é possível utilizar as funções malloc() e free() sem nenhuma consideração especial.

Capacidades de memória

O ESP32 contém diferentes tipos de memórias:
     -> DRAM (Data RAM): usada para manter dados. Esse é o tipo de memória mais comum acessado pela heap.
   -> IRAM (Instruction RAM): usualmente mantém apenas dados executáveis. Se acessada como memória genérica, todo o acesso deve ser alinhado a 32-bit.
     -> D/IRAM: pode ser acessada tanto para dados, quanto para instruções.

DRAM

    Ao iniciar, a heap DRAM contém toda a memória que não é alocada estaticamente pelo aplicativo. A redução de buffers alocados estaticamente aumentará a quantidade de heap livre disponível.

IRAM

    Ao iniciar, a heap IRAM contém todas as instruções de memórias que não são utilizadas pelo código executável do aplicativo.

D/IRAM

    Algumas memórias do ESP32 estão disponíveis como DRAM ou IRAM. Se a memória for alocada a partir de uma região D/IRAM, o tamanho de heap livre para os dois tipos de memória diminuirá.

Watchdogs

O ESP-IDF tem suporte para dois tipos de watchdogs: o timer de watchdog de interrupção e o Timer de watchdog de tarefa (TWDT). Ambos podem ser habilitados usando make menuconfig, entretanto o TWDT também pode ser habilitado durante o tempo de execução. O Watchdog de Interrupção é responsável por detectar instâncias em que a alternância de tarefas do FreeRTOS é bloqueada por um período prolongado de tempo. O TWDT é responsável por detectar instâncias de tarefas em execução que não liberam o core do processador por um período prolongado.