Récepteur infrarouge RC5

From Eric

Revision as of 17:21, 10 May 2013 by Ejenn (Talk | contribs)
Jump to: navigation, search

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.

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).

  ---+  +--+     +--+  +--+--+
     |  |  |     |  |  |     |
     +--+  +--+--+  +--+     +--+
   <---> <---> <---> <---> <--->    
 
     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, last_bit;
   static uint16_t tmp_code; 
   static uint16_t t, last_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 - last_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
     last_bit = 1;
       
     // Initialiser le compteur de bits
     ctr = 0;
 
     tmp_code = 0;
       
     last_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
       last_bit = 1-last_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 |= last_bit;
   
     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...
   }
   last_t = t;
 }
Personal tools