Sistemas Operacionais de Tempo Real - Teclados Matriciais

Teclados Matriciais

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.

 

Teclado_1_Cod
Figura 1: Teclado matricial com seleção por linhas codificadas

 .

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.

Teclado_1
Figura 2: Teclado matricial com seleção por linhas separadas

 

 

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).

.

teclado_Soft_1a
Figura 3: Arquivos no diretório de teste

 

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.

 

Teclado_Soft_2
Figura 4: Programa Keil pronto com o programa exemplo carregado

 

 

A seguir coloque o programa no modo de Debug (Figura 5, Figura 6 e Figura 7) para que se possa iniciar a simulação.

 

Teclado_Soft_3
Figura 5: Selecionando o modo Debug do programa

 

Antes de entrar no modo de depuração, aparece na tela a mensagem da Figura 6. Clique no “OK” para continuar.

 

Keil_warning

 Figura 6: Aviso de que no modo de avaliação, o código fica limitado a 2K

  

 

Teclado_Soft_4
Figura 7: Modo Debug de operação

 

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

 

 

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.

 

Referências

 

http://www.ti.com/lit/ds/symlink/sn54s139.pdf

https://www.keil.com/demo/eval/c51.htm

https://www.embarcados.com.br/download/Testa_Teclado.zip

Outros artigos da série

<< Sistemas Operacionais de Tempo Real - Displays de 7 segmentosSistemas Operacionais de Tempo Real - Alguns Periféricos >>
NEWSLETTER

Receba os melhores conteúdos sobre sistemas eletrônicos embarcados, dicas, tutoriais e promoções.

Obrigado! Sua inscrição foi um sucesso.

Ops, algo deu errado. Por favor tente novamente.

Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.

1
Deixe um comentário

avatar
 
1 Comment threads
0 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
0 Comment authors
Sistemas Operacionais de Tempo Real – Alguns Periféricos | Blog do Engenheiro Puhlmann Recent comment authors
  Notificações  
recentes antigos mais votados
Notificar
trackback

[…] Teclados Matriciais  – É 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; […]