LOCODUINO

Forum de discussion
Dépôt GIT Locoduino
Flux RSS

mardi 7 juillet 2020

48 visiteurs en ce moment

La bibliothèque ACAN (2)

Identifiants et filtres

. Par : Jean-Luc

Dans l’article précédent, « La bibliothèque ACAN (1) », nous avons examiné la communication en loopback de la manière la plus simple possible en employant les méthodes tryToSend et receive. Puis nous avons construit une application avec deux Arduino et deux modules CAN, le premier Arduino gérant 2 boutons poussoir, le second 2 LED, la pression d’un bouton changeant l’état de la LED correspondante. Mais nous ne nous somme pas préoccupés des identifiants de message et du filtrage, deux caractéristiques pourtant essentielles et que nous allons examiner aujourd’hui.

Mais avant d’entrer dans le vif du sujet, un petit rappel et quelques précisions. Jusqu’à maintenant nous avons vu la classe ACANSettings qui permet de configurer un objet ACAN2515 en spécifiant dans son constructeur la fréquence du quartz et la fréquence du bus désirée. ACAN2515 dont une instance permet de gérer un MCP2515 et qui fournit les méthodes begin pour démarrer le bus, tryToSend pour (tenter d’) envoyer un message et receive pour recevoir un message. Il existe également la méthode available qui renvoie un booléen, true si au moins un message en réception est disponible, false si aucun message n’est disponible.

La place mémoire

Le MCP2515 possède 2 tampons pour la réception des messages, RXB0 et RXB1. Ces tampons peuvent contenir chacun exactement un message. Ils peuvent être configurés de telle sorte qu’ils soient indépendant ou bien de telle sorte que si le tampon RXB0 est plein, le message est déposé dans RXB1. Ce mode est appelé rollover. Par défaut ACANSettings est en rollover. La bibliothèque ACAN gère, dans la mémoire de l’Arduino, une file d’attente des messages en réception qui est dimensionnée à 32. À chaque nouveau message reçu, la bibliothèque se charge de le récupérer du MCP2515 pour le déposer dans cette file d’attente. Pour l’émission, le MCP2515 possède 3 tampons indépendants, TXB0, TXB1 et TXB2, pouvant également contenir chacun exactement un message. La bibliothèque ACAN, dans la mémoire de l’Arduino, associe à chacun de ces tampons une file d’attente. Elles sont dimensionnée à 16 pour TXB0 et à 0 pour les deux autres. Dès q’un des tampons est disponible, la bibliothèque se charge de déplacer le prochain message de la file d’attente correspondante vers le tampon. La figure 1 donne le cheminement des messages entre le MCP2515 et le sketch via la bibliothèque ACAN.

Figure 1 : Gestion des files d'attente
Figure 1 : Gestion des files d’attente
Un tryToSend ⓐ envoie le message dans la file d’attente correspondant au champ idx du message. Si la file d’attente est de taille nulle ou vide, le message est directement écrit dans le tampon correspondant du 2515 si il est vide. Quand le message est expédié sur le bus CAN, une interruption survient et ACAN remplit de nouveau le tampon avec un message venant de la file d’attente correspondante ⓑ. Lorsqu’un message arrive, ACAN le déplace du tampon de réception vers la file d’attente de réception ⓒ. Enfin un receive déplace un message de la file d’attente de réception vers votre message.

Pour déterminer la file d’attente en émission et le tampon correspondant où le message est déposé, tryToSend utilise un champ de CANMessage dont nous n’avons pas encore parlé, le champ idx. Par défaut ce champ est à 0 dans un objet CANMessage et pour utiliser la file d’attente de TXB1 ou TXB2, il faut mettre respectivement 1 ou 2 dans idx [1].

Il convient d’adapter la taille de ces files d’attente à vos besoins. Plus les files d’attente en émission sont grandes et moins il y a de risque d’échouer lors d’un tryToSend. Plus la file d’attente en réception est grande et moins il y a de risque de perdre un message. Un emplacement de la file d’attente nécessitant 16 octets, il est à contrario nécessaire de faire attention à la mémoire consommée. On voit que par défaut la taille totale des files d’attente est de (32 + 16) × 16 = 768 octets, ce qui est gros pour un malheureux Uno puisque plus d’⅓ de la mémoire RAM est occupée par les files d’attente.

Changer la taille des files d’attente

Bien évidemment, il est possible de configurer les tailles des files d’attente. Une fois que votre objet de type ACANSettings est créé, il suffit de changer les valeurs des membres mReceiveBufferSize pour fixer la taille de la file d’attente en réception, mTransmitBuffer0Size pour fixer la taille de la file d’attente du buffer d’émission 0 ou encore mTransmitBuffer1Size et mTransmitBuffer2Size pour fixer les tailles des files d’attente du buffer d’émission 1 et 2. En reprenant la dernière application de « La bibliothèque ACAN (1) », le programme émetteur ne recevant aucun message, il est légitime de fixer une taille nulle pour la file d’attente en réception. On peut également fixer la taille de la file d’attente d’émission à 4 messages par exemple.

  1. ...
  2. ACAN2515Settings reglages(FREQUENCE_DU_QUARTZ, FREQUENCE_DU_BUS_CAN);
  3. reglages. mReceiveBufferSize = 0;
  4. reglages. mTransmitBuffer0Size = 4;
  5. const uint16_t codeErreur = controleurCAN.begin(reglages, [] { controleurCAN.isr(); } );
  6. ...

De même, le programme récepteur n’émettant rien, la taille de sa file d’attente de TXB0 peut être nulle également. On peut également fixer la taille de la file d’attente de réception à 4 messages.

  1. ...
  2. ACAN2515Settings reglages(FREQUENCE_DU_QUARTZ, FREQUENCE_DU_BUS_CAN);
  3. reglages. mReceiveBufferSize = 4;
  4. reglages. mTransmitBuffer0Size = 0;
  5. const uint16_t codeErreur = controleurCAN.begin(reglages, [] { controleurCAN.isr(); } );
  6. ...

Déterminer les tailles

Déterminer à priori la taille d’une file d’attente dans un système de communication est une tâche difficile car elle dépend de beaucoup de facteurs. Il est possible toutefois de l’ajuster en mesurant la taille nécessaire. La bibliothèque ACAN2515 permet de connaître de combien les files d’attente sont remplies au maximum pendant l’exécution. Ces informations sont retournées par les méthodes receiveBufferPeakCount() [2] pour la file d’attente en réception, et transmitBufferPeakCount(x) pour les files d’attente en émission, x est ici le numéro de la file d’attente, de 0 à 2. Elles sont très utile pour diminuer la taille de files d’attente à ce qui est réellement nécessaire pour votre application.

Si le nombre retourné par ces fonctions a pour valeur la taille de la file d’attente correspondante + 1, cela signifie qu’il y a eu au moins une tentative pour y écrire alors que la file d’attente était pleine.

Par exemple, si la file d’attente en réception est de taille 16 et que receiveBufferPeakCount() retourne 17 alors au moins un message a été perdu.

Identifier et filtrer

Dans les applications vues dans « La bibliothèque ACAN (1) », l’identifiant des messages était implicitement à 0 car c’est la valeur d’initialisation de id dans les objets de type CANMessage. L’identifiant permet d’étiqueter un message et par convention de qualifier ce qu’il contient. Il permet également de définir une priorité entre les messages. Cette priorité sert lorsque plusieurs microcontôleurs, ou nœuds émettent chacun un message au même moment. Le message dont l’identifiant est le plus petit passera en premier. Quand on conçoit une messagerie CAN, c’est à dire l’ensemble des messages qui vont être véhiculés sur le réseau, l’identifiant doit être unique [3], c’est à dire que deux messages émis par des nœuds différents ne peuvent pas avoir le même identifiant.

Les identifiants des messages CAN ne sont pas les adresses des nœuds destination même si il est possible de concevoir une messagerie qui fonctionne de cette façon, ce sont des étiquettes qui indiquent la nature du message. Le filtrage consiste à mettre en place des règles qui sélectionnent les messages pertinents pour un nœud.

Il existe deux tailles d’identifiant : 11 bits (2048 valeurs possibles) pour les messages dits « standard » et 29 bits (plus de 536 millions de valeurs possibles) pour les messages dits « étendus », il s’agit de la seule différence entre ces deux types de message.

Le filtrage

Le réseau CAN étant partagé entre plusieurs nœuds, il va de toute évidence transporter des messages qui n’intéressent qu’un sous-ensemble des nœuds. C’est pourquoi les contrôleurs CAN proposent une fonction de filtrage qui, en se basant sur l’identifiant, va déterminer si un message est destiné au nœud et, dans le contraire, l’ignorer. Cette fonction de filtrage fait entrer en jeu deux entités, le masque et le filtre en lui même. Le masque sert à isoler la partie de l’identifiant qui est intéressante pour discriminer les messages. Cette opération consiste à faire un ET bit à bit entre l’identifiant et le masque.

Un exemple, de masquage est donné dans la table ci-dessous.

Identifiant 10011101100
Masque 11111100011
Identifiant masqué 10011100000

Ensuite le filtre est comparé avec l’identifiant masqué. En cas d’égalité le message est destiné au nœud.

Prenons un cas simple avec 3 nœuds. Le premier ne reçoit que les messages standards dont l’identifiant est 1, le second, ceux dont l’identifiant est 2 et le troisième ceux dont l’identifiant est 3. Le masque est facile à déterminé : la totalité de l’identifiant doit être comparé, le masque est donc la valeur sur 11 bits où tous les bits sont à 1, soit 11111111111 en binaire et 0x7FF en hexadécimal. Les filtres, eux, sont également simples, il suffit d’y mettre l’identifiant que l’on souhaite.

D’autres exemples de masquage/filtrage sont présentés dans l’article « Mise en oeuvre du Bus CAN entre modules Arduino (1) ».

Dans le cas du MCP2515, il existe 2 masques, un pour le buffer 0 et un pour le buffer 1, et 6 filtres. 2 sont dédiés au buffer 0 et 4 au buffer 1. Dans le cadre de l’usage de la bibliothèque ACAN2515, ces détails peuvent, la plupart du temps, être ignorés. De plus, mais cela semble commun à d’autres contrôleurs CAN, le filtrage dans le cas d’un message standard peut être appliqué aux deux premiers octets de données du message. En effet, comme les registres permettant de stocker dans le MCP2515 les masques et les filtres font 29 bits au total pour les messages étendus, les utiliser pour des messages standards laisse 29 - 11 = 18 bits disponibles. 16 de ces 18 bits sont donc mis à profit pour filtrer les messages standards sur la base des deux premiers octets de données.

Une première application

Dans cette première application, nous n’allons pas utiliser le filtrage pour limiter les messages reçu par un nœud puisqu’il n’y a que deux nœuds, l’émetteur et le récepteur, et que tous les messages émis par le premier sont destinés au second. Nous allons dans un premier temps l’utiliser pour distribuer les messages en fonction de l’identifiant. L’application de départ est la dernière que nous avons vue dans l’article « La bibliothèque ACAN (1) ». Cette application est modifiée de la manière suivante :

  • l’émetteur a désormais 4 boutons poussoir. Deux des boutons servent à commander des LED situées sur le récepteur et les deux autres servent à commander deux servomoteurs également situés sur le récepteur.
  • le récepteur possède 2 LED et 2 servomoteurs.
  • 2 types de messages sont envoyés, chacun avec un identifiant différent. Le premier type est réservé aux LED et l’unique octet de donnée contient le numéro de la LED concernée. Le second type est pour les servos et l’octet de donnée contient le numéro de servo dont la position doit changer.
    Les connexions entre les différents éléments sont présentées à la figure 2.
Figure 2 : Connexions des éléments pour la première application.
Figure 2 : Connexions des éléments pour la première application.

L’émetteur

Les 2 poussoirs de l’application précédente étaient connectés sur les broches 3 et 4, les 2 poussoirs additionnels sont connectés sur les broches 5 et 6. Tout cela est stocké dans un tableau constant pour un accès facile via un numéro de bouton. Nous avons également besoin d’un tableau de 4 éléments pour les objets Bounce qui gèrent les boutons.

  1. const uint8_t brocheBouton[] = { 3, 4, 5, 6 };
  2. Bounce poussoir[4];

Nous avons également besoin de deux messages CAN, un pour les LED et un pour les servos. On pourrait tout à fait n’en utiliser qu’un seul mais il faudrait changer son identifiant en fonction du type de message au moment où il est envoyé.

  1. CANMessage messageCANLEDs;
  2. CANMessage messageCANServos;

Dans setup, nous allons en profiter pour mettre la taille de la file d’attente de réception à 0, puisqu’aucun message n’est reçu, et celle d’émission à 1, ce qui sera suffisant étant donné la lenteur des humains.

  1. reglages.mReceiveBufferSize = 0;
  2. reglages.mTransmitBuffer0Size = 1;

Mais sinon, les initialisations du contrôleur CAN et des boutons, si ce n’est le nombre qui est maintenant de 4 pour ces derniers, ne diffèrent pas de l’application précédente. On va en revanche trouver une initialisation additionnelle pour les messages afin de leur donner leur identifiant. Le message concernant les LED se voit attribué l’identifiant 1 et celui concernant les servos, l’identifiant 2. Dans les deux cas, la taille du message est initialisée une fois pour toute à 1.

  1. messageCANLEDs.id = 1;
  2. messageCANLEDs.len = 1;
  3.  
  4. messageCANServos.id = 2;
  5. messageCANServos.len = 1;

Dans loop, on commence par mettre à jour l’état des 4 boutons.

  1. for (uint8_t bouton = 0; bouton < 4; bouton++) {
  2. poussoir[bouton].update();
  3. }

Puis, ils sont de nouveau parcourus pour détecter un appui. En cas d’appui, si le bouton est le 0 ou le 1, Le message messageCANLEDs est émis avec comme donnée le numéro du bouton. Si le bouton est 2 ou 3, le message messageCANServos est émis avec comme donnée bouton - 2, c’est à dire 0 ou 1.

  1. for (uint8_t bouton = 0; bouton < 4; bouton++) {
  2. if (poussoir[bouton].fell()) {
  3. if (bouton < 2) {
  4. /*
  5.   * Si il s'agit des boutons 0 ou 1, on envoie
  6.   * bouton (ie 0 ou 1) dans le message des LED
  7.   */
  8. messageCANLEDs.data[0] = bouton;
  9. const bool ok = controleurCAN.tryToSend(messageCANLEDs);
  10. if (ok) {
  11. Serial.print("led ");
  12. Serial.print(bouton);
  13. Serial.println(" envoye !");
  14. }
  15. else {
  16. Serial.print("echec de l'envoi de led ");
  17. Serial.println(bouton);
  18. }
  19. }
  20. else {
  21. /*
  22.   * Sinon il s'agit des boutons 2 ou 3,
  23.   * on envoie bouton - 2 (ie 0 ou 1) dans le message
  24.   * des servos.
  25.   */
  26. messageCANServos.data[0] = bouton - 2;
  27. const bool ok = controleurCAN.tryToSend(messageCANServos);
  28. if (ok) {
  29. Serial.print("servo ");
  30. Serial.print(bouton - 2);
  31. Serial.println(" envoye !");
  32. }
  33. else {
  34. Serial.print("echec de l'envoi du servo ");
  35. Serial.println(bouton - 2);
  36. }
  37. }
  38. }
  39. }

Le récepteur

Du côté du recepteur, nous avons donc deux LED et deux servomoteurs. Pour faciliter et simplifier l’écriture du programme, les broches de ces deux types de dispositifs sont mis dans deux tableaux constants.

  1. const int NB_LED = 2;
  2. const int NB_SERVO = 2;
  3.  
  4. const uint8_t brocheLED[NB_LED] = { 3, 4 };
  5. const uint8_t brocheServo[NB_SERVO] = { 5, 6 };

Deux objets Servo, également dans un tableau sont déclarés.

  1. Servo servoMoteur[NB_SERVO];

Nous définissons ensuite deux fonctions qui prennent en charge la gestion des LED et la gestion des servomoteurs. Elle seront appelées respectivement quand un message pour les LED ou un message pour les servomoteurs arrive.

  1. void messagePourLesLEDs(const CANMessage & inMessage)
  2. {
  3. Serial.print("Message LED reçu, changement de l'etat de la LED ");
  4. Serial.println(inMessage.data[0]);
  5. uint8_t numeroLED = inMessage.data[0];
  6. digitalWrite(brocheLED[numeroLED], !digitalRead(brocheLED[numeroLED]));
  7. }
  8.  
  9. void messagePourLesServos(const CANMessage & inMessage)
  10. {
  11. Serial.print("Message Servo reçu, changement de position du servo ");
  12. Serial.println(inMessage.data[0]);
  13. uint8_t numeroServo = inMessage.data[0];
  14. /*
  15.   * Les deux angles sont 45 et 135. Pour passer de 45 à 135,
  16.   * on fait 180 - 45 = 135. Pour passer de 135 à 45,
  17.   * on fait 180 - 135 = 45
  18.   */
  19. servoMoteur[numeroServo].write(180 - servoMoteur[numeroServo].read());
  20. }

Venons en maintenant au masque et aux filtres. Dans notre cas, la totalité de l’identifiant nous intéresse, nous allons donc utiliser un masque pour sélectionner les 11 bits de l’identifiant. Comme nous l’avons vu préalablement, le MCP2515 permet également d’appliquer masque et filtre aux deux premiers octets de données quand le message est standard. Ici, cela ne nous intéresse pas et le masque correspondant sera donc à 0. La déclaration de notre masque se fait donc de la manière suivante.

  1. const ACAN2515Mask masque = standard2515Mask(0x7FF, 0, 0);

standard2515Mask désigne donc un masque applicable aux messages standard. 0x7FF correspond en binaire à 11 bits à 1. Cet argument est le masque appliqué à l’identifiant du message standard. Les deux autres arguments correspondent respectivement au masque appliqué au premier premier octet de données et au deuxième octet de données.

Ensuite nous définissons deux filtres sous forme de tableau, le premier pour sélectionner les messages d’identifiant 1 et le second les messages d’identifiant 2. À chacun des deux filtres est associé la fonction correspondante que nous avons préalablement définie.

  1. const ACAN2515AcceptanceFilter filtres[] = {
  2. { standard2515Filter(1, 0, 0), messagePourLesLEDs },
  3. { standard2515Filter(2, 0, 0), messagePourLesServos }
  4. };

Les 3 arguments de standard2515Filter sont, dans l’ordre, l’identifiant voulu, les filtres sur les premier et deuxième octets de données et la fonction à appeler pour ce filtre. les filtres des premier et deuxième octets de données sont à 0 car, comme le masque est à 0, le résultat du masquage donnera 0, ce qui permettra l’acceptation du message quelque soit la valeur des données si l’identifiant correspond.

Dans setup, reglages est modifié pour changer les tailles des files d’attente. La taille de la file d’émission est mise à 0, puisqu’aucun message n’est émis par le récepteur, et la file de réception est mise à 1.

  1. reglages.mReceiveBufferSize = 1;
  2. reglages.mTransmitBuffer0Size = 0;

Et nous allons utiliser une autre version de begin qui permet de spécifier le masque et les filtres.

  1. const uint16_t codeErreur = controleurCAN.begin(
  2. reglages,
  3. [] { controleurCAN.isr(); },
  4. masque,
  5. filtres,
  6. 2
  7. );

reglages est identique à ce qui existait déjà dans la dernière application de « La bibliothèque ACAN (1) », à l’exception de la taille des files d’attente et [] { controleurCAN.isr(); } est la closure permettant de connecter l’interruption à la méthode qui la prend en charge dans ACAN. Les 2 arguments suivants sont le masque et les filtres que nous venons de définir. Enfin le dernier argument, 2, est le nombre de filtres dans le tableau filtres.

La fin de setup consiste en l’initialisation des LED et des servomoteurs.

  1. for (uint8_t led = 0; led < NB_LED; led++) {
  2. pinMode(brocheLED[led], OUTPUT);
  3. }
  4.  
  5. for (uint8_t servo = 0; servo < NB_SERVO; servo++) {
  6. servoMoteur[servo].attach(brocheServo[servo]);
  7. servoMoteur[servo].write(45);
  8. }

Enfin, dans loop, nous trouvons juste ceci.

  1. controleurCAN.dispatchReceivedMessage();

Et le programme est terminé. controleurCAN.dispatchReceivedMessage récupère les messages arrivés dans la file d’attente en réception, Ceux-ci sont étiquetés par le numéro du filtre qui les a sélectionnés et ACAN s’occupe d’appeler la fonction qui vous avez associée au filtre.

On peut également noter que nous n’avons plus besoin de déclarer un CANMessage pour le récepteur puisqu’un objet de ce type est passé comme argument aux deux fonctions et est entièrement géré par la bibliothèque ACAN.

Le code complet des deux sketchs est téléchargeable ci-dessous.

Téléchargement 1 : Application émetteur (4 boutons) / récepteur (2 LED et 2 servomoteurs).

Deuxième application, 3 nœuds

Pour cette seconde application, il vous faut 3 Arduino et 3 contrôleurs CAN et donc 3 sketchs. Le sketch de l’émetteur est identique à celui de l’application précédente : nous avons nos 4 boutons poussoir et nos deux messages CAN, un pour les LED et un pour les servomoteurs.

Cette fois ci nous avons deux récepteurs que nous appellerons recepteur-led et recepteur-servo. recepteur-led s’occupe naturellement des deux LED et recepteur-servo s’occupe des deux servomoteurs. Passer de l’application précédente à celle-ci est simple car il suffit de séparer le sketch en deux parties : la partie qui s’occupe des LED et celle qui s’occupe des servos, masque et filtres compris.

Le branchement de tout ceci est présenté à la figure 3.

Figure 3 : Connexions des éléments pour la deuxième application.
Figure 3 : Connexions des éléments pour la deuxième application.
Note : l’ordre dans lequel les cartes CAN sont connectées n’a pas d’importance mais il faut prendre garde à placer des straps sur le connecteur J1 pour les deux cartes sitiuées aux extrémités du bus, ce qui est symbolisé par un ovale rouge sur ce croquis.

recepteur-led

Le sketch conserve donc la déclaration des broches des LED mais élimine les déclarations concernant les servomoteurs ainsi que leurs initialisations dans setup. Il conserve le même masque mais les filtres sont revus à la baisse puisque ce récepteur ne reçoit que les messages correspondant aux LED.

  1. const ACAN2515AcceptanceFilter filtres[] = {
  2. { standard2515Filter(1, 0, 0), messagePourLesLEDs }
  3. };

Et bien entendu l’appel de begin mentionnera qu’un seul filtre est défini.

  1. const uint16_t codeErreur = controleurCAN.begin(
  2. reglages, [] { controleurCAN.isr(); },
  3. masque,
  4. filtres,
  5. 1
  6. );

recepteur-servo

Nous y trouvons le complémentaire de recepteur-led avec l’élimination de ce qui concerne les LED y compris la suppression du filtre correspondant.

  1. const ACAN2515AcceptanceFilter filtres[] = {
  2. { standard2515Filter(2, 0, 0), messagePourLesServos }
  3. };

Comme pour recepteur-led l’appel de begin mentionnera qu’un seul filtre est défini.

  1. const uint16_t codeErreur = controleurCAN.begin(
  2. reglages, [] { controleurCAN.isr(); },
  3. masque,
  4. filtres,
  5. 1
  6. );

Le code de cet exemple est téléchargeable ci-dessous. L’archive comprend les 3 sketchs.

Téléchargement 2 : Application avec 1 émetteur (4 boutons) et 2 récepteurs (2 LED pour le premier et 2 servomoteurs pour le second).

Troisième et dernière application

Pour la dernière application nous allons inverser les rôles : 2 émetteurs et un seul récepteur. Les 2 émetteurs reçoivent chacun 2 boutons et le premier de ces deux boutons commande 1 LED du récepteur et le second 1 servo du récepteur. Appelons ces émetteurs émetteur-1 et émetteur-2. Le récepteur comporte donc 2 LED et deux servos.

La façon dont les éléments sont connectés est présentée à la figure 4.

Figure 4 : Connexions des éléments pour la deuxième application.
Figure 4 : Connexions des éléments pour la deuxième application.
Note : l’ordre dans lequel les cartes CAN sont connectées n’a pas d’importance mais il faut prendre garde à placer des straps sur le connecteur J1 pour les deux cartes situées aux extrémités du bus, ce qui est symbolisé par un ovale rouge sur ce croquis.

Comme mentionné au début de l’article, les messages doivent avoir un identifiant unique. Autrement dit les deux émetteurs ne peuvent envoyer des messages ayant le même identifiant. Nous allons donc attribuer aux messages les identifiants suivants en binaire sur 11 bits, et entre parenthèses la valeur correspondante en décimal, puisqu’il s’agit de messages standards :

Émetteur Id message LED Id message Servo
émetteur-1 00000000001 (1) 00000000010 (2)
émetteur-2 00000000101 (5) 00000000110 (6)

On voit que le 3e bit à partir de la droite permet d’identifier l’émetteur. Comme le récepteur veut recevoir tous ces messages, on voit également que la valeur de ce 3e bit est indifférente. Le masque va donc être tel que ce bit est ignoré puisque quelque soit sa valeur le message doit être reçu. Ceci nous donne donc un masque où tous les bits sont à 1 sauf le 3e à partir de la droite : 11111111011, ce qui en hexadécimal donne 0x7FB. Ce 3e bit étant masqué, et donc à 0, Le filtre permettant la réception des messages pour les LED est donc 00000000001 et celui pour les servos 00000000010.

On peut également noter que comme l’identifiant permet de savoir d’où vient le message et si il s’agit d’une commande de LED ou de servomoteur, il n’est plus nécessaire ici d’avoir un octet de donnée désignant la LED ou le servomoteur à commander.

Les deux émetteurs et le récepteur

Les émetteurs sont identiques à l’exception des identifiants des messages envoyés qui sont données à la table 1.

Le récepteur, quant à lui, voit son masque modifié de manière à accepter les messages venant des deux émetteurs.

  1. const ACAN2515Mask masque = standard2515Mask(0x7FB, 0, 0); /* Que des 1 sur 11 bits sauf le 3e à partir de la droite */
  2.  
  3. const ACAN2515AcceptanceFilter filtres[] = {
  4. { standard2515Filter(1, 0, 0), messagePourLesLEDs },
  5. { standard2515Filter(2, 0, 0), messagePourLesServos }
  6. };

Les deux fonctions messagePourLesLEDs et messagePourLesServos sont modifiées pour aller chercher le numéro de la LED ou du servomoteur dans l’identifiant du message au lieu d’aller les chercher dans les données. Ce numéro est le 3e bit de l’identifiant à partir de la droite. Il faut donc commencer par le placer complètement à droite en décalant l’identifiant de 2 bits puis éliminer les autres bits de l’identifiant en faisant un ET (opérateur &) avec la valeur 1.

  1. void messagePourLesLEDs(const CANMessage & inMessage)
  2. {
  3. uint8_t numeroLED = (inMessage.id >> 2) & 1;
  4. Serial.print("Message LED reçu, changement de l'etat de la LED ");
  5. Serial.println(numeroLED);
  6. digitalWrite(brocheLED[numeroLED], !digitalRead(brocheLED[numeroLED]));
  7. }
  8.  
  9. void messagePourLesServos(const CANMessage & inMessage)
  10. {
  11. uint8_t numeroServo = (inMessage.id >> 2) & 1;
  12. Serial.print("Message Servo reçu, changement de position du servo ");
  13. Serial.println(numeroServo);
  14. /*
  15.   * Les deux angles sont 45 et 135. Pour passer de 45 à 135,
  16.   * on fait 180 - 45 = 135. Pour passer de 135 à 45,
  17.   * on fait 180 - 135 = 45
  18.   */
  19. servoMoteur[numeroServo].write(180 - servoMoteur[numeroServo].read());
  20. }

Le code complet de cette troisième et dernière application est téléchargeable ci-dessous.

Téléchargement 3 : Application avec 2 émetteurs (2 boutons par émetteur) et 1 récepteur (2 LED et 2 servomoteurs).

Dans le prochain article, nous verrons les messages étendus et les messages remote.

[1On peut se poser la question de l’intérêt de faire cela. La raison est que le MCP2515 permet d’associer une priorité aux tampons TXBn. Cette priorité entre en jeu lorsque plus d’un tampon est plein. Par défaut cette priorité est 0 et à priorités égales, le MCP2515 applique une seconde règle : le tampon de plus grand numéro est le plus prioritaire. Par conséquent, si on utilise plusieurs tampon en émission, l’ordre de passage des messages sur le bus n’est pas nécessairement l’ordre d’écriture dans les tampons, ce qui peut s’avérer indésirable.

[2Attention qu’il n’y ait pas de confusion avec le fait que le terme Buffer apparaissent dans le nom de ces méthodes. receiveBufferPeakCount() et transmitBufferPeakCount(x) retournent la taille de la file d’attente correspondante. Ces files sont dans la mémoire de l’Arduino, pas dans le MCP2515 qui n’en contient pas.

[3Sauf en ce qui concerne les trames remote mais nous verrons ceci plus tard

5 Messages

Réagissez à « La bibliothèque ACAN (2) »

Qui êtes-vous ?
Votre message

Pour créer des paragraphes, laissez simplement des lignes vides.

Lien hypertexte

(Si votre message se réfère à un article publié sur le Web, ou à une page fournissant plus d’informations, vous pouvez indiquer ci-après le titre de la page et son adresse.)

Rubrique « Bibliothèques »

La bibliothèque Servo

Bibliothèque SoftWare Serial

Bibliothèque Serial

Bibliothèque EEPROM

Bibliothèque Wire : I2C

Bibliothèque LCD

La bibliothèque ScheduleTable

Bibliothèque MemoryUsage

Bibliothèque EEPROMextent

La bibliothèque SlowMotionServo

Bibliothèque Commanders

Bibliothèque DCCpp

Bibliothèque DcDccNanoController

Bibliothèque Accessories (1)

Bibliothèque Accessories (2)

Un décodeur d’accessoires universel (1)

Un décodeur d’accessoires universel (2)

Un décodeur d’accessoires universel (3)

Bibliothèque LcdUi (1)

Bibliothèque LcdUi (2)

Les derniers articles

La bibliothèque ACAN (2)


Jean-Luc

La bibliothèque ACAN (1)


Jean-Luc

La bibliothèque SlowMotionServo


Jean-Luc

Bibliothèque DCCpp


Thierry

Bibliothèque DcDccNanoController


Thierry

Bibliothèque LcdUi (2)


Thierry

Bibliothèque LcdUi (1)


Thierry

Bibliothèque Accessories (2)


Thierry

Bibliothèque Accessories (1)


Thierry

Bibliothèque Commanders


Thierry

Les articles les plus lus

Bibliothèque Wire : I2C

Bibliothèque EEPROM

Un décodeur d’accessoires universel (1)

Bibliothèque SoftWare Serial

La bibliothèque Servo

La bibliothèque ACAN (1)

Bibliothèque DCCpp

Bibliothèque Serial

Bibliothèque LCD

La bibliothèque ScheduleTable