Récepteur infrarouge RC5

From Eric

(Difference between revisions)
Jump to: navigation, search
m
m
 
(5 intermediate revisions not shown)
Line 9: Line 9:
* le traitement nécessitait une temporisation de bonne qualité.
* le traitement nécessitait une temporisation de bonne qualité.
-
J'ai repris en code afin que l'échantillonnage des bits repose (quasi-)uniquement sur l'interruption externe.
+
J'ai repris en code afin que l'échantillonnage des bits repose (quasi-)uniquement sur l'interruption externe.
 +
 
 +
=La structure d'un trame RC5=
 +
 
 +
Les schémas suivants sont issus du document [http://daniel.menesplier.free.fr/Doc/CODE%20%20RC5.pdf rc5.pdf] réalisé par Daniel Menesplier (que je remercie).
 +
 
 +
[[File:rc5_timing.jpg|600px|thumb|none]]
 +
 
 +
 
 +
[[File:rc5_frame.jpg|600px|thumb|none]]
 +
 
 +
 
 +
=Le logiciel=
 +
 
 +
L'idée consiste à distinguer un créneau court d'un créneau long et à utiliser cette information pour détarminer la valeur du bit n+1 sachant la valeur du bit n.
-
L'idée consiste à distinguer un créneau court d'un créneau long.
 
Prenons la trame suivante (qui début par deux bits à un, comme toute trame RC5).  
Prenons la trame suivante (qui début par deux bits à un, comme toute trame RC5).  
Line 51: Line 64:
     static uint16_t t, previous_t=0, threshold;
     static uint16_t t, previous_t=0, threshold;
     uint16_t dt;
     uint16_t dt;
-
 
+
 
     // Acquérir le temps courant
     // Acquérir le temps courant
     t = TCNT1;
     t = TCNT1;
Line 101: Line 114:
       // Initialiser le compteur de bits
       // Initialiser le compteur de bits
       ctr = 0;
       ctr = 0;
-
 
+
 
       tmp_code = 0;
       tmp_code = 0;
          
          
Line 118: Line 131:
         previous_bit = 1-previous_bit;   
         previous_bit = 1-previous_bit;   
         // On reste dans cet état
         // On reste dans cet état
-
 
+
 
       }
       }
       else
       else
Line 129: Line 142:
       tmp_code <<= 1;
       tmp_code <<= 1;
       tmp_code |= previous_bit;
       tmp_code |= previous_bit;
-
 
+
 
-
   
+
      // On met à jour le compteur.
       ctr++;
       ctr++;
 +
 
       // A-t-on reçu tous les bits?
       // A-t-on reçu tous les bits?
       if ( ctr == 12 )
       if ( ctr == 12 )
Line 138: Line 152:
         if ( !queue_is_full(&rc5_queue))
         if ( !queue_is_full(&rc5_queue))
           queue_put(&rc5_queue, tmp_code & 0xFF);
           queue_put(&rc5_queue, tmp_code & 0xFF);
-
       
+
         
         // Et on réinitialise la machine.
         // Et on réinitialise la machine.
         state = K_RC5_WAIT_START;     
         state = K_RC5_WAIT_START;     
Line 154: Line 168:
      
      
     previous_t = t;
     previous_t = t;
-
 
+
 
   }
   }
    
    
Line 164: Line 178:
     static uint8_t prev_code=0, match_ctr=0;
     static uint8_t prev_code=0, match_ctr=0;
     uint8_t new_code=0;
     uint8_t new_code=0;
-
 
+
 
     // Y a-t-il un message dans la file?
     // Y a-t-il un message dans la file?
     if ( !rc5_queue_is_empty() )
     if ( !rc5_queue_is_empty() )
Line 189: Line 203:
       }
       }
     }
     }
-
   
+
     
     return FALSE;
     return FALSE;
   }
   }
 +
 +
On notera que lorsqu'une trame est reçue, elle est stockée dans une FIFO. Cette FIFO est utilsée pour communiquer de façon asynchrone avec les autres parties de l'application. Par ailleurs, une commande RC5 n'est validée que la trame qui représente la commande est reçue 2 ou trois fois.
 +
 +
=Le matériel=
 +
 +
Du point de vue matérel, le montage ne présente aucun intérêt ou difficulté : le capteur RC5 est simplement câblé sur l'entrée INT1 de l'ATMEGA 8.
 +
 +
Sur une plaquette d'expérimentation, ça donne la chose suivante :
 +
 +
[[File:recepteur_rc5.jpg|300px|thumb|none]]

Latest revision as of 17:39, 10 May 2013

L'un de mes premiers montage à AVR fut un récepteur infra-rouge RC5.

A l'époque, j'avais opté pour un traitement combinant interruptions et timers :

  • le premier bit état traité par une interruption externe sur le signal RC5 afin de déterminer la durée d'un "petit" créneau ;
  • les autres bits étaient traité par une interruption issue d'un timer dont la durée était déterminée lors de la première étape ci-dessus.

Ca fonctionnait, mais

  • je n'avais pas traité la robustesse (que se passe-t-il si on a perdu un bit en cours de route?)
  • le traitement nécessitait une temporisation de bonne qualité.

J'ai repris en code afin que l'échantillonnage des bits repose (quasi-)uniquement sur l'interruption externe.

La structure d'un trame RC5

Les schémas suivants sont issus du document rc5.pdf réalisé par Daniel Menesplier (que je remercie).

Rc5 timing.jpg


Rc5 frame.jpg


Le logiciel

L'idée consiste à distinguer un créneau court d'un créneau long et à utiliser cette information pour détarminer la valeur du bit n+1 sachant la valeur du bit n.


Prenons la trame suivante (qui début par deux bits à un, comme toute trame RC5).

  ---+  +--+     +--+  +--+--+
     |  |  |     |  |  |     |
     +--+  +--+--+  +--+     +--+
   <---> <---> <---> <---> <--->    
 
     1     1     0     0     1

On constate qu'il y a un changement de valeur du bit n au bit n+1 (inversion) si le créneau est "long" ; sinon, la valeur est conservée.

L'idée est donc de mesurer la largeur d'un "petit" créneau (que l'on sait arriver en début de trame) puis à déterminer, lors de (presque) chaque front si on a affaire à un créneau long ou court.

Le code de la machine à états est donné ci-après (j'ai laissé d'horrible constantes... désolé...).:

 /* -------------------------------------------------------------
  * Handler d'interruption INT1, utilisé pour l'IR, protocole RC5
  * -------------------------------------------------------------
  *                     
  *          t0   t1                   v 
  *  -----+   +---+   +---+       +---+---+
  *       |   |   |   | . |       |     . |
  *       |   |   |   | . |       |     . |
  *       +---+   +---+ . +---+---+     . +---
  *      start. start   . .     . .     .
  Un bit dure 2x889us
  Une trame dure 14x1778us = 24892us
  Il y a au moins 88886us entre deux trames
  */
 
 typedef enum { K_RC5_WAIT_START, K_RC5_WAIT_FIRST_EDGE, K_RC5_WAIT_NEXT_EDGE, K_RC5_WAIT_NEXT_EDGE_AND_SKIP } rc5_int1_state_t;
 ISR(INT1_vect)
 { 
   static uint8_t state = K_RC5_WAIT_START;
   static uint8_t ctr, previous_bit;
   static uint16_t tmp_code; 
   static uint16_t t, previous_t=0, threshold;
   uint16_t dt;
 
   // Acquérir le temps courant
   t = TCNT1;
   
   // Calculer la durée entre deux fronts 
   if (ISBITSET(TIFR,TOV1))
     dt = K_MAX_DT;
   else
     dt = t - previous_t;
   
   // On est dans l'état d'attente d'un début de trame
   if ( state == K_RC5_WAIT_START) 
   {      
     // Ce front arrive-t-il après la période de silence inter-trame?
     if ( dt > 10000 ) 
     {
       // OUI : La trame est arrivée après le temps de silence...
       // Effacer le bit d'overflow (au cas où...)
       SETBIT(TIFR,TOV1);
   
       // Réinitialiser le timer
       TCNT1 = 0;
       
       // Fixer l'origine des temps
       t = 0;
       
       state =K_RC5_WAIT_FIRST_EDGE;
     }
     else
     {
       // NON : Le front n'est pas arrivée après le temps de silence...
       // On l'ignore simplement...
     }        
   
   }
   else
   if ( dt > 3000 )
   {
     state = K_RC5_WAIT_START;
   }
   else
   if ( state == K_RC5_WAIT_FIRST_EDGE)
   { 
     threshold = dt+(dt>>1);
       
     // Le premier bit (ignoré) vaut 1
     previous_bit = 1;
       
     // Initialiser le compteur de bits
     ctr = 0;
 
     tmp_code = 0;
       
     previous_t = t;
       
     state = K_RC5_WAIT_NEXT_EDGE_AND_SKIP;
   }
   else
   if ( state == K_RC5_WAIT_NEXT_EDGE)
   {
   
     // Est-ce un délai long?
     if ( dt  > threshold )
     {
       // C'est un délai long : il y a inversion des deux bits successifs
       previous_bit = 1-previous_bit;  
       // On reste dans cet état
 
     }
     else
     {
       // C'est un délai court : c'est le même bit ; on ignore le prochain front
       state = K_RC5_WAIT_NEXT_EDGE_AND_SKIP;  
     }
                 
     // On stocke le bit
     tmp_code <<= 1;
     tmp_code |= previous_bit;
 
     // On met à jour le compteur.
     ctr++;
 
     // A-t-on reçu tous les bits?
     if ( ctr == 12 )
     {
       // Oui, on stocke le code (si on peut...)
       if ( !queue_is_full(&rc5_queue))
         queue_put(&rc5_queue, tmp_code & 0xFF);
         
       // Et on réinitialise la machine.
       state = K_RC5_WAIT_START;    
     }
   }
   else
   if ( state == K_RC5_WAIT_NEXT_EDGE_AND_SKIP)
   {
       state = K_RC5_WAIT_NEXT_EDGE;    
   }
   else
   {
     // C'est un cas d'erreur...
   }
   
   previous_t = t;
 
 }
 
 // Un message n'est validé que s'il apparait plusieurs fois successivement.
 // Idéalement, il faudrait réinitialiser le compteur de matching tous les x appels
 // sans code <todo>
 bool_t rc5_get_filtered ( uint8_t *code )
 {
   static uint8_t prev_code=0, match_ctr=0;
   uint8_t new_code=0;
 
   // Y a-t-il un message dans la file?
   if ( !rc5_queue_is_empty() )
   {
     // OUI : on le récupère
     new_code = rc5_queue_get();
     // S'agit-il du même code que précédemment?
     if ( new_code == prev_code )
     {
       // OUI : On incrémente le compteur.
       match_ctr++;
       // Le compteur a-t-il atteint le seuil de validation.
       if ( match_ctr == 1 )
       {
         match_ctr=0;
         *code = new_code;
         return TRUE;
       }
     }
     else
     {
       prev_code = new_code;
       match_ctr = 0;
     }
   }
     
   return FALSE;
 }

On notera que lorsqu'une trame est reçue, elle est stockée dans une FIFO. Cette FIFO est utilsée pour communiquer de façon asynchrone avec les autres parties de l'application. Par ailleurs, une commande RC5 n'est validée que la trame qui représente la commande est reçue 2 ou trois fois.

Le matériel

Du point de vue matérel, le montage ne présente aucun intérêt ou difficulté : le capteur RC5 est simplement câblé sur l'entrée INT1 de l'ATMEGA 8.

Sur une plaquette d'expérimentation, ça donne la chose suivante :

Recepteur rc5.jpg
Personal tools