quarta-feira, setembro 04, 2019

De Volta ao Básico: Piscando um LED de Quatro Formas Diferentes

É um ritual obrigatório: a primeira coisa a fazer com uma placa ou microcontrolador é piscar um LED. Vamos ver aqui quatro formas diferentes de fazer isto com um Arduino Uno.



Usando um Delay


Esta é a forma usada no exemplo Blink do Arduino:
  1. void setup() {  
  2.   pinMode(LED_BUILTIN, OUTPUT);  
  3. }  
  4.   
  5. void loop() {  
  6.   digitalWrite(LED_BUILTIN, HIGH);  
  7.   delay(500);  
  8.   digitalWrite(LED_BUILTIN, LOW);  
  9.   delay(500);  
  10. }  
Simples, direto, mas fica difícil fazer outras coisas ao mesmo tempo.

A Solução por Hardware


Pulsar um sinal é trabalho para PWM!

O ATmega328 possui tês timers, cada um com dois canais de PWM, vamos usar aqui o Timer1. A programação é um pouco complicada, pois o timer pode trabalhar de diversas formas.

No código abaixo vamos usar um divisor de 1024 para o clock do Arduino (16MHz), o que significa que vamos contar passos de 1024/16000 milissegundos. O PMW é configurado para inverter o sinal de saída a cada 7812 passos (7812*1024/16000 = 500ms).
  1. void setup() {  
  2.   pinMode(9, OUTPUT);  
  3.   TCCR1A = _BV(COM1A0);  
  4.   TCCR1B = _BV(WGM12)|_BV(CS12)|_BV(CS10);  
  5.   OCR1A = 7812;  
  6. }  
  7.   
  8. void loop() {  
  9.   delay(100); // nada para fazer  
  10. }  
Parece mágica, não é? A desvantagem é que a seleção de pinos é limitada, neste caso não dá para usar o LED da placa e precisei colocar o LED na saída digital 9.

Usando Tempos


Para não ficarmos com o Arduino parado no delay, podemos testar no loop() se está na hora de mudar o LED:
  1. void setup() {  
  2.   pinMode(LED_BUILTIN, OUTPUT);  
  3. }  
  4.   
  5. void loop() {  
  6.   static bool aceso = false;  
  7.   static unsigned long pisca = 0;  
  8.   
  9.   if (millis() > pisca) {  
  10.     digitalWrite(LED_BUILTIN, aceso? LOW : HIGH);  
  11.     aceso = !aceso;  
  12.     pisca = millis() + 500L;  
  13.   }  
  14.   
  15.   delay (50); // faz outras coisas  
  16. }  
Obs: variáveis static em uma função mantém o valor de uma chamada para outra.

Embora este método libere o microcontrolador para fazer outras coisas, a piscada do LED fica sensível à duração destas outras coisas (experimente aumentar o delay(50) para delay(300)).

Usando Interrupção de Tempo


Este é o meu método preferido... Usando interrupção o processamento corre normalmente a maior parte do tempo. Vamos usar de novo o timer1, mas neste caso vou usar a biblioteca TimerOne (que você pode instalar direto da IDE do Arduino).
  1. #include <timerone .h="">  
  2.   
  3. void setup() {  
  4.   pinMode(LED_BUILTIN, OUTPUT);  
  5.   Timer1.initialize(500000L);  
  6.   Timer1.attachInterrupt(piscaLED);  
  7. }  
  8.   
  9. void loop() {  
  10.   delay(100); // nada para fazer  
  11. }  
  12.   
  13. void piscaLED(void)  
  14. {  
  15.   static bool aceso = false;  
  16.     
  17.   digitalWrite(LED_BUILTIN, aceso? LOW : HIGH);  
  18.   aceso = !aceso;  
  19. }  
  20. </timerone>  

A desvantagem (tem que ter, não é?) é que o código fica um pouco mais complicado (a biblioteca TimerOne escondeu a maior parte). À medida que se coloca mais coisas na interrupção é preciso tomar cuidado com a concorrência no acesso a variáveis e não deixar a interrupção demorada demais.

Nenhum comentário: