29 de abr de 2012

Teclado Matricial

 

Dentre as várias maneiras de interagirmos com o hardware em uma IHM (Interface Homem Máquina) uma das formas mais comuns é através do pressionamento de chaves e botoeiras para que o programa possa então executar uma tarefa específica.

Hoje vamos interfacear um teclado matricial, como o encontrado em telefones celulares e calculadoras, onde temos várias chaves conectadas ao microcontrolador de maneira eficiente a fim de não despediçarmos pinos do hardware.

foto

Para efetuar a conexão de uma tecla ao microcontrolador, podemos citar a maneira mais simples que utiliza apenas uma tecla e um resistor de Pull-up ou Pull-down conforme o nível de tensão que desejarmos quando ocorrer o pressionamento da tecla.

Chave HI-LO_1

Porém nesta configuração precisamos um pino para cada tecla conectada ao microcontrolador, o que caracteriza o desperdício de hardware, porém isto pode ser resolvido  com  implementação de uma simples matriz, onde conseguimos maximizar a utilização dos pinos de forma eficiente.

No esquema abaixo podemos observar a utilização das linhas e colunas que formam a matriz.

 

Capturar

 

O número máximo de chaves para esta implementação pode ser obtida através da equação abaixo:

                 N°teclas = Colunas x Linhas

O funcionamento do teclado matricial consiste em energizar apenas uma única linha por vez e efetuar a leitura das colunas para verificar o pressionamento de alguma das teclas, quando detectado um nível alto em alguma das colunas durante o rastreamento se torna possível a identificação da tecla pressionada. Para que não haja erros de leituras durante este processo, se faz necessário o uso de resistores de pull-down para garantir um nível baixo nas colunas que não estão sendo ativas pelo pressionamento de tecla.

Função para escaneamento de teclado

Para efetuar a leitura do teclado matricial, vamos criar uma função que retornará o valor da tecla pressionada em hexadecimal ou o valor 0xFF caso nenhuma tecla tenha sido pressionada.No nosso exemplo utilizaremos um teclado matricial de [4x3] que foi adquirido através do site www.goodluckbuy.com.

Assim que a função é chamada, colocamos um nivel alto na primeira linha e efetuamos a leitura das quatro colunas, caso alguma coluna responda nivel alto, retornamos da função com o valor correspondente a matrizagem, vejamos como fica o código:


#define Coluna_1  PORTD.B0
#define Coluna_2  PORTD.B1
#define Coluna_3  PORTD.B2

#define Linha_1    PORTD.B6
#define Linha_2    PORTD.B5
#define Linha_3    PORTD.B4
#define Linha_4    PORTD.B3

char Le_Teclado()         // Função que efetua a leitura do teclado matricial
{
Coluna_1 = 1;
Coluna_2 = 0;
Coluna_3 = 0;
Delay_ms(1);
if(Linha_1) return 1;
if(Linha_2) return 4;
if(Linha_3) return 7;
if(Linha_4) return 10;
 
Coluna_1 = 0;
Coluna_2 = 1;
Coluna_3 = 0;
Delay_ms(1);
if(Linha_1) return 2;
if(Linha_2) return 5;
if(Linha_3) return 8;
if(Linha_4) return 0;
 
Coluna_1 = 0;
Coluna_2 = 0;
Coluna_3 = 1;
Delay_ms(1);
if(Linha_1) return 3;
if(Linha_2) return 6;
if(Linha_3) return 9;
if(Linha_4) return 11;
 
return 255;
}


Este código  mostra como efetuar uma leitura “simples” do teclado matricial, porém dependendo do hardware utilizado pode apresentar problemas de estabilidade efetuando leituras falsas ou deixando de apresentar um pressionamento.

MikroC PRO

O compilador MikroC PRO, possui internamente uma biblioteca própria para uso com teclados matriciais e que torna o processo de escaneamento uma tarefa simples. Podem ser utilizadas as configurações [4x1] , [4x2] , [4x3] ou [4x4].

Keypad_Init()

Efetua a inicialização do teclado matricial, esta função requer que seja declarado anteriormente uma variável chamada keypadPort que define o PORT onde será conectado o nosso hardware do teclado.

char keypadPort at PORTD;  

Keypad_Key_Press()

Efetua a leitura de uma tecla pressionada, retornando o valor da tecla (1 –16) ou 0 caso nenhuma tecla seja pressionada.

Keypad_Key_Click()

Efetua a leitura de uma tecla pressionada, retornando o valor da tecla (1 –16) ou 0 caso nenhuma tecla seja pressionada, porém é uma função com propriedades de bloqueio, ou seja, quando chamada, aguarda o pressionamento da tecla, bloqueando o processo do programa durante a espera do pressionamento da tecla.

Notem que esta biblioteca do MikroC PRO, embora nos ajude a interfacear o teclado não retorna o valor direto das teclas e sim a posição dela na matriz.

Vejamos como ficou o nosso código completo utilizando o compilador MikroC PRO.

/******************************************************************************

                            JL Audio – www.esquemas .org

Data: 04/2012
Autor: Jean Carlos
Projeto: Teclado Matricial
Microprocessador: PIC18F4550
Clock do processador: 48MHz
Estação de desenvolvimento: PK2Lab V 1.1
Compilador: MikroC PRO V 4.60.0.0
Versão atual: 1.0
Descrição:
                 Implementação de leitura de teclado matricial na
                 placa PK2Lab, através de biblioteca do MikroC PRO.

*******************************************************************************/
// Inicialização do teclado no PORTD

char keypadPort at PORTD;

// Configuração do LCD da placa PK2Lab V.1.1

sbit LCD_RS at RB2_bit;
sbit LCD_EN at RB3_bit;
sbit LCD_D4 at RB4_bit;
sbit LCD_D5 at RB5_bit;
sbit LCD_D6 at RB6_bit;
sbit LCD_D7 at RB7_bit;

sbit LCD_RS_Direction at TRISB2_bit;
sbit LCD_EN_Direction at TRISB3_bit;
sbit LCD_D4_Direction at TRISB4_bit;
sbit LCD_D5_Direction at TRISB5_bit;
sbit LCD_D6_Direction at TRISB6_bit;
sbit LCD_D7_Direction at TRISB7_bit;

//******************************************************************************
// Variáveis Globais

char tecla, tecla_antiga = 0;

//******************************************************************************
// Rotinas Auxiliares

//******************************************************************************
//Rotina Principal

void main()
{
TRISA     = 0b00000000;
PORTA    = 0b00000000;
TRISB     = 0b00000000;
PORTB    = 0b00000000;
TRISC     = 0b00000000;
PORTC    = 0b00000000;
PORTD    = 0b00000000;
TRISE     = 0b00000000;
PORTE    = 0b00000000;
ADCON1  = 0X0F;          // Entradas digitais.


Keypad_Init();          // Inicializa o Teclado Matricial

Lcd_Init();
Lcd_Cmd(_Lcd_Cursor_Off);
Lcd_Cmd(_LCD_CLEAR);
Lcd_Out(1,1,"***  PK2Lab  ***");
Lcd_Out(2,2,"Tecla Press:");
Delay_ms(100);

while(1)
{

tecla = Keypad_Key_Press();      // Efetua leitura do teclado
 
if(tecla != 0)
{
  switch (tecla)                          // Efetua conversão para impressão
      {
      case  1: tecla = 49; break; // 1
      case  2: tecla = 50; break; // 2
      case  3: tecla = 51; break; // 3
      case  4: tecla = 65; break; // A
      case  5: tecla = 52; break; // 4
      case  6: tecla = 53; break; // 5
      case  7: tecla = 54; break; // 6
      case  8: tecla = 66; break; // B
      case  9: tecla = 55; break; // 7
      case 10: tecla = 56; break; // 8
      case 11: tecla = 57; break; // 9
      case 12: tecla = 67; break; // C
      case 13: tecla = 42; break; // *
      case 14: tecla = 48; break; // 0
      case 15: tecla = 35; break; // #
      case 16: tecla = 68; break; // D
      }
  if(tecla != tecla_antiga)       // Atualiza a ultima tecla pressionada
    {
    tecla_antiga = tecla;
    }
  Lcd_Chr(2,15,tecla);            // Imprime a tecla pressionada no LCD
}
}//while(1)
}//main


Abaixo o vídeo do projeto funcionando!

Bom projetos a todos e até a próxima!

1 de abr de 2012

Sensor Ultra-Sônico


Sensores de ultra som são componentes muito úteis no desenvolvimento de projetos microcontrolados por serem fáceis de implementar e ter uma resposta linear diretamente proporcional a distância em que se encontra de um determinado objeto.
Veremos agora como funciona o sensor de ultrasom DYP-ME007 que pode  ser adquirido diretamente da China através do site www.goodluckbuy.com

Sensor ultra1

O Sensor de ultrasom pode detectar objetos a distâncias que variam entre 2 e 350cm de distância com uma precisão de 3mm, sendo que o modelo testado retornou uma precisão de 2mm.
A detecção de distância é feita através do disparo de um pulso de nível alto com duração de 10us no pino de Trigger do sensor, assim que ele detectar este sinal, emite uma sequencia de 8 pulsos na frequência de 40KHz, frequência esta que define o termo Ultra-sônico utilizado na nomenclatura. Após este som emitido “Bater” em algum objeto, retorna e é capturado pelo microfone do sensor para processamento das informações.

Sensor

A distância “D” corresponde a real distância do objeto, como o sensor recebe a informação de distância de emissão e recepção, obtêm duas vezes a distância real, como a velocidade do som no ar a temperatura média de 25°C é de 340m/s, facilmente se obtém a relação de distância em milímetros através da equação:
Distância = Velocidade * Tempo
2 * Distância = 340 * Tempo
Distância = 170 * Tempo
 
Todos estes cálculos são efetuados diretamente pelo sensor DYP-ME007 que ao final entrega no pino Echo um pulso ativo correspondente a distância real do objeto como mostra a figura abaixo:
 
Temporização
 
Para se transformar o tempo (us) retornado pelo sensor  em distância(mm), basta dividir valor do tempo por 58.
Este valor é obtido da seguinte relação:
Um metro corresponte a um pulso de aproximadamente 5764us, transformando em cm temos 57,64cm que arredondamos para 58 para facilitar os cálculos no microcontrolador.

Teste de funcionamento.

Este sensor possui quatro pinos de conexão sendo:
VCC – Alimentação de 5V (Consumo médio de 20ma)
GND – Alimentação GND
Trigger- Pino para disparo de leitura (10ms)
Echo – Pino de retorno de informações referente a distância medida
Out – Este pino NÃO deverá ser conectado

Podemos detectar a largura de pulso do sensor de várias maneiras, porém a maneira mais elegante de se fazer isto com microcontroladores PIC, é utilizando o módulo CCP na função CAPTURE.
Para isto temos que conhecer um pouco mais sobre este módulo interno do microcontrolador.
O módulo CCP utiliza os Timers internos do microcontrolador como base de tempo, neste caso específico do modo CCP que é o CAPTURE, é utilizado o TIMER1 incrementando a cada us.
Desta forma quando o módulo CCP detecta a borda alta correspondente ao pino ECHO do sensor, ativa a contagem do TIMER,(incrementeado a cada us) e muda a configuração da borda de sensibilidade para baixa, aguardando assim o término do pulso referente a distância, com isto conseguimos saber qual o período do pulso entrege pelo sensor em já em us.
Após esta informação ser processada temos que alterar novamente a borda de sensibilidade para alta afin de estarmos prontos para uma nova informação proveniente so sensor.
Através do uso deste módulo, trabalhamos apenas com interrupções liberando o microcontrolador para outras tarefas tornando o programa mais eficiente.


Abaixo segue o programa exemplo da placa PK2Lab para uso com estes sensores.
/******************************************************************************
                      JL Audio Manutenção Eletrônica
Data: 01/2012
Autor: Jean Carlos
Projeto: Sensor de distância DYP-ME007
Microprocessador: PIC18F4550
Clock do processador: 8MHz
Estação de desenvolvimento: PK2Lab V 1.1
Compilador: MikroC PRO V 4.60.0.0
Versão atual:
Descrição:

          Este projeto demonstra o uso do sensor de distancia DYP-DM007
          Com a placa PK2Lab, o valor referente a distância entre o
          objeto e o sensor é exibido no display LCD.

          Trigger -> RC0
          Echo    -> RC2 -> Entrada do módulo CCP1

          O resultado da captura é salvo em CCPR1H e CCPR1L
          Para efetuar este projeto, iremos configurar o TMR1 como contador
          sincrono,para ser incrementado pela fonte de clock externo que é
          o cristal de 8MHz conectado a placa PK2Lab(Sem PLL).

          8MHz -> (Fosc/4)2MHz -> Prescaler 2:1 -> 1MHz (1uS)
          Para calcular a distancia em centimetros, usamos a formula abaixo:


*******************************************************************************/
// Configuração do LCD da placa PK2Lab V.1.1

sbit LCD_RS at RB2_bit;
sbit LCD_EN at RB3_bit;
sbit LCD_D4 at RB4_bit;
sbit LCD_D5 at RB5_bit;
sbit LCD_D6 at RB6_bit;
sbit LCD_D7 at RB7_bit;

sbit LCD_RS_Direction at TRISB2_bit;
sbit LCD_EN_Direction at TRISB3_bit;
sbit LCD_D4_Direction at TRISB4_bit;
sbit LCD_D5_Direction at TRISB5_bit;
sbit LCD_D6_Direction at TRISB6_bit;
sbit LCD_D7_Direction at TRISB7_bit;


//******************************************************************************
// Definições e  Variáveis Globais

#define Trigger RC0_bit
char txt[4];                  // Usado para conversão de dados em string
char Tempo_H, Tempo_L, Distancia_cm;
unsigned int Periodo;         // Variável que armazena o período do Pulso ECHO

//******************************************************************************
// Funções Auxiliares

void interrupt()
{
if(CCP1IF_bit && CCP1CON.B0)  // Captura por CCP1 e borda configurada como subida...
{
  CCP1IF_bit  = 0;            // Limpa a flag para nova captura
  CCP1IE_bit  = 0;            // Desabilita interrupção por periféricos(CCP1)
  CCP1CON     = 4;          // Configura a borda de captura para DESCIDA. (ESTE EVENTO CAUSARIA INTERRUPÇÃO ERRÔNEA)
  CCP1IE_bit  = 1;            // Habilita interrupção por periféricos(CCP1)
  TMR1H       = 0;             // Zera registradores do TMR1 para contagem de tempo
  TMR1L       = 0;
  TMR1ON_bit  = 1;          // Habilita contagem de tempo.
}

else if(CCP1IF_bit)
      {
       CCP1IF_bit  = 0;        // Limpa a flag para nova captura
       TMR1ON_bit  = 0;      // Desabilita contagem de tempo.
       CCP1IE_bit  = 0;        // Desabilita interrupção por periféricos(CCP1)
       CCP1CON     = 5;      // Configura a borda de captura para SUBIDA. (ESTE EVENTO CAUSARIA INTERRUPÇÃO ERRÔNEA)
       CCP1IE_bit  = 1;       // Habilita interrupção por periféricos(CCP1)
       Tempo_H     = CCPR1H;  // Carrega valores de tempo capturado(Neste caso em us)
       Tempo_L     = CCPR1L;
      }

}
Pulso()                       // Gera o Pulso de trigger para o sensor ultra som.
{
Trigger = 1;
Delay_us(10);
Trigger = 0;
}

//******************************************************************************
//Rotina Principal

void main()
{
TRISC  = 0b00000100;         // Entrada CCP1
PORTC  = 0b00000000;
INTCON = 0b11000000;         // Liga interruptores GIE e PEIE
TMR1IE_bit = 0;              // Desabilita interrpções de TMR1
CCP1IE_Bit = 1;              // Habilita interrupções por CAPTURA(CCP1)
CCP1CON    = 5;              // Configura módulo CCP1 para CAPTURA e borda de SUBIDA.

T1CKPS1_bit = 0;             // Prescaller TMR1 2:1
T1CKPS0_bit = 1;
TMR1CS_bit  = 0;             // Clock selecionado -> OSC/4
TMR1ON_bit  = 0;             // Timer -> 1 = Habilita contagem

Lcd_Init();
Lcd_Cmd(_Lcd_Cursor_Off);
Lcd_Cmd(_LCD_CLEAR);
Lcd_Out(1,1,"***  PK2Lab  ***");
Lcd_Out(2,1,"Distancia:    cm");
Delay_ms(100);

while(1)
{
Pulso();                                 // Dispara leitura de distância.
Delay_ms(100);                     // Intervalo entre leituras.
Periodo = (Tempo_H<<8)+ Tempo_L;   // Obtenção do período relativo ao ECHO
Distancia_cm = Periodo/58;    // Conversão da distância informada para cm

                  //------------------------------------------------------------------------------------//
                  // Um Pulso de 58us corresponde a uma distância de 1cm  //
                  //------------------------------------------------------------------------------------//


// Distância MINIMA 3cm e MAXIMA 3m

if((Distancia_cm < 3)||Distancia_cm >300) Lcd_Out(2,12,"---");
else
    {
    byteToStr(Distancia_cm, txt);   // Converte texto para impressão
    Lcd_Out(2,12,txt);                   // Imprime valores no display
    }
}
}