Leitura de Botões e o Bounce

Botões são utilizados na criação de interfaces entre maquinas e humanos. Nesse tutorial veremos como fazer com que o Arduino reconheça o estado de um botão e entenderemos o que é o Bounce.

Esse tutorial requer conhecimento em assuntos explicados em posts anteriores. Caso tenha dificuldades em acompanhar os assuntos abordados aqui, confira alguns de nossos tutoriais anteriores:

[toc]

REALIZANDO A LEITURA DE UMA ENTRADA DIGITAL ACIONADA POR UM BOTÃO

Para que o Arduino consiga saber o estado na qual o botão se encontra devemos fazer com que cada um de seus estados defina um nível lógico diferente na entrada na qual ele esta conectado. Para isso podemos utilizar o pull up interno do Arduino como mostra o esquema da figura a seguir.

No esquema da Figura acima, quando o botão estiver pressionado o pino escolhido estará com 0V e quando não estiver pressionado estará com 5V.

A Figura abaixo mostra o esquema de ligação para os exemplos apresentados a seguir. O resistor de 1k é utilizado para proteger o pino 8 de um possível curto para a terra.

Exemplo 1: Utilizando o botão para controlar um led.

void setup() {
pinMode(13, OUTPUT); // Configura o pino 13 (led interno) como saída;
pinMode(8, INPUT_PULLUP); // Configura pino 8 como entrada e habilita pull up interno;
}
void loop() {
 if (digitalRead(8) == LOW) { // Botão Pressionado;
  digitalWrite(13, HIGH); // Liga led.
 }
 else { // Botão Não Pressionado
  digitalWrite(13, LOW); // Desliga led.
 }
}

Apesar do esquema acima funcionar perfeitamente um fenômeno indesejado está acontecendo. Vamos analisá-lo:

O QUE É O BOUNCE?

Ao pressionar um botão nós fazemos com que os seus contatos se choquem o que fará com que o botão trepide um pouco antes de se estabilizar. Esse fenômeno é similar ao que acontece com uma bolinha de ping pong quando jogada contra ao chão, que quica algumas vezes antes de se estabilizar. A figura abaixo mostra a forma de onda do sinal produzido pelo botão ao ser pressionado.

Podemos observar que o botão varia entre nível alto e baixo várias vezes antes de se estabilizar em 0V, ou seja, o led do exemplo 1 pisca várias vezes cada vez que o botão é pressionado, porém isso acontece tão rápido que nosso olho não consegue perceber.

Exemplo 2: Detectando o Bounce.

O código abaixo implementa um contador, que conta o número de vezes que o botão mudou de estado. Essa quantidade é impressa na porta serial a cada segundo. Ao apertar e soltar o botão esperamos que esse contador incremente de duas unidades.

bool last; // Guarda o último estado do botão;
uint32_t print_timer; // Timer para a impressão na porta serial;
uint8_t counter = 0; // Conta o número de mudança de estados no botão;

void setup() {
 Serial.begin(9600);
 pinMode(8, INPUT_PULLUP); // Configura pino 8 como entrada e habilita pull up interno;
 last = digitalRead(8);
}

void loop() {
 bool now = digitalRead(8); // Lê o estado atual do botão;
 if (now != last) { // Checa se houve uma mudança de estado;
  ++counter;
  last = now; // Atualiza o ultimo estado;
 }

 if (millis() - print_timer > 1000) { // Imprime a quantidade de mudanças a cada segundo;
  Serial.println(counter);
  print_timer = millis();
 }
}

Ao pressionarmos o botão com força uma única vez e soltarmos veremos que o contador será incrementado mais que duas vezes como mostra a Figura 4. Isso ocorre devido a trepidação do botão.

Exemplo 3: Tratamento simples para o Bounce.

Podemos tratar a trepidação de um botão utilizando um pequeno delay assim que uma mudança por detectada. Se ao final desse delay a mudança ainda persiste então realmente houve uma alteração no estado do botão. O tempo na qual um botão trepida dependerá de vários fatores e pode ser diferente até mesmo em botões similares. Experimente diferentes valores de delay.

bool last; // Guarda o último estado do botão
uint32_t print_timer;
uint8_t counter = 0;

void setup() {
 Serial.begin(9600);
 pinMode(8, INPUT_PULLUP); // Configura pino 8 como entrada e habilita pull up interno;
 last = digitalRead(8);
}

void loop() {
 bool now = digitalRead(8); // Lê o estado atual do botão;
 if (now != last) { // Checa se houve uma mudança de estado;
  delay(10); // Espera até que a trepidação pare;
  if (now == digitalRead(8)) { // Checa se a mudança ainda persiste;
   ++counter;
   last = now; // Atualiza o ultimo estado;
  }
 }

 if (millis() - print_timer > 1000) {
  Serial.println(counter); // Imprime um ponto para indicar a mudança;
  print_timer = millis();
 }
}

Por utilizar um delay essa abordagem faz com que o Arduino fique bloqueado/parado enquanto o botão trepida o que não é interessante.

Exemplo 4: Tratando Bounce sem bloquear o Arduino.

Criaremos uma variável para guardar o tempo em millisegundos. Sempre que uma mudança for detectada no botão essa variável receberá o tempo do millis atual. Caso a diferença entre o millis atual e essa variável seja maior que o tempo de bounce o botão parou de trepidar e então podemos checar se a mudança realmente aconteceu.

bool stable; // Guarda o último estado estável do botão;
bool unstable; // Guarda o último estado instável do botão;
uint32_t bounce_timer;
uint8_t counter = 0;

bool changed() {
 bool now = digitalRead(8); // Lê o estado atual do botão;
 if (unstable != now) { // Checa se houve mudança;
  bounce_timer = millis(); // Atualiza timer;
  unstable = now; // Atualiza estado instável;
 }
 else if (millis() - bounce_timer > 10) { // Checa o tempo de trepidação acabou;
  if (stable != now) { // Checa se a mudança ainda persiste;
   stable = now; // Atualiza estado estável;
   return 1;
  }
 }
 return 0;
}

void setup() {
 Serial.begin(9600); // Configura comunicação serial a uma taxa de 9600 bauds.
 pinMode(8, INPUT_PULLUP); // Configura pino 8 como entrada e habilita pull up interno;
 stable = digitalRead(8);
}

void loop() {
 if (changed()) {
  ++counter;
  Serial.println(counter);
 }
 // Outras tarefas;
}

Biblioteca auxiliar

A biblioteca Bounce2 possui uma implementação eficiente e de fácil uso para o tratamento de bounce. Confira!

#include <Bounce2.h>

Bounce debouncer = Bounce();

void setup() {
 pinMode(8, INPUT_PULLUP); // Configura pino 8 como entrada e habilita pull up interno;
 debouncer.attach(8); // Informa que o tratamento de debouce será feito no pino 8;
 debouncer.interval(10); // Seta o intervalo de trepidação;
}

void loop() {
 debouncer.update(); // Executa o algorítimo de tratamento;

 int value = debouncer.read(); // Lê o valor tratado do botão;

 if (value == HIGH) {
  Serial.println("Botao Nao Pressionado!");
 } else {
  Serial.println("Botao Pressionado!");
 }
}

FINALIZANDO

Este conteúdo foi totalmente voltado para esclarecer alguns pontos à respeito da leitura de botões. Esperamos que você tenha gostado deste conteúdo, sinta-se à vontade para nos dar sugestões, críticas ou elogios. Lembre-se de deixar suas dúvidas nos comentários abaixo.

Apostila Arduino Básico

 

Privacy Preference Center