Para se realizar a leitura de uma tecla ou chave é necessário utilizar um terminal do microcontrolador. No post sobre debounce são explicados mais detalhes deste procedimento. Para teclados com uma quantidade maior de chaves é possível que o microcontrolador não possua terminais disponíveis em quantidade suficiente, ou que o controlador que possua essa quantidade seja de um custo mais elevado.
A solução padrão para este problema é realizar-se a multiplexação das chaves. Isto aumenta a quantidade de chaves que pode ser lida para uma mesma quantidade de terminais. Esta economia de hardware, no entanto, aumenta a complexidade do software e o custo, em termos de tempo de computação.
Uma das técnicas mais eficientes de multiplexação para leitura de teclados é o arranjo em formato matricial. Com esta configuração podemos, com N terminais, ler até (N/2)^2 chaves, ou seja, com apenas 8 terminais é possível fazer uma leitura de 16 teclas. Já um teclado alfanumérico padrão de 105 teclas pode ser montado com apenas 21 terminais, uma redução de quase 80%.
A multiplexação é a capacidade de enviar mais de um sinal utilizando um mesmo caminho, o dividindo de algum modo, geralmente em frequências diferentes, ou em períodos de tempo diferentes.
Técnicas para leitura de teclado hadware e firmware
Na leitura matricial dispõe-se os grupos de chaves separados em colunas, de modo que seja possível ligar uma coluna por vez. Isto pode ser feito utilizando terminais de saída para acionamento de cada coluna e terminais de entrada para a leitura das linhas. É muito comum utilizar resistores de pull-ups na entrada do microcontrolador ou, caso o microcontrolador possua, pull-ups internos das próprias entradas. Para proteger o sistema de curto entre os terminais, geralmente se utiliza resistores em série nas saídas a serem controladas. Um modelo deste circuito é apresentado na figura 1.
Conforme podemos ver na Figura 1, cada chave pode ser identificada unicamente pela sua posição (linha, coluna). A leitura é realizada então por um processo conhecido como varredura: liga-se uma coluna por vez e verifica-se quais chaves daquela coluna estão ligadas. O código abaixo apresenta um modelo de varredura utilizando-se a porta B.
void main(void)
{
unsigned char i, j;
unsigned char chave[4][4] = {{0,0,0,0},{0,0,0,0}};
INTCON2 &= 0x7F; //habilita pull-up
TRISB = 0xF0; //4 terminais de entrada e 4 terminais de saída
for(;;)
{
for(i = 0; i < 4; i++)
{
PORTB = 0xff; //desliga todas as colunas
BitClr(PORTB,i); //liga a coluna correspondente
//tempo para estabilização do sinal
for(j = 0; j < 100; j++);
//realiza o teste para cada chave
for(j = 0; j < 4; j++)
{
if (!BitTst(PORTB,j+4))
{
chave[i][j] = 1;
}
else
{
chave[i][j] = 0;
}
}
}
}
}
É importante notar que o código acima não apresenta debounce em software para as teclas. Há apenas uma pequena contagem de tempo entre os acionamentos das colunas para permitir que as capacitâncias, tanto as utilizadas como filtro nas chaves quanto as parasitas, possam se carregar para informar corretamente o estado das chaves.
É incomum utilizar uma matriz para armazenar o estado das chaves, sendo mais comum a utilização de um “vetor” de bits. Utilizando uma variável unsigned int de 16 bits, é possível guardar o valor de até 16 chaves. Esta variável pode então ser interpretada como um vetor de 16 bits. Outra vantagem é a possibilidade de realizar o debounce de todas as chaves simultaneamente, bastando esperar que o valor da variável estabilize durante um tempo antes de termos certeza do estado das chaves. Juntando-se esta idéia com a questão da varredura chega-se ao código abaixo.
//último valor confiável, pode estar atrasado devido ao processo de debounce
//define a quantidade de leituras iguais antes que o sinal seja considerado estável
#define DEBOUNCE_TIME 10
static unsigned int valor = 0x0000;
unsigned int LerTeclas(void){
return valor;
}
//Função para varredura e debounce, deve ser chamada periódicamente
void VarreduraDebounceTeclas(void){
unsigned char i,j;
//variáveis estáticas para gerenciamento do debounce
static unsigned char tempo;
static unsigned int valorNovo = 0x0000;
static unsigned int valorAntigo = 0x0000;
//inicio do processo de varredura
for(i = 0; i < 4; i++){
PORTB |= 0x0F; //desliga todas as colunas
BitClr(PORTB,(i)); //liga uma coluna
//tempo para estabilizar o sinal
for(j=0;j<100;j++);
//realiza o teste para cada bit e atualiza a variável
for(j = 0; j < 4; j++) {
if (!BitTst(PORTB,j+4)) { //lógica invertida
BitSet(valorNovo,(i*4)+j);
}else{
BitClr(valorNovo,(i*4)+j);
}
}
}
//inicio do processo de debounce
if (valorAntigo == valorNovo){
tempo --;
}else{
tempo = DEBOUNCE_TIME;
valorAntigo = valorNovo;
}
if (tempo == 0){
valor = valorAntigo;
}
}
void InicializaTeclado(void){
TRISB = 0xF0; //quatro entradas e quatro saídas
INTCON2 &= 0x7F; //habilita pull-up
}
Para aqueles que gostariam de utilizar o sistema de modo ainda mais otimizado (com relação ao número de pinos do processador) existe uma técnica de multiplexação conhecida como Charlieplexing. Essa técnica foi inicialmente pensada para o acionamento de Leds. Devido à característica dos leds de conduzirem apenas em um dos sentidos é possível colocar os Leds em anti-paralelo aumentando a quantidade em duas vezes. Adicionando-se a isto a capacidade de alguns terminais que podem ser configurados como entrada e saída, permitindo levá-los a estados similares ao tristate, é possível colocar até (N^2-N) leds usando apenas N terminais. Deste modo um painel com 90 leds pode ser acionado com apenas 10 terminais do microcontrolador!
Para utilizar essa topologia para a leitura de teclas basta adicionar alguns diodos no esquemático. Isso quer dizer que é possível fazer a leitura do teclado de 16 chaves com apenas 5 terminais! Se fosse utilizada uma topologia matricial seria necessário 8 terminais do microcontrolador. Um teclado inteiro de 105 teclas precisaria de apenas 11 terminais para ser implementado. No entanto o custo de processamento, além de questões de segurança e confiabilidade, podem ser motivos suficientes para evitar esse modelo em projetos mais complexos.
Para os curiosos segue o modelo de um teclado 3*4 com apenas 4 terminais retirado do electronicdesing.com na figura 2 (frente aos 7 terminais necessários para uma topologia matricial).

Referências
Imagem Sofa-Teclado: https://www.zo-loft.com/design/






muito bom Rodrigo parabéns pelo artigo de excelente qualidade. Também tem um artigo sobre teclado matricial e arduino se alguém quiser ler como complementação do seu texto segue o link: https://blog.silvatronics.com.br/utilizando-teclado-matricial-com-arduino/
Soh uma observao. Talvez o diodo 1N4007 mostrado no circuito possa ser problematico para circuitos CMOS. Talvez um diodo schottky seja mais adequado porque tem um tensao com polarizacao direta de apenas 0,3V contra 0.8 do 1N4007.
Muito bom. tambem nao conhecia este metodo.
caramba!
Este método do Charlieplexing eu não conhecia.