Go Down

Topic: i2c MPU6050 Mega freeze Wire.requestFrom (Read 801 times) previous topic - next topic

Vincent_TH

Bonjour,
J'ai une mega avec un gyro MPU6050 (DFrobot 6 dof ou PHIrobotics 6 dof) installée sur un robot roulant. Donc ils subissent pas mal de vibrations… de bruits et autres agressions… Je communique en i2c avec les 2 à la fois (0x68 et 0x69) pour reconstruire un signal de meilleure qualité (capteur à 2 endroits sur le robot). Je rencontre des problèmes lors de la lecture des accel / vitesse angulaire avec la fonction Wire.requestFrom(address_GYRO_DF,14,true). Même dans le cas où j'ai qu'un seul gyro. Le problème est que la carte arduino semble entrer dans un pédalage dans la semoule pour ne jamais en sortir… Pour éviter de tous planter, j'ai alors ajouté un watchdog (wdt_reset /  wdt_enable(WDTO_4S) / à 4 secondes)… mais c'est clairement pas l'idéal, car à chaque redémarrage je dois rejouer toutes les initialisations de actionneurs…
Dans la boucle principale, pour réduire ces « freeze » intempestif, avant chaque lecture de mon MPU, je test si l'i2c est bien là avec la fonction checkBus()
Code: [Select]

bool checkBus()
{
  if (digitalRead(20) == LOW || digitalRead(21) == LOW)
  {
    BusErrorBool = true;
  }
}

Ça améliore les choses… mais pas à 100 %
Du coup, j'ai ajouté entre chaque instruction de lecture de l'i2c des délais
Code: [Select]

      Wire.endTransmission(true);
      delayMicroseconds(50);
      checkBus();
      if (BusErrorBool==false)
      {
        Wire.beginTransmission(address_GYRO_DF);
        Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
        delayMicroseconds(50);
        Wire.endTransmission(false);
        delayMicroseconds(50);
        Wire.requestFrom(address_GYRO_DF,14,true);
        delayMicroseconds(200);
        GyroDF_AcX=Wire.read()<<8|Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
        GyroDF_AcY=Wire.read()<<8|Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
        GyroDF_AcZ=Wire.read()<<8|Wire.read();  // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
        GyroDF_Tmp=Wire.read()<<8|Wire.read();  // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)
        GyroDF_GyX=Wire.read()<<8|Wire.read()-0;  // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
        GyroDF_GyY=Wire.read()<<8|Wire.read()-0;  // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
        GyroDF_GyZ=Wire.read()<<8|Wire.read()-0;  // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)
      }
      BusErrorBool=false;
      Wire.endTransmission(true);
      GyroDF_Angle = -(atan2(GyroDF_AcZ, GyroDF_AcY) * 180 / pi -87.6); // angle de base [deg]
      GyroDF_omega = (GyroDF_GyY+430)/500; // vitesse rotation [rd/s]




Comme pour le "checkBus », ça améliore encore un peu les choses… mais c'est toujours pas nickel
Dès que le robot roule, j'ai des plantages de la Mega tous les 30 à 40 secondes….

Si je mets des Serial.print(xxx) entre chaque ligne, je vois que le plantage est toujours à la ligne Wire.requestFrom(address_GYRO_DF,14,true); que le dernier argument soit à « true » ou « false ».

La question :
 :o
Avez-vous déjà rencontré ce genre de problème épineux ?
Y a-t-il une fonction qui dit « Wire.requestFrom » que si elle dépasse par exemple 1ms, elle s'arrête et redonne la main au reste du programme (un time out). Si je perds l'info une fois toutes les 30 ou 40 secondes, je dois pouvoir largement survivre sachant que j'ai un « timer » (scheduler) dans mon programme qui me rafraichit l'info toutes les 100 ms.

Merci par avance pour votre aide
Vincent

3Sigma

Bonjour,

Pourquoi n'utilises-tu pas quelque chose de plus évolué pour communiquer avec ton capteur ?
La librairie suivante a fait ses preuves:
https://github.com/jrowberg/i2cdevlib/tree/master/Arduino/MPU6050

rjnc38

La question :
Y a-t-il une fonction qui dit « Wire.requestFrom » que si elle dépasse par exemple 1ms, elle s'arrête et redonne la main au reste du programme (un time out). Si je perds l'info une fois toutes les 30 ou 40 secondes, je dois pouvoir largement survivre sachant que j'ai un « timer » (scheduler) dans mon programme qui me rafraichit l'info toutes les 100 ms.
il y a une librairie wire avec timeout ex , je ne l'ai pas encore essayé car certaine autres librairies font appel a wire

f-leb

Bonjour,

Un souci matériel peut-être : câbles trop longs, résistances de tirage mal choisies, alimentation...

Une autre façon peut-être, qui récupère les octets un par un dans un buffer tant qu'il y a des octets qui arrivent :

Code: [Select]
Wire.requestFrom(address_GYRO_DF, 14);
while (Wire.available()) {
buffer[cnt] = Wire.read();
cnt++;
}

if (cnt >= 14) { // si les 14 octets sont reçus, reçu 5/5
// on traite les données dans le buffer
}

Vincent_TH

Bonjour,
Merci  les 3 pour vos réponses

Après des tests des 3 solutions, pas de miracle… mais je pense que ça vient de plus loin…

@f-leb :
Le « while » est après la fonction qui plante -> Wire.requestFrom(address_GYRO_DF, 14);
Du coup, ça continue à planter comme avant. Mais je garde l'idée pour d'autres cas.  :)

@rjnc38
J'ai essayé » la lib WSWire. Elle a était faite exactement pour ça. Les gars avaient aussi des plantages du bus i2c sur leurs ballons atmosphérique. Mais la lib ne semble pas être 100% compatible avec les instructions de Wire de base. Et n'étant pas un grand expert du codage… j'ai pas réussi à la faire fonctionner correctement après plusieurs heures… je dois faire une connerie que je vois pas !

@3sigma
La lib de Jrowberg marche bien. J'ai testé avec un gyro actif et l'autre inactif, et c'est OK plus de plantage. Par contre, je ne vous ai pas dit que je me sers de ce bus i2c aussi pour communiquer avec 2 autres cartes Arduino (en mode slave). Je remonte des infos à ma mega qui en sorte un superviseur du robot. Les autre cartes contrôle des sous parties comme la traction. Et dès que je remets ces cartes « slave » sur le réseau i2c, même le wire de Jrowberg plante et la mega superviseur pédale dans la semoule... l'interupt n'y fait rien…

Du coup, je crois que c'est mes fonctions « slave » des 2 autres arduino qui interfèrent avec la mega superviseur.

Code: [Select]

#include <Wire.h>

#define ADDRESS_TRAC_Slave 20
#define ADDRESS_MSL_Master 21
#define PAYLOAD_SIZE 5
byte nodePayload[PAYLOAD_SIZE];

void setup()
  {
  Wire.begin(ADDRESS_TRAC_Slave);
  Wire.onRequest(requestEvent);
  }
 
void loop()
  {
  nodePayload[0] = 25;
  nodePayload[1] = 26;
  nodePayload[2] = 27;
  nodePayload[3] = 28;
  nodePayload[4] = 29;
  }

void requestEvent()
  {
  Wire.write(nodePayload,PAYLOAD_SIZE); 
  }


Pourtant dans le cas du test, j'ai uploadé dans la mega superviseur que le code exemple de Jrowberg, donc qui n'appel jamais les autres cartes arduino…

Question : cela vous semble possible que je plante à cause de ça ?

Question sup : après réflexion (mais je ne sais pas si j'ai bien fait), j'ai mis 2 résistances de pull-up de 4.7 ohm sur du +5V sur mes lignes SCL et SDA. Mes Gyro marchent bien en +5 V. Je n'ai pas désactivé les pull-up de la mega sur les ports 20 et 21. -> un avis

Merci par avance pour vos conseils et éclairages avertis
Vincent



3Sigma

Dans ce genre de cas de figure, il faut tester chaque chose séparément:
  • chaque slave seul (avec le master bien sûr)
  • chaque MPU6050 seule
  • etc...


Puis ensuite ajouter les éléments les uns après les autres. Et avec un peu de chance, tu trouveras celui qui plante ou bien l'interaction qui fait planter.
C'est fastidieux mais je ne connais pas de meilleure méthode.

Go Up