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);
}
}
}
//##############################################################################
//##############################################################################
#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!
Deus, Jesus de Nazaré abençoe amigo !!! Me ajudou muito !!! Obrigado !!!
ResponderExcluirÉ possivel acender um led quando ele detectar que o sinal não é do HT6P20 ???
ResponderExcluirSaudações Daniel.
ExcluirFico 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.
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 !!!
ResponderExcluirEste comentário foi removido pelo autor.
ResponderExcluirEste comentário foi removido pelo autor.
ResponderExcluirEste comentário foi removido pelo autor.
ResponderExcluirEste comentário foi removido pelo autor.
ResponderExcluirOlá Valdir,
ResponderExcluirOnde 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?
Corrigindo...
ExcluirO controle é ECP
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:
ResponderExcluirbyte 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!
}
Muito bem pensado Aldo. Assim, obtemos mais segurança na validação dos dados recebidos. Muito obrigado pela sua contribuição. Abraços!
ExcluirAldo, 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.
Excluirif (decoder_1byte(4) != 10) return (false); // falso se não for 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È possível usar um Cristal de 8M no lugar do cristal de 20M ?
ResponderExcluirCreio que seria sim, mas teria que alterar os tempo de delay.
ExcluirCreio que seria sim, mas teria que alterar os tempo de delay.
ExcluirOlá Valdir, tudo bem?
ResponderExcluirParabéns pelo material. Você chegou a testar este código com outros encoders (ex: M1EN)?
Grato Valdir...
ResponderExcluirParabéns pelo material didático.
Foi de muita ajuda.
Ola como eu poderia visualizar o code o comando printf() ?
ResponderExcluirBoa noite amigos. Alguem tem o código completo com um botão para gravar os controles?
ResponderExcluir