Puisqu'on aborde les communications par le bus CAN, je vais décrire ce que j'ai réalisé pour le moment.
D'abord un petit rappel de ce que j'ai compris du CAN :
Le bus CAN possède plusieurs caractéristiques intéressantes. Il est :
• Temps-réel. C’est à dire que les temps de transmission sont déterministes et peuvent être calculés à priori.
• Peu coûteux. Les contrôleurs CAN sont soit disponibles en tant que composant destiné à être connecté à un micro-contrôleur, soit intégrés directement dans un micro-contrôleur. La quantité de logiciel nécessaire pour le faire fonctionner est relativement succincte. Les composants CAN sont fabriqués en grand nombre, ce qui abaisse leur coût.
• Multi-maître asynchrone. pas d’arbitre centralisé, pas d’horloge globale, une station peut émettre dès que le bus est libre.
• Priorité entre les messages. Les collisions, quand plusieurs contrôleurs CAN émettent sur le bus simultanément, sont résolues sans destruction du message le plus prioritaire.
• Messages de taille limitée. 8 octets de données au plus.
Le bus CAN est fiable :
• Une détection d’erreur est incluse. La probabilité qu’une erreur reste non détectée est inférieure à 4,7 10-11.
• Le protocole définit une surveillance très stricte du bus.
• La cohérence des données est garantie. Un message est soit accepté par tous les contrôleurs CAN du réseau, soit rejeté par tous.
• Acquittement des messages par toutes les stations, le signalement d’erreur émis par une station est reçu par toutes les stations.
• Retransmission automatique des messages après signalement d’erreur.
• Le confinement des erreurs. Un contrôleur CAN gère de façon autonome les erreurs temporaires qu’il détecte.
Les performances
Le bus CAN propose des débits allant de 100kbits/s à 1Mbits/s.
La distance maximale entre 2 noeuds dépend de cette vitesse : 100 m à 500kb/s, ou 500 m à 125 kb/s, ce qui ne pose aucun problème pour nos réseaux, même excessivement grands !
Qu’est-ce qu’un message ?
Quand on construit un système avec un bus CAN, on définit une messagerie : l’ensemble des messages qui vont transiter et leur identifiant.
Les messages CAN sont courts, 94 bits maximum. Il n’y a pas d’adresse explicite donc aucun système de routage de messages entre un émetteur et un récepteur. Tout le monde reçoit tout et doit filtrer pour ne traiter que ce qui le concerne.
La partie "données" d’un message contient un identifiant de 11 bits (mode standard) ou 29 bits (mode étendu que je n'utilise pas dans cette application) et des données de 0 à 8 octets. L’identifiant est indispensable, les données non.
Dans mon application, je n’utilise pratiquement qu’un seul octet de données, en plus de l’identifiant. Seuls quelques cas spécifiques comme les messages de configuration utilisent plusieurs octets.
Comme dans tout système de transmission, il y a d’autres bits de contrôle et de calcul d’erreur qui servent uniquement au circuit d’interface MCP2515, donc il n’est pas besoin d’en parler, « ça se débrouille tout seul » !
L’identifiant joue un rôle particulièrement important
Il sert à plusieurs choses :
• l’arbitrage en cas de contention, c’est à dire quand 2 ou plusieurs messages sont émis sur le bus simultanément,
• le filtrage, c’est à dire la possibilité pour le récepteur de ne recevoir que ce qui l’intéresse,
• l’adressage qui n’est pas un adressage classique de messagerie, mais qui est utilisé dans ce but, d’abord au moyen du filtrage, puis par le récepteur pour déterminer à quel fonction le message est destiné.
En cas de contention, chaque émetteur regarde si un identifiant émis est plus prioritaire que le sien. Si oui, il stoppe d’émettre et attend son tour. Celui qui a l’identifiant le plus prioritaire continue d’émettre comme si de rien n’était. Il n’y a donc pas de perte de temps !
La règle de priorité est simple : le plus prioritaire est le plus petit !
Une autre règle en découle, qui est que 2 émetteurs différents ne doivent pas émettre de messages avec un même identifiant.
Par exemple, supposons que le Central du réseau veuille envoyer des ordres à des moteurs d’aiguillage (donc à la carte de commande d’aiguilles) et d’autres ordres à des signaux (donc à la carte de commande des signaux) et d’autres encore à la traction (donc la carte générateur du DCC ou du PWM ).
On va donc définir une série d’identifiants pour les messages destinés exclusivement aux commandes d’aiguille, par exemple la série de 16 identifiants de Id = 0x40 à 0x4F, une autre série d’identifiants pour les commandes de signaux, par exemple Id = 0x50 à 0x5F, et une autre pour la traction, par exemple Id 0x30 à 0x3F.
On remarque au passage qu’on ne s’intéresse qu’aux messages reçus par la carte CAN dont il faut définir les masques et les filtres.
On va rester en trame standard, donc les identifiants font 11 bits. Les valeurs de 1 à 2047 sont largement suffisantes pour tous les types de messages possibles. Et même en gardant uniquement les 8 bits de poids faible, 256 types de messages me semblent suffisants.
Supposons qu’au maximum il y ait 22 moteurs d’aiguillage et quelques dételeurs : 5 bits suffiront dans un unique octet de message pour désigner un moteur d’aiguilles ou un dételeur, les 3 bits supplémentaires permettant de définir les types de commande qui s’appliquent aux aiguilles.
Les réponses de la carte d’aiguilles vers le gestionnaire de réseau et/ou d’autres cartes étant filtrées par leur destinataire, les Id des messages émis seront donc pris dans la liste des Id qu’elles acceptent de recevoir.
Par exemple, pour gérer mes aiguilles, les commandes reçues seront décodées en fonction de l’octet de données qui a la forme suivante :
• les bits 0 à 5 définissent le numéro de l’aiguille ;
• les bits 6 et 7 portent la consigne à appliquer.
- bit 7 = 1, bit 6 = 1 : sens « droit »
- bit 7 = 1, bit 6 = 0 : sens « dévié »
Comme la carte de commande d’aiguilles ne doit réagir qu’aux ordres émis par le central (avec l’Id = 0x40) et comme le TCO permet de positionner les aiguilles "à la main" avec des clés, chaque fois qu’un opérateur tournera une clé d’aiguille sur le TCO, la demande du TCO sera d’abord adressée au Central. Ce dernier devra la valider avant de l’envoyer à l’aiguille si l’opération est possible.
Dans ce schéma, le TCO enverra les même données que dans les commandes ci-dessus mais avec un Id = 0x11 (dans la plage des Id acceptés par le Central). Dès que le Central les aura validées, ce dernier les renverra alors avec l’Id = 0x40 et elles seront exécutées par la carte de commande d’aiguilles. Ensuite les réponses de la carte d’aiguilles avec l’Id = 0x12 seront reçues par le central (pour la mise à jour des tables de positions ou des objets aiguilles) et ce dernier enverra un message vers le TCO avec l’Id 0x20 pour l’allumage de la diode verte qui représente la position d’aiguille (et l’extinction de l’autre Led) à coté de la clé de commande.
Le schéma des échanges est le suivant pour une commande d’aiguille :
Demande TCO -> Id=0x11-> Central -> Commande -> Id=0x40 -> Aiguilles
Réponse état Aiguilles -> Id=0x12 -> Central -> Etat -> Id=0x20 -> TCO
Dans le projet de réseau, il faut donc étudier tous les échanges possibles et dresser le tableau des Id et des données de tous les messages.
Rappel : ce qui compte, c’est de rassembler les identifiants des messages reçus dans une plage qui permet de définir les masques et filtres de façon pratique.
Et les filtres alors ?
On l’a entrevu dans la routine de setup : il y a 2 masques et 6 filtres.
Les masques définissent les bits de l’identifiant qui sont significatifs (sinon ils sont ignorés par le filtre).
Les filtres font que seuls les identifiants présentés sont acceptés Les bits du masque travaille avec les bits des filtres de même position.
Un bit « 0 » du masque permet d’accepter n’importe quel bit de l’identifiant (pas de filtre). Cela permet d’éviter de filtrer tous les bits et donc d’accepter des plages entières d’identifiants.
Un bit « 1 » du masque autorise le filtrage : pour qu’un bit de l’identifiant soit accepté, il doit être identique à celui du filtre. Tous les bits de l’identifiant doivent être acceptés pour que l’identifiant soit accepté.
Exemple :
Filtrage total pour accepter seulement l’Id = 0x317 dans un buffer
• - Masque = 111 1111 1111 • - Filtre = 011 0001 0111
Filtrage partiel pour accepter une plage d’Id de 0x310 à 0x317
• - Masque = 111 1111 1000 • - Filtre = 011 0001 0xxx
Pas de filtrage du tout
• - Masque = 000 0000 0000 • - Filtre = xxx xxxx xxxx
Dans mon cas, je n’ai qu’une carte de commande d’aiguilles, le masque est fixé à 0x7F0 et les filtres définissent les identifiants des seuls messages attendus : 0x40 à 0x4F.