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.
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.
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.
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.
Silvio excelente post parece que agora finalmente entendi esse maldito deste millis que estava travado na garganta, otima explicação valeu grande abraço
como eu faço para adaptar essa rotina trocando o delay pelo millis, já estudei e fiz de tudo mas não consegui fazer funcionar.
void loop()
{
entrada = !digitalRead(sensor);
if (entrada == HIGH )
{
flag = 1;
}
while(flag==1)
{
digitalWrite(rele,HIGH);
segundos–;
delay(1000);
if(segundos == 0)
{
segundos = 5;
flag = 0;
digitalWrite(rele, LOW);
delay(3000);
}
}
delay(200);
}
Olá,
Se você comparar os códigos usando a função delay() e millis() vai entender como usar ela corretamente.
Abraço!
Rosana – Equipe MakerHero
Corrigir texto acima “à 13 anos” por “há 13 anos”
muito bom seu exemplo, parabens!
Olá! Bom dia! Não posso deixar de parabenizá-los pela excelente comparação com as duas modalidades, uma com “delay” normal e outra com delay por “millis”. Sou iniciante em Arduino, mas muitos anos de bagagem em eletrônica industrial e hospitalar. Fantástico.
Gostaria de usar um temporizador para que, decorridos 10minutos sem um sensor indutivo reconhecer um material, enviar uma mensagem de parada para o computador. Uso o millis mesmo?
Boa tarde Silvio,e seeu tivesse que acionar as saídas por um botão usando o millis como fcaria? desde já agradeço.
Olá, muito bom o post!
Queria ajuda em um projeto… preciso de três saídas do arduíno sendo controladas por três entradas também do arduíno, sendo que quando um pulso em qualquer entrada ,uma saída corresponde fique em nível lógico alto por um tempo e retorne a zero. O detalhe é que preciso que as saídas não travem o programa a nenhum momento…ou seja, entradas e saídas monitoradas independentes.
Sergio,
Dê uma olhada nesse nosso outro post: https://www.makerhero.com/blog/tarefas-paralelo-no-arduino/
Abraços!
Diogo – Equipe MakerHero
Muito obrigado, SÍLVIO. Explicação clara, objetiva e muito didática.
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?
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;
}
}
}
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?
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++;
}
}
}
}
Parabéns pelo post e obrigado por compartilhar seu conhecimento.