Club robotique de Sophia-Antipolis

Accueil > POBOTpedia > Communications > Communications filaires > Echanges maître/esclaves en I2C

Echanges maître/esclaves en I2C

jeudi 17 mars 2011, par Julien H.

Voici un article qui décrit dans plusieurs langages les échanges qu’il faut mettre en œuvre entre deux dispositifs équivalents (deux micro-contrôleurs par exemple) pour une liaison I2C.

Il ne s’agit pas de piloter un périphérique I2C mais de programmer à la fois le maitre et l’esclave, afin d’expliquer les différentes étapes. Pour compléter cet exercice, on utilisera plusieurs langages et si possible plusieurs cibles.



Les principes

Pour établir une communication sur un bus I2C, il faut choisir la topologie du réseau : quel système est maître, quel système est esclave. On choisit la plupart du temps un seul maître, et tant que possible le dispositif qui centralise l’information, car c’est le maître qui envoie et reçoit des informations.

Il faut ensuite déterminer les adresses : pour identifier chacun des nœuds du bus, il y a 7 bits que l’on complète par un 1 ou 0 final selon que le maître veuille lire ou écrire sur cet esclave. On obtient ainsi le premier octet d’une trame I2C.

Lorsqu’on utilise des composants I2C "du commerce", il faut veiller sur les dispositifs programmables à utiliser une adresse qui n’est pas déjà utilisée. Certains fabricants (Philips en tête) ont en effet obtenu une adresse universelle pour chaque composant (en fait, une plage d’adresses afin de permettre plusieurs exemplaires du même composant d’exister sur le même bus I2C). Mais il est tout à fait possible de réutiliser l’adresse d’un composant enregistré, s’il ne se trouve pas sur votre bus.

Avec ce 1 ou 0 final, vous constatez qu’il y a deux modes d’échange entre le maitre et l’esclave. Il faut conserver cette logique, même si votre objectif n’est pas forcément orienté vers une lecture/écriture. Il peut s’agir de faire bouger un robot (ce sera une écriture, le maitre n’attend rien en retour) ou de lire la valeur de capteurs (ce sera une lecture, il faut que vous receviez des valeurs). Si vous avez envisagé un dialogue entre vos deux dispositifs, il faut le découper en autant de lectures et écritures successives.

Premier cas : très simple

Pour ce premier exemple, on utilisera seulement deux nœuds sur le bus, un maitre et un esclave, les deux étant des cartes Arduino (donc un micro-contrôleur AVR) avec une librairie (Wire) qui masque tout le fonctionnement I2C.

Les fonctions à utiliser pour le maitre sont :

 begin
 beginTransmission
 endTransmission
 send
 requestFrom
 available
 receive

Les fonctions à utiliser pour l’esclave sont :

 begin
 onReceive
 onRequest
 send
 receive

Exemple pour le maitre, un code qu’on peut écrire où on veut :

Wire.begin(); // on se connecte au bus, comme maitre car pas de paramètre
Wire.beginTransmission (0x52); // on souhaite écrire à l'esclave 0x52
Wire.send (0x40);		// on lui envoie un octet
Wire.send (0x00);		// encore un octet
Wire.endTransmission ();	 // et on déclare avoir terminé

Exemple pour l’esclave, c’est plus complexe car il faut initialiser les fonctions qui vont recevoir les ordres du maitre :

int premier;
int second;
void init() {
   Wire.begin(0x52); // on se connecte au bus en se déclarant l'esclave 0x52
   Wire.onReceive(maFonctionDeLecture); // on déclare une fonction à coder ci-dessous pour la réception
}

void maFonctionDeLecture(int combien) // le paramètre est obligatoire, c'est la "signature" attendue par Arduino
{
   if (combien == 2) {
      premier = Wire.receive(); // octet par octet
      second = Wire.receive(); //
  }
}

Second cas : aussi simple

On continue avec les micro-contrôleurs AVR, mais cette fois sans la couche additionnelle des bibliothèques Arduino. On reste quand même avec une bibliothèque qui facilite l’accès aux registres bas-niveau, avec AVRLIB.

Extraits du code pour le maitre :

#define I2C_SEND_DATA_BUFFER_SIZE		0x20
#define I2C_RECEIVE_DATA_BUFFER_SIZE	0x20

#define SLAVE_CODEUR_ADDR	0xA1

#include "i2c.h"

// local I2C data buffer
unsigned char localI2CBuffer[] = "12345678901234567890";
unsigned char localI2CBufferLength = 0x20;

/**
 * Programme principal du robot
 */
int main(void)
{	
	i2cInit();	
        // un premier test en envoyant A à l'esclave "codeur"
	localI2CBufferLength = 0; // on part de 0
	localI2CBuffer[localI2CBufferLength++] = 'A';		// on place un octet et on augmente la longueur de 1
	i2cMasterSend(SLAVE_CODEUR_ADDR, localI2CBufferLength, localI2CBuffer);
        // puis on reçoit 4 octets
        i2cMasterReceive(SLAVE_CODEUR_ADDR, 4, localI2CBuffer);

}

Extraits du code pour l’esclave :

#define I2C_SEND_DATA_BUFFER_SIZE		0x20
#define I2C_RECEIVE_DATA_BUFFER_SIZE	0x20

#define SLAVE_CODEUR_ADDR	0xA1

#include "i2c.h"

// comme pour Arduino (onRequest et onReceive), il faut déclarer les fonctions de traitement I2C
void i2cSlaveReceiveService(u08 receiveDataLength, u08* receiveData);
u08 i2cSlaveTransmitService(u08 transmitDataLengthMax, u08* transmitData);

int main(void)
{	
	i2cInit();	
        // contrairement au maitre, l'esclave doit donner son adresse (comme sur Arduino avec begin)
	i2cSetLocalDeviceAddr(SLAVE_CODEUR_ADDR, TRUE);
        // et bien sûr enregistrer les deux fonctions (dont le nom est modifiable)
	i2cSetSlaveReceiveHandler( i2cSlaveReceiveService );
	i2cSetSlaveTransmitHandler( i2cSlaveTransmitService );

        while (1) {
              // faire quelque chose pour occuper l'esclave même si le traitement I2C est fait en-dehors
        }
}


/**
 *
 */
void i2cSlaveReceiveService(u08 receiveDataLength, u08* receiveData)
{
	// rien, pas de gestion de la réception du maitre pour l'instant
}

u08 tableau[4] = {0,1,2,3};

/**
 *
 */
u08 i2cSlaveTransmitService(u08 transmitDataLengthMax, u08* transmitData)
{			
	// copie des données dans le buffer d'envoi I2C
	memcpy(transmitData, (u08*) &tableau, 4);
		
	// retour du nombre de caractères à transmettre
	return 4;
}

Un message, un commentaire ?

modération a priori

Attention, votre message n’apparaîtra qu’après avoir été relu et approuvé.

Qui êtes-vous ?

Pour afficher votre trombine avec votre message, enregistrez-la d’abord sur gravatar.com (gratuit et indolore) et n’oubliez pas d’indiquer votre adresse e-mail ici.

Ajoutez votre commentaire ici

Ce champ accepte les raccourcis SPIP {{gras}} {italique} -*liste [texte->url] <quote> <code> et le code HTML <q> <del> <ins>. Pour créer des paragraphes, laissez simplement des lignes vides.