Placar eletrônico no Arduino e módulo display 7 segmentos com TN1638
O módulo com chip TN1638 facilita controlar 8 dígitos, 8 leds e ler 8 botões.
Vamos implementar um placar para tênis de mesa, respeitando a regra oficial do esporte, para demonstrar o uso de um módulo de display, LEDs e botões.
Para projetos que exigem uma quantidade razoável de LEDs, botões e displays numéricos, o chip TN1638 é uma alternativa interessante. Ele cuida diretamente de até 8 botões, 8 leds e displays numéricos de 8 dígitos, e se comunica com o Arduino (ou outro microcontrolador) usando apenas 3 pinos, adotando um padrão de comunicação bastante similar ao que já vimos ao conhecer o shift register 74HC595.
Embora projetado originalmente para aplicações como aparelhos de ar condicionado, rádio relógio ou forno de microondas, existe uma variedade de módulos de uso geral baseados no TN1638, prontos para plugar em pinos do Arduino ou de um microcontrolador com pelo menos 3 pinos livres.
O artigo de hoje usa um desses produtos: o Módulo Display 7 Segmentos + 8 Leds + 8 Push Buttons Programáveis - TM1638, cedido pela Usinainfo, que patrocina o experimento deste artigo.
O módulo conta com 8 displays numéricos de 7 segmentos1, 8 LEDs vermelhos e 8 botões, que podem ser controlados diretamente por 3 pinos digitais quaisquer do Arduino.
Conexão e acionamento
A conexão ao Arduino é simples. O módulo conta com 5 pinos, denominados VCC, GND, DIO, CLK e STB. Os 2 primeiros devem ser conectados respectivamente aos pinos 5V e GND do Arduino, e os outros podem ser conectados a 3 pinos digitais quaisquer do Arduino.
A biblioteca tm1638-library, do português Ricardo Batista, oferece as funções básicas para interagir com o módulo: controlar individualmente cada led e cada botão, e exibir números (em base decimal, hexadecimal ou binária) no display.
Ao instanciar o módulo em seu programa com essa biblioteca, você passará como parâmetro os pinos do Arduino conectados ao DIO, CLK e STB, nesta ordem. Por exemplo, eu conectei o DIO ao 10, o CLK ao 11 e o STB ao 12, portanto posso instanciar o módulo assim: TM1638 modulo(10,11,12);
A partir daí, é só usar funções como setLEDs
, getButtons
e setDisplayToDecNumber
para, respectivamente, definir quais LEDs devem acender, verificar quais botões estão pressionados, e exibir um número decimal de até 8 dígitos no display. O conjunto completo de funções da biblioteca pode ser consultado no TM1638.h.
Cada LED e cada botão são representados por bits individuais nos parâmetros e retornos das funções que os gerenciam.
Um detalhe interessante: os conjuntos de leds, de botões e de pontos decimais são representados binariamente, mas os 2 primeiros (leds e botões) são referenciados com os dígitos na ordem inversa da numeração estampada na placa.
Assim, ao ler o estado dos botões, se só o S3 estiver pressionado, o número retornado pela função getButtons
será 00000100 (equivalente em decimal a 4). Se só o S5 estiver pressionado, o número será 00010000 (equivalente em decimal a 16). Note que no primeiro caso, o botão pressionado é o 3º da esquerda para a direita, mas o dígito 1 da resposta está na 3ª posição da direita para a esquerda; da mesma forma, no segundo caso o dígito 1 está na 5ª posição da direita para a esquerda.
Da mesma forma, para acender apenas o led LED3, passaríamos como parâmetro para a função setLEDs
o valor binário 00000100 (equivalente em decimal a 4). Note que o 1 é o terceiro dígito à direita, e o led que acende é o 3º à esquerda.
Para acender um ponto decimal junto a um dígito, não existe a inversão: para acender o do 4º dígito à esquerda, usaríamos como segundo argumento da função setDisplayToDecNumber
o valor 00010000 (equivalente em decimal a 16).
Se para você as operações e conversões de números binários não são simples, sugiro praticá-las, pois podem ser essenciais para o uso de funções de baixo nível para interface com hardware.
Placar no Arduino
Como exemplo de uso dos recursos do módulo, optei por implementar um placar de tênis de mesa, que segue (ao menos tanto quanto as partidas aqui da minha rua também seguem...) as regras simplificadas da Confederação Brasileira de Tênis de Mesa para partidas de 5 sets de 11 pontos cada.
Como as partidas são de 5 sets, vence o primeiro jogador que ganhar 3 deles. A contagem de pontos de cada set tem uma exceção, conhecida como "vai a 2": um set pode se prolongar indefinidamente se houver empate de 10 x 10, caso em que o vence quem primeiro alcançar 2 pontos de vantagem.
Optei por usar os 4 dígitos da esquerda o set e os pontos do jogador A, e os 4 da direita para o jogador B. Cada um desses grupos de 4 dígitos foram assim divididos: S.PPP
, onde S indica o set, e P significam os dígitos dos pontos.
Os 4 dígitos da esquerda representam a contagem de sets e pontos do primeiro jogador. Os da direita, naturalmente, indicam os do segundo jogador.
Por razões de implementação, optei por representar os sets como a contagem de sets ganhos que faltam para cada jogador ser o vencedor da partida. Assim, no início, S é 3 para ambos (pois vence o primeiro que ganhar 3 sets). Se o jogador A ganhar o primeiro, seu S passa a ser 2, e assim por diante. Vence o primeiro cujo S chegar a 0.
Assim, no primeiro set, quando o jogador A estiver com 10 pontos e B estiver com 7 pontos, o display vai mostrar 3.010
3.007
. Se o jogador A ganhar os 2 primeiros sets (e estiver faltando apenas 1 para ser o vencedor da partida) e o 3º set estiver 5 a 2 para ele, o placar mostrará 1.005
3.002
O botão S1 computa um ponto para o jogador A, e S5 faz o mesmo para o jogador B. A rotina que trata esses botões sabe aplicar a regra da mudança de set (inclusive quando há o empate de 10x10). Os botões S2 e S6 podem ser usados para descontar um ponto de A ou de B (para corrigir erros do usuário), mas têm um limite: não retrocedem para sets anteriores.
Para completar, os LEDs mostram uma animação contínua simulando a bolinha indo de um lado para outro e, quando encerra um set, piscam comemorativamente durante alguns segundos (o dobro do tempo quando é o fim da partida).
O programa
#include <TM1638.h>
TM1638 painel(10,11,12);
// numero de sets vencidos necessarios para ser o ganhador da partida:
const byte SETSVITORIA=3;
long SetA=SETSVITORIA, SetB=SETSVITORIA;
long PontoA=0, PontoB=0;
void atualizaPlacar() {
long p=SetA * 10000000 + PontoA * 10000 + SetB * 1000 + PontoB;
painel.setDisplayToDecNumber(p, 136);
}
void pisca(byte quantas) {
for (byte i=0;i<quantas;i++) {
painel.setLEDs(24);
delay(30);
painel.setLEDs(60);
delay(40);
painel.setLEDs(126);
delay(50);
painel.setLEDs(255);
delay(60);
}
}
void calculaPartida() {
if (PontoA<0) PontoA=0;
if (PontoB<0) PontoB=0;
if ((PontoA>10) & (PontoA-PontoB > 1)) {
atualizaPlacar();
SetA--;
PontoA=0;
PontoB=0;
pisca(5);
}
if ((PontoB>10) & (PontoB-PontoA > 1)) {
atualizaPlacar();
SetB--;
PontoA=0;
PontoB=0;
pisca(5);
}
if ((SetA==0) | (SetB==0)) {
atualizaPlacar();
PontoA=0;
PontoB=0;
SetA=SETSVITORIA;
SetB=SETSVITORIA;
pisca(10);
}
}
void setup() {
atualizaPlacar();
}
void soltouTecla(byte t) {
switch (t) {
case 1:
PontoA++;
break;
case 2:
PontoA--;
break;
case 16:
PontoB++;
break;
case 32:
PontoB--;
break;
}
calculaPartida();
atualizaPlacar();
}
byte anim=2;
byte ultimatecla;
word tempotecla=0;
word contavoltas=0;
boolean crescendo=true;
void animaLeds() {
painel.setLEDs((128 | anim | 1));
if (crescendo) anim=anim*2;
else anim=anim/2;
if (anim>63) crescendo=false;
if (anim<3) crescendo=true;
}
void loop() {
byte teclas = painel.getButtons();
if (teclas!=ultimatecla) {
if ((teclas==0) & (tempotecla>4)) {
soltouTecla(ultimatecla);
}
ultimatecla=teclas;
tempotecla=0;
}
else {
tempotecla++;
}
if (contavoltas%300==0) {
animaLeds();
}
contavoltas++;
}
Colorizei os trechos relevantes para a interface entre o Arduino e o módulo, começando pelas 2 linhas em roxo que incluem a biblioteca e instanciam o módulo, como já descrito no texto acima.
A seguir temos, em vermelho, a função atualizaPlacar
, que converte a contagem de sets de cada jogador (SetA
e SetB
) e as respectivas pontuações (PontoA
e PontoB
) em um número de 8 dígitos, na forma também já apresentada no texto acima, e usa o método setDisplayToDecNumber
para exibir esse número no painel.
Note o segundo parâmetro, que é o número 136, cujo equivalente binário é 10001000 – ele é o responsável por termos um ponto decimal aceso após o 1º dígito, e outro após o 5º dígito.
Logo após, em verde, vem a função pisca, que repete uma animação simples no conjunto de 8 leds. Os números 24, 60, 126 e 255, usados junto às chamadas à função setLEDs
indicam a animação, e ficam mais fáceis de visualizar se os convertermos para binário:
00011000 (24)
00111100 (60)
01111110 (126)
11111111 (255)
A função calculaPartida
é chamada sempre que precisamos atualizar a pontuação, e é onde está concentrada a interpretação das regras de pontuação do tênis de mesa, mas não tem qualquer contato direto com o módulo (para isso ela usa as funções atualizaPlacar
e pisca
).
A função soltouTecla
, em marrom, incrementa ou decrementa a pontuação de cada jogador, dependendo do botão que tiver sido pressionado (ela é chamada apenas no momento em que o usuário solta o botão). Ela recebe como parâmetro um valor que foi previamente lido do módulo por meio da função getButtons
(veremos adiante a respectiva chamada), e age a partir da conversão binária já detalhada acima. Os números das cláusulas case
correspondem aos botões S1 (00000001 ou 1), S2 (00000010 ou 2), S5 (00010000 ou 16) e S6 (00100000 ou 32).
Em rosa temos a função animaLeds
que, com um pouco de aritmética binária e a função OR (representada por uma barra vertical) simula nos LEDs uma bolinha de tênis de mesa indo e voltando durante a partida. O entendimento dos números envolvidos fica como exercício de fixação para o leitor interessado.
Para completar, em verde dentro da função loop
, temos o trecho que monitora constantemente, por meio da função getButtons
, o estado dos 8 botões. Quando esse estado muda, quer dizer que uma tecla foi pressionada ou solta.
Sempre que uma tecla é pressionada, esse trecho a armazena na variável ultimatecla
, e começa a contar por quanto tempo2 ela ficou pressionada. Quando a tecla é liberada, caso esse tempo tenha sido suficiente para ser considerado um acionamento válido (e não um mero esbarrão na tecla), a nossa já conhecida função soltouTecla
é acionada, recebendo como parâmetro a ultimatecla
.
Basicamente, o nosso loop do programa é composto por esse controle do teclado, e por chamadas periódicas3 à função animaLeds
. Toda a lógica do programa é comandada a partir do acionamento de teclas.
Exercícios para o leitor
a) As regras simplificadas da Confederação Brasileira de Tênis de Mesa admitem como base para a partida qualquer número ímpar de sets, e o vencedor é aquele que ganhar mais da metade deles. Assim, ao optar por melhor de 3 sets, o vencedor da partida será aquele que primeiro vencer 2 deles; em melhor de 7 sets, o vencedor da partida será aquele que primeiro ganhar 4 deles, etc. A lógica de funcionamento do nosso placar trata especificamente de partidas disputadas em melhor de 5 sets (na qual o vencedor é aquele que primeiro vencer 3 sets), mas eu já deixei o fundamento preparado para facilitar a seleção desse número. Como você alteraria o programa para permitir, com o pressionamento do botão S3, selecionar o número de sets da partida?
b) Como você modificaria o programa para zerar o placar (iniciar uma nova partida) quando for pressionado o botão S4?
Patrocínio: Usinainfo
Quero agradecer à UsinaInfo, que vem patrocinando alguns experimentos do BR-Arduino, inclusive este com o Módulo Display 7 Segmentos + 8 Leds + 8 Push Buttons Programáveis - TM1638.
Além de receber material da empresa para os experimentos como parte do acordo de patrocínio, eu já fiz compras de componentes lá, aproveitando a variedade, o estoque bem suprido, as descrições detalhadas e a qualidade do seu sistema de comércio eletrônico. Recomendo sem ressalvas.
Agradeço também pela confiança que ficou expressa nos termos do patrocínio: a empresa me enviou os componentes necessários ao experimento combinado, mas não fez qualquer exigência sobre a forma e nem buscou exercer controle sobre o que eu fosse escrever. Obrigado, UsinaInfo!
- Mais propriamente, são displays numéricos de 8 segmentos: há os 7 segmentos tradicionais referentes aos elementos de cada número, e o 8º diz respeito ao ponto decimal, que pode ser controlado individualmente junto a cada dígito. ↩
- Ou, mais precisamente, quantas voltas da função
loop
. ↩ - A cada 300 voltas do
loop
. ↩
Comentar