segunda-feira, 4 de abril de 2016

Decoder HT6P20

Descrição

      Biblioteca em C compilada com PCW da CCS para decodificar sinais provenientes do chip HT6P20 da Holtek. Usando um PIC (12F629) e um módulo receptor de RF 433MHz, irei demonstrar seu funcionamento.

Funcionamento

      O encoder HT6P20 é um chip amplamente utilizado principalmente em controles remotos de RF na frequência de 433MHz. Isso porque, além de não precisar de 'jumpers' em seus pinos para configuração de seu segredo, ele também conta com 24 bits em seu código. Segue tais detalhes:


      Com esse módulo RX, iremos receber os dados e entregar para alguma porta do PIC:


Esquema


Código

////////////////////////////////////////////////////////////////////////////////
///                               HT6P20.C                                   ///
///                  Driver para receptores Learning Code                    ///
///                                                                          ///
///  As seguintes variáveis devem ser definidas como byte no código main:    ///
///                                                                          ///
///  dado_H - armazenará a parte mais significativa do encoder               ///
///  dado_M - armazenará a parte intermediária do encoder                    ///
///  dado_L - armazenará a parte menos significativa do encoder              ///
///                                                                          /// 
///  A função principal é a 'decoder()'. Ela irá retornar um booleano        ///
///  Se retornar um 'verdadeiro' indica que foi decodificado um sinal        ///
///  válido no receptor. Nas variáveis 'dado_X' estarão os valores recebidos ///
///                                                                          ///
///  autor: Valdir Santos                             valdir.st@gmail.com    ///
///                                                                          ///
////////////////////////////////////////////////////////////////////////////////

//configuraçoes do pino de entrada
#define     entrada        PIN_A2   

//******************************************************************************
// Definir essas 3 variáveis no código principal
extern byte dado_H;  // Dado alto. MSB
extern byte dado_M;  // Dado médio.
extern byte dado_L;  // Dado baixo. LSB

int16  tempo;
int16  largura_pulso;
//******************************************************************************
// Decodifica 1/3 do pacote de dados. Se essa rotina retornar 0xFF, indica
// que não é o sinal com protocolo HT6P20.
byte decoder_1byte()
{
   int n_bits;
   byte dado = 0;

   for(n_bits = 8; n_bits > 0; n_bits--)
   {
      // verifica a 1ª parte do bit. Tem que ser nível '0'
      delay_us(largura_pulso/2);
      if(input(entrada)) return(0xFF); // houver erro
      
      // Marca valor do bit
      delay_us((largura_pulso));
      rotate_left(&dado, 1);
      if(input(entrada)) bit_set(dado, 0);
      
      // verifica a 3ª parte do bit. Tem que ser nível '1'
      delay_us((largura_pulso));
      if(!input(entrada)) return(0xFF); // houver erro

     // aguarda descida
      tempo = largura_pulso/2;
      while(input(entrada) && tempo>0)
      tempo--;   
      
      if(tempo==0) return(0xFF);    // houve erro
   }
   return(dado);
}
//******************************************************************************
// Decodifica os 24 bits identificando o inicio de cada word
// Se essa função retornar um verdadeiro, indica que os 24 bits foram recebidos 
boolean decoder()
{
   // Verifica se está no inicio do código. Aguarda transação de descida por 3mS
   tempo = 1000;
   while(input(entrada) && tempo > 0) // cada cilco desse laço, demora 3uS 
   tempo--;
   
   if(tempo==0) return(0);
   
   // testa os 23 clocks. Esse período pode variar de 7.6mS (para osciladores 
   // de 3KHz) a 12mS (para osciladores de 2KHz). Testa zero por 5mS
   tempo = 1666;
   while(!input(entrada) && tempo > 0) // cada ciclo desse laço, dura 3uS (@ 20MHz)
   tempo--;
   
   if(input(entrada)) return(0);
   
   // aguarda nivel 1 por 10mS (start bit)
   tempo = 3333;
   while(!input(entrada) && tempo > 0) 
   tempo--;

   if(tempo==0) return(0);
   
// ajusta largura do pulso de acordo com o start bit com limite de 600uS (1.5KHz)
   largura_pulso = 0;
   while(input(entrada))  // cada ciclo desse laço, dura 4uS (@ 20MHz)
   {
   delay_us(1);
   largura_pulso++;
   if(largura_pulso == 150) break; // limita largura do pulso em 600uS (1.5KHz)
   }

   if(largura_pulso==150) return(0); 

   // Corrige o valor da largura de pulso
   largura_pulso *= 4;

   // Decodifica os próximos 24 bits
   dado_H = decoder_1byte();
   dado_M = decoder_1byte();
   dado_L = decoder_1byte();
   
   // Rejeita se no pacote houver 0xFF
   if(dado_H == 0xFF || dado_M == 0xFF || dado_L == 0xFF) return(0);
   
   // aguarda o tempo dos ultimos 4 bits do anti-código   
   delay_us(largura_pulso * 12);
   
   return(true);
}
////////////////////////////////////////////////////////////////////////////////

Programa Demo

      Para fins didáticos, iremos demonstrar um simples programa que acende um led por 1 segundo quando receber um sinal válido (com protocolo HT6P20) através do receptor.

//##############################################################################
#include <C:\Program Files (x86)\PICC\Devices\12F629.h>
#fuses HS, NOWDT, NOPROTECT, NOMCLR, NOBROWNOUT, PUT
#use delay(clock=20000000)
#include "HT6P20.c"
#use fast_io(A)

//*****************************************************************************
// Definições de Hardware
#define  led         PIN_A0

//*****************************************************************************
// Variáveis
// Nessas 3 variáveis, serão armazenadas os dados recebidos do rádio
byte     dado_H;              // Dado alto. MSB
byte     dado_M;              // Dado médio.
byte     dado_L;              // Dado baixo. LSB   

//############################################################################## 
//Inicialização
void main() 
{
   {
      set_tris_a(0b00001110);
      output_low(led);
   }
//############################################################################## 
// Laço principal
while(true)

// Verifica se está recebendo dados do rádio
   if(decoder())
   { 
      output_high(led);
      delay_ms(1000);
      output_low(led);
   }
}
}
//##############################################################################


      Claro que é apenas uma pequena demonstração da biblioteca para decodificar o sinal de um controle. Mas a partir daqui, poderá ser adicionados vários recursos.
         Um incremento de um botão para gravar em memória o código recebido. 
         Usar mais de um pino como saída, expandido, assim, diversos canais para controle.

         Enfim, são diversas possibilidades.

         Imagine. 

        Acredite em você.

        E crie com eletrônica.

Abraços!


21 comentários:

  1. Deus, Jesus de Nazaré abençoe amigo !!! Me ajudou muito !!! Obrigado !!!

    ResponderExcluir
  2. É possivel acender um led quando ele detectar que o sinal não é do HT6P20 ???

    ResponderExcluir
    Respostas
    1. Saudações Daniel.

      Fico feliz em tê-lo ajudado e agradeço o comentário.

      Quanto a sua pergunta, vamos lá:
      Se você colocar um fone de ouvido na saída DATA do módulo de RF irá ouvir um monte de ruído devido á estática. Como um rádio de FM sem sintonia. Isso significa que a função 'decoder()' será chamada o tempo todo e como não tem o sinal do HT retornará 'false' constantemente, mas não sabemos exatamente em que ponto será retornada. No entanto, se escolhermos um ponto entre esse 'chiado' e um código proveniente de algum controle, podemos atingir seu objetivo. Dentro da função 'decoder()' incluiremos os seguintes comandos:

      output_high(pin_a1); // esse seria um pino correspondente ao led “NÃO HT”
      delay_ms(1000); // pisca por 1 seg
      output_low(pin_a1);

      Podemos realizar algumas tentativas nas seguintes posições:
      A função ficaria mais ou menos assim:

      boolean decoder()
      {
      // Verifica se está no inicio do código. Aguarda transação de descida por 3mS
      tempo = 1000;
      while(input(entrada) && tempo > 0) // cada cilco desse laço, demora 3uS
      tempo--;

      if(tempo==0) return(0);

      // testa os 23 clocks. Esse período pode variar de 7.6mS (para osciladores
      // de 3KHz) a 12mS (para osciladores de 2KHz). Testa zero por 5mS
      tempo = 1666;
      while(!input(entrada) && tempo > 0) // cada ciclo desse laço, dura 3uS (@ 20MHz)
      tempo--;

      if(input(entrada))
      {
      // output_high(pin_a1); // pode ser colocado aqui
      // delay_ms(1000); // pisca um led correspondente à 'NÃO HT'
      // output_low(pin_a1);
      return(0);
      }

      // aguarda nivel 1 por 10mS (start bit)
      tempo = 3333;
      while(!input(entrada) && tempo > 0)
      tempo--;

      if(tempo==0)
      {
      // output_high(pin_a1); // ou pode ser colocado aqui
      // delay_ms(1000); // pisca um led correspondente à 'NÃO HT'
      // output_low(pin_a1);
      return(0);
      }

      // ajusta largura do pulso de acordo com o start bit com limite de 600uS (1.5KHz)
      largura_pulso = 0;
      while(input(entrada)) // cada ciclo desse laço, dura 4uS (@ 20MHz)
      {
      delay_us(1);
      largura_pulso++;
      if(largura_pulso == 150) break; // limita largura do pulso em 600uS (1.5KHz)
      }

      if(largura_pulso==150)
      {
      // output_high(pin_a1); // ou pode ser colocado aqui
      // delay_ms(1000); // pisca um led correspondente à 'NÃO HT'
      // output_low(pin_a1);
      return(0);
      }

      // Corrige o valor da largura de pulso
      largura_pulso *= 4;

      // Decodifica os próximos 24 bits
      dado_H = decoder_1byte();
      dado_M = decoder_1byte();
      dado_L = decoder_1byte();

      // Rejeita se no pacote houver 0xFF
      if(dado_H == 0xFF || dado_M == 0xFF || dado_L == 0xFF) return(0);

      // aguarda o tempo dos ultimos 4 bits do anti-código
      delay_us(largura_pulso * 12);

      return(true);
      }
      //******************************************************************************

      Vale ressaltar também, que você pode filtrar apenas um determinado código da tecla do controle transmissor. Para isso, você faz a verficação depois que receber o sinal. Por exemplo: digamos que uma determinada tecla possua o código 0xAC546C, então, no loop principal d seu programa ficaria assim:

      // Laço principal
      while(true)
      {
      // Verifica se está recebendo dados do rádio
      if(decoder())
      {
      if((dado_H == 0xAC) && ((dado_H == 0x54) && ((dado_H == 0x6C))
      {
      output_high(led_verde);
      delay_ms(1000);
      output_low(led_verde);
      }else
      {
      output_high(led_vermelho);
      delay_ms(1000);
      output_low(led_vermelho);
      }
      }
      }
      }

      Vale a tentativa.

      Espero ter ajudado.

      Excluir
  3. Obrigado pelo retorno!! ... então eu queria identificar um sinal de controle remoto que não use o HT, por exemplo que use um tipo de encoder qualquer, e acender um LED .. sem querer abusar do seu conhecimento, é possivel contar os bits na entrada ??? por exemplo 1024 bits e acender um led, é possivel??? .. Obrigado novamente !!! Suce$$o !!!

    ResponderExcluir
  4. Este comentário foi removido pelo autor.

    ResponderExcluir
  5. Este comentário foi removido pelo autor.

    ResponderExcluir
  6. Este comentário foi removido pelo autor.

    ResponderExcluir
  7. Este comentário foi removido pelo autor.

    ResponderExcluir
  8. Olá Valdir,

    Onde e coloco o código do botão?

    Eu tenho um controle PPA de 3 botões e o código do botão 1, por exemplo, é:

    111923200

    Como faço pro programa validar esse código e acender um LED quando eu pressionar o botão correspondente a ele?

    ResponderExcluir
  9. Bom exemplo, simples, direto e muito didático. Gostaria de sugerir aos mais aventureiros uma pequena mudança para checar também o "anticode", os últimos 4 bits: eles devem ser "0101", o que ajuda a validar o valor recebido. Para isso, seria necessário mudar as primeiras linhas da rotina decoder_1byte() para aceitar um número variável de bits:


    byte decoder_1byte(int qBits)
    {
    int n_bits;
    byte dado = 0;

    for(n_bits = qBits; n_bits > 0; n_bits--)
    {
    ...

    Aí se muda o final da rotina para usar o novo formato de decoder_1byte e para checar o anticode:

    ...
    // Decodifica os próximos 24 bits
    dado_H = decoder_1byte(8); // lê bits 23-16
    dado_M = decoder_1byte(8); // lê bits 15-8
    dado_L = decoder_1byte(8); // lê bits 7-0

    // Rejeita se no pacote houver 0xFF
    if(dado_H == 0xFF || dado_M == 0xFF || dado_L == 0xFF) return(0);

    // verifica os últimos 4 bits do anti-código

    if (decoder_1byte(4) != 5) return (false); // falso se não for 5

    return (true); // passou por todos os testes, aprovado!
    }

    ResponderExcluir
    Respostas
    1. Muito bem pensado Aldo. Assim, obtemos mais segurança na validação dos dados recebidos. Muito obrigado pela sua contribuição. Abraços!

      Excluir
    2. Aldo, sua rotina funciona bem, porém lembrar que não é 5 o valor do anticode, é 10. o primeiro bit que chega no pacote é o bit zero, então pacote é 0101 mas o dado na verdade é 1010 = 10.
      if (decoder_1byte(4) != 10) return (false); // falso se não for 10.

      Excluir
  10. Ola Valdir muito interessante o código, tenho uma pergunta pra você na parte das variáveis H,L,M é devido a variável inteira ser de 24 bits por isso a divisão em 3 partes de 8?

    ResponderExcluir
  11. È possível usar um Cristal de 8M no lugar do cristal de 20M ?

    ResponderExcluir
    Respostas
    1. Creio que seria sim, mas teria que alterar os tempo de delay.

      Excluir
    2. Creio que seria sim, mas teria que alterar os tempo de delay.

      Excluir
  12. Olá Valdir, tudo bem?
    Parabéns pelo material. Você chegou a testar este código com outros encoders (ex: M1EN)?

    ResponderExcluir
  13. Grato Valdir...
    Parabéns pelo material didático.
    Foi de muita ajuda.

    ResponderExcluir
  14. Ola como eu poderia visualizar o code o comando printf() ?

    ResponderExcluir
  15. Boa noite amigos. Alguem tem o código completo com um botão para gravar os controles?

    ResponderExcluir