IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Arduino Discussion :

i2c (Uno & ATTiny85) : Quelle approche/protocole adopter ?


Sujet :

Arduino

  1. #1
    Membre émérite Avatar de SofEvans
    Homme Profil pro
    Développeur C
    Inscrit en
    Mars 2009
    Messages
    1 082
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France

    Informations professionnelles :
    Activité : Développeur C

    Informations forums :
    Inscription : Mars 2009
    Messages : 1 082
    Par défaut i2c (Uno & ATTiny85) : Quelle approche/protocole adopter ?
    Bonjour,

    Désolé si mon titre est un peu vague, je ne sais pas trop comment exprimer mon problème.

    En gros, j'essaye de faire communiquer un Arduino Uno avec un ATTiny85 via i2c.
    L'Uno est maître, le tiny est esclave.

    Sur le tiny, j'utilise TinyWire comme bibliothèque.
    Sur l'Uno, celle de base.

    Ma problématique est la suivante : jusqu'à quel point je dois envisager les échecs de communication entre les deux µC ?

    J'ai déjà fait des programmes qui communiquent en TCP via socket pour échanger des trames de longueur arbitraire (entre autre des logs), et pour distinguer quand je devais m'arrêter de lire, nous avions convenue d'un protocole maison.
    Grossièrement, les 4 premiers octet servaient à transmettre la longueur du message.

    J'ai voulu appliquer le même principe, mais il semblerait que ce ne soit pas possible entièrement.

    Quand mon Uno communique avec mon tiny et qu'il attends une réponse de 3 byte, j'utilise "Wire.requestFrom(I2C_ADRESS_SLAVE, 3)".
    Le hic, c'est que si je fais un delay dans mon tiny ou si je n'envoie absolument rien sur l'i2c, la fonction me renvoie quand même 3, la fonction "Wire.available()" aussi (comme si j'avais bien écrit ces 3 bytes alors que pas du tout) et les 3 "Wire.read()" me renvoie 255.

    Comment suis-je supposer aborder cette problématique ?

    Dois-t-on partir du principe que tant que la connexion i2c est active, alors il n'y a aucune erreur possible ?
    Que si je suis supposé avoir X bytes à lire, alors ils sont forcement là ?


    Si mon code peut aider à voir si j'ai fait une erreur, le voici :

    Fichier commun "Configuration.h"
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     
    #define I2C_ADRESS_SLAVE  0x00 /* Adresse i2c pour la communication avec l'esclave */
     
    /* "header" est une variable de type "Byte" : c'est la première valeur reçu lors d'une transmission i2c 
     *  Les 3 octets de poids faible sont le nombre de byte à lire après le header 
     *  Les 5 octets suivant (de poids fort) sont l'ordre */
     
    #define I2C_CREATE_HEADER(nb_bytes_to_send, order) ((nb_bytes_to_send & 7) + (order & 31) * 8)
     
    #define I2C_HEADER_GET_NB_BYTES_TO_READ(header) (header & 7)
    #define I2C_HEADER_GET_ORDER(header) (header / 8)
     
     
    #define I2C_ORDER_ASK_NUMBER    0 // On demande le nombre  
                                      // 0 bytes transmit après
    #define I2C_ORDER_GIVE_NUMBER   1 // On transmet le nombre
                                      // 2 bytes transmis après
    Fichier du Uno (maitre) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    #include "Configuration.h"
     
    #include "Wire.h"
     
    bool button1State; // Etat du bouton
     
    void setup()
    {
    	// Initialisation i2c
    	Wire.begin(); // On rejoint i2c en tant que maitre
     
    	// Bouton
    	pinMode(A1, INPUT_PULLUP);
     
    	// DEBUG
    	Serial.begin(9600); //Initialisation de la communication avec la console
    	button1State = HIGH; // Boutton non pressé
    }
     
    void loop()
    {
    	int etat = digitalRead(A1);
     
    	if (etat != button1State) {
    		// Je passe de HIGH (buton non appuyé) à LOW (bouton appuyé)
    		if (etat == LOW) {
    			// Transmission a l'esclave
    			Wire.beginTransmission(I2C_ADRESS_SLAVE);
    			Wire.write(I2C_CREATE_HEADER(I2C_ORDER_ASK_NUMBER, 0));
    			Wire.endTransmission(); 
     
    			// Ici, on devrait normalement attendre  500ms avant d'avoir la vrai réponse (voir code du tiny)
    			Serial.print("Wire.requestFrom = ");
    			Serial.print(Wire.requestFrom(I2C_ADRESS_SLAVE, 3));
    			Serial.print("\n");
     
    			Serial.print("Wire.available   = ");
    			Serial.print(Wire.available());
    			Serial.print("\n");
     
    			Serial.print("1 Wire.read()    = ");
    			Serial.print(Wire.read());
    			Serial.print("\n");
     
    			Serial.print("2 Wire.read()    = ");
    			Serial.print(Wire.read());
    			Serial.print("\n");
     
    			Serial.print("3 Wire.read()    = ");
    			Serial.print(Wire.read());
    			Serial.print("\n");
    		}
    		button1State = etat;
    	}
    }

    Fichier du tiny (esclave) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    #include "Configuration.h"
    #include "TinyWireS.h"
     
    /*   ATTiny85 (Clock 20MHz)
     
                 +-\/-+
        A0 PB5  1|    |8  Vcc
        A3 PB3  2|    |7  PB2 D0 A1
        A2 PB4  3|    |6  PB1 D1
           GND  4|    |5  PB0 D2
                 +----+        
    */
     
    #define __AVR_ATtiny85__
     
     // On déclare l'adresse de la LED de debug sur la pin 2
    #define LED1_PIN PB3
     
    // Simple compteur de test ...
    byte count; 
     
    void setup()
    {
    	// On rejoint le bus i2c en tant qu'esclave
    	TinyWireS.begin(I2C_ADRESS_SLAVE); 
     
    	// A but de test
    	pinMode(LED1_PIN, OUTPUT);
    	count = 0;
    }
     
    void loop()
    {
    	// Si on recoit quelque chose sur le bus I2C
    	if (TinyWireS.available()) {
    		byte header;        // Premier bit : il s'agit du header (composé du nombre de byte à lire (3 bit) + du numero d'ordre (5 bit) 
    		byte order;         // Numero d'ordre : il s'agit d'une action que le µC dois faire
    		byte nbByteToRead;  
     
    		// On recupere le header
    		header = TinyWireS.read();
    		// On recupere l'ordre et le nombre de byte à lire
    		nbByteToRead = I2C_HEADER_GET_NB_BYTES_TO_READ(header);
    		order = I2C_HEADER_GET_ORDER(header);
     
    		// On execute l'ordre envoyé 
    		switch (order) {
    			case I2C_ORDER_ASK_NUMBER:
    				i2cDiscardRead(nbByteToRead, 100); // nbByteToRead devrait être égale à 0
     
    				delay(500); // Simulation d'un probleme de transmission : "normalement", le maitre i2c devrait "voir" que je n'ai pas encore fais les TinyWireS.write 
     
    				TinyWireS.write(I2C_CREATE_HEADER(2, I2C_ORDER_GIVE_NUMBER));
    				TinyWireS.write(0);
    				TinyWireS.write(count);
     
    				// Limitation de count dans l'intervalle [0:100]
    				++count; 
    				if (100 < count) {
    				count = 0;
    				}
    				break;
     
    			default:
    				// Ordre non pris en charge : On consumme tout
    				i2cDiscardRead(nbByteToRead, 100);
    				break;
    		}
    	}
    }
     
     
    #define DELAY_BETWEEN_READ_TENTATIVE 5 /* en milliseconde */
     
    /* +------------------------------------------------------------------------------------+ */
    /* | Tente de lire `nbReadToDiscard` byte pendant `timeout` millisecondes               | */
    /* +------------------------------------------------------------------------------------+ */
    /* | [IN]     Timeout : nombre de milliseconde avant d'être considéré en timeout        | */
    /* | [RETURN] true si nbReadToDiscard lu avant timeout, false sinon                     | */
    /* +------------------------------------------------------------------------------------+ */
     
    bool i2cDiscardRead(int nbReadToDiscard, int timeout)
    {
      for (int i = 0; i < nbReadToDiscard; ++i) {
        if (!i2cRead(NULL, timeout)) {
          Blink(LED1_PIN, 1000); // DEBUG (en cas d'erreur, led allumé pendant 1s)
          return (false);
        }
      }
      Blink(LED1_PIN, 20); // DEBUG
      return (true);
    }
     
     
    /* +------------------------------------------------------------------------------------+ */
    /* | Tente de lire un byte pendant `timeout` millisecondes                              | */
    /* +------------------------------------------------------------------------------------+ */
    /* | [OUT]    result : byte lu                                                          | */
    /* | [IN]     Timeout : nombre de milliseconde avant d'être considéré en timeout        | */
    /* | [RETURN] true si byte lu avant timeout, false sinon                                | */
    /* +------------------------------------------------------------------------------------+ */
     
    bool i2cRead(byte *result, int timeout)
    {
      /* Attente de timeout milliseconde max */
      while (!TinyWireS.available()) {
        delay(DELAY_BETWEEN_READ_TENTATIVE);
        timeout -= DELAY_BETWEEN_READ_TENTATIVE;
        if (timeout <= 0) {
          return (false);
        }
      }
      /* Lecture */
      if (result) {
        result = TinyWireS.read();
      } else {
        TinyWireS.read();
      }
     
      return (true);
    }
     
     
    /* +------------------------------------------------------------------------------------+ */
    /* | Blink une led via delay : pour but de test uniquement                              | */
    /* +------------------------------------------------------------------------------------+ */
    /* | [IN]     led : port de la led                                                      | */
    /* | [IN]     duration : temps d'eclairage en millisecondes                             | */
    /* +------------------------------------------------------------------------------------+ */
     
    void Blink(byte led, int duration)
    {
      digitalWrite(led, HIGH);
      delay(duration);
      digitalWrite(led, LOW);
      delay(duration);
    }

  2. #2
    Responsable Arduino et Systèmes Embarqués


    Avatar de f-leb
    Homme Profil pro
    Enseignant
    Inscrit en
    Janvier 2009
    Messages
    13 120
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Sarthe (Pays de la Loire)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Janvier 2009
    Messages : 13 120
    Billets dans le blog
    47
    Par défaut
    Bonjour,

    Ce n'est pas facile à diagnostiquer a priori... Si tu avais un oscillo ou un analyseur logique, ce serait sans doute réglé en mois de deux

    Du côté de l'esclave, le jeu d'instructions i2c est encore plus limité, on ne sait pas trop ce qui se passe avec le protocole i2c.

    Du point de vue général, tu devrais peut-être regarder davantage les valeurs de retour. endTransmission() renvoie une valeur qui permet de voir le statut de l'envoi (avec succès ou bien pb d'acquittement ou de timeout...).

    Tu as aussi beaucoup de delay() côté esclave qui semblent suspects. Ces pauses sont bloquantes dans le programme, et on sent les problèmes en envoi ou réception pendant que le programme est bloqué dans un delay().

    S'il y a des traitements qui prennent du temps côté esclave, il peut être bien de préparer un octet de données dans le protocole qui renseigne sur le statut du traitement. Par exemple avec un bit DONE initialement à 0 et qui passe à 1 quand le traitement est terminé.
    Le maître envoie la commande de statut et requiert cet octet de statut à intervalle régulier. Et quand le bit DONE est passé à 1, c'est que les données sont prêtes et le maître peut les réclamer sans délai.

    C'est tout ce que je vois pour l'instant

  3. #3
    Membre Expert

    Homme Profil pro
    Directeur de projet
    Inscrit en
    Mai 2013
    Messages
    1 581
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Service public

    Informations forums :
    Inscription : Mai 2013
    Messages : 1 581
    Par défaut Le chef c'est le chef, les autres obéissent
    Bonjour,

    En fait, malgré la déclaration du mode esclave pour le Tiny, il travaille ou essaie de travailler en mode maître.

    Il suffit de regarder l'exemple fourni avec TinyWireS pour voir dans le setup les deux déclaration des fonctions appelées par le maître selon qu'il envoie des données ou qu'il en demande :
    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    TinyWireS.onReceive(receiveEvent);
    TinyWireS.onRequest(requestEvent);
    Ce sont ces deux fonctions et non le loop() du Tiny qui géreront les réceptions et les demandes de données. Comme elles n'existent pas, il ne se passe rien (en informatique : n'importe quoi).

    Je propose de s'inspirer de l'exemple fourni avec TinyWireS.

    Salutations

Discussions similaires

  1. Gestion des vérrous : quelle approche adoptée ?
    Par hisoft dans le forum Débuter
    Réponses: 3
    Dernier message: 14/09/2012, 18h10
  2. JAAS + recaptcha : quelle approche ?
    Par NaBuCO dans le forum Développement Web en Java
    Réponses: 0
    Dernier message: 18/04/2011, 11h53
  3. [Interface Données par le Web] Quelles approches ?
    Par FwRamaro dans le forum Général Conception Web
    Réponses: 0
    Dernier message: 22/03/2008, 16h49
  4. Réponses: 17
    Dernier message: 25/06/2007, 17h56
  5. Quelle approche pour ce problème de conception bien spécifique ?
    Par wokmichel dans le forum XML/XSL et SOAP
    Réponses: 5
    Dernier message: 23/10/2006, 08h50

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo