Bonjour
Décidément, le site locoduino est vraiment très vivant, avec des articles publiés ou mis à jour très régulièrement.
J'y jette un oeil avec plaisir presque tous les jours.
Bravo et merci
Aujourd'hui j'ai lu l'article sur la bibliothèque Wire.
Cela m'a fait penser que j'ai du dépiauter cette lib récemment (pour mon module contrôleur d'aiguilles) et que j'ai vu quelques trucs complémentaires qui pourraient être utiles à partager. Alors les voici tels que je les ai compris
Tout d'abord, que l'on soit maître ou esclave, émetteur ou récepteur, cette lib s'appuie sur des buffer alloués de manière statique en RAM : un pour l'émission, un pour la réception. Ceux-ci ont une taille de 32 octets. Cette limite n'est pas imposée par le protocole I2C, mais à moins de modifier la bibliothèque, c'est une limite de taille de message à prendre en compte.
Côté maître émetteur :
- beginTransmission() ne fait rien d'autre qu'enregistrer en RAM l'adresse du destinataire
- write() ne fait rien d'autre qu'alimenter en RAM le buffer d'émission
- tout se passe lors du endTransmission() : c'est uniquement lors de l'exécution de cette fonction qu'a lieu la communication physique sur le bus
Côté maître récepteur :
- tout se passe lors du requestFrom(), qui retourne le nombre d'octets effectivement chargés dans le buffer de réception, une fois la communication physique terminée.
- available(), read() ou peek() ne font que piocher dans le buffer de RAM
Du coup, personnellement je trouve la fonction available() assez inutile, et préfère coder des trucs du style
if (requestFrom(esclave, 5) == 5)
{
for (uint8_t i=0; i<5; i++) reponse[i] = Wire.read();
...traiter la réponse
}
else
{
... traiter l'erreur
}
Côté esclave : même gestion via deux buffers en RAM
Le handler onReceive() est appelé une fois la communication physique terminée, pour exploiter les octets du buffer. Là encore, l'usage de available() me semble superflu car le nombre d'octets reçus est passé directement en paramètre d'appel du handler.
(bigre au passage, je réalise que ce terme handler fait bizarre, au sein d'articles aussi attachés au respect de la langue française
)
Le handler onRequest() est plus subtil, car c'est le seul qui intervient entre deux étapes de la communication physique.
Le but est alimenter le buffer d'émission via la fonction write().
Côté esclave, je me suis pris la tête sur deux points :
1) d'abord ces handler sont déclenchés par des interruptions, c'est-à-dire au beau milieu de n'importe quel endroit du programme.
Et dès lors que l'information échangée fait plus d'un octet, il y a quelques précautions à prendre.
Par exemple en réception, il ne faudrait pas aller mettre à jour des variables globales pile au moment où une partie du programme était en train de les utiliser. Surtout si ces variables ont des liens de corrélation entre elles. Cela peut générer un comportement inattendu du programme.
Pareil en émission : par exemple, si le message émis contient les valeurs de 2 variables, et que l'interruption se déclenche au moment où le programme vient de mettre à jour la première variable mais pas encore la seconde, cela peut entraîner l'émission d'un message incohérent.
Du coup, je préfère passer systématiquement par des buffers de cloisonnement, entre les routines liées aux interruptions et le programme principal.
2) l'impossibilité par l'esclave de connaître le nombre d'octets attendus par le maître
Là pour le coup, la bibliothèque Wire n'y est pour rien. C'est le protocole I2C qui veut ça.
Le paramètre de quantité passé à requestFrom() reste au niveau du maître, qui interrompt la communication en provenance de l'esclave lorsqu'il a reçu la quantité d'octets attendue.
C'est bien dommage, car lorsqu'on souhaite que l'esclave puisse retourner plusieurs types de message différents, on ne peut pas utiliser le nombre d'octets attendus comme élément discriminant.
Il n'y a que deux options :
a) mettre en place un protocole logiciel. Par exemple le maître commence par envoyer une information discriminante, puis lance un requestFrom(). Les périphériques I2C industriels marchent d'ailleurs souvent comme ça.
b) partir du principe que l'esclave renvoie toutes les informations possibles à chaque sollicitation, en mettant les plus importantes en premier, charge au maître de couper quand bon lui semble.
Pour mon contrôleur d'aiguilles, je suis parti sur cette dernière option, la plus simple à première vue, mais je le regrette.
Cela oblige à faire tous les write dans le handler onRequest (avec la limite de 32 octets), car à ce moment là on ne sait pas encore combien d'octets le maître va réellement lire.
Mais au final, je me suis retrouvé dans l'embarras quand j'ai ajouté un dernier octet de CRC antiparasite.
Parce que là, même si le maître ne s'intéresse qu'aux premiers octets du message, il doit quand même tout lire afin de vérifier le CRC.
Pour bien faire, il faudrait soit segmenter le message avec un CRC à la fin de chaque segment, afin que le maître puisse interrompre la communication par endroits. Soit repartir sur l'option a) avec une petite couche de protocole logiciel.