terça-feira, 22 de outubro de 2024

Interruptor de toque - aproximação com 12F675

 Algum tempo atrás, tive a necessidade de fazer um simples interruptor de toque para controlar interruptores de luz da minha casa. Algo simples, sem WiFi e nada de sofisticado. Tipo; instalar no abajour da cabeceira da cama, por exemplo como um simples hobby.

Fui atrás e achei uma nota de aplicação da Microchip (AN1202) que descreve um exemplo de aplicação para ta finalidade.

Trata-se de um interuptor capacitivo baseado num PIC da familia 10F. (Capacitive Sensing with PIC10F). Mais detalhes no link abaixo: 

https://drive.google.com/file/d/1MILW4CE5PvCgcoIYgp4cQCqbayP3ZmbL/view?usp=drive_link.

No exemplo em questão, eles descrevem uma frequência na faixa de alguns Kilohertz. No entanto, pensei: Por que não usar uma frequência na faixa de alguns Megahertz?! 

Claro que o circuito final poderia ficar com mais componentes tornando a placa de circuito impresso final, de um tamanho maior que o espaçamento de uma caixinha do interruptor convencional de 4x2. 

Mas....

O PIC pode trabalhar com frequência de até 20MHz. Eis aqui o 'pulo do gato'
Pensei em ultilizar um cristal de 20MHz no clock do microcontrolar e usar essa frequência para aplicar na 'antena' (superficie metálica de toque). 

Assim:



Como funciona?

Todo o circuito é alimentado por uma fonte capacitiva sendo o C6 como atenuador. 

R4 serve para amortecer a partida no primeiro instante que é ligado, protegendo o diodo zener para não queimar (uma vez que o capacitor C6 está descarregado). A linha do positivo da alimentação é comum, pois isso ajuda o disparo dos 4 quadrantes do TRIAC independente do ângulo da senóide. 

C7 ficar responsavél por jogar pulsos positivo e negativos no Gate do TRIAC conforme carga e descarga do mesmo. No PIC, é gerado um 'trem' de pulso de alta frequência pra isso.

No pino 3 do PIC, encontra-se a alta frequência da qual iremos usufruí-la. Após C2, aplica-se o sinal na superficie do toque e em seguida há a retificação e filtragem pelos componentes C3, D1, D2 e C4. 

R1, fica de carga para descarregar  tal filtro. 

Esse sinal é entregue ao pinos 7, cuja a configuração é de entrada Analógica. O algoritimo do PIC fica assim:

Realiza a leitura analógica desse sinal. Quando esse valor digital cair em relação ao anterior (sinal sem toque), é porque alguém tocou na superfície. A partir disso, é só montar o FLIP-FLOP no software do código.

Os pontos A.C. são entrada de rede 110 ou 220V. LAMP, são os dois fios que vão para lâmpada. 

A sensilidade é tão boa, que quando aproxima a mão há 15mm da superfície, já identifica como toque.

Para sinalizar, (além da lâmpada, é claro) coloquei um led. LED aceso, luz apagada. LED pagado, luz acesa. Fica fácil e intuitivo acha o interruptor no escuro. Usei um resistor de alto valor (100K) pois ultilizei um led de 3mm e alto brilho que acaba sendo um incômodo quando ele acende no seu brilho normal.

Modificações podem serem feitas como o uso de um relê para carga de alta potência por exemplo. 

Como podem ver, o circuito tem poucos componentes e é de baixo custo. O que viabiliza a montagem pra impressionar aquela namoradinha...(Mulheres gostam de um cara inteligente...) Menos as do meu bairro.

Eu, fiz assim:

Escolhi um espelho cego e somente furei para o LED:





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!