Subtituindo delay por millis no Arduino 6

O Arduino é composto por um só núcleo de processador, e por isso, todas as tarefas devem ser executadas sequencialmente. Se em algum momento do código existir uma instrução para pausar e aguardar um período de tempo, normalmente usado pela função delay(), quase nenhuma outra instrução no código irá funcionar, até que esse período de delay seja concluído. Nesse post irei demonstrar como usar a função millis() no Arduino no lugar da função delay(), simulando, assim, tarefas em “paralelo”.

Materiais necessários

No projeto exemplo desse tutorial usamos os seguintes componentes:

Como funciona a função delay() no Arduino?

O funcionamento da função delay() basicamente é “congelar” o programa em determinada parte do código por um tempo especificado em milissegundos.

Durante o período em que o código está parado, não pode ocorrer nenhuma leitura de sensores, cálculos matemáticos ou manipulação de pinos.

No entanto, certas coisas continuam acontecendo:

  • Monitoramento dos pinos ou comandos de interrupções
  • Na comunicação serial, os comandos continuam sendo enviados para o Arduino (pino RX), mas ficam armazenados e aguardando liberação do microcontrolador para poderem ser interpretados.
  • Os valores setados para algum pino PWM, através do comando analogWrite(), continuarão em funcionamento.
  • Os estados dos pinos (High/Low) são mantidos.

Exemplo de funcionamento da função delay() no Arduino

Para exemplificar o funcionamento da função delay(), vamos montar um circuito utilizando dois leds e dois resistores de 220 ohms.  Nesse exemplo, observe quanto tempo os leds ficam acesos e apagados e também se estes piscam um depois do outro ou em paralelo. Cada led é controlado por uma tarefa codificada no programa.Imagem 1 - Função delay() no Arduino

Exemplo de código usando a função delay() no Arduino

No exemplo abaixo temos duas tarefas para serem executadas. A tarefa 1 faz com que o led vermelho fique piscando numa velocidade de 200 milissegundos e a tarefa 2 faz com que o led amarelo fique piscando numa velocidade de 1000 milissegundos.

/*
 * Exemplo de código usando delay()
 */
// Iniciação
void setup(){
  // Inicia comunicação na porta serial
  Serial.begin(9600);
  // Define pinos dos leds como saída
  pinMode(7, OUTPUT);
  pinMode(8, OUTPUT);
}
// Laço perpétuo
void loop(){
  // Executa tarefa 1
  tarefa1();
  // Executa tarefa 2
  tarefa2();
}

void tarefa1(){
  // Acende o led do pino 7
  digitalWrite(7, HIGH);
  // Congela o sistema por 200 milisegundos
  delay(200);
  // Apaga o led do pino 7
  digitalWrite(7, LOW);
  // Congela o sistema por 200 milisegundos
  delay(200);
}

void tarefa2(){
  // Acende o led do pino 8
  digitalWrite(8, HIGH);
  // Congela o sistema por 1000 milisegundos
  delay(1000);
  // Apaga o led do pino 8
  digitalWrite(8, LOW);
  // Congela o sistema por 1000 milisegundos
  delay(1000);
}

Como estamos usando a função delay() pode-se observar que não há um paralelismo na execução das tarefas e sim são executadas em sequência.

Imagem 2 - Função delay() no Arduino

Como funciona a função millis() no Arduino?

A função millis() retorna um número indicando há quantos milissegundos o Arduino está ligado.

Agora, ao invés de pausar o sistema durante um tempo determinado usando a função delay(), iremos trabalhar com o valor retornado pela função millis() e calcular indiretamente o tempo decorrido.

Primeiramente, salvamos o valor da função millis() nas variáveis millisTarefa1 e millisTarefa2. Em seguida, calculamos a diferença de tempo entre as variáveis armazenadas e o tempo atual retornado pela função millis(). Dessa forma, é possível verificar se já passou o tempo necessário para que uma tarefa seja executada.

Após executarmos a tarefa desejada, atualizamos o valor da variável millisTarefa1 ou millisTarefa2 com o valor atual de millis(). E o ciclo recomeça.

Exemplo de código usando a função millis() no Arduino

No exemplo abaixo temos duas tarefas para serem executadas. A tarefa 1 faz com que o led vermelho fique piscando numa velocidade de 200 milissegundos e a tarefa 2 faz com que o led amarelo pisque numa velocidade de 1000 milissegundos. Ou seja, é o mesmo exemplo anterior, mas ao invés de usarmos a função delay() iremos usar a função millis().

/*
 * Exemplo de código usando millis()
 */
// Inicia variáveis de tempo
unsigned long millisTarefa1 = millis();
unsigned long millisTarefa2 = millis();

// Iniciação
void setup(){
  // Inicia comunicação na porta serial
  Serial.begin(9600);
  // Define pinos dos leds como saída
  pinMode(7, OUTPUT);
  pinMode(8, OUTPUT);
}
// Laço perpétuo
void loop(){
  // Executa tarefa 1
  tarefa1();
  // Executa tarefa 2
  tarefa2();
}

void tarefa1(){
  // Verifica se já passou 200 milisegundos
  if((millis() - millisTarefa1) < 200){
    // Acende o led do pino 7
    digitalWrite(7, HIGH);
  }else{
    // Apaga o led do pino 7
    digitalWrite(7, LOW);
  }
  // Verifica se já passou 400 milisegundos
  if((millis() - millisTarefa1) > 400){
    millisTarefa1 = millis();
  }
}

void tarefa2(){
  // Verifica se já passou 1000 milisegundos
  if((millis() - millisTarefa2) < 1000){
    // Acende o led do pino 8
    digitalWrite(8, HIGH);
  }else{
    // Apaga o led do pino 8
    digitalWrite(8, LOW);
  }
  // Verifica se já passou 2000 milisegundos
  if((millis() - millisTarefa2) > 2000){
    millisTarefa2 = millis();
  }
}

O fato de usar uma subtração para saber se já atingiu o tempo necessário para ligar ou desligar o led permite que o sistema continue executando outras tarefas em paralelo. Agora pode-se observar que há um paralelismo na execução das tarefas. Enquanto o led vermelho esta piscando numa velocidade de 200 milisegundos o led amarelo pisca na velocidade de 1000 milisegundos.

Imagem 3 - Função millis() no Arduino

Conclusão

Vimos nesse post que uma das formas de contornar a pausa de uma tarefa no Arduino é substituindo a função delay() pela função millis(). Se você gostou desse assunto de processamentos paralelos e gostaria de aprender mais, visite os posts listados abaixo:

Além disso, ajude-nos a melhorar o blog, participe com a sua sugestão, dúvida ou comentário aqui embaixo. E se quiser postar seu projeto, acesse o fórum.

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

6 Comentários

  1. Parabéns pelo post e obrigado por compartilhar seu conhecimento.

  2. Interessante, mas, se caso quiser que ele pisque 2x rapidamente, tipo, ligado por 50ms, e apagado por 90ms 2x, como ficaria ali na linha 26 pra frente?

    FREDERICO DOURADO DE ARAUJO
    1. Tenta assim:

      bool estadoLed1 = 1;
      bool contTarefa1 = 0;

      void tarefa1(){
      if(contTarefa1 50){
      estadoLed1 = 0;
      millisTarefa1 = millis();
      }
      }else{
      digitalWrite(7, LOW);
      if((millis() – millisTarefa1) > 90){
      estadoLed1 = 1;
      millisTarefa1 = millis();
      contTarefa1++;
      }
      }
      }
      }

  3. Muito obrigado pelo esclarecimento. Percebi que essa função vai me ajudar a calcular o intervalo de tempo entre um objeto que interrompe a iluminação do ldr e um led associado acende, quando eu volto a interromperia luz sobre o ldr, tipo acendendo evaporando a luz, o tempo que essas ações levaram. Mas não sei como fazer o código. Pode ajudar?

    1. Tenta assim:

      int valorLDR;
      unsigned long millisLDR = millis();
      bool estadoLed1 = 0;

      void tarefa1(){
      valorLDR = analogRead(A0);

      if(valorLDR < 500){
      digitalWrite(7, HIGH); // Acende led
      if(estadoLed1 != 1){
      millisLDR = millis();
      estadoLed1 = 1;
      }
      }else{
      digitalWrite(7, LOW); // Apaga led
      if(estadoLed1 != 0){
      Serial.println(millis() – millisLDR); // Tempo que o led ficou aceso
      estadoLed1 = 0;
      }
      }
      }

  4. Muito obrigado, SÍLVIO. Explicação clara, objetiva e muito didática.