Introdução
Chaves eletromecânicas são uma forma quase primitiva de interface entre um sistema eletrônico e um ser humano. Por meio delas, podemos selecionar, programar e responder a estímulos do sistema. As chaves organizadas em arranjos matriciais, por exemplo, formam dispositivos mais complexos, que conhecemos por teclados. Este artigo trata de rotinas padronizadas em linguagem C para microcontroladores da família MCS-51, para configurar e tratar as particularidades de um teclado, gerando os sinais de estímulo e realizando a leitura, realizando também debouncing por software, identificando a(s) tecla(s) acionada(s) e preparando uma rotina para o tratamento dessa(s) tecla(s). Para uma melhor compreensão do que será tratado nesse artigo, recomendo a leitura dos seguintes artigos técnicos:
Arquitetura de Hardware
Na biblioteca de rotinas padronizadas, que será apresentada a seguir, são consideradas duas formas distintas de seleção da matriz de teclas:
Seleção por linhas codificadas
A seleção por linhas codificadas, como ilustrado na Figura 1, pode ser realizada utilizando-se um decodificador/ Multiplexador na saída dos sinais de seleção. Um componente desse tipo é o 74LS139. Nessa forma de seleção do teclado são utilizados poucos pinos do port para codificar o número da linha de seleção a ser ativada em código binário. Ou seja, 1 bit seleciona até duas linhas, 2 bits quatro etc. A intenção foi a de economizar pinos nos ports do microcontrolador, especialmente se o teclado for grande. A rotina da biblioteca para essa configuração foi batizada de ROTINA DE LEITURA DE MATRIZ DE CONTATOS I.
.
Seleção por linhas separadas
A seleção com linhas separadas é realizada conforme ilustrado na Figura 2. A rotina da biblioteca para essa configuração foi batizada de ROTINA DE LEITURA DE MATRIZ DE CONTATOS II.
Rotinas padronizadas em C para teclados matriciais
O código em linguagem C para os teclados matriciais segue o mesmo padrão do desenvolvido para os timers em Sistemas Operacionais de Tempo Real – Timers. Tem um conjunto de definições e variáveis, uma parte a ser inserida na rotina de interrupção do timer, de forma que quando o timer virtual do teclado terminar a contagem, é sinalizado para o programa principal que a contagem terminou. No programa principal são inseridas algumas inicializações, as rotinas de leitura do teclado, debounce e tratamento da tecla. Essa solução é um pouco diferente da implementada para os displays de 7 segmentos, pois entende-se que tratar teclado não é uma tarefa prioritária para um sistema e não necessita de uma temporização precisa. Assim, o trecho de código para a leitura do teclado não foi colocado dentro da rotina de interrupção do timer. Assim ela é executada na medida em que é possível.
Definições e variáveis
A seguir os códigos exemplo referentes às definições e configurações correspondentes às arquiteturas descritas anteriormente. Esse trecho de código é inserido tipicamente num arquivo do tipo .h do programa.
ROTINA DE LEITURA DE MATRIZ DE CONTATOS I (Sinais de seleção codificados) – Definições e Inicializações
//*******************************************************************************
//* *
//* ROTINAS PADRONIZADAS PARA USO EM APLICAÇÕES *
//* DE SISTEMAS MICROCONTROLADOS (FAMÍLIA 8051) *
//* (Resp. Eng. Puhlmann) *
//* *
//* (31/05/2000 - Rev. 14/06/2000) *
//* *
//*******************************************************************************
//===============================================================================
//= =
//= ROTINAS PADRONIZADAS LEITURA DE MATRIZES DE CONTATOS =
//= =
//===============================================================================
// Vamos definir inicialmente, que as matrizes de contatos são definidas como um
// conjunto de bits consecutivos organizados em N * M bits lidos direto nos ports
//
//===============================================================================
//===============================================================================
//
// ROTINA DE LEITURA DE MATRIZ DE CONTATOS I - Linhas de seleção codificadas
// (Necessita de decodificador na saída para acionar as linhas de seleção)
//
// Entradas : - bFlagDeTemporizacaoDaMatrizDeContatos - Sinaliza que pode realizar a leitura
// Saídas : - bFlagDeNovaLeituraDeMatrizDeContatos - Sinaliza que foi realizada nova leitura da matriz
// - ucMatrizDeContatosDeEntrada[] - Leituras atualizadas no vetor
//
//===== =====
//----------- Definições de parâmetros -------------------------
#define FREQ_DE_INTERRUP_TIMER_MATRIZ_DE_CONTATOS 10.0 // Hz , que corresponde à freqüencia de leitura da matriz/ teclado
// Inicializa o valor de preset do timer de software - Observe que FREQUENCIA_DE_INTERRUPCAO_TIMER_0 está
// definido no módulo do timer
#define PRESET_TEMPORIZADOR_DA_MATRIZ_DE_CONTATOS (unsigned int)((FREQUENCIA_DE_INTERRUPCAO_TIMER_0/ FREQ_DE_INTERRUP_TIMER_MATRIZ_DE_CONTATOS)+ 0.5)
// Define o número de linhas da matriz (exemplo)
#define NUMERO_DE_LINHAS_DA_MATRIZ 3 // Número de linhas de seleção
// Aqui define-se os ports onde estão conectadas a matriz e as linhas de selação
#define PORTA_DE_ENTRADA_DA_MATRIZ P1 // Define o Port onde está conectada a matriz
#define PORTA_DE_SAIDA_DA_SELECAO_DA_MATRIZ P1 // Define o port onde estão conectadas as linhas de seleção
// As máscaras a seguir definem máscaras do tipo cheia de "uns", onde existem linhas ativas. Obrigatoriamente essas linhas
// têm que ser consecutivas e no máximo 8, ocupando o port inteiro
// Alguns exemplos ... 3 linhas a partir do bit 3 do port 1
#define MASCARA_DA_MATRIZ 0x38 // Filtra os bits de entrada da matriz (3 nesse exemplo)
// Duas linhas de selação ... bits 6 e 7 do port 1
#define MASCARA_DA_SELECAO 0xc0 // Filtra os bits dos sinais de seleção
// definição do "mais um" para o contador responsável pela seleção da matriz (A ser somado nos bits
// de seleção). Nesse exemplo, o bit 6 = 1. Tem que ser casado, com as linhas de seleção, é claro!
#define INCREMENTO_PARA_O_CONTADOR_DE_SELECAO 0x40 // Define o "mais 1" no contador
// Aqui define-se se as saídas são normais ou invertidas, complemento de 1. Se forem invertidas, realiza-se
// um ou exclusivo da máscara de seleção com elas, invertendo a todos os bits..
//#define SAIDAS_INVERTIDAS 1 // Define se as saidas de selecao sao invertidas ou nao
#ifdef SAIDAS_INVERTIDAS
#define MASCARA_DE_INVERSAO MASCARA_DA_SELECAO
#else
#define MASCARA_DE_INVERSAO 0x00
#endif
//*** Aqui definem-se as variáveis globais desse trecho da rotina (fora do bloco principal - main)***
volatile bit bFlagDeTemporizacaoDaMatrizDeContatos = DESLIGA; // Sinaliza que pode realizar nova leitura
unsigned int unTimerDaMatrizDeContatos = PRESET_TEMPORIZADOR_DA_MATRIZ_DE_CONTATOS;
unsigned char ucMatrizDeContatosDeEntrada[NUMERO_DE_LINHAS_DA_MATRIZ] = { 0, 0, 0};
volatile bit bFlagDeNovaLeituraDeMatrizDeContatos = DESLIGA; // Sinaliza que foi realizada nova leitura
ROTINA DE LEITURA DE MATRIZ DE CONTATOS II (Sinais de seleção separados) – Definições e Inicializações
//*******************************************************************************
//* *
//* ROTINAS PADRONIZADAS PARA USO EM APLICAÇÕES *
//* DE SISTEMAS MICROCONTROLADOS (FAMÍLIA 8051) *
//* (Resp. Eng. Puhlmann) *
//* *
//* (31/05/2000 - Rev. 14/06/2000) *
//* *
//*******************************************************************************
//===============================================================================
//= =
//= ROTINAS PADRONIZADAS LEITURA DE MATRIZES DE CONTATOS =
//= =
//===============================================================================
// Vamos definir inicialmente, que as matrizes de contatos são definidas como um
// conjunto de bits consecutivos organizados em N * M bits lidos direto nos ports
//
//===============================================================================
//===============================================================================
//
// ROTINA DE LEITURA DE MATRIZ DE CONTATOS II - Linhas de seleção separadas
// (Cada linha de seleção corresponde a um bit da Porta)
//
// Entradas : - bFlagDeTemporizacaoDaMatrizDeContatos - Sinaliza que pode realizar a leitura
// Saídas : - bFlagDeNovaLeituraDeMatrizDeContatos - Sinaliza que foi realizada nova leitura da matriz
// - ucMatrizDeContatosDeEntrada[] - Leituras atualizadas no vetor
//===== =====
//----------- Definições de parâmetros -------------------------
#define FREQ_DE_INTERRUP_TIMER_MATRIZ_DE_CONTATOS 10.0 // Hz, que corresponde à freqüencia de leitura da matriz/ teclado
// Inicializa o valor de preset do timer de software - Observe que FREQUENCIA_DE_INTERRUPCAO_TIMER_0 está
// definido no módulo do timer
#define PRESET_TEMPORIZADOR_DA_MATRIZ_DE_CONTATOS (unsigned int)((FREQUENCIA_DE_INTERRUPCAO_TIMER_0/ FREQ_DE_INTERRUP_TIMER_MATRIZ_DE_CONTATOS)+ 0.5)
// Define o número de linhas da matriz (exemplo)
#define NUMERO_DE_LINHAS_DA_MATRIZ 4 // Número de linhas de seleção
// Aqui define-se os ports onde estão conectadas a matriz e as linhas de selação
#define PORTA_DE_ENTRADA_DA_MATRIZ P1 // Define o Port onde está conectada a matriz
#define PORTA_DE_SAIDA_DA_SELECAO_DA_MATRIZ P3 // Define o port onde estão conectadas as linhas de seleção
// As máscaras a seguir definem máscaras do tipo cheia de "uns", onde existem linhas ativas. Obrigatoriamente essas linhas
// têm que ser consecutivas e no máximo 8, ocupando o port inteiro
// Alguns exemplos ... 3 linhas a partir do bit 3 do port 1
#define MASCARA_DA_MATRIZ 0x38 // Filtra os bits de entrada da matriz (3 nesse exemplo)
// Quatro linhas de selação ... bits 2 a 5 do port 3
#define MASCARA_DA_SELECAO 0x3c // Filtra os bits dos sinais de seleção
// Bit de seleção a ser shiftado, para gerar o sinal de seleção ... bit 2 (define a primeira linha a ser lida)
#define SELETOR_DOS_SINAIS_DE_SELECAO 0x04 // Define posição do primeiro bit do conjunto de seleção
// Aqui define-se se as saídas são normais ou invertidas, complemento de 1. Se forem invertidas, realiza-se
// um ou exclusivo da máscara de seleção com elas, invertendo a todos os bits..
//#define SAIDAS_INVERTIDAS 1 // Define se as saidas de selecao sao invertidas ou nao
#ifdef SAIDAS_INVERTIDAS
#define MASCARA_DE_INVERSAO MASCARA_DA_SELECAO
#else
#define MASCARA_DE_INVERSAO 0x00
#endif
//*** Aqui definem-se as variáveis globais desse trecho da rotina (fora do bloco principal - main)***
volatile bit bFlagDeTemporizacaoDaMatrizDeContatos = DESLIGA; // Sinaliza que pode realizar nova leitura
unsigned int unTimerDaMatrizDeContatos = PRESET_TEMPORIZADOR_DA_MATRIZ_DE_CONTATOS;
unsigned char ucMatrizDeContatosDeEntrada[NUMERO_DE_LINHAS_DA_MATRIZ] = { 0, 0, 0, 0};
volatile bit bFlagDeNovaLeituraDeMatrizDeContatos = DESLIGA; // Sinaliza que foi realizada nova leitura
São utilizadas algumas convenções com relação às definições e uso das constantes. Em todo o programa, 1 quer dizer ligado ou ativo e 0 desligado ou inativo. Este trecho de código é na sua essência o configurador do teclado, amarrando as particularidades do hardware com o software. O que deve ser configurado:
- #define FREQ_DE_INTERRUP_TIMER_MATRIZ_DE_CONTATOS: Define a frequência com a qual é realizada um a varredura completa do teclado. 10 vezes por segundo é uma taxa razoável;
- #define NUMERO_DE_LINHAS_DA_MATRIZ: Define o número de linhas de seleção da matriz de teclas;
- #define PORTA_DE_ENTRADA_DA_MATRIZ: Define em que port estão conectadas as entradas do teclado;
- #define PORTA_DE_SAIDA_DA_SELECAO_DA_MATRIZ: Define em que port estão conectadas as linhas de seleção das teclas;
- #define MASCARA_DA_MATRIZ: Aqui é definida a posição e abrangência das linhas de entrada;
-
#define MASCARA_DA_SELECAO: Filtra os bits dos sinais de seleção;
-
#define SELETOR_DOS_SINAIS_DE_SELECAO: Define posição do primeiro bit do conjunto de seleção que será deslocado em anel para selecionar as colunas de teclas ;
-
#define SAIDAS_INVERTIDAS : Colocar essa linha como comentário, se acaso as linhas de seleção não precisarem ser invertidas;
- #define INCREMENTO_PARA_O_CONTADOR_DE_SELECAO: Define o “mais 1” no contador, no caso da seleção codificada;
- unsigned char ucMatrizDeContatosDeEntrada[NUMERO_DE_LINHAS_DA_MATRIZ]: É necessário iniciar com zeros essa matriz;
As demais variáveis e constantes são calculadas automaticamente pelo programa.
Rotina de debounce – Definições e Inicializações
//===============================================================================
//
// ROTINA QUE REALIZA O "DEBOUNCE" DA MATRIZ DE CONTATOS
//
// uma vez lidos os contatos, pode-se inserir essa rotina para realizar o debounce, se necessário
//
// Entradas : - bFlagDeNovaLeituraDeMatrizDeContatos - Sinaliza que há leitura nova da matriz de entrada
// - ucMatrizDeContatosDeEntrada[ - Vetor contendo as leituras atualizadas da matriz
// - ucMatrizDeContatosDeEntradaAnterior[] - Vetor contendo as leituras anteriores da matriz
// Saídas : - ucMatrizDeContatosDeEntradaAnterior[] - Vetor contendo as leituras anteriores da matriz atualizadas
// - bFlagDeTerminoDeDebounce - Sinalização de término de debounce, permissão para análise
//
//===== =====
#define PRESET_CONTADOR_DEBOUNCE 3 // Número de dados novos com a leitura estável ( 0 - 255)
unsigned char ucMatrizDeContatosDeEntradaAnterior[NUMERO_DE_LINHAS_DA_MATRIZ] = { 0, 0, 0, 0};
unsigned char ucContadorDeDebounce = PRESET_CONTADOR_DEBOUNCE;
volatile bit bFlagDeTerminoDeDebounce = DESLIGA;
volatile bit bFlagNovosDados = DESLIGA;
Rotina de Identificação de Contatos – Definições e Inicializações
//===============================================================================
//
// ROTINA QUE REALIZA A IDENTIFICAÇÃO DOS CONTATOS FECHADOS
//
// Após a realização da leitura, ou após o debounce, pode-se identificar os
// contatos que foram acionados, se houver. Ela numero os contatos sequencialmente
// a partir do primeiro.
//
// Entradas : - bFlagDeTerminoDeDebounce - Sinaliza que há leitura estável da matriz de entrada
// - ucMatrizDeContatosDeEntrada[] - Vetor contendo as leituras atualizadas da matriz
// - ucMatrizDeContatosDeReferencia[] - Vetor contendo as máscaras de referência para identificar
// os contatos acionados
// Saídas : - bFlagResultadosMatrizDeContatos - Sinaliza que há contatos acionados
// - ucContadorDeContatosFechados - Número de contatos acionados
// - ucVetorDeResultados[] - Vetor contendo a posição relativa do contato acionado
//
//===== =====
// Define o número de colunas da matriz.. (quantos bits tem em cada leitura)
#define NUMERO_DE_COLUNAS_DA_MATRIZ 03
// Identifica a posição do primeiro bit a ser identificado, e utilizado para comparação (com operação AND
// com a máscara) e deslocamento da máscara para leitura dos bits subseqüentes
#define MASCARA_DE_SELECAO_DE_COLUNA_INICIAL 0x08
// Define um número máximo de resultados para o vetor de resultados
#define TAMANHO_MAXIMO_DO_VETOR_DE_RESULTADOS 5
// define e inicializa um vetor de referência, onde os bits = 1, sinalizam que o contato fechado = 1, e
// bits = 0, quando está em zero... Abrange apenas os bits conectados da matriz... Aqui no exemplo, bits
// de 4 a 6
unsigned char ucMatrizDeContatosDeReferencia[NUMERO_DE_LINHAS_DA_MATRIZ] = {0x38, 0x38, 0x38, 0x38};
unsigned char ucVetorDeResultados[TAMANHO_MAXIMO_DO_VETOR_DE_RESULTADOS] = {0, 0, 0, 0, 0};
// Contador que indica o número de contatos fechados no vetor
unsigned char ucContadorDeContatosFechados;
// bit de sinalização de que encontrou contatos "fechados"
bit bFlagResultadosMatrizDeContatos = DESLIGA;
//*************************************************************************
Código a ser inserido no corpo do programa principal
O código a seguir testa um flag (bFlagDeTemporizacaoDaMatrizDeContatos) que é ativado pela rotina de interrupção quando for terminada a contagem do temporizador virtual de leitura do teclado. Se o flag foi acionado, é realizada a varredura do teclado e testado se há teclas acionadas. Se houver teclas acionadas, a rotina sinaliza com outro flag (bFlagDeNovaLeituraDeMatrizDeContatos) de que há contatos acionados. Nesse código estão indicadas as rotinas de debounce, identificação dos contatos acionados e de execução de ações em função dos contatos acionados. Esses códigos serão apresentados mais adiante.
ROTINA DE LEITURA DE MATRIZ DE CONTATOS I (Sinais de seleção codificados)
//******************************************************************************************
//********* O Bloco seguinte refere-se aos blocos a serem inseridos no loop principal ******
//********* ******
void main(void)
{
.
.
.
.
for(;;)
{
.
.
.
//*******************************************************************************
//****** Trecho a ser inserido no programa principal - Matriz de Contatos - I ***
//****** ***
unsigned char ucIndice;
unsigned char ucContadorDeSelecao;
if(bFlagDeTemporizacaoDaMatrizDeContatos == LIGA) // Testa se pode realizar a leitura da matriz
{
bFlagDeTemporizacaoDaMatrizDeContatos = DESLIGA; // Desliga o Flag
PORTA_DE_ENTRADA_DA_MATRIZ |= MASCARA_DA_MATRIZ; // Prepara a porta para leitura
ucContadorDeSelecao = 0;
//*** Início da rotina para realizar a leitura da matriz de contatos ***
for(ucIndice = 0; ucIndice < NUMERO_DE_LINHAS_DA_MATRIZ ; ucIndice++)
{
unsigned char ucAux;
ucAux = ~MASCARA_DA_SELECAO; // Inverte os bits da mascara
ucAux &= PORTA_DE_SAIDA_DA_SELECAO_DA_MATRIZ; // Filtra os bits de seleção
ucAux |= (ucContadorDeSelecao ^ MASCARA_DE_INVERSAO);
PORTA_DE_SAIDA_DA_SELECAO_DA_MATRIZ = ucAux;
// Le a linha de contatos, e filtra, deixando só o que interessa
ucMatrizDeContatosDeEntrada[ucIndice] = PORTA_DE_ENTRADA_DA_MATRIZ & MASCARA_DA_MATRIZ;
ucContadorDeSelecao += INCREMENTO_PARA_O_CONTADOR_DE_SELECAO; // Incrementa o contador
}
// Desliga as linhas de seleção
ucIndice = ~MASCARA_DA_SELECAO; // Inverte os bits da mascara
ucIndice &= PORTA_DE_SAIDA_DA_SELECAO_DA_MATRIZ; // Filtra os bits de seleção
PORTA_DE_SAIDA_DA_SELECAO_DA_MATRIZ = (ucIndice ^ MASCARA_DE_INVERSAO);
}
//************* Fim da rotina Matriz de Contatos - I ****************************
//***************************************
//******** Rotina de "Debounce " ********
//******* ********
//**************************************************************
//************* Rotina que identifica os contatos acionados ****
//************* ****
//**********************************************************************************
//************** Inicio da rotina de execução de ações em função dos contatos ******
//************** ******
.
.
.
} // fim do loop principal
.
.
.
} // Fim do bloco principal
ROTINA DE LEITURA DE MATRIZ DE CONTATOS II (Sinais de seleção separados)
//******************************************************************************************
//********* O Bloco seguinte refere-se aos blocos a serem inseridos no loop principal ******
//********* ******
void main(void)
{
.
.
.
.
for(;;)
{
.
.
.
//********************************************************************************
//****** Trecho a ser inserido no programa principal - Matriz de Contatos - II ***
//****** ***
unsigned char ucIndice;
if(bFlagDeTemporizacaoDaMatrizDeContatos == LIGA) // Testa se pode realizar a leitura da matriz
{
bFlagDeTemporizacaoDaMatrizDeContatos = DESLIGA; // Desliga o Flag
PORTA_DE_ENTRADA_DA_MATRIZ |= MASCARA_DA_MATRIZ; // Prepara a porta para leitura
//*** Início da rotina para realizar a leitura da matriz de contatos ***
for(ucIndice = 0; ucIndice < NUMERO_DE_LINHAS_DA_MATRIZ ; ucIndice++)
{
unsigned char ucAux;
ucAux = ~MASCARA_DA_SELECAO; // Inverte os bits da mascara
ucAux &= PORTA_DE_SAIDA_DA_SELECAO_DA_MATRIZ; // Filtra os bits de seleção
ucAux |= ((SELETOR_DOS_SINAIS_DE_SELECAO << ucIndice) ^ MASCARA_DE_INVERSAO);
PORTA_DE_SAIDA_DA_SELECAO_DA_MATRIZ = ucAux;
// Le a linha de contatos, e filtra, deixando só o que interessa
ucMatrizDeContatosDeEntrada[ucIndice] = PORTA_DE_ENTRADA_DA_MATRIZ & MASCARA_DA_MATRIZ;
}
// Desliga as linhas de seleção
ucIndice = ~MASCARA_DA_SELECAO; // Inverte os bits da mascara
ucIndice &= PORTA_DE_SAIDA_DA_SELECAO_DA_MATRIZ; // Filtra os bits de seleção
PORTA_DE_SAIDA_DA_SELECAO_DA_MATRIZ = (ucIndice ^ MASCARA_DE_INVERSAO);
// Inserir essa instrução apenas se necessitar de "debounce"
bFlagDeNovaLeituraDeMatrizDeContatos = LIGA; // Sinaliza que foi realizada nova leitura
}
//******** Término da rotina Matriz de Contatos - II ********
//***************************************
//******** Rotina de "Debounce " ********
//******* ********
//**************************************************************
//************* Rotina que identifica os contatos acionados ****
//************* ****
//**********************************************************************************
//************** Inicio da rotina de execução de ações em função dos contatos ******
//************** ******
.
.
.
} // fim do loop principal
.
.
.
} // Fim do bloco principal
Rotina de debounce
Na saída da rotina de leitura de contatos é acionado um flag (bFlagDeNovaLeituraDeMatrizDeContatos) para sinalizar que há novos dados. Esse flag é testado e, se afirmativo, inicia-se o processo de debounce. A rotina de debounce basicamente testa se o que foi lido está estável há pelo menos N leituras. Se não, é reiniciado o contador de debounce. Como a frequência de leitura do teclado é baixa, o valor de N pode ser pequeno também, por exemplo 2 ou 3. Quando a rotina decide que a leitura está estável e tem tecla acionada, é acionado um flag (bFlagDeTerminoDeDebounce) para ativar o próximo passo do tratamento de teclas.
//***************************************
//******** Rotina de "Debounce " ********
//******* ********
// Testa se há leitura nova
if(bFlagDeNovaLeituraDeMatrizDeContatos == LIGA)
{
bit bFlagOk = LIGA;
unsigned char ucIndice;
// Reseta o flag de sinalização
bFlagDeNovaLeituraDeMatrizDeContatos = DESLIGA; // Sinaliza que foi realizada nova leitura
// Se o contador for diferente de zero, decrementa o contador
if (ucContadorDeDebounce != 0)
{
ucContadorDeDebounce--;
}
for(ucIndice = 0; ucIndice < NUMERO_DE_LINHAS_DA_MATRIZ ; ucIndice++)
{
// Compara a matriz de entrada com a matriz anterior
if(ucMatrizDeContatosDeEntrada[ucIndice] != ucMatrizDeContatosDeEntradaAnterior[ucIndice])
{
// Se diferentes, sinaliza
bFlagNovosDados = LIGA;
bFlagOk = DESLIGA;
}
// Transfere a matriz para a área da matriz anterior
ucMatrizDeContatosDeEntradaAnterior[ucIndice] = ucMatrizDeContatosDeEntrada[ucIndice];
}
// Testa se houve alteração das entradas
if(bFlagOk == DESLIGA)
{
// Se houve alteração, reinicializa o contador
ucContadorDeDebounce = PRESET_CONTADOR_DEBOUNCE;
}
// Testa se estabilizou as leituras
if((ucContadorDeDebounce == 0) && (bFlagNovosDados == LIGA))
{
// Sinaliza que estabilizou a leitura
bFlagDeTerminoDeDebounce = LIGA;
}
}
//************* Término da rotina de debounce ***************
Rotina que identifica os contatos acionados
Esta rotina varre a matriz de teclas, verifica se há teclas acionadas. Se positivo, determina a posição dessas teclas e o número total de teclas acionadas. Se houver teclas acionadas, a rotina sinaliza com um flag (bFlagResultadosMatrizDeContatos) para ativar o próximo passo do programa de tratamento de teclas.
//**************************************************************
//************* Rotina que identifica os contatos acionados ****
//************* ****
// Verifica se terminou o "debounce"
if(bFlagDeTerminoDeDebounce ==LIGA)
{
unsigned char ucIndice, ucContadorAux;
bFlagDeTerminoDeDebounce = DESLIGA;
bFlagNovosDados = DESLIGA;
// Inicializa o contador de contatos
ucContadorDeContatosFechados = 0; // Inicializa contador de contatos fechados
ucContadorAux = 0; // Inicializa o contador Auxiliar, que indicará o índice do contato
for(ucIndice = 0; ucIndice < NUMERO_DE_LINHAS_DA_MATRIZ ; ucIndice++)
{
unsigned char ucContatoAux;
// Verifica se nesssa linha há contatos fechados
ucContatoAux = (ucMatrizDeContatosDeEntrada[ucIndice] ^ ucMatrizDeContatosDeReferencia[ucIndice]);
if(ucContatoAux != 0)
{
unsigned char ucAux, ucRegistroDeDeslocamento;
ucRegistroDeDeslocamento = MASCARA_DE_SELECAO_DE_COLUNA_INICIAL;
for(ucAux = 0; ucAux < NUMERO_DE_COLUNAS_DA_MATRIZ; ucAux++)
{
if((ucRegistroDeDeslocamento & ucContatoAux) != 0)
{
ucVetorDeResultados[ucContadorDeContatosFechados++] = ucContadorAux; // Transfere a posição do contato
bFlagResultadosMatrizDeContatos = LIGA; // Sinaliza que encontrou contatos acionados
}
ucRegistroDeDeslocamento = ucRegistroDeDeslocamento << 1; // Desloca a máscara de um
ucContadorAux++; // Incrementa o contador
}
}
}
}
//************** Fim da rotina e identificação dos contatos acionados ***
Rotina de execução de ações em função dos contatos
//**********************************************************************************
//************** Inicio da rotina de execução de ações em função dos contatos ******
//************** ******
// Testa se há contatos acionados
if(bFlagResultadosMatrizDeContatos == LIGA)
{
unsigned char ucIndice;
// Verifica todos os contatos localizados
for(ucIndice = 0; ucIndice < ucContadorDeContatosFechados; ucIndice++)
{
// Aqui verifica qual é o contato acionado e realiza alguma ação dependendo de qual for...
switch(ucVetorDeResultados[ucIndice])
{
case 0:
{
break;
}
case 1:
{
break;
}
.
.
.
.
default:
{
}
}
}
Código a ser inserido na rotina de interrupção do Timer 0
O trecho de código a seguir implementa um temporizador virtual para temporização do início de varredura do teclado. Sempre que o contador termina a contagem, o contador é reinicializado e é sinalizado que a temporização terminou.
//*******************************************************************************
// TIMER 0
// rotina de interrupção
//
void vInterrupcaoTimer0() interrupt 1
{
.
.
.
//****** Trecho a ser inserido na rotina de interrupção do timer - Matriz de Contatos - Timer de software***
if(--unTimerDaMatrizDeContatos == 0)
{
unTimerDaMatrizDeContatos = PRESET_TEMPORIZADOR_DA_MATRIZ_DE_CONTATOS; // Reinicializa temporizador
bFlagDeTemporizacaoDaMatrizDeContatos = LIGA; // Sinaliza que terminou a contagem
}
//************** Fim da Rotina **************************************************
.
.
.
}
EXEMPLO
Nesse programa foi aproveitado o mesmo programa desenvolvido para a demonstração de operação dos timers e displays de 7 segmentos de tempo real, acrescidos os trechos relativos ao teclado. Aqui será programado o Timer 0 no modo 2 de operação, com o microcontrolador operando com um clock de 4 MHz e uma frequência de interrupção de 1,5 kHz. Foram definidos 3 displays de 7 segmentos do tipo II com a seleção separada, e atualizados a cada 30 contagens cada um. Também foi definido um teclado matricial, cujas linhas de entrada estão alocadas aos bits 0 a 2 do Port 2 e as linhas de seleção aos bits 4 a 7 do mesmo Port. Confira o código no quadro abaixo.
//***************************************************************************
//** **
//** ROTINA EXEMPLO PARA TESTE DO USO DO TIMER, DISPLAYS E TECLADO **
//** PARA SISTEMAS OPERACIONAIS DE TEMPO REAL **
//** (Resp. Eng. Puhlmann) **
//** **
//** (15/12/2014 - rev. 16/12/2014) **
//** **
//***************************************************************************
#include <AT89S53.H> // AT89S8253
//----------------------------
#define DESLIGA 0
#define LIGA 1
// Define-se aqui a freqüência de clock da CPU
#define FREQUENCIA_DO_CLOCK_DA_CPU 4.0e+6 // Hz ----> 4 MHz por exemplo
// Define o pre-scaler
#define FATOR_DE_ESCALA_CLOCK_TIMER 12.0f // Divide o clock por 12
// Define a freqüência de interrupção do timer
#define FREQUENCIA_DE_INTERRUPCAO_TIMER_0 1.5e+3 // Hz ---- > 1,5 kHz
//--------------
// Definição de variáveis referentes aos timers
//------- Timer 0 ----------
#define MASCARA_CT_0 0xfb
// Definição utilizada apenas quando o timer estiver no modo 2 de operação
// se acaso nao for usado no modo 2, comentar a linha abaixo
// Define o Timer 0 como operando no modo 2
// A conta desse preset tem que dar um número de 0 a 255, senão não funciona...
#define PRESET_TIMER_0_MODO_2 (255 - (unsigned char)((FREQUENCIA_DO_CLOCK_DA_CPU/(FATOR_DE_ESCALA_CLOCK_TIMER * FREQUENCIA_DE_INTERRUPCAO_TIMER_0))+ 0.5))
#define MASCARA_DO_MODO_DO_TIMER_0 0xfc
#define MODO_0_TIMER_0 0x00
#define MODO_1_TIMER_0 0x01
#define MODO_2_TIMER_0 0x02
#define MODO_3_TIMER_0 0x03
//=============== Fim da definição dos parâmetros do Timer 0 ===========
#define PRESET_TIMER_DE_SOFTWARE 10 // Define o valor inicial do temporizador de software
int nTimerDeSoftware = PRESET_TIMER_DE_SOFTWARE; // Inicializa o temporizador de software com 10
bit bFlagTimerDeSoftware = DESLIGA; // Inicializa a sinalização de término de contagem
//===============================================================================
//
// ROTINA DE LEITURA DE MATRIZ DE CONTATOS II - Linhas de seleção separadas
// (Cada linha de seleção corresponde a um bit da Porta)
//
// Entradas : - bFlagDeTemporizacaoDaMatrizDeContatos - Sinaliza que pode realizar a leitura
// Saídas : - bFlagDeNovaLeituraDeMatrizDeContatos - Sinaliza que foi realizada nova leitura da matriz
// - ucMatrizDeContatosDeEntrada[] - Leituras atualizadas no vetor
//===== =====
//----------- Definições de parâmetros -------------------------
#define FREQ_DE_INTERRUP_TIMER_MATRIZ_DE_CONTATOS 10.0 // Hz, que corresponde à freqüencia de leitura da matriz/ teclado
// Inicializa o valor de preset do timer de software - Observe que FREQUENCIA_DE_INTERRUPCAO_TIMER_0 está
// definido no módulo do timer
#define PRESET_TEMPORIZADOR_DA_MATRIZ_DE_CONTATOS (unsigned int)((FREQUENCIA_DE_INTERRUPCAO_TIMER_0/ FREQ_DE_INTERRUP_TIMER_MATRIZ_DE_CONTATOS)+ 0.5)
// Define o número de linhas da matriz (exemplo)
#define NUMERO_DE_LINHAS_DA_MATRIZ 4 // Número de linhas de seleção
// Aqui define-se os ports onde estão conectadas a matriz e as linhas de selação
#define PORTA_DE_ENTRADA_DA_MATRIZ P2 // Define o Port onde está conectada a matriz
#define PORTA_DE_SAIDA_DA_SELECAO_DA_MATRIZ P2 // Define o port onde estão conectadas as linhas de seleção
// As máscaras a seguir definem máscaras do tipo cheia de "uns", onde existem linhas ativas. Obrigatoriamente essas linhas
// têm que ser consecutivas e no máximo 8, ocupando o port inteiro
// Alguns exemplos ... 3 linhas a partir do bit 0 do port 2
#define MASCARA_DA_MATRIZ 0x07 // Filtra os bits de entrada da matriz (3 nesse exemplo)
// Quatro linhas de selação ... bits 4 a 7 do port 2
#define MASCARA_DA_SELECAO 0xf0 // Filtra os bits dos sinais de seleção
// Bit de seleção a ser shiftado, para gerar o sinal de seleção ... bit 4 (define a primeira linha a ser lida)
#define SELETOR_DOS_SINAIS_DE_SELECAO 0x10 // Define posição do primeiro bit do conjunto de seleção
// Aqui define-se se as saídas são normais ou invertidas, complemento de 1. Se forem invertidas, realiza-se
// um ou exclusivo da máscara de seleção com elas, invertendo a todos os bits..
//#define SAIDAS_INVERTIDAS 1 // Define se as saidas de selecao sao invertidas ou nao
#ifdef SAIDAS_INVERTIDAS
#define MASCARA_DE_INVERSAO MASCARA_DA_SELECAO
#else
#define MASCARA_DE_INVERSAO 0x00
#endif
//*** Aqui definem-se as variáveis globais desse trecho da rotina (fora do bloco principal - main)***
volatile bit bFlagDeTemporizacaoDaMatrizDeContatos = DESLIGA; // Sinaliza que pode realizar nova leitura
unsigned int unTimerDaMatrizDeContatos = PRESET_TEMPORIZADOR_DA_MATRIZ_DE_CONTATOS;
unsigned char ucMatrizDeContatosDeEntrada[NUMERO_DE_LINHAS_DA_MATRIZ] = { 0, 0, 0, 0};
volatile bit bFlagDeNovaLeituraDeMatrizDeContatos = DESLIGA; // Sinaliza que foi realizada nova leitura
//===============================================================================
//
// ROTINA QUE REALIZA O "DEBOUNCE" DA MATRIZ DE CONTATOS
//
// uma vez lidos os contatos, pode-se inserir essa rotina para realizar o debounce, se necessário
//
// Entradas : - bFlagDeNovaLeituraDeMatrizDeContatos - Sinaliza que há leitura nova da matriz de entrada
// - ucMatrizDeContatosDeEntrada[] - Vetor contendo as leituras atualizadas da matriz
// - ucMatrizDeContatosDeEntradaAnterior[] - Vetor contendo as leituras anteriores da matriz
// Saídas : - ucMatrizDeContatosDeEntradaAnterior[] - Vetor contendo as leituras anteriores da matriz atualizadas
// - bFlagDeTerminoDeDebounce - Sinalização de término de debounce, permissão para análise
//
//===== =====
#define PRESET_CONTADOR_DEBOUNCE 3 // Número de dados novos com a leitura estável ( 0 - 255)
unsigned char ucMatrizDeContatosDeEntradaAnterior[NUMERO_DE_LINHAS_DA_MATRIZ] = { 0, 0, 0, 0};
unsigned char ucContadorDeDebounce = PRESET_CONTADOR_DEBOUNCE;
volatile bit bFlagDeTerminoDeDebounce = DESLIGA;
volatile bit bFlagNovosDados = DESLIGA;
//===============================================================================
//
// ROTINA QUE REALIZA A IDENTIFICAÇÃO DOS CONTATOS FECHADOS
//
// Após a realização da leitura, ou após o debounce, pode-se identificar os
// contatos que foram acionados, se houver. Ela numero os contatos sequencialmente
// a partir do primeiro.
//
// Entradas : - bFlagDeTerminoDeDebounce - Sinaliza que há leitura estável da matriz de entrada
// - ucMatrizDeContatosDeEntrada[] - Vetor contendo as leituras atualizadas da matriz
// - ucMatrizDeContatosDeReferencia[] - Vetor contendo as máscaras de referência para identificar
// os contatos acionados
// Saídas : - bFlagResultadosMatrizDeContatos - Sinaliza que há contatos acionados
// - ucContadorDeContatosFechados - Número de contatos acionados
// - ucVetorDeResultados[] - Vetor contendo a posição relativa do contato acionado
//
//===== =====
// Define o número de colunas da matriz.. (quantos bits tem em cada leitura)
#define NUMERO_DE_COLUNAS_DA_MATRIZ 03
// Identifica a posição do primeiro bit a ser identificado, e utilizado para comparação (com operação AND
// com a máscara) e deslocamento da máscara para leitura dos bits subseqüentes
#define MASCARA_DE_SELECAO_DE_COLUNA_INICIAL 0x01
// Define um número máximo de resultados para o vetor de resultados
#define TAMANHO_MAXIMO_DO_VETOR_DE_RESULTADOS 5
// define e inicializa um vetor de referência, onde os bits = 1, sinalizam que o contato fechado = 1, e
// bits = 0, quando está em zero... Abrange apenas os bits conectados da matriz... Aqui no exemplo, bits
// de 4 a 6
unsigned char ucMatrizDeContatosDeReferencia[NUMERO_DE_LINHAS_DA_MATRIZ] = {0x38, 0x38, 0x38, 0x38};
unsigned char ucVetorDeResultados[TAMANHO_MAXIMO_DO_VETOR_DE_RESULTADOS] = {0, 0, 0, 0, 0};
// Contador que indica o número de contatos fechados no vetor
unsigned char ucContadorDeContatosFechados;
// bit de sinalização de que encontrou contatos "fechados"
bit bFlagResultadosMatrizDeContatos = DESLIGA;
//*************************************************************************
//===============================================================================
//
// DEFINIÇÕES DOS DÍGITOS UTILIZADOS NA ROTINA DOS DISPLAYS DE 7 SEGMENTOS II
// Linhas de seleção separadas
// (Cada linha de seleção corresponde a um bit da Porta)
//
// Entradas : -
// Saídas : - ucFilaDeSaidaDosDisplays[] - Vetor de saídas para os displays
// - ucIndiceDisplayAtivo - ìndica o display que está ativo
//
// Deve-se definir um port dedicado a isso, configurado da seguinte maneira:
//
// PORT.Bit - Segmento do Display
//
// Px.0 - a
// Px.1 - b
// Px.2 - c
// Px.3 - d
// Px.4 - e
// Px.5 - f
// Px.6 - g
// Px.7 - DP
//
// Composição dos números decimais utilizando a especificação acima:
//
// a
// --------
// | |
// f | | b
// | g |
// ---------
// | |
// e | | c
// | d |
// -------
//
// SEGMENTOS: g f e | d c b a Código Hexadecimal
//-----------------------------------------
// Dígito: apagado 0 0 0 | 0 0 0 0 = 0x00
// 0 0 1 1 | 1 1 1 1 = 0x3f
// 1 0 0 0 | 0 1 1 0 = 0x06
// 2 1 0 1 | 1 0 1 1 = 0x5b
// 3 1 0 0 | 1 1 1 1 = 0x4f
// 4 1 1 0 | 0 1 1 0 = 0x66
// 5 1 1 0 | 1 1 0 1 = 0x6d
// 6 1 1 1 | 1 1 0 0 = 0x7c
// 7 0 0 0 | 0 1 1 1 = 0x07
// 8 1 1 1 | 1 1 1 1 = 0x7f
// 9 1 1 0 | 0 1 1 1 = 0x67
//
// P 1 1 1 | 0 0 1 1 = 0x73
// r 1 0 1 | 0 0 0 0 = 0x50
// E 1 1 1 | 1 0 0 1 = 0x79
// F 1 1 1 | 0 0 0 1 = 0x71
// i 0 0 0 | 0 1 0 0 = 0x04
//
//===== =====
//#define DISPLAY_INVERTIDO 1
#define TEM_PONTO_DECIMAL 1
#ifdef DISPLAY_INVERTIDO
#ifdef TEM_PONTO_DECIMAL
#define MASCARA_INV_DISPLAY 0xff
#define DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD MASCARA_INV_DISPLAY
#define LIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD 0x00
#else
#define MASCARA_INV_DISPLAY 0x7f
#define DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD MASCARA_INV_DISPLAY
#define LIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD 0x00
#endif
#else
#define MASCARA_INV_DISPLAY 0x00
#ifdef TEM_PONTO_DECIMAL
#define DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD MASCARA_INV_DISPLAY
#define LIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD 0xff
#else
#define DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD MASCARA_INV_DISPLAY
#define LIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD 0x7f
#endif
#endif
#define DISPLAY_7_SEG_APAGADO 0x00 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_ZERO 0x3f ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_UM 0x06 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_DOIS 0x5b ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_TRES 0x4f ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_QUATRO 0x66 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_CINCO 0x6d ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_SEIS 0x7c ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_SETE 0x07 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_OITO 0x7f ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_NOVE 0x67 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_PE 0x73 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_ERRE 0x50 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_E 0x79 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_EFE 0x71 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_I 0x04 ^ MASCARA_INV_DISPLAY
#define NUMERO_DE_DISPLAYS 0x03
#define FREQ_DE_INTERRUP_TIMER_DISPLAYS 90.0 // Hz
// ************* Precisa revisar essas linhas !!!!! *****
//#define PRESET_TEMPORIZADOR_DOS_DISPLAYS (unsigned int)(((FREQUENCIA_DE_INTERRUPCAO_TIMER_0 * NUMERO_DE_DISPLAYS)/ FREQ_DE_INTERRUP_TIMER_DISPLAYS)+ 0.5) // Original, mas acho que está errado
#define PRESET_TEMPORIZADOR_DOS_DISPLAYS (unsigned int)(((FREQUENCIA_DE_INTERRUPCAO_TIMER_0 )/ (FREQ_DE_INTERRUP_TIMER_DISPLAYS * NUMERO_DE_DISPLAYS))+ 0.5)
//********************************************************
#define NUMERO_DE_LINHAS_DO_DISPLAY 3 // Número de linhas de seleção
#define PORTA_DE_SAIDA_DOS_DISPLAYS P3 // Define o Port onde estão conectados os displays
#define PORTA_DE_SAIDA_DA_SELECAO_DOS_DISPLAYS P1 // Define o port onde estão conectadas as linhas de seleção
#ifdef TEM_PONTO_DECIMAL
#define MASCARA_DOS_DISPLAYS 0xff // Filtra os bits de saída do port dos Displays incluindo o do Ponto decimal
#define FIM_DO_TESTE_DISPLAY 0x08
#else
#define MASCARA_DOS_DISPLAYS 0x7f // Filtra os bits de saída do port dos Displays, sem o ponto decimal
#define FIM_DO_TESTE_DISPLAY 0x07
#endif
#define LIGA_PONTO_DECIMAL 0x80
#define MASCARA_DA_SELECAO_DE_DISPLAY 0xE0 // Filtra os bits dos sinais de seleção dos displays
// Bit de seleção a ser shiftado, para gerar o sinal de seleção ... bit 1 (define a primeira linha a ser lida)
#define SELETOR_DOS_SINAIS_DE_SELECAO_DISPLAY 0x20 // Define posição do primeiro bit do conjunto de seleção
//#define SAIDAS_INVERTIDAS_SEL_DISP 1 // Define se as saidas de selecao sao invertidas ou nao
#ifdef SAIDAS_INVERTIDAS_SEL_DISP
#define MASCARA_DE_INVERSAO_SEL_DISP MASCARA_DA_SELECAO_DE_DISPLAY
#else
#define MASCARA_DE_INVERSAO_SEL_DISP 0x00
#endif
volatile int unTimerDosDisplays = PRESET_TEMPORIZADOR_DOS_DISPLAYS;
unsigned char ucFilaDeSaidaDosDisplays[NUMERO_DE_DISPLAYS] = { DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD,
DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD,
DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD};
unsigned char ucIndiceDisplayAtivo = 0;
unsigned char ucContadorDeSelecaoDisplay = 0;
//-------- Definição das variáveis globais ---------------
code unsigned char ucTabelaDeDisplayDeSeteSegmentos[10] = { DISPLAY_7_SEG_ZERO,
DISPLAY_7_SEG_UM,
DISPLAY_7_SEG_DOIS,
DISPLAY_7_SEG_TRES,
DISPLAY_7_SEG_QUATRO,
DISPLAY_7_SEG_CINCO,
DISPLAY_7_SEG_SEIS,
DISPLAY_7_SEG_SETE,
DISPLAY_7_SEG_OITO,
DISPLAY_7_SEG_NOVE};
//----------------- Fim dos parâmetros e variáveis dos displays do tipo II ---------
//*******************************************************************************
// TIMER 0
// rotina de interrupção
//
void vInterrupcaoTimer0() interrupt 1
{
nTimerDeSoftware--; // Decrementa o temporizador de software
if(nTimerDeSoftware == 0)
{
bFlagTimerDeSoftware = LIGA; // Sinaliza que terminou a temporização de software
nTimerDeSoftware = PRESET_TIMER_DE_SOFTWARE; // Reinicializa o temporizador
}
//********************************************************************************
//****** Trecho a ser inserido na rotina de interrupção do timer - DISPLAYS II ***
//****** ***
if(--unTimerDosDisplays == 0)
{
unsigned char ucAux;
unTimerDosDisplays = PRESET_TEMPORIZADOR_DOS_DISPLAYS; // Reinicializa temporizador
ucAux = PORTA_DE_SAIDA_DOS_DISPLAYS & ~MASCARA_DOS_DISPLAYS; // Lê o conteúdo do port, para salvar o bit do Ponto decinal, se for o caso
ucAux |= DISPLAY_7_SEG_APAGADO; // Apaga o display
PORTA_DE_SAIDA_DOS_DISPLAYS = ucAux; // Atualiza a saída
if(ucIndiceDisplayAtivo == NUMERO_DE_DISPLAYS) // Testa se atualizou o último display
{
ucIndiceDisplayAtivo = 0; // Reinicializa o Indice do display
ucContadorDeSelecaoDisplay = 0; // Reinicializa o contador de seleção
}
ucAux = ~MASCARA_DA_SELECAO_DE_DISPLAY; // Inverte os bits da mascara
ucAux &= PORTA_DE_SAIDA_DA_SELECAO_DOS_DISPLAYS; // Filtra os bits de seleção
ucAux |= ((SELETOR_DOS_SINAIS_DE_SELECAO_DISPLAY << ucIndiceDisplayAtivo) ^ MASCARA_DE_INVERSAO_SEL_DISP);
PORTA_DE_SAIDA_DA_SELECAO_DOS_DISPLAYS = ucAux;
ucAux = PORTA_DE_SAIDA_DOS_DISPLAYS & ~MASCARA_DOS_DISPLAYS; // Lê o conteúdo do port, para salvar o bit do Ponto decinal, se for o caso
ucAux |= ucFilaDeSaidaDosDisplays[ucIndiceDisplayAtivo++]; // Lê o valor a ser apresentado no display
PORTA_DE_SAIDA_DOS_DISPLAYS = ucAux; // Atualiza a saída
}
//************** Fim da Rotina Displays II ************************************
//****** Trecho a ser inserido na rotina de interrupção do timer - Matriz de Contatos - Timer de software***
if(--unTimerDaMatrizDeContatos == 0)
{
unTimerDaMatrizDeContatos = PRESET_TEMPORIZADOR_DA_MATRIZ_DE_CONTATOS; // Reinicializa temporizador
bFlagDeTemporizacaoDaMatrizDeContatos = LIGA; // Sinaliza que terminou a contagem
}
//************** Fim da Rotina **************************************************
}
//================================================================================
//
// PROGRAMA PRINCIPAL
void main(void)
{
int nContadorSemFuncao = 0;
//********************* Inicializações *************
//************************ TIMER 0 ********************************************
// Inicializa os bits do timer 0
TR0 = DESLIGA;
TMOD &= MASCARA_CT_0; // Seleciona função de "timer"
T2CON = 0x00;
TR2 = DESLIGA;
T2MOD &= 0x0f; // Seleciona função de "timer"
TH0 = PRESET_TIMER_0_MODO_2; // Inicializa o preset do contador
TL0 = PRESET_TIMER_0_MODO_2;
TMOD &= MASCARA_DO_MODO_DO_TIMER_0; // Seta M1 e M0 para timer 0 - modo 2
TMOD |= MODO_2_TIMER_0;
//******
//********************* Inicializa Displays para demostração ******
ucFilaDeSaidaDosDisplays[0] = DISPLAY_7_SEG_UM;
ucFilaDeSaidaDosDisplays[1] = DISPLAY_7_SEG_DOIS;
ucFilaDeSaidaDosDisplays[2] = DISPLAY_7_SEG_TRES;
{
unsigned char ucAux1;
ucAux1 = ~MASCARA_DA_SELECAO_DE_DISPLAY; // Inverte os bits da mascara
ucAux1 &= PORTA_DE_SAIDA_DA_SELECAO_DOS_DISPLAYS; // Filtra os bits de seleção
PORTA_DE_SAIDA_DA_SELECAO_DOS_DISPLAYS = ucAux1; // Inicializa bits de selecao
}
//******
//********** Fim das Inicializações ******************
//********************************************************************************
//
// TIMER 0
//
// Este bloco deve ser colocado no programa principal, para iniciar a operação do timer
TR0 = LIGA; // Liga o timer
ET0 = LIGA; // Libera a interrupção do timer
EA = LIGA; // Habilita as interrupções
//****
for(;;) // Loop principal
{
nContadorSemFuncao++; // Soma um no contador. Essa instrução foi inserida aqui apenas para não dar a impressão que a simulação está parada
if(bFlagTimerDeSoftware == LIGA) // Verifica se terminou a temporização por software (10 vezes a interrupção do Timer 0)
{
P1_1 = ~P1_1; // Inverte o bit 1 da porta 1 - ;
bFlagTimerDeSoftware = DESLIGA; // Desliga a sinalização de término
}
//********************************************************************************
//****** Trecho a ser inserido no programa principal - Matriz de Contatos - II ***
//****** ***
if(bFlagDeTemporizacaoDaMatrizDeContatos == LIGA) // Testa se pode realizar a leitura da matriz
{
unsigned char ucIndice;
bFlagDeTemporizacaoDaMatrizDeContatos = DESLIGA; // Desliga o Flag
PORTA_DE_ENTRADA_DA_MATRIZ |= MASCARA_DA_MATRIZ; // Prepara a porta para leitura
//*** Início da rotina para realizar a leitura da matriz de contatos ***
for(ucIndice = 0; ucIndice < NUMERO_DE_LINHAS_DA_MATRIZ ; ucIndice++)
{
unsigned char ucAux;
ucAux = ~MASCARA_DA_SELECAO; // Inverte os bits da mascara
ucAux &= PORTA_DE_SAIDA_DA_SELECAO_DA_MATRIZ; // Filtra os bits de seleção
ucAux |= ((SELETOR_DOS_SINAIS_DE_SELECAO << ucIndice) ^ MASCARA_DE_INVERSAO);
PORTA_DE_SAIDA_DA_SELECAO_DA_MATRIZ = ucAux;
// Le a linha de contatos, e filtra, deixando só o que interessa
ucMatrizDeContatosDeEntrada[ucIndice] = PORTA_DE_ENTRADA_DA_MATRIZ & MASCARA_DA_MATRIZ;
}
// Desliga as linhas de seleção
ucIndice = ~MASCARA_DA_SELECAO; // Inverte os bits da mascara
ucIndice &= PORTA_DE_SAIDA_DA_SELECAO_DA_MATRIZ; // Filtra os bits de seleção
PORTA_DE_SAIDA_DA_SELECAO_DA_MATRIZ = (ucIndice ^ MASCARA_DE_INVERSAO);
// Inserir essa instrução apenas se necessitar de "debounce"
bFlagDeNovaLeituraDeMatrizDeContatos = LIGA; // Sinaliza que foi realizada nova leitura
}
//******** Término da rotina Matriz de Contatos - II ********
//***************************************
//******** Rotina de "Debounce " ********
//******* ********
// Testa se há leitura nova
if(bFlagDeNovaLeituraDeMatrizDeContatos == LIGA)
{
bit bFlagOk = LIGA;
unsigned char ucIndice;
// Reseta o flag de sinalização
bFlagDeNovaLeituraDeMatrizDeContatos = DESLIGA; // Sinaliza que foi realizada nova leitura
// Se o contador for diferente de zero, decrementa o contador
if (ucContadorDeDebounce != 0)
{
ucContadorDeDebounce--;
}
for(ucIndice = 0; ucIndice < NUMERO_DE_LINHAS_DA_MATRIZ ; ucIndice++)
{
// Compara a matriz de entrada com a matriz anterior
if(ucMatrizDeContatosDeEntrada[ucIndice] != ucMatrizDeContatosDeEntradaAnterior[ucIndice])
{
// Se diferentes, sinaliza
bFlagNovosDados = LIGA;
bFlagOk = DESLIGA;
}
// Transfere a matriz para a área da matriz anterior
ucMatrizDeContatosDeEntradaAnterior[ucIndice] = ucMatrizDeContatosDeEntrada[ucIndice];
}
// Testa se houve alteração das entradas
if(bFlagOk == DESLIGA)
{
// Se houve alteração, reinicializa o contador
ucContadorDeDebounce = PRESET_CONTADOR_DEBOUNCE;
}
// Testa se estabilizou as leituras
if((ucContadorDeDebounce == 0) && (bFlagNovosDados == LIGA))
{
// Sinaliza que estabilizou a leitura
bFlagDeTerminoDeDebounce = LIGA;
}
}
//************* Término da rotina de debounce ***************
//**************************************************************
//************* Rotina que identifica os contatos acionados ****
//************* ****
// Verifica se terminou o "debounce"
if(bFlagDeTerminoDeDebounce ==LIGA)
{
unsigned char ucIndice, ucContadorAux;
bFlagDeTerminoDeDebounce = DESLIGA;
bFlagNovosDados = DESLIGA;
// Inicializa o contador de contatos
ucContadorDeContatosFechados = 0; // Inicializa contador de contatos fechados
ucContadorAux = 0; // Inicializa o contador Auxiliar, que indicará o índice do contato
for(ucIndice = 0; ucIndice < NUMERO_DE_LINHAS_DA_MATRIZ ; ucIndice++)
{
unsigned char ucContatoAux;
// Verifica se nesssa linha há contatos fechados
ucContatoAux = (ucMatrizDeContatosDeEntrada[ucIndice] ^ ucMatrizDeContatosDeReferencia[ucIndice]);
if(ucContatoAux != 0)
{
unsigned char ucAux, ucRegistroDeDeslocamento;
ucRegistroDeDeslocamento = MASCARA_DE_SELECAO_DE_COLUNA_INICIAL;
for(ucAux = 0; ucAux < NUMERO_DE_COLUNAS_DA_MATRIZ; ucAux++)
{
if((ucRegistroDeDeslocamento & ucContatoAux) != 0)
{
ucVetorDeResultados[ucContadorDeContatosFechados++] = ucContadorAux; // Transfere a posição do contato
bFlagResultadosMatrizDeContatos = LIGA; // Sinaliza que encontrou contatos acionados
}
ucRegistroDeDeslocamento = ucRegistroDeDeslocamento << 1; // Desloca a máscara de um
ucContadorAux++; // Incrementa o contador
}
}
}
}
//************** Fim da rotina e identificação dos contatos acionados ***
//**********************************************************************************
//************** Inicio da rotina de execução de ações em função dos contatos ******
//************** ******
// Testa se há contatos acionados
if(bFlagResultadosMatrizDeContatos == LIGA)
{
unsigned char ucIndice;
// Verifica todos os contatos localizados
for(ucIndice = 0; ucIndice < ucContadorDeContatosFechados; ucIndice++)
{
// Aqui verifica qual é o contato acionado e realiza alguma ação dependendo de qual for...
switch(ucVetorDeResultados[ucIndice])
{
case 0:
{
break;
}
case 1:
{
break;
}
default:
{
}
}
}
}
}
}
Simulação do Exemplo
Se você quiser simular o programa exemplo acima e explorá-lo um pouco mais, siga os seguintes passos:
- Instale o KEIL C51 μVision;
- Baixe o arquivo Testa_Teclado.zip;
- Instale os arquivos de Testa_Teclado.zip num diretório para testes;
- Navegue até o diretório de testes e acione o arquivo Testa_teclado.uvproj (2 “clickes”) (Figura 3).
.
Nesse instante deverá abrir o programa Keil e a tela deverá ser a retratada na Figura 4. Esse painel permite navegar no código em C, editá-lo e compilá-lo. Não é necessário fazer isso para realizar a simulação, mas você poderá posteriormente alterar alguns parâmetros do código e testar o programa com as novas alterações.
A seguir coloque o programa no modo de Debug (Figura 5, Figura 6 e Figura 7) para que se possa iniciar a simulação.
Antes de entrar no modo de depuração, aparece na tela a mensagem da Figura 6. Clique no “OK” para continuar.
Figura 6: Aviso de que no modo de avaliação, o código fica limitado a 2K
Observe que já estão destacados os painéis de monitoramento do Timer 0, do Port 1, cujo bit 1 deverá ser alternado a cada término de contagem do temporizador de software do teste do timer e cujos bits 5, 6 e 7 são usados como linhas de seleção dos 3 displays, e o Port 3, onde aparecem os códigos de cada dígito de 7 segmentos. Nessa simulação, aparecem os números 1, 2 e 3 respectivamente nos displays 0, 1 e 2. Também aparece o painel de monitoramento do Port 2, onde os bits de 0 a 2 são as entradas do teclado e os bits 4 a 7 os sinais de seleção da matriz
É possível utilizar as teclas de função F11 para executar o programa passo a passo ou F5 para entrar em execução. Recomendo que você inicie a simulação utilizando o passo a passo para poder observar detalhadamente como que funciona o mecanismo de operação desse programa.
Resumo
Neste artigo técnico foram apresentadas algumas possíveis soluções padronizadas para a leitura e tratamento de teclados matriciais com auxílio de um timer de hardware. Essas soluções constituem uma biblioteca padronizada para a utilização dos teclados matriciais em microcontroladores da família MCS-51, codificada em C, para ser compilada no programa da Keil. Depois foi desenvolvido um programa exemplo para que você possa simular o que foi apresentado e observar os detalhes dessa implementação. Repare que a aplicação tem uma complexidade razoável e foi montada a partir das rotinas de biblioteca em menos de meia hora. E funciona!!!!
Este artigo é o quarto da série de artigos que abordam algumas funções comuns em projetos de sistemas embarcados de tempo real. Confira os demais artigos (em breve).
Sistemas Operacionais de Tempo Real
- Sistemas Operacionais de Tempo Real – Introdução – Apresentação introdutória do que é um sistema operacional e as características que o tornam um sistema de tempo real. Também são apresentadas algumas estruturas que facilitam o seu projeto.
Bibliotecas de funções e rotinas padronizadas em linguagem C para MCS-51
- Timers – É apresentada uma biblioteca desenvolvida em linguagem C para a inicialização e o uso dos Timers do MCS-51;
- Displays de 7 segmentos – É apresentada uma biblioteca desenvolvida em linguagem C para a inicialização e o uso em displays de 7 segmentos;
- Teclados Matriciais (este artigo) – É apresentada uma biblioteca desenvolvida em linguagem C para varredura, leitura, debounce, identificação da tecla acionada e desvio para a rotina de tratamento. A forma como as rotinas foram escritas, permitem sua fácil reutilização em outros projetos;
- Periféricos – É apresentada uma biblioteca desenvolvida em linguagem C para a inicialização e o uso de alguns periféricos, tais como conversores A/D, sensor de temperatura e memórias seriais.









