Multiprocessamento - ESP32

Multiprocessamento com ESP32

As vezes é necessário que façamos o uso de sistemas multicores para as mais diversas finalidades, como por exemplo, fazer verificações de dados pela Serial, I2C ou sensores, enquanto o outro processador (Core) faz uma outra atividade em que não possa ser interrompida ou seja indesejado esse tipo de ação. Aprenderemos o que é multiprocessamento e usaremos o incrível ESP32 que tem ao todo três cores, para criar dois LOOP(), permitindo que você rode dois códigos ao mesmo tempo!

Lembrando: O terceiro core do ESP32 é um ULP, que é apenas programado em Assembly. Também é possível programa-lo para as mais diversas finalidades. Será mostrado apenas a programação do core principal.

Para conhecer mais sobre o ESP32 você pode conferir nosso tutorial: Conhecendo o ESP32

[toc]

Computação paralela - Multiprocessamento

computação paralela - Multiprocessamento
computação paralela - Multiprocessamento

Computação paralela é um assunto extremamente complicado, porem podemos simplificar de um método prático:

Você tem um supermercado com um caixa e em horários de pico, o caixa não é rápido suficiente para atender os clientes, então é necessário contratar outro funcionário que fique em outro caixa, fazendo assim que a velocidade de espera seja até 50% menor. (será atendido o dobro de clientes em relação a apenas um caixa)

Um sistema computacional paralelo, permite realizar diversos cálculos (tarefas, algoritmos, etc) simultaneamente. Há diversas maneiras de se paralelizar um código que é normalmente escrito em sequência: em bit, instrução, de dado ou de tarefa. O método mais simples é por exemplo dividir um FOR() pela metade a atribuir cada metade em cores diferentes, diminuindo o tempo de execução em até 50%. Apesar de parecer ser extremamente útil, há inúmeros problemas relacionados, como condição de corrida: o acesso simultâneo da mesma variável pode gerar erros em cálculos ou se o próprio calculo for dependentes de outros resultados, a paralelização disto é inconveniente, uma vez que utilizar semáforos para sincronizar as tarefas (exclusão mutua), pode ser mais lento que o simples código em sequencial. A questão à se pensar com uso de semáforos para sincronia de processos, deve ser analisada com o grau de granulação, uma vez que o uso excessivo de semáforos, pode deixar o processo mais lento que o código sequencial (single core).


Mãos à obra

Componentes necessários

Código do projeto

int tempo;//Variavel que armazena o tempo.

void setup()
{
	Serial.begin(115200);//Inicia a comunicaçao serial
	pinMode(2, OUTPUT);//Define o led Onboard como saída


	Serial.printf("\nsetup() em core: %d", xPortGetCoreID());//Mostra no monitor em qual core o setup() foi chamado
	xTaskCreatePinnedToCore(loop2, "loop2", 8192, NULL, 1, NULL, 0);//Cria a tarefa "loop2()" com prioridade 1, atribuída ao core 0
	delay(1);
}

void loop()//O loop() sempre será atribuído ao core 1 automaticamente pelo sistema, com prioridade 1
{
	Serial.printf("\n Tempo corrido: %d", tempo++);
	delay(1000);//Mantem o processador 1 em estado ocioso por 1seg
}

void loop2(void*z)//Atribuímos o loop2 ao core 0, com prioridade 1
{
	Serial.printf("\nloop2() em core: %d", xPortGetCoreID());//Mostra no monitor em qual core o loop2() foi chamado
	while (1)//Pisca o led infinitamente
	{
		digitalWrite(2, !digitalRead(2));
		delay(100);
	}
}

Colocando para funcionar

Podemos observar tanto no Serial Monitor quanto no ESP32, o funcionamento esperado do código. Enquanto o core 1 faz a contagem do tempo e espera 1seg para repetir a mensagem (o que o deixa em IDLE [travado]), o core 0 pisca o led, mostrando que um não interfere no outro.


Entendendo a fundo

Software

-Função xTaskCreatePinnedToCore()

xTaskCreatePinnedToCore(loop2, "loop2", 8192, NULL, 1, NULL, 0);

Esta função cria uma tarefa e atribuí a um especifico processador. O FreeRTOS pode definir automaticamente em qual core a tarefa será rodada, para isto, use xTaskCreate() (Mais informações no site FreeRTOS).

Neste caso, criamos a tarefa loop2, com "tamanho" de 8192 Bytes (words), nenhum parâmetro, prioridade 1 e atribuída ao core 0.

Vamos esclarecer os parâmetros em ordem (da esquerda à direita):

xTaskCreatePinnedToCore(pxTaskCodepcNameusStackDepthpvParametersuxPrioritypxCreatedTaskxCoreID)

pxTaskCode: Ponteiro para a tarefa, apenas o nome da tarefa.   loop2.

pcName: O nome (String) da tarefa (entre aspas), é usado para facilitar o debug.   "loop2".

usStackDepth: O tamanho de Stack reservada à tarefa, mais informações clique aqui.   8192.

pvParameters: O valor a ser passado para a tarefa no momento de criação. Nossa tarefa não precisa de parâmetros, então:   NULL.

uxPriority: A prioridade da tarefa. É comum se usar 1 para tarefas simples, já que funções de delay (IDLE) tem prioridade 0.   1.

Se duas tarefas com mesma prioridade estiverem na fila, a primeira da fila irá ser executada e depois a próxima. Caso uma tarefa com prioridade 1 esteja na fila, porém uma tarefa com prioridade 2 também, será executado a tarefa com prioridade 2 e depois as outras.

pxCreatedTask: Valor opcional caso seja necessário manipulação das tarefas (Handle).   NULL.

xCoreID: Atribuí a tarefa a um core especifico.   0.

Observações:

  • Se você por exemplo criar dois loops, ao chamar uma subrotina (função), ela será executada no core em que foi chamada.
  • Caso você não crie uma tarefa "infinita", será necessário deletar a tarefa com xTaskDelete().
  • No caso do loop2() criado, é necessário o uso de pelo menos delay(1) dentro do loop, para que o Task Watchdog não seja ativado. Há maneiras de contornar isso, mas precisa fazer alterações no BootLoader.

-Função xPortGetCoreID()

xPortGetCoreID()

Esta função retorna o core em que a tarefa esta sendo executada.


Fechamento

A computação paralela se mostra útil quando necessário alto poder computacional ou monitoramento de itens específicos. Este assunto é gigantesco, complicado e intrigante, foi mostrado apenas o básico sobre o assunto; se você quer se aprofundar mais, veja os PDFs que foram citados no começo.

Dúvidas? Sugestões? Críticas? Comente abaixo!