Projetos

Uso de funções recursivas na programação de microcontroladores

Eletrogate 18 de julho de 2024Tempo de leitura: 10 minutos

O que são funções recursivas?

Funções recursivas são funções que podem chamar a si mesmas. Qualquer função em linguagem de programação utilizada para programar em um Arduino ou em um ESP pode ser chamada recursivamente.

Neste tutorial você aprenderá como chamar uma função de forma recursiva. Isso aprimorará seu código e pode lhe ajudar a resolver problemas de forma mais simples e elegante, dividindo um mesmo problema em problemas menores.


Materiais Necessários

Para esse projeto você precisará dos seguintes componentes:


Como aplicar a recursividade em uma função?

Para aplicar a recursividade em uma função, basta chamá-la a si mesma dentro de si própria. Contudo, caso não deseje criar um loop, você deve se atentar a criar um mecanismo de saída para que esta função não atrapalhe o algoritmo do seu código. Vamos criar um exemplo: uma função para mudar o estado lógico de um LED.

Primeiro, cria-se o cabeçalho da função. Neste exemplo, a função criada chama-se “led”. Atente-se para o valor de entrada da função, que é “bool estado”. Você entenderá o porquê:

void led (bool estado)
{
  
}

void setup()
{

}

void loop()
{
  
}

Agora, precisamos definir uma porta para controlar e chamar a função led, que servirá como um loop neste caso.

void led (bool estado)
{
 
}

void setup()
{
  pinMode (12, OUTPUT);
  digitalWrite (12, LOW);
  led (1);
}

void loop()
{
  
}

Perceba que, no void setup ( ), o pino 12 do Arduino foi definido como output e iniciado com 0 volts. Logo abaixo, chamados a função “led” com um valor de entrada, que é o valor 1, colocado entre parênteses. Esse valor será adicionado no valor booleano de entrada (“estado”) da função recursiva que criamos. Ele será útil para mudarmos o estado lógico do nosso led, como podemos ver abaixo:

void led (bool estado)
{
  digitalWrite (12, estado);
  
  delay (1000);
  estado = !estado;
  led (estado);
}

void setup()
{
  pinMode (12, OUTPUT);
  digitalWrite (12, LOW);
  led (1);
}

void loop()
{
  
}

Na atualização do código acima, podemos observar que o valor o qual entramos na função foi adicionado na porta de número 12 do Arduino, assim, o led acende. Após isso, adicionamos um delay de 1 segundo. Depois, trocamos o valor lógico da variável booleana “estado”, que antes estava 1, para 0, e chamamos, dentro da própria função, a função “led” novamente, agora com um valor de entrada igual a 0, para podermos mandar para a porta 12 o sinal lógico baixo e apagarmos o led.

Esse processo torna a função recursiva em um loop, já que não adicionamos nenhum mecanismo de saída da função. Caso quisermos que o led mude de estado 4 vezes (pisque duas vezes) e depois saia da função de piscar, podemos fazer o seguinte ajuste:

int piscar = 0;

void led (bool estado)
{
  piscar++;
  
  digitalWrite (12, estado);
  
  if (piscar == 4){
 	piscar = 0;
  	return;}
  
  delay (1000);
  
  estado = !estado;
  
  led (estado);
}

void setup()
{
  pinMode (12, OUTPUT);
  digitalWrite (12, LOW);
  led (1);
}

void loop()
{
  
}

Agora, a função recursiva não se tornou mais um loop, mas sim, um mecanismo inteligente, simples e prático de fazer piscar um led apenas duas vezes quando acionada a função. Para isso, adicionamos uma variável inteira chamada “piscar”, que guarda quantas vezes aquela função chamou a si mesma. Assim, quando chega no valor 4, a condicional “if” que criamos zera o valor da variável que criamos e sai da função recursiva por meio do “return”.

Assim, o microcontrolador volta a ler o resto do código a partir da próxima linha de onde a função foi chamada. Nesse exemplo, a função foi chamada na linha 24. Logo, quando acionado o “return” dentro da função recursiva, o microcontrolador volta a ler o resto do código a partir da linha 25.

Vejamos, abaixo, o pseudocódigo do nosso primeiro algoritmo de recursividade:

Blog-Eletrogate-Recursividade-Fluxograma-Led

Confira abaixo o circuito utilizado neste exemplo:

Blog_Eletrogate_Exemplo_1_Led_Recursividade

Confira, agora, o vídeo de exemplificação do funcionamento do código:


Recursividade como mecanismo de resolução de problemas fatoriais

Fatorial, na matemática, refere-se ao produto de todos os números inteiros maiores que 0, iguais e menores a n, com a regra de que o fatorial de 0 é sempre 1. Vejamos um exemplo:

Fatorial-Blog-Eletrogate

Para resolver um fatorial utilizando microcontroladores, precisamos entender que, se 5! é 5x4x3x2x1 e 4! é 4x3x2x1, então 5! também pode ser escrito como 5×4!, como podemos ver na imagem abaixo:

Blog-Eletrogate-Fatorial_2

Isso é a quebra de um problema em problemas menores. Para calcular o fatorial de qualquer número inteiro, calcularemos, antes, todos os fatoriais anteriores a esse número. Vejamos como fazer isso, passo a passo:

unsigned long numero = 0;
unsigned long resposta = 1;

unsigned long calculo (unsigned long valor){
  
  if (valor == 0) {
    return resposta;
  } 
 
}

void setup()
{
  Serial.begin (9600);  
}

void loop()
{ 
  numero = 0;

  calculo (numero);
  
  Serial.println (resposta);

  resposta = 1;  
}

Perceba que a nova função criada (calculo) não é mais do tipo void, e sim do tipo unsigned long. Funções do tipo void não retornam nenhum valor ao programa, elas apenas executam. Já as demais funções, retornam valores de diferentes tipos a depender de como você declarar ela. Nesse caso, a função retornará valores do tipo unsigned long. O valor de entrada da função também é do mesmo tipo.

A primeira coisa que precisamos fazer é criar um ponto de saída da função, que está na condicional “if”. Caso o próximo número a ser calculado o fatorial for 0, ele sai da função, pois o cálculo terminou.

A variável “resposta”, do tipo unsigned long, foi declarada com valor de setup igual a 1. Isso porque, caso o usuário deseje o cálculo do fatorial de 0, esse valor retornará 1.

O próximo passo será escrever a linha de código de diálogo entre o usuário e o microcontrolador utilizado o Serial Monitor. Aqui você perguntará ao usuário qual o fatorial que ele deseja calcular.

unsigned long numero = 0;
unsigned long resposta = 1;
unsigned long retorno = 0;

unsigned long calculo (unsigned long valor){
  
  if (valor == 0) {
    return resposta;
  } 
 
}

void setup()
{
  Serial.begin (9600);
 
}

void loop()
{
  Serial.println ("Digite um numero para cálculo do fatorial");
  
  while (Serial.available ()==0){}
  
  numero = Serial.parseInt();
  
  Serial.read ();

  calculo (numero);
  
  Serial.println (resposta);
    
  resposta = 1;
  
  }

Aqui, adicionamos uma variável do tipo unsigned long chamada “número”, que guardará o número que o usuário digitar. Adicionamos também a função “while ()”, para que o programa só volte a executar quando o usuário digite o número, ou seja, quando houver algo disponível no buffer da placa microcontrolada.

Salvamos o número com a função “Serial.parseInt ()” e utilizamos a função “Serial.read ()” para limpar o buffer.

Agora, precisamos criar o algoritmo de solução do fatorial. Vejamos:

unsigned long numero = 0;
unsigned long resposta = 1;
unsigned long retorno = 0;

unsigned long calculo (unsigned long valor){
  
  if (valor == 0) {
    return resposta;
  } 
  
  resposta = resposta * valor;
  
  valor = valor - 1;
  
  calculo (valor);
 
}

void setup()
{
  Serial.begin (9600);
  
}

void loop()
{
  Serial.println ("Digite um numero para cálculo do fatorial");
  
  while (Serial.available ()==0){}
  
  numero = Serial.parseInt();
  
  Serial.read ();

  calculo (numero);
  
  Serial.println (resposta);
    
  resposta = 1;
  
  }

Iniciamos a função “unsigned long calculo” com a condicional: o número digitado foi 0? Se sim, o programa sai da função, que retorna 1, valor que representa o fatorial de 0. Caso o valor tiver sido diferente de 0, multiplicamos o valor por 1 e reduzimos em 1 o valor para que possamos multiplicar novamente pela resposta anterior.

Imagine que você digita o valor 4. Primeiro, o programa identifica se o valor que você escolheu foi 0. Se não, ele multiplica 4 por 1, que é o valor de setup da variável “resposta”. Após isso, ele transforma o 4 em 3 (reduz uma unidade) e chama a função a si mesma, com o recurso da recursividade (linha 15).

A função vai verificar se agora o número da variável “valor” é 0. Como esse valor ainda é 3, ele continua o cálculo. Agora, o valor de resposta que era 4 (1 x 4), vai ser multiplicado por 3, tornando-se 12. O número da variável “valor” é reduzido em uma unidade e torna-se 2. A função chama a si mesma pelo processo de recursividade e, agora, o valor da variável “resposta”, que era 12, é multiplicado por 2, e torna-se 24. Após isso, reduzimos 2 em uma unidade e o valor é multiplicado por 1. Quando o programa reduzir 1 unidade de 1, e o número da variável “valor” torna-se 0, fazendo a função entrar na condicional de retorno e devolvendo ao programa o valor calculado que, nesse caso, foi 24 (fatorial de 4). Veja, no fluxograma abaixo, o pseudocódigo deste algoritmo de recursividade:
Blog-Eletrogate-Recursividade-Fluxograma

Para este exemplo, vamos exibir o valor da resposta em display LCD 16×2. Você pode aprender mais sobre ele clicando aqui e acessando outro post de nosso blog.

Assim, o código final é o seguinte:

#include <LiquidCrystal.h>
 
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

unsigned long numero = 0;
unsigned long resposta = 1;
unsigned long retorno = 0;

unsigned long calculo (unsigned long valor){
  
  if (valor == 0) {
    lcd.clear();
    return resposta;
  } 
  
  resposta = resposta * valor;
  
  valor = valor - 1;
  
  calculo (valor);
 
}

void setup()
{
  Serial.begin (9600);

  lcd.begin(16, 2);
  lcd.clear();
  
}

void loop()
{
  Serial.println ("Digite um numero para calculo do fatorial");
  
  while (Serial.available ()==0){}
  
  numero = Serial.parseInt();
  
  Serial.read ();

  calculo (numero);
  
  Serial.println (resposta);

  lcd.setCursor(0,0);
  lcd.print("Fatorial de: ");
  lcd.setCursor(13,0);
  lcd.print(numero);
  lcd.setCursor(0,1);
  lcd.print(resposta);
    
  resposta = 1;
  
  }

Confira abaixo o circuito utilizado neste exemplo:

Blog_Eletrogate_Exemplo_2_Led_Recursividade

Confira, agora, o vídeo de exemplificação do funcionamento do código:


Considerações finais

Como podemos perceber, as funções recursivas são um ótimo recurso para você que deseja resolver problemas com microcontroladores de forma mais simples e elegante. Você pode utilizar o algoritmo desses códigos para resolver diversos outros problemas, a única coisa que deve tomar cuidado é em não ultrapassar o valor máximo da variável que você adicionar na sua função. Neste tutorial, o valor máximo que a função pode calcular de fatorial é 4.294.967.295, valor limite da variável unsigned long.


Sobre o autor


Pedro Cavalcante

Estudante de Sistemas de Energia Renovável no Instituto Federal da Paraíba. Entusiasta da automação em Arduino, ESP32 e Raspberry Pi.


Lista de Materiais

Eletrogate

18 de julho de 2024

A Eletrogate é uma loja virtual de componentes eletrônicos do Brasil e possui diversos produtos relacionados à Arduino, Automação, Robótica e Eletrônica em geral.

Conheça a Metodologia Eletrogate e Lecione um Curso de Robótica nas Escolas da sua Região!

Eletrogate Robô

Cadastre-se e fique por
dentro de novidades!