Problemas al decodificar HT12E con microcontrolador.

Me encuentro trabajando en un proyecto de un decodificador con microcontrolador de un HT12E. Según las hojas de datos del mismo, éstos trabajan en un margen de frecuencias de entre 1,75 a 2 KHz. Aunque eso depende del resistor que se le conecte entre los pines del oscilador interno. Es posible realizar la decodificación, ya que éste envía un pulso y un bit de sincronía para establecer la comunicación. Ahora bien, el tiempo del bit de sincronía puede variar por el nivel de batería, el resistor que lleva entre sus patas 15 y 16, entre otros factores. Es posible realizar dicha decodificación, haciendo uso del timer0 como temporizador, cuyo desborde evitaría que lleguen pulsos de más de 2 ciclos (ya que los descarta y los toma como inválidos). También se utiliza el timer1 como contador, para determinar la duración del pulso alto en piloto y así comparar los siguientes que llegan. Se puede utilizar la interrupción por cambio de estado RB4-RB7 para detectar los mismos (El pin RB4 iría al RX digital del módulo receptor). He logrado hacer funcionar uno, pero no "discrimina". Toma hasta el "ruido" que se le introduce a la radio. Es decir, falta pulirlo. Pero una vez pulido funcionará perfectamente. Si alguien puede aportar una solución a mi problema, se lo agradeceré. Les dejo todo en un *.rar, así pueden descargar la simulación en proteus, además del código fuente en CCS. ...

Olvidé el adjunto. Aquí está.
 

Adjuntos

  • HT12E.zip
    98.1 KB · Visitas: 17
Podrías explicar un poco lo que hace tu código como para poder ayudarte de forma más fácil? Sobre todo la forma en la que detectas el código y como lo decodificas.
 
Última edición:
Podrías explicar un poco lo que hace tu código como para poder ayudarte de forma más fácil? Sobre todo la forma en la que detectas el código y como lo decodificas.

Encantado. Según la hoja de datos del HT12E, la traza se envía mediante pulsos cuya duración depende del nivel de tensión en la batería y el resistor conectado a los pines 15-16, el cual define dicha frecuencia de oscilación. Es por tanto incalculable dicha frecuencia, ya que depende de variaciones que están fuera de nuestro alcance. Volviendo a los códigos que transmite, la traza sería la siguiente:
PILOTO (12Bits L + 1/3bit H) | DIRECCIÓN (12Bits) | DATOS (8Bits) |
El código piloto contiene además un bit de sincronía (1), el cual sirve para medir la duración del pulso. Y eso es lo que el código hace al principio: Medir la duración del pulso. Esto es:

Pilot code:
LLL LLL LLL LLL LLL LLL LLL LLL LLL LLL LLL LLL |H
Periódo de 12 bits de duración de pulsos Low (36 Alpha) + 1 Alpha de pulso High (período de sincronía).

El código piloto tiene una duración de 12bits (todos pulsos bajos) y un tercio de bit en pulso alto. Ese tercio de bit mide exactamente 1 Alpha (Donde Alpha = 1 ciclo reloj).

Los bits codificados por el HT12E se transmiten de la siguiente forma:

LHH = bit 0
LLH = bit 1
Cada bit dura exactamente 3 alpha.

es preciso discriminar lo que se recibe para descartar paquetes de otras fuentes (interferencia), por lo que sabiendo lo anterior, esto se hace mediante medición de la duración de pulsos altos y bajos, con cierta tolerancia añadidas por los ciclos máquina que ocupan todas las instrucciones programadas en el microcontrolador.

Este proceso de decodificación hace simplemente eso:
1) Mide el pulso en alto del código piloto, el cual será el valor de un ciclo máquina, denominado Alpha.
2) Recibe el pulso o los pulsos negativo(s), luego el pulso o los pulsos positivo(s), los almacena y verifica si su duración corresponde o no a un bit enviado por el transmisor o si se trata de una interferencia.
3) Parsea los datos y los ordena para luego presentarlos en el LCD (en formato decimal).

Eso sería más o menos todo. El desborde del timer0 se programó para que discrimine según duración máxima de 2ms de pulso bajo o alto. El timer1 para llevar el tiempo de duración de los pulsos recibidos, mientras que la interrupción por cambio de estado de RB4-RB7 sirve para detectar los flancos de subida/bajada de la salida digital del RX. Espero haber respondido tu pregunta.
 
Última edición:
Y tu problema es que el ruido te lo toma a veces como un código válido? Este programa sólo sirve para el HT12 o también sirve con otros encoders? Has probado algún otro?

 
No he probado ningún otro. Sé que tengo un decoder para OTP que sí funciona. Pero no funciona con el HT12E. Al parecer el formato es el mismo pero el rango de frecuencias de pulsos varía notablemente. Además el de OTP me almacena 22bits, mientras que el HT12E sólo 12 bits en total. Estos paquetes enviados no tienen reconocimiento de final de código (endcode) como el OTP. Eso también me ayudaba a descartar ruido, ya que los paquetes que no terminaban en 0101 (endcode) directamente los filtraba a todos. Sí, efectivamente: Mi problema es que me toma cualquier ruido y no tengo forma de discriminar mejor esos paquetes.
 
Cuantas tramas de código analiza tu programa para decir si es o no un código válido? Por ahí añadiendo un bucle que repita la secuencia y que por lo menos el código se repita 2 o 3 o 4 veces harías que descarte un poco códigos erroneos por ruido. Por otro lado, otro dato que puedes usar para detectar un código válido es el espacio entre una trama y la otra, lo lees la primera vez, y luego en el bucle de repetición buscas ese mismo valor, tomando un margen de seguridad, si lo vuelves a encontrar puede que sea un código válido y lo vuelves a analizar sino lo descartas.

 
El ruido es muy propenso a que en algún momento determinado haga que tu decodificador interprete que se recibió un código válido. Por ejemplo el MC145026 y MC145027 pares encoder decoder, como protocolo tienen que pare ser válido un código tiene que llegar 2 veces el mismo código, de lo contrario lo descarta, de esa forma evita el ruido, ya que es casi imposible, o muy muy poco probable que 2 veces seguidas se de la misma secuencia en un lapso de tiempo corto.



----------------- Actualización --------------------

Mirá aca te dejo un código hecho en CCS por mi que permite reconocer los encoders: PT2262, PT2264, PT2242, PT2240, SC2260, SC2262, HX2260, HX2262, HS2262, HS2260, AX5026, SMC918, SMC926, AX5326, RT1527, FP1527, FP527, HS1527, HX1527, HT600, HT680, HT6201, HT6010, HT6012, HT6014, HT6013, HT12A, HT12E. Te comento que funciona, actualmente lo tengo implementado en centrales para portones automáticos y sin problemas. Fijate que realizo un bucle en el que verifico que el código llegue bien 2 veces, de lo contrario lo descarto. Luego si es un código correcto lo almaceno en la EEPROM para luego poder comparar cuando llegue el mismo código. Esto me permite copiar el código de un control cualquiera a la central, así lo aprende, y luego cuando recibe un código se fija si lo tiene almacenado en su memoria, si lo tiene activa el portón, sino, no hace nada y lo ignora. Cualquier duda que tengas consultame.
Código:
#FUSES NOWDT                    //No Watch Dog Timer
#FUSES INTRC_IO                 //Internal RC Osc, no CLKOUT 
#FUSES NOPROTECT                //Code not protected from reading 
#FUSES BROWNOUT                 //Brownout reset 
#FUSES NOMCLR                   //Master Clear pin used for I/O 
#FUSES NOCPD                    //No EE protection 
#FUSES PUT                      //Power Up Timer
#FUSES RESERVED                 //Used to set the reserved FUSE bits  
#use delay(clock=4000000)  

// Definiciones del puerto A  

#define Datos PIN_A0 
#use fast_io(A)  

int i,Trama_encontrada,Contador_bits,Codigo[3],Auxiliar[3]; 
int Contador_codigos,Bien,Mal,Nuevo_codigo,Aux,Salto; 
long Tiempo_entre_tramas,Aux_tiempo_entre_tramas; 
long Tiempo_ET_delta_neg,Tiempo_ET_delta_pos,Tiempo_bits,Tiempo_bit,Bit,Tiempo;  

void main() 
{    
   set_tris_a (0b11111111); // Entradas:A0,A1,A2,A3,A4,A5    
   Salto = read_eeprom (252);    
   if (Salto==255)       
      Salto = 0;    
   setup_timer_1 (T1_INTERNAL|T1_DIV_BY_1); // Base de tiempo de 1 useg    
   for (;;)    
   {    
      Tiempo_entre_tramas = 0;    
      Trama_encontrada = false;    
      Contador_bits = 0;    
      Nuevo_codigo = 0;    
      Contador_codigos = 0;    
      Mal = 0;    
      Bien = 0;    
      for (i=0;i<25;i++)    
      {       
         while (input(Datos) == 1);       
         set_timer1(0);         
         while (input(Datos) == 0);       
         Aux_tiempo_entre_tramas = get_timer1();       
         if (Tiempo_entre_tramas < Aux_tiempo_entre_tramas)          
            Tiempo_entre_tramas = Aux_tiempo_entre_tramas;    
      }    
      Tiempo_ET_delta_neg = Tiempo_entre_tramas-Tiempo_entre_tramas/64;    
      Tiempo_ET_delta_pos = Tiempo_entre_tramas+Tiempo_entre_tramas/64;    
      for (i=0;i<25;i++)    
      {       
          while (input(Datos) == 1);       
          set_timer1(0);         
          while (input(Datos) == 0);       
          if ((Tiempo_ET_delta_neg < get_timer1())&(get_timer1() < Tiempo_ET_delta_pos))       
          {          
              i = 25;          
             Trama_encontrada = true;       
          }    
       }    
       if (Trama_encontrada == true)    
       {       
           while (input(Datos) == 1);       
           set_timer1(0);         
           while (input(Datos) == 0);       
           while ((Tiempo_ET_delta_neg > get_timer1())|(get_timer1() > Tiempo_ET_delta_pos)&(Contador_bits<25))       
           {           
               Contador_bits++;         
               while (input(Datos) == 1);         
               set_timer1(0);           
               while (input(Datos) == 0);       
            }       
            if (Contador_bits < 25)       
            {          
                while (input(Datos) == 0);          
                set_timer1(0);            
                while (input(Datos) == 1);          
                while (input(Datos) == 0);          
                Tiempo_bits = get_timer1();          
                while (input(Datos) == 0);          
                set_timer1(0);            
                while (input(Datos) == 1);          
                while (input(Datos) == 0);;          
                Tiempo = get_timer1();          
                if (((Tiempo_bits - Tiempo_bits/64) > Tiempo)|(Tiempo > (Tiempo_bits + Tiempo_bits/64)))          
                {             
                    if (Tiempo < Tiempo_bits)                
                       Tiempo_bit = Tiempo_bits/2;             
                    else                
                       Tiempo_bit = Tiempo/2;          
                }          
                else             
                       Tiempo_bit = Tiempo_bits/2;          
                do          
                {             
                   do             
                   {                
                        while (input(Datos) == 1);                
                        set_timer1(0);                  
                        while (input(Datos) == 0);                
                        Tiempo = get_timer1();             
                    }while ((Tiempo_ET_delta_neg > Tiempo)|(Tiempo > Tiempo_ET_delta_pos));             
                    for (i=0;i<Contador_bits;i++)             
                    {                
                       while (input(Datos) == 1);                
                       set_timer1(0);                  
                       while (input(Datos) == 0);                
                       Bit = get_timer1();                
                       if (Bit < Tiempo_bit)                   
                          shift_left(Codigo,3,1);                
                       else                   
                          shift_left(Codigo,3,0);             
                    }             
                    if ((Codigo[0]==0)&(Codigo[1]==0)&(Codigo[2]==0))                
                        Mal = 1;             
                    if ((Nuevo_codigo == 0)&(Mal == 0))             
                    {                
                       for (i=0;i<3;i++)                   
                          Auxiliar[i]=Codigo[i];             
                    }             
                    if ((Mal == 0)&(Nuevo_Codigo == 1))             
                    {                
                        if (Auxiliar[0] == Codigo[0])                
                        {                   
                            if (Auxiliar[1] == Codigo[1])                   
                            {
                                if (Auxiliar[2] == Codigo[2])                         
                                   Bien = 1;
                                else                      
                                {
                                   Mal = 1;
                                   Bien = 0;
                                 }
                             }
                             else
                             {
                                 Mal = 1;
                                 Bien = 0;
                             }
                        }                
                        else
                        {
                           Mal = 1;
                           Bien = 0;
                        }
                    }             
                    Nuevo_codigo = 1;
                    Contador_codigos++;             
                    for (i=0;i<3;i++)                
                       Codigo[i] = 0;          
                }while((Mal == 0)&(Contador_Codigos < 4));
                if (Bien == 1)          
                {             
                   for (i=0;i<3;i++)
                   {                
                      Aux = Auxiliar[i];                
                      write_eeprom(i+Salto,Aux);             
                   }             
                   Salto+=3;             
                   write_eeprom(252,Salto);          
                }       
            }    
        }    
    } 
}
 
Última edición:
Wow! Bueno, yo estuve trabajando en algo así... pero sólo funciona para el HT6P20 y utilizo el mismo formato del typedef y el struct, combinado con el switch porque me es más simple. Además el HT6P20 tiene endcode y me es muy simple discriminar la interferencia. Pero sí, la idea es esa básicamente. Combinar 2 arrays y que a ambos les llegue lo mismo. Luego los comparo y es como si hubiera un código de 24 bits. Ahora lo hago.
 
Última edición:
Acabo de leer que el PT2261 y el HT12E envían 4 words (word = 12 bits = Address + data) por cada vez que el pin NOT(TE) va a GND. Es decir que no es necesario ponerlo a chequear repetidas veces. Se puede con un frame de datos hacer el filtro. Sólo basta tomar el frame completo (pilot + 4 words = 48bits-pilot) y compararlos cada 12 bits. De esa forma tendrías listo el filtro y eso te serviría para todos los integrados que has mencionado. Adjunto las hojas de datos de los que mencioné.

Nota: Iré modificando el programa para que pueda decodificar los demás integrados que me mencionabas antes.
 

Adjuntos

  • 380-00619-0-PT2261A.pdf
    391.6 KB · Visitas: 18
  • ht12a_ht12e.pdf
    166.2 KB · Visitas: 7
Esta bien lo que dices, pasa que hay muchas formas de encarar el mismo problema, en tu caso tu entras toda la trama (4 words) y a partir de ahi analizas si corresponde o no a un código, pero para ello debes perder todo el tiempo de entrar toda la trama. En mi caso lo trabajo distinto, directamente analizo bit a bit a medida que va entrando y si por algún motivo un bit tiene una duración distinta a la que había comenzado a entrar, entonces directamente no sigo analizando el código. Luego si el primer código fue correcto repito la secuencia N veces, donde N es un número que yo puedo ajustar a mi gusto. Pero de todas formas esta interesante tu código, quizás es un poco más estructurado que el mio y seguramente lleva menos código de programación, con lo cual se acorta el tiempo de respuesta del micro.

 
Una pregunta: ¿Has probado tu código con los otros integrados? Porque parece ser que el formato de los frames del PT2261 difiere del que envía el HT12E. Yo ya pude terminar con el deco para el HT12E. Te adjunto nuevo código y simulación. Además fue probado en protoboard, y está funcionando 100%.
 

Adjuntos

  • HT12E.zip
    262 KB · Visitas: 40
Si, mi codigo contempla todo los caso, identifica el bit de sincronización y a partir de ello identifica a que tipo de formato corresponde y lo decodifica. Esta probado con el formato del ht12e y con el ev1527.



--------------------- Actualizado -------------------------

Estuve viendo tu código, y por lo que veo vos designas que como mucho tu Alpha puede valer 2ms. Lo cual significa que si ingresa un ancho mayor tu timer0 desborda y descarta lo que estas midiendo. Ahora bien que pasa si tu Alpha vale más de 2ms? o sea lo pienso para otros integrados en el que el Alpha pueda llegar a valer más que eso. Con la verificación de las 2 palabras word0 y word1 pudiste solucionar el problema de la falsa activación por ruido? Otra variable con la que podes jugar para minimizar falsas activaciones es con la "Tolerancia" achicandola.
 
Última edición:
Quiero conseguirme todos los llaveros que pueda. Así logro hacer uno completamente universal. Desconozco todos los tipos de decodificación que hay. Hasta ahora he decodificado el HT12E y el OTP. No sé qué formato es el que acabo de decodificar. Creo que es AFK o Manchester.

Con la verificación en 2 pasos he podido efectivamente quitar el error de activación por falsos códigos. He dejado 2 arrays más, con el objeto de fortalecerlo en áreas donde la interferencia sea mayor (zonas urbanizadas, industrias, ...). La hoja de datos del HT12E nos brinda una tabla. Dicha tabla contiene información sobre la fosc y cómo ésta varía según el resistor ROSC y la tensión aplicada. En mi caso tengo un control que tiene un resistor de 1,8Mohms y es alimentado a 12V (rango de frecuencias de 1,25-2KHz), siendo su inversa máxima de 0,0005 segundos. Considerando el cálculo del timer0 y además que el pulso a veces es 2 veces ese valor, sumado a eso los tiempos de ejecución, creo que estamos ante un desborde aceptable de 1,5ms. Puse 2ms para asegurarme. Además ni siquiera es necesario programar una interrupción por desborde de timer0, pero sí que está bueno para filtrar el período bajo del código piloto y más efectivo para "medir" el ancho de pulso mediante el cual luego filtro gran parte de la interferencia, al no corresponderse con un bit.

De acuerdo con la simulación, el número de tolerancia aún puede acortarse aproximadamente unas 4 veces.
 
Última edición:
Pues fijate por ejemplo que el EV1527 es diferente el comienzo al HT12E. En el HT12E tenes un piloto de 12 bits y luego un bit de sincronización. En el EV1527 es exactamente alreves, primero un bit de sincronización y luego un piloto. Puede mirar por acá los dos tipos de codificación más comunes, que son los que utilizan los integrados que yo te puse anteriormente.

PD: Fijate un post anterior que justo vos escribias al mismo tiempo que yo y creo que no alcanzaste a ver lo que actualice. Se solucionó el problema del ruido?
 
Última edición:
Me he tomado el atrevimiento de modificar un poco tu código para optimizar la memoria tanto la RAM como la ROM ya que me parecia demasiado excesivo usar 12 byte por cada palabra para solo colocar un bit en cada byte y posteriormente pasar esos 12 bytes a un registro de Direccion y otro de Datos. Asi que lo que modifique basicamente fue que directamente se obtenga la Dirección y los Datos sin tener que pasar por los Words. Otra cosa que me pareció innecesario fue el timer0, directamente se realiza una comparación ni bien se obtiene el valor de Alpha y si resulta menor que 250 o sea 2mseg continúa analizando, de lo contrario, da error. Otra cosa que modifique fue la del control de los bits, directamente analiza cada estado alto o bajo y verifica si cumple con las condiciones necesarias a partir del valor del Alpha, de lo contrario da error y no sigue. No lo simule ya que no tengo Proteus 8 o superior y tampoco lo probe en forma física, por lo que puede no estar funcional, pero lo vas a saber modificar para que funcione. Luego me comentas si funcionó y si te sirvió las mejoras. =)
 

Adjuntos

  • HT12E.rar
    255.2 KB · Visitas: 37
Última edición:
Voy a intentar realizar una fusión entre mi código y el tuyo a ver que sale del apareamiento. La combinación de lo mejor de cada uno tendrá la cualidad de alto tiempo de respuesta y gran versatilidad de decodificación, o al menos a eso apunto. En la medida que avance lo postearé, no se si tu objetivo esta siendo el mismo, pero creo que vamos hacia el mismo lado.

 
Mi objetivo es hacer un decodificador universal, que permita usar cualquier tipo de control remoto, siempre que sea en 433MHz. Estaría excelente lograrlo. Estuve probando y parece que el código que hice yo también puede leer OTP. El tema es que no lo estaría decodificando, ni guardando porque tiene variantes.
 
Atrás
Arriba