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!


sexta-feira, 1 de abril de 2016

PIC Player EEPROM

Descrição

        Esse pequeno projeto descreve a possibilidade de reproduzir pequenas mensagens em áudio, provenientes de uma memória EEPROM (24Cxx) com um simples microcontrolador: 16F628A. 

Funcionamento

       Com apenas 16%  de programa do microcontrolador, é possível realizarmos a leitura de uma memória externa pelo barramento I2C e jogar esses bytes para o registrador de PWM do PIC. Com a interrupção de TIMER 2, podemos configurar a frequência de amostragem para saída, tendo assim, uma demodulação em PCM com filtro passa-baixa (~3,4KHz) formado pelo resistor 470R e capacitor de 100nF.
       Na memória, deve conter os dados de áudio vindos de um arquivo WAV. Para tanto, poderá ser gravado através de uma variedade de programas gravadores de EEPROM.  Maiores detalhes nas próximas linhas.

Esquema

      A finalidade desse projeto, é demonstrar a possibilidade da reprodução do áudio pelo PIC. Por isso, o esquema está muito simples. 


      
      Devido a simplicidade do circuito, não irei disponibilizar lay out de PCB.

Preparando o Áudio

      Como foi descrito acima, para que o PIC reproduza o som, precisamos tê-lo em memória. Para isso, irei demonstrar passo a passo tal função:

   Em seu computador, escolha o arquivo de áudio desejado. Abra-o e o edite com o Audacity (Clique aqui para baixá-lo). Remova os espaços de silêncio contidos no início e no fim do arquivo. Eles ocupam desnecessariamente, a memória.


      Para obter melhores resultados, ajuste volume da faixa.

      Na parte inferior esquerda do programa, ajuste a taxa de amostragem para 11025 Hz.


      Agora vá em "Ficheiro >> Exportar Áudio". Na caixa de diálogo que se abre, selecione: "Outros ficheiros sem compressão". Em seguida, clique em "Opções". Em cabeçalho, deixe: "WAV". Em codificação, deixe: "Unsigned 8 bit PCM". Observe:


Clique em OK e Salvar. Na janela de edição de Metadados, clique em Limpar e OK.

Gravando a memória

      Para saber se a capacidade da memória irá suportar nosso arquivo, abra suas propriedades e verifique seu tamanho. Guarde esse número. Você vai precisar dele mais tarde. 
        Agora, escolha a EEPROM. Lembrando que o sufixo da memória, é a capacidade dela em kbits. Para converter em Bytes, divida esse número por 8. 
        Por exemplo: a memória 24C256 tem 256 Kbits, que é o mesmo que 32 KBytes (256 / 8 = 32). Portanto, se caso queira usar essa memória, seu arquivos deverá ter menos que 32 KB (32 000 bytes).

  

      Com qualquer gravador de EEPROM, abra o arquivo WAV e grave na memória. 
OBS:  Por padrão, todos os gravadores só 'enxergam' arquivos .hex ou  .bin. Selecione 'todos os arquivos' quando for abrir o arquivo de áudio.
      Pronto! Já temos assim, nosso 'disco'.

Código Fonte

      O código fonte a seguir, foi compilado com PCHW da CCS. É apenas um código simples para tocar a mensagem da memória a cada segundo. Você pode baixá-lo aqui. 
      Alterações poderão ser realizadas para melhor atender sua necessidade:

///////////////////////////////////////////////////////////////////////////////
//                                                                            //
//                          PIC Player EEPROM                                 //
//                          =================                                 //
//                                                                            //
// Código simples para reproduzir som através de uma memória externa pelo     //
// barramento I2C. A saida de som será decodificada por PCM.                  //
//                                                                            //
// Autor: Valdir Silva                             Data: 01/04/2016 - 17:35   //
// Email: valdir.st@gmail.com                      Versão: 1.0                //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////

#include <C:\Program Files (x86)\PICC\Devices\16F628A.h>
#use delay (clock=20000000)
#fuses HS,NOWDT,NOPROTECT,NOBROWNOUT,NOLVP,NOPUT,NOCPD, NOMCLR
#use i2c (master, sda=PIN_B4, scl=PIN_B5, FAST=500000) // Configura I2C com 500 KHz

#use fast_io(A)
#use fast_io(B)

// Define taxa de amostragem
#define FOSC      20000000    
#define T2_FREQ   22050       // 22 KHz

#define PR2    FOSC/4/T2_FREQ 
  
// --------------------------------------------------------------------------
// Global variables
int      wait;
byte     buffer_pwm;

// --------------------------------------------------------------------------
// Endereça e aponta o dispositivo com seu respectivo endereço
void set_start(int16 start_eeprom)
{
      i2c_start();      // Start 
      i2c_write(0xa0);  // Endereço da categoria do dispositivo
      i2c_write(start_eeprom >> 8);     // Endereço Alto de Dados
      i2c_write(start_eeprom);     // Endereço Baixo de Dados
      i2c_start();      // Start
      i2c_write(0xa1);  // Endereço do dispositivo
}
// --------------------------------------------------------------------------
// Lê sequencialmente, todos os endereços entre end_inicial ao end_final
void play (int16 end_inicial, int16 end_final)
{
   int16 i;
   
   set_start(end_inicial);
   
   for(i=0; i < end_final; i++)
   {         
      buffer_pwm = i2c_read(1);   // ACK
         
      wait=1;
      while(wait);
         
      set_pwm1_duty(buffer_pwm);
   }   
   
   buffer_pwm = i2c_read(0);     // NOACK - para finalizar
   i2c_stop();                   // Stop 
}  

// --------------------------------------------------------------------------
#INT_TIMER2
void int_tmr2()
   if(wait) wait--;
}
      
// --------------------------------------------------------------------------
// Direciona as portas do PIC e configura Interrupção de Timer 2
void main(void)
{
   delay_ms(10);
   set_tris_a(0b11111111);
   set_tris_b(0b11110011);
   
   setup_timer_2(T2_DIV_BY_1, PR2, 1);
   setup_ccp1(CCP_PWM);
   
   enable_interrupts(GLOBAL);
   enable_interrupts(INT_TIMER2);
   
// --------------------------------------------------------------------------
// Loop principal
   while(1)
   {
      play(60, 31618); // o endereço final, é o mesmo que o tamanho do arquivo obtido nas propriedades do mesmo.
      delay_ms(1000);
   }
}
// --------------------------------------------------------------------------

      
Nota: Na chamada da função 'play' são passados 2 parâmetros; o endereço inicial e o endereço final de reprodução. Não é recomendável que se use '0' no endereço inicial, isso porque, os primeiros 60 bytes de um arquivo WAV são apenas informações dele mesmo, como taxa de amostragem, nome, número de canais, etc. e essas informações não são últeis pra nós. Pelo menos nesse projeto. Além que evita de ouvir aquele clic no início da reprodução. Já o endereço final, deve ser aquele obtido nas propriedades do arquivo.

      Futuramente, caso seja necessário, poderemos adicionar mais memórias com endereços diferentes configurados em hardware, pelos pinos A0, A1, A2 ou usar até a memória 24C512. Assim, teríamos mais capacidade de armazenamento. Lembrando que o objetivo, é de usar pequenas mensagens, para avisos, alertas, campanhias, etc. 

      Algumas sugestões para alterações podem ser bem vindas. Por exemplo: usar um PIC de 8 pinos (12F752) poderia deixar a placa final mais compacta. 
      Incrementar um amplificador de áudio com LM386 por exemplo, poderia ser muito bom para falar um número digitado numa tecla.

       Enfim, são várias as aplicações. 

       Só depende da sua imaginação e capacidade.

       Imagine. 

       Acredite em você. 

       E crie com Eletrônica.

      Forte abraço a todos!