[Résolue] ISR perturbé par un Ecran Lcd I2c

bonjour a tous, j’ai un petit problème de code que je n’arrive pas a solutionner j’utilise un récepteur infrarouge type TSOP34856 surveiller par un une fonction ISR comme ceci :

//ISR ROUTINE

ISR(TIMER2_OVF_vect, ISR_NOBLOCK){
  
//RECEPTION
        

        rxRead1 = (PIND & B10000000) >> 7; //aka read pin 7, the fast way
        rxRead2 = (PIND & B00010000) >> 4; //aka read pin 7, the fast way

    
        rxTimer++;      
        
        switch (rxState) { //check current state of rx message CAPTEUR 1
          case STATE_IDLE: //idle, waiting for something to start
            if (rxRead1 == MARK) {
              if(rxRead2 == MARK){
                dircapteur = 1;
              }
              else{
                dircapteur = 0;
              }
              rxTimer = 0;
              rxState = STATE_MARK;  // transmission begins, check HDR (header)
            }
          break;
          case STATE_MARK: // timing MARK
            if (rxRead1 == SPACE){   // MARK ended, record time
              rawData[rawIndex] = rxTimer;
              rawIndex++;
              
              rxTimer = 0;
              rxState = STATE_SPACE;
            }
          break;
          case STATE_SPACE: // timing SPACE
            if (rxRead1 == MARK){   // MARK ended, record time
            
              rawData[rawIndex] = rxTimer;
              rawIndex++;
              
              rxTimer = 0;
              rxState = STATE_MARK;
            }
            else if(rxTimer > 34) {rxState = STATE_STOP;}
          break;      
        } 
}

Cette fonction marche très bien sauf quel est perturbé par la librairie LIquid_CrystalI2c ou wire.

Le récepteur infrarouge reçoit des trame de code en rafale et si l’écran est utilisé en même temps certains code sont erronée, par-contre si je n’envoie pas de commande a l’écran il décode 100% des trame.

...
     receivehit(); //Il s'agit de la fonction qui décode les trame
     
       //osd(); //Gestion de l'affichage de parametre
       //Gestion backlight
       if(backlightlcd == 1){
         //lcd.backlight();
         backlightoff = 0;
       }else if(backlightlcd == 0 && backlightoff == 0){
         backlightoff = 1;
         //lcd.noBacklight();
       }
     
....

void receivehit(){
  // COMPUTE INCOMING DATA
  if(rxState == STATE_STOP){          // DATA! a STOP has been issued meaning there's something to compute
        for(rawIndex = 0; rawIndex < 40; rawIndex++){    
          if(rawIndex % 2 == 0){ //even positions, no spaces         
            if(rawIndex == 0 && !match_hdr(rawData[rawIndex])) {  //first position (hdr)
              break; 
            }
            else if(rawIndex != 0) { //not the hdr
            
              if(match_one(rawData[rawIndex])) rxData = (rxData << 1) | 1;
              else if(match_zero(rawData[rawIndex])) rxData <<= 1;
              else{
                break; 
              }
            }
          } 
          else{  //odd positions, spaces         
            if(rawData[rawIndex] == 0) break;
            else if(!match_zero(rawData[rawIndex])){
             break; 
            }         
          }
          //Serial.print(" ");
          //Serial.print(rawData[rawIndex]);
        }
         //Serial.println("");
         Serial.println(rxData,HEX); 
         
   
      //prepare for next data
      for(rawIndex=0; rawIndex<40; rawIndex++){ //clear rawData array    
        rawData[rawIndex] = 0;   
      }
      
      rxData = 0; 
      rawIndex = 0;
      rxState = STATE_IDLE;	 // sets the state of the receiver to idle (default)
  }  
}

Si je dé commente une de ces lignes la réception infrarouge n’est plus fiable.

Quelqu’un aurais une idée ?

Merci d’avance.

Bonjour,

Regarde le code de LiquidCrystal_I2C mais j'ai comme l'impression que tu as un conflit de ressource.

Ton code utilise le timer 2 pour la réception et je suis quasi sûr que LiquidCrystal_I2C utilise ce même timer 2 pour faire la PWM qui fait marcher le rétroéclairage de l'écran.

Une lecture rapide du code source de la lib te donneras toutes les réponses à tes questions.

Merci pour ton aide skywodd c’est pas le première fois ^^

J’avais déjà penser a ca mais je n’ai pas réussi a trouver de référence a ce timer dans la lib lcd.

Je poste la librairie si tu peut y jeter un oeuil.

LiquidCrystal_I2C.cpp (8.64 KB)

LiquidCrystal_I2C.h (3.35 KB)

En fait je suis idiot (ou alors vraiment pas réveillé ... je sais pas).

Ta version de LiquidCrystal_i2c n'utilise pas de PWM pour le rétro-éclairage. Il est juste éteint ou allumé. Donc pas de problème de Timer2.

Par contre la lib utilise Wire pour l'I2C. Et Wire utilise en interne une interruption pour fonctionner.

Donc si tu utilises l'écran trop souvent, l'interruption de Wire s’exécute tout le temps et bloque ton interruption à toi. Le mieux que tu puisses faire c'est de réduire/limiter la fréquence de rafraîchissement de l'écran pour éviter de perdre trop d'interruptions de ton côté. Tu peux pas faire grand chose d'autre ...

Ah ok, enbetant cette histoire, la réception infrarouge correspond a tir d'un autre joueur car il s'agit d'un système de laser game donc je ne peut pas vraiment perdre de trame pour l’équité du jeu.

Je ne peut pas donner la priorité a mon instruction, ou autre ?

lemat: Ah ok, enbetant cette histoire, la réception infrarouge correspond a tir d'un autre joueur car il s'agit d'un système de laser game donc je ne peut pas vraiment perdre de trame pour l’équité du jeu.

Effectivement c'est assez embêtant ...

lemat: Je ne peut pas donner la priorité a mon instruction, ou autre ?

Non ce n'est pas possible. Et il me semble pas avoir vu un jour une version modifié de Wire n'utilisant pas d'interruptions ...

Ok tampis je vais essayer de limiter au maximum l'actualisation de l’écran si un tir peut arriver. Si quelqu'un a d'autres idée je suis preneur ^^

Merci quand même skywodd, j'y voit plus clair ^^

Ps: si j'utilise sei() et cli() en debut et fin de mon ISR est-ce que cela peut changer quelques chose ?

Peut-être essayer de diminuer la fréquence du bus I²C. Ça ne résoudra sans doute pas le problème, mais ça peut en diminuer la fréquence d'apparition.

Dans twi.h le réglage est déjà sur :

  #ifndef TWI_FREQ
  #define TWI_FREQ 100000L

Donc 100Khz d'après la doc c'est le minimum.

Je sèche complètement sur ce problèmes je ne pense qu'en même pas avoir attend les limites du 328p.

haifger: Peut-être essayer de diminuer la fréquence du bus I²C. Ça ne résoudra sans doute pas le problème, mais ça peut en diminuer la fréquence d'apparition.

Diminuer la fréquence de l'i2c risque d'être compliqué vu que c'est la lib qui fait l’initialisation. Diminuer la fréquence de rafraîchissement de l'écran est plus simple et plus rapide, en bonus on peux aussi essayer de rendre l'interruption de l'IR pour la rendre plus rapide (mais c'est déjà plus complexe).

Pour le rafraîchissement de l’écran je peut le limiter a toutes les une seconde (Car affichage d'un compte a rebours avec MM:SS). J'ai des événement qui demande a être plus rapide que ça mais je peut les afficher que lorsque c'est vraiment nécessaire.

skywodd: Diminuer la fréquence de rafraîchissement de l'écran est plus simple et plus rapide, en bonus on peux aussi essayer de rendre l'interruption de l'IR pour la rendre plus rapide (mais c'est déjà plus complexe).

De quelle manière pourrait t'on rendre l'ISR plus rapide ?

Bonsoir, après optimisation de mon osd :

  • Mise a jour de l'affichage des donnée quand celle ci sont modifié
  • Compte a rebours mis a jour a l'affichage toutes les 5s quand le rétroéclairage de l’écran est éteint (quand même lisible). J'ai aussi fait en sorte qu'il n’écrive pas en boucle sur l’écran des donnée qui vont être remplacer quelques seconde puis ré-afficher ensuite surtout pour celle ou ça peut arriver seulement 3-4 fois par partie.

Voila ci c'est quelques petites infos sur la correction de ce problèmes peuvent en aider d'autre tant mieux ;)

Merci beaucoup skywodd, le problème est résolue je ne ressent presque plus du tout l'interruption wire. :)

tu as tenté un ISR(TIMER2_OVF_vect, ISR_BLOCK){ ?

quel type d'ecran i2C est-ce? ce que tu as sur ton montage s'en tiend a IR + 1 lcd + I/O basique?

1ere solution simple: passer sur un ecran en com // 7 fils: la lib n'est pas interruptive 2eme solution plus penible: reproduire la com i2c pour piloter l'ecran (le briquoler un peu si besoin)

Je ne savais même pas que je pouvais faire : ISR(TIMER2_OVF_vect, ISR_BLOCK){

Je viens de faire un essais même en déclenchant des action de l’écran pendant les réception, plus aucuns code raté :)

Tu vient de régler définitivement mon problèmes jean-l MERCI :)

J'ai un écran alphanumérique 2x16 lignes de caractères.

J'utilise presque tout ce que sais faire l'arduino : Bluetooth en serial NRf24l01+ en SPI I2C pour l'ecran I/0 basique et pwm pour émission et réception IR

L’écran était obligatoirement en I2C sinon je n'avait pas assez de pin sur le 328PU ^^

Mais bon maintenant que tout roule ce n'est plus un problèmes :D

les interruptions c'est tres simple et tres compliqué a la fois.

ISR(TIMER2_OVF_vect, ISR_BLOCK) est la meme chose que ISR(TIMER2_OVF_vect)

une interruption peut en interrompre une autre, c'est a toi de gerer l'imbrication. d'une maniere generale, une interruption materielle sera prioritaire.

il faut dans la mesure du possible reduire au minimum le temps d'interruption. l'utilisation de

rxRead1 = (PIND & B10000000) >> 7; //aka read pin 7, the fast way
rxRead2 = (PIND & B00010000) >> 4; //aka read pin 7, the fast way

est donc plus que judicieuse. et si tu te sert de rxRead que dans cette routine, tu peut meme t'abstenir de shifter >>.

... c'est quoi comme module bluetooth que t'utilise?

Ok je comprend mieux merci pour l'info.

Je me sert de rxRead que dans cette routine comment je désigne ma pin 7 et 4 sans shifter ?

J'utilise le module bluetooth HC-05 :

quand tu fait rxRead1 = (PIND & B10000000) >> 7; rxRead1 vaut donc 0 ou 1, pas autre chose.

tu test ensuite si if (rxRead1 == MARK) ou if (rxRead1 == SPACE) je presume que MARK et SPACE sont soit 0, soit 1.

tout ce qui est 0 n'est forcement pas 1, y'a pas besoin de shifter, le masque & renvoi 0 ou pas 0. ... ni meme donc d'effectuer un test de comparaison. si en plus rxRead1, MARK ou SPACE sont des int au lieu de char, ca fait 2 octets a tester (j'ai pas chercher a savoir comment le compilateur pouvait optimiser)

si tu t'en tiend a if (rxRead1) et if (not rxRead1)

t'economise quelques cycles horloges (et quelques octets de prog, t'as qu'a regarder la difference au message de compil).

mais bon, ca n'a pas l'air critique a ce point, vu que ca fonctionne.

Je n'ai pas tout a fait compris, par quoi devrait je remplacer : rxRead1 = (PIND & B10000000) >> 7; par : rxRead1 = (PIND & B10000000) ; ?

MARK et SPACE sont des #define

Comme tu le dit ça fonctionne mais si je peut gagner quelques octet je prend ça commence a être juste, par rapport a ma première version j'ai réussi a gagner presque 6Ko d'espace en optimisant (type de variable, fonction, non répétition de code etc) :)

oui.

si par exemple MARK define 0 et SPACE define 1, tu peut faire

rxRead1 = (PIND & B10000000) ; if (rxRead1) // == MARK) ou if (not rxRead1) // == SPACE)

si ca se trouve, le compilateur a déja optimisé ces deux test (si effectivement 0 ou pas 0), mais ca m'etonerai qu'il shift tout seul.

Ok, j’ai essayer ton exemple ça ne fonctionne pas le MCU plante.