Nem tudo funciona na velocidade que desejamos. Aprendemos, a duras penas, que saber esperar é uma virtude difícil de se conseguir. Dentro do universo dos processadores então, esperar um determinado tempo é tarefa crítica e é muito comum vermos programadores por vezes não tão iniciantes se perderem nesse assunto.
Aguardar pela conclusão de alguma tarefa, dentro de um microcontrolador, é algo que precisa ser estudado e implementado com cuidado para que, entre outras coisas, o processador não fique sem fazer nada – além de gastar tempo – esperando que um determinado intervalo de tempo se passe. Normalmente, se espera que um equipamento microcontrolado faça inúmeras tarefas simultaneamente e perder tempo nesse contexto é inadmissível.
O intuito desse artigo é apresentar uma pequena classe em C++ que implementa timers que podem ser ajustados para qualquer intervalo de tempo e consultados para saber se esse tempo já passou. Usaremos como exemplo o uso de tarefas em paralelo no Arduino, por ser uma plataforma bastante didática. Qualquer modelos serve, desde que suporte o comando millis() e tenha pelo menos três pino de GPIO disponíveis. Fácil. O código apresentado é padrão da plataforma Arduino, C++, mas é facilmente portável para outras plataformas.
Imagine, por exemplo, que o equipamento precise ler alguma informação de um sensor que leve um segundo para retornar o dado pedido mas que, ao mesmo tempo, outras coisas estão acontecendo que precisam ser atendidas em menos tempo do que isso. O programa não pode disparar o pedido de informação e simplesmente ficar ali, esperando passar um segundo, para ler o resultado e seguir o processo. Sabemos que é preciso disparar o pedido e sair para continuar o fluxo do programa, para ele executar tarefas em paralelo no Arduino. Após esse um segundo, aí sim volta para ler o resultado e seguir em frente novamente.
Exemplo de tarefas em paralelo no Arduino
Vamos fazer algo que é muuuuito chato de fazer sem uma classe bacana dessas: Fazer três LEDs piscarem em paralelo no Arduino, cada um numa frequência diferente, sem relação uma com a outra e que pode ser alterado a qualquer momento e de maneira fácil. Usaremos um Arduino Nano, mas qualquer outro modelo serve.
Vamos precisar de:
O esquema é bastante simples, basta ligar os LEDs nas portas 8, 9 e 10 da placa.
No IDE do Arduino, crie um novo sketch e cole esse código, inclua os arquivos da biblioteca, que podem ser encontrados no GitHub da MakerHero, compile e suba no Arduino:
/** * Programa de teste para a classe cTimer * Conecte LEDs aos pinos GPIO 8, 9 e 10. * */ #include "ctimer.h" #define LED0 8 #define LED1 9 #define LED2 10 cTimer g_Timer0(true); cTimer g_Timer1(true); cTimer g_Timer2(true); // Defina aqui os tempos em ms para cada LED piscar: #define TEMPO_0 100 #define TEMPO_1 275 #define TEMPO_2 717 bool gLed0 = false; bool gLed1 = false; bool gLed2 = false; void setup() { pinMode(LED0, OUTPUT); digitalWrite(LED0, 0); pinMode(LED1, OUTPUT); digitalWrite(LED1, 0); pinMode(LED2, OUTPUT); digitalWrite(LED2, 0); g_Timer0.SetTimeOut(TEMPO_0); g_Timer1.SetTimeOut(TEMPO_1); g_Timer2.SetTimeOut(TEMPO_2); } void loop() { if(g_Timer0.IsTimeOut(true)) { digitalWrite(LED0, gLed0); gLed0 = !gLed0; } if(g_Timer1.IsTimeOut(true)) { digitalWrite(LED1, gLed1); gLed1 = !gLed1; } if(g_Timer2.IsTimeOut(true)) { digitalWrite(LED2, gLed2); gLed2 = !gLed2; } /* * Faca aqui o que voce quiser... * */ }
O resultado são vários LEDs piscando em frequências diferentes.
É muito simples de utilizar objetos cTimer. Para cada LED, no exemplo, é preciso somente fazer o seguinte (examinando o LED0):
if(g_Timer0.IsTimeOut(true)) { digitalWrite(LED0, gLed0); gLed0 = !gLed0; }
Ou seja, se o timer já atingiu o tempo limite (IsTimeOut() retorna verdadeiro), inverta o estado do LED e reinicie o contador novamente (parâmetro true no IsTimeOut) .
O código claramente está escrito para ser didático. Utilizando-se vetores, por exemplo, consegue-se fazer a mesma coisa com cinco ou seis linhas… Mas o objetivo aqui é demonstrar o funcionamento do objeto timer.
Classe cTimer
Essa classe utiliza a função millis() para capturar o tempo decorrido. A função inicializa com zero quando o Arduino é ligado e seu valor é incrementado a cada milissegundo. Como é armazenado como um número longo sem sinal, o registrador ‘capota’ aproximadamente a cada 50 dias. Embora nem todos os sistemas irão rodar direto por tanto tempo, a classe leva isso em conta e trata esse overflow corretamente.
Como a implementação é feita na forma de classe, pode-se criar quantos timers forem necessários, cada um disparando no momento desejado sem que um interfira no outro, permitindo sincronizar tarefas e executar processos em qualquer intervalo de tempo quase intuitivamente.
A definição da classe é a seguinte:
class cTimer { private: uint32_t m_u32step; uint32_t m_u32TimeOut; uint32_t m_u32timeNow; bool mEnabled; public: cTimer(bool _enabled = true); ~cTimer(void); void SetTimeOut(uint32_t _u32mSeg); bool IsTimeOut(bool _reset = false); uint32_t ReadTimeOut(void); void Enable(bool _enable); bool IsEnabled(void); };
Vamos analisar os métodos mais importantes dessa classe:
cTimer::cTimer(bool _enabled);
Pode-se criar um timer deixando-o já habilitado ou não. Abaixo, onde se verifica se o timer já disparou, a resposta depende do fato dele estar habilitado ou não.
cTimer::SetTimeOut(uint32_t _u32mSeg);
Esse método é usado para se definir o tempo necessário para que o timer dispare, em milissegundos, a partir do momento em que é chamado. Analisando internamente, o que o método faz é guardar o valor atual de millis() mais o valor passado no parâmetro. Ele também faz uma cópia do valor de millis() para poder tratar o caso de overflow discutido mais acima.
bool cTimer::IsTimeOut(bool _reset)
Esse método retorna se o tempo decorrido desde que SetTimeOut foi chamado já se esgotou. Caso o timer esteja desabilitado, ele sempre irá retornar falso. Em caso de retorno verdadeiro, se o parâmetro _reset for TRUE, o timer é setado novamente. Os demais métodos são bem diretos e servem para habilitar e desabilitar o timer, ou consultar quanto tempo ainda falta.
Gostou de aprender como fazer tarefas em paralelo no Arduino? Ajude-nos a melhorar o blog comentando abaixo sobre este tutorial.
Que biblioteca top heim, obrigado por compartilhar o conhecimento, agradecer é o mínimo que se pode fazer. A tempos estava sofrendo com a doença do delay(), e agora com essa medição ai, consegui a cura intelectual kkkk. Parabéns pelo trabalho da FelipeFlop, e parabéns Luiz Cressoni.
Quis dizer medicação kkkkkk
Olá Tiago!
Ficamos contentes em saber que pudemos te ajudar!
Abraço,
Rosana – Equipe MakerHero
Estou iniciando
Quero fazer uma seguecial de leds, que: começa ligando, deixa todos ligados, espera um tempo, e desliga na seguencia que iniciou.
Finaliza todos desligados, espera um novo pulso esterno, roda e desliga novamente.
Obrigado!
Sebastião,
Que dê tudo certo com seu projeto!!
Abraços!
Diogo – Equipe MakerHero
Excelente trabalho. Tenho apenas uma duvida, a função millis é acumulada em uma variável do tipo long, portanto ela irá encher com o passar do tempo, como dito no texto em aproximadamente 50 dias. Para aplicações onde o Arduíno precisará ficar mais de 50 dias ligado o código não serve?
Parabéns! ficou muito legal o seu trabalho.
Mudei os Pin dos leds para 10, 11 e 12 e testei com o arduino multishielded .
Olá Carlos,
Que bom que deu certo!
Abraço!
Rosana – Equipe MakerHero
Parabéns ! bem legal e educativo.
Olá Carlos,
É muito bom saber que você gostou do nosso trabalho 😀
Abraço!
Rosana – Equipe MakerHero
Excelente material. Estava procurando algo semelhante para um projeto! Obrigado.
Olá Marcos,
É ótimo saber que nosso trabalho te ajudou!
Abraço!
Rosana – Equipe MakerHero
Show. Bem interessante essa biblioteca.
Olá André,
É muito bom saber que você gostou do nosso trabalho 😀
Abraço!
Rosana – Equipe Filipe Flop
Uma postagem com um assunto muito útil, mas eu acho que seria bom dar um exemplo um pouco mais complexo , pois o exemplo dado acaba sendo bem mais simples usando apenas ‘contadores’.
Ótima abordagem sobre o assunto! Gostaria inclusive de sugerir ao Editor, uma breve pesquisa sobre a biblioteca Arduino Thread, que é uma ótima opção para quem quer trabalhar com otimização do tempo utilizando “Fake Threads” nas placas chinoitalianas e está incluída oficialmente no Gerenciador de Bibliotecas da Arduino IDE. Segue link: https://github.com/ivanseidel/ArduinoThread
Olá Marcelo,
Excelente dica!
Abraço,
Rosana – Equipe MakerHero