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:
void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  digitalWrite(LED_BUILTIN, HIGH);
  delay(500);
  digitalWrite(LED_BUILTIN, LOW);
  delay(500);
}
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).
void setup() {
  pinMode(9, OUTPUT);
  TCCR1A = _BV(COM1A0);
  TCCR1B = _BV(WGM12)|_BV(CS12)|_BV(CS10);
  OCR1A = 7812;
}

void loop() {
  delay(100); // nada para fazer
}
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:
void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  static bool aceso = false;
  static unsigned long pisca = 0;

  if (millis() > pisca) {
    digitalWrite(LED_BUILTIN, aceso? LOW : HIGH);
    aceso = !aceso;
    pisca = millis() + 500L;
  }

  delay (50); // faz outras coisas
}
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).
#include 

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  Timer1.initialize(500000L);
  Timer1.attachInterrupt(piscaLED);
}

void loop() {
  delay(100); // nada para fazer
}

void piscaLED(void)
{
  static bool aceso = false;
  
  digitalWrite(LED_BUILTIN, aceso? LOW : HIGH);
  aceso = !aceso;
}

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: