Une Passerelle entre le bus S88 et le bus CAN pour la rétro signalisation

Test et application sur ECOS

. Par : JPClaude. URL : https://www.locoduino.org/spip.php?article180

Nous nous proposons dans cet article de réaliser une passerelle entre le bus S88 assez courant sur nos consoles pour la rétro signalisation, et le bus CAN. Le problème du bus S88 (pour ceux qui ont des anciennes consoles comme moi) est d’être très sensible aux perturbations alors que le bus CAN n’y est pas, certes amélioré depuis avec les câbles RJ45. L’idée est de réaliser un bus S88 sur quelques centimètres et les connexions avec les détecteurs et les capteurs distants via le bus CAN qui permet une répartition en réseau et non plus en bus. On allie donc ainsi la simplicité du bus S88 avec les potentialités du bus CAN. Le test a été effectué avec une console ECOS.

Introduction

Je vous invite à lire les articles sur la rétro signalisation S88 dans La rétro-signalisation sur Arduino et celui sur le bus CAN dans Mise en oeuvre du Bus CAN entre modules Arduino (1) .

Le bus S88 est un bus par chaînage dont chaque module du chaînage doit être à 2m pour le bus S88 et de 5m maximum pour le S88N, et encore dans le meilleur des cas. Ce type de configuration est très contraignant car si depuis la console on veut aller sur la droite puis sur la gauche on n’a que ce débattement.

Configuration bus S88

Le principe est simple. Chaque module regroupe 8 ou 16 capteurs dont les valeurs sont stockées dans un registre à décalage. Chaque capteur est représenté par un bit repéré en fonction de sa position dans le chaînage. Ainsi le premier module de la chaîne a le numéro 1 et ainsi de suite. A l’intérieur d’un module le bit 0 du registre représente le premier capteur et ainsi de suite. Un capteur est donc défini par [numéro de module : numéro du bit + 1] dans le registre à décalage (i.e. : 1 : 1 pour le premier capteur du premier module). Les décalages sont définis par une horloge qui détermine le nombre de tops en fonction du nombre de module et du nombre de capteurs par module (8 ou 16). Ainsi si le premier module à 8 capteurs, le second 16 et le dernier 8, l’horloge sait qu’il lui faut 16 tops pour balayer tous les capteurs, 8 tops pour le premier, 16 pour le second et 8 pour le dernier. Le principe est donc simple à mettre en œuvre.

Le bus CAN effectue les transmissions sur une paire différentielle CANL et CANH. L’immunité électrostatique est assurée car les deux fils sont affectés en même temps par un signal parasite et donc ne joue pas sur le différentiel. Toutes les sorties CANL de tous les composants sont reliées entre elles, ainsi que les sorties CANH.
Il existe deux types de transmission « low speed » et « high speed », ici nous utiliserons le type « high speed ». Ce dernier offre des débits de 125 kb/s sur 500m jusqu’à 1Mb/s sur 30m et de 2 à 30 nœuds. C’est un bus partagé, tous les modules sont reliés à ce bus à n’importe quel endroit du bus. Il suffit simplement que deux éléments du bus aux extrémités fournissent une résistance de 120 ohms pour qu’il fonctionne. On a donc une très grande liberté de déploiement des modules sur nos réseaux. Voici un exemple de configuration possible :

Déploiement du bus CAN

La gestion de ce bus est nettement plus complexe que le bus S88 mais heureusement il existe des composants et des bibliothèques qui font ça pour nous.

L’objectif de cette approche est de proposer des modules passerelles entre le bus S88 et le bus CAN et des modules détecteurs distribués tout autour du bus CAN. L’avantage, est pour ceux qui ont une interface S88, de pouvoir réduire la taille du bus S88 (à quelques centimètres) et de distribuer les capteurs n’importe où de façon fiable sur le réseau via le bus CAN.

Architecture de la proposition

L’architecture se compose d’une console avec une interface S88, des modules connectés au bus S88 d’un côté et au bus CAN de l’autre que l’on appelle Gateway, des modules de détections qui regroupent 15 capteurs (nous expliquons plus loin pourquoi 15) connectés au bus CAN et que l’on nomme Detector, et des capteurs connectés aux différents Detector. Un module Gateway peut prendre en charge 4 modules Detector. L’ensemble de ces modules tourne sur des Arduino NANO. Le bus S88 admet jusqu’à 512 capteurs. Chaque Gateway prend en charge au maximum 4 * 15 = 60 capteurs soit un maximum de 8 Gateway pour un total de 480 capteurs.

Le schéma suivant présente cette architecture :

Architecture de la proposition

Chaque Gateway regroupe 4 modules Detector, soit l’équivalent de quatre modules S88.
Chaque Gateway possède un numéro (son rang dans la chaîne), 1 pour le premier et ainsi de suite.
Chaque Detector possède un numéro entre 1 et 4 et le numéro du Gateway auquel il est rattaché.
Le premier Detector représente les 15 premiers capteurs et ainsi de suite.
Le Gateway 1 (premier dans la chaîne S88) regroupe donc les adresses S88 1:1 à 4:16
Les adresses S88 1:1 à 1:16 correspondent donc au Gateway (1) et à son premier detector (1),
Les adresses S88 2:1 à 2:16 correspondent au Gateway (1) et à son second detector (2),
Les adresses S88 5:1 à 5:16 correspondent au Gateway (2) et à son premier detector (1)
Ainsi de suite...

La conversion s’opère donc de la manière suivante :
Soit X : Y l’adresse S88 d’un capteur.
On recherche sur quel Gateway il se situe en testant les différentes valeur de G dans la comparaison :
si (G-1) * 4 < X <= G * 4 alors il se situe sur le Gateway de numéro G
Puis on calcule le numéro de détecteur D tel que :
D = X – ((G-1) * 4)
Et on est en position Y sur ce détecteur.

Exemple soit l’adresse S88 du capteur 19 : 5 alors le capteur se trouve sur le Gateway 5 (4*4 < 19 <= 5*4), sur le détecteur 3 (19 – (5-1)*4) et en position 5, d’où 19 : 5 => 5 : 3 : 5.

Connexion avec le bus S88

Le bus S88 est un connecteur qui possède 6 broches :
• broche 1 : data (blanc) - sortie des données vers un autre module ou vers la centrale
• broche 2 : GND (marron) - GND
• broche 3 : clock (vert) – top de l’horloge
• broche 4 : PS (jaune) – signal PS démarrage de l’horloge
• broche 5 : Reset (violet) - reset du comptage
• broche 6 : V+ (rose) – alimentation 4,5 à 5V
et l’interface avec le Nano se fait de la manière suivante :
• broche 0 (rx) : entrée depuis un autre Arduino
• broche 1 (tx) : sortie vers la station de contrôle (broche 1 S88) ou vers un autre Arduino
• broche 2 (int0) : interruption de l’horloge du bus S88 (broche 3 S88 sur tous les Arduino du chaînage)
• broche 3 (int1) : interruption du signal PS du bus S88 (broche 4 S88 sur tous les Arduino du chaînage)
• Gnd : masse commune (broche 2 S88 sur tous les Arduino du chaînage)

ATTENTION  : Jusqu’à 2 Nano on peut utiliser le 5V de l’interface S88 pour les alimenter, au delà de 2 Nano il est préférable de passer par une alimentation externe régulée, dans ce cas ne pas connecter le 5v de l’interface S88, il faut relier les masses entre elles et suivant l’alimentation externe, si 5V connecter à la broche 5V de l’Arduino, si entre 7v et 12v connecter à la broche Vin.

Connexion avec le bus CAN

Elle se fait en utilisant un petit composant le MCP_2515, sur une petite carte CAN qui contient également un transmetteur de puissance (MCP2551 ou TJA1050) vers le bus CAN. C’est une carte que l’on trouve facilement sur le web pour un coût de l’ordre de 3€. Elle implémente la version 2.0B, des champs d’information jusqu’à 8 octets et admet le mode standard ou étendu. D’un côté cette carte propose une interface avec le bus CAN (CANL et CANH) et de l’autre une interface avec le SPI.

MCP_2515

Du côté du bus CAN il suffit de relier tous les CANH entre eux et de même pour les CANL. Il ne faut pas oublier qu’il faut que deux cartes mettent en place une résistance de 120 ohms, ce qui est fait en fermant le strap de terminaison. Enfin connecter GND et VCC respectivement au GND et 5V de l’Arduino.
Du coté Arduino Nano il faut connecter l’interface SPI de la façon suivante :
Arduino NANO/UNO/MINI/PRO
MCP2515 -> Arduino
SCK -> pin 13 (SCK)
SO -> pin 12 (MISO)
SI -> pin 11 (MOSI)
CS -> pin 10 (SS)
INT -> pin 4 (pour le Gateway, pas de connexion pour le Detector)

La bibliothèque mcp_can

Il existe une bibliothèque qui nous permet de programmer de façon simple la configuration du bus CAN via le MCP 2515, c’est la bibliothèque mcp_can que l’on trouve sur le site [1] .

Voici les éléments essentiels à connaitre.
Les bibliothèques à utiliser :

#include <SPI.h> // la bibliothèque du bus SPI
#include <mcp_can.h> // la bibliothèque du bus CAN

Les constantes :

Les débits pré définis
CAN_XKBPS (X = 5,10,20,40,50,80,100,125,200,250,500,1000)
Les contrôles :
CAN_OK, CAN_FAILINIT, CAN_FAIL, CAN_FAILTX, CAN_CTRLERROR
CAN_GETTXBFTIMEOUT, CAN_SENDMSGTIMEOUT
CAN_MSGAVAIL, CAN_NOMSG

Les méthodes

Instance MCP_CAN :

Il faut donner un nom à l’instance et le numéro de la broche CS

#define CS   10 
MCP_CAN CAN(CS) ;

Initialisation du bus CAN  :

begin (debit du bus) ;
Ex :

#define BAUDRATE   CAN_500KBPS 
CAN.begin(BAUDRATE) ;

Initialisation des masques :

init_Mask(numéro du masque, mode, valeur du masque) ;
Numéro du masque : 0 ou 1 (RXM0 ou RXM1)
Mode : 0 pour identificateur sur 11 bits, 1 pour identificateur sur 29 bits
Ex :

#define ID_MODE   0
#define MASK0   0x7F0
#define MASK1  0x70
CAN.init_MASK(0,ID_MODE,MASK0) ;
CAN.init_MASK(1,ID_MODE,MASK1) ;

Initialisation des filtres :

Les filtres 0 et 1 vont avec le masque 0, les filtres 2 à 5 avec le masque 1
init_Filt(numero du filtre,mode, valeur du filtre) ;
Ex :

#define FILTER0   0x40            
#define FILTER1   0x40
#define FILTER2   0x40
#define FILTER3   0x40
#define FILTER4   0x00           
#define FILTER5   0x00
CAN.init_Filt(0,ID_MODE,FILTER0);
CAN.init_Filt(1,ID_MODE,FILTER1);
CAN.init_Filt(2,ID_MODE,FILTER2);
CAN.init_Filt(3,ID_MODE,FILTER3);
CAN.init_Filt(4,ID_MODE,FILTER4);
CAN.init_Filt(5,ID_MODE,FILTER5);

Émission d’un message :

Il va nous falloir trois variables, une pour l’identificateur (Id), une pour le nombre d’octets de données émis (Len) et un buffer de 8 octets maximum pour l’information (Buf). L’émission se fait simplement avec la méthode sendMSGBuf(identificateur, mode, longueur, buffer).
Ex :

unsigned char buf[8] ;
CAN.sendMsgBuf(Id, ID_MODE, 1, buf) ;

Réception des messages :

Il faut vérifier s’il y a un message à recevoir avec la méthode checkReceive(), prendre alors l’identificateur avec la méthode getCanId(), le nombre d’octets et l’information avec la méthode readMsgBuf(&Len,Buf).
Ex :

int Id ;
unsigned char Len = 0 ;
unsigned char Buf[8] ;
if (CAN_MSGAVAIL == CAN.checkReceive()) {  // message en attente ?
  Id = CAN.getCanId(); // son identificateur
  CAN_readMsgBuf(&Len,Buf); // nombre d’octets et information

Plutôt que de tester si une arrivée a eu lieu on peut utiliser une interruption. On déclare simplement une variable booléenne qui déterminera s’il y a une arrivée de message.

volatile bool FlagReceive = false ;
void MCP2515_ISR() {FlagReceive = true ;} // la routine d’interruption

Puis dans le setup on attache l’interruption
attachInterrupt(N°IT,MCP_ISR,FALLING);
il suffit alors dans la boucle de surveiller ce booléen
if (FlagReceive) {FlagReceive = false ; “recevoir message” ;} // test du drapeau d’IT

La passerelle Gateway

La passerelle va d’une part contrôler le bus S88 et donc utiliser deux interruptions, l’une pour l’horloge l’autre pour la commande PS et utiliser les broches TX, RX, D2 et D3. D’autre part elle doit également contrôler le bus CAN via l’interface SPI et le MCP 2515 et utiliser une interruption pour la réception des messages CAN. Le Nano ne possède que deux interruptions externes, il faut donc créer une nouvelle interruption ce qui est possible par l’utilisation de la bibliothèque PinChangeInt que l’on trouve sur [2] .

Les broches 10, 11, 12,13 seront utilisées pour l’interface SPI et la broche D4 pour l’interruption du bus CAN. La tension est fournie par l’Arduino (GND et 5V). La connexion des passerelles sera donc la suivante :

Passerelle Gateway

ATTENTION RAPPEL : au delà de deux Nano utiliser une alimentation externe et relier les GND entre eux.

La connexion S88 sera donc très courte puisque l’on peut mettre l’ensemble des Gateway côte à côte, les capteurs étant distribués au travers du bus CAN.
Le fonctionnement du Gateway est très simple. Il possède 4 buffers de 16 bits qui sont remplis périodiquement, en fonction des interruptions du bus Can, par les différents capteurs distribués sur ce bus. Ces buffers sont vidés périodiquement dans des registres à décalage qui sont gérés par l’horloge du bus S88.
La configuration du bus CAN est à un débit de 500 Kb/s en mode 0 (11 bits d’identificateur). Les filtrages ne sont pas utilisés ici, il est à noter que ce bus est entièrement dédié à la rétro signalisation. Les données échangées déterminent le numéro du Gateway concerné, le numéro du Detector émetteur et la valeur de son registre de capteurs.

La gestion des interruptions

La gestion des interruptions utilise la bibliothèque PinChangeInt de la manière suivante :
On limite les broches pouvant soulever une interruption :

#define NO_PORTB_PINCHANGES // 8,9,10,11,12,13
#define NO_PORTC_PINCHANGES // A0, A1, A2, A3, A4, A5

Puis on inclut la bibliothèque

#include <PinChangeInt.h>

On déclare ensuite les broches d’interruption utilisées :

const byte clockS88 = 2;  // broche d’interruption de l’horloge S88
const byte PSS88 = 3;     // broche d’interruption du signal PS
const byte CANPin = 4;    // broche d’interruption du bus CAN

On déclare ensuite les routines d’interruption

Pour l’horloge :

void clock() {
  cli();
  digitalWrite(dataOut, bitRead(data[0],clockCounter)); // décalage du premier registre à décalage vers la console externe
  for (int i = 0; i <NB_DETECTOR - 1; i++) {
    bitWrite(data[i], clockCounter, bitRead(data[i+1],clockCounter));
  } // décalage de tous les autres registres
  bitWrite(data[NB_DETECTOR - 1],clockCounter,digitalRead(dataIn)); // décalage depuis un autre Arduino chainé
  clockCounter = (clockCounter + 1) % 16; // mise à jour du compteur d’horloge modulo 16
  sei();
}

Pour le signal PS :

void PS() {
  cli();
  clockCounter = 0; // réinitialisation de l’horloge
  for (int i = 0; i< NB_DETECTOR; i++) {
    data[i] = sensors[i];
  } // on charge les registres à décalage avec les buffers des capteurs
}

Pour le CAN

void MCP2515_ISR() {FlagReceive = true;}  // juste un drapeau pour savoir si un message est arrive

Il faut ensuite faire les attachements des routines d’interruption avec les broches associées :

pinMode(clockS88, INPUT_PULLUP); // pour l’horloge S88
PCintPort::attachInterrupt(clockS88, &clock, RISING);
pinMode(PSS88,INPUT_PULLUP); // pour le signal PS
PCintPort::attachInterrupt(PSS88, &PS, RISING);
pinMode(CANPin, INPUT); // pour le bus CAN
PCintPort::attachInterrupt(PCINT1, &MCP2515_ISR, FALLING);

La boucle du programme

La boucle du programme consiste alors à vérifier si un message est arrivé du bus CAN, à vérifier si ce message s’adresse à ce Gateway et si oui, de ranger son contenu (valeurs des capteurs distants) dans le bon buffer. Les messages reçus ont pour données : le numéro du Gateway concerné, le numéro du Detector émetteur et la valeur du registre des capteurs contenus dans ce Detector.

Ce programme est mis dans un fichier CAN_S88_Gateway.cpp et on l’utilise par l’appel au fichier CAN_S88_Gateway.h. Vous pouvez les télécharger ici :

Programme Gateway S88-CAN

Configuration des Gateway

La configuration d’un Gateway est des plus simples, il suffit d’inclure le fichier CAN_S88_Gateway.h et de déclarer le numéro du Gateway en commençant toujours par le numéro 1 pour le premier Gateway, et ainsi de suite, puis de le charger dans un Nano.

#include "CAN_S88_Gateway.h"
void setup() {Setup(donner le numéro du Gateway ici);}
void loop() {Loop();}

Les Detector

Le Detector n’a besoin que de se connecter au bus CAN qu’en émission seule, il utilise donc un MCP_2515 et les broches associées pour l’interface SPI (10, 11, 12, 13) sans la broche int puisqu’il n’y a pas de réception en attente. Il ne reste donc que 15 broches disponibles pour les capteurs, ce qui explique cette limitation par rapport au 16 normalement, on perd donc le capteur 16, mais cela tourne sur un Nano seulement. Les broches utilisées sont les suivantes :

Numéro capteur
broche
position binaire
1
RX
0
2
2
1
3
3
2
4
4
3
5
5
4
6
6
5
7
7
6
8
8
7
9
9
8
10
A0
9
11
A1
10
12
A2
11
13
A3
12
14
A4
13
15
A5
14

L’alimentation des Detector peut se faire depuis les rails par un pont redresseur filtré et un régulateur 78M9, on a donc plus que deux fils sur lesquels viennent se connecter des paquets de 15 capteurs via un Nano.
Le programme est très simple il reprend la gestion du bus Can et envoie périodiquement les états de ses capteurs. Pour cela il lit toutes les broches des capteurs, range les valeurs dans deux octets (high et low) et envoie un message constitué de quatre octets, le numéro du Gateway concerné, le numéro du Detector et les deux octets de données. Chaque Detector a un délai aléatoire différent entre deux émissions afin de minimiser les collisions (voir l’article Comment gérer l’aléatoire ? ).

Le programme se trouve dans un fichier CAN_S88_Detector.cpp et CAN_S88_Detector.h, vous pouvez les télécharger ici :

Programme Detector

Configuration des Detector

La configuration d’un Detector consiste à inclure le fichier CAN_S88_Detector.h, de déclarer le numéro du Detector (1 à 4) et le numéro de son Gateway de rattachement puis de le charger dans un Nano.

#include "CAN_S88_Detector.h" 
void setup() {Setup(numéro du Gateway de rattachement, numéro du Detector);}             
void loop() {Loop();}

Le banc de test

Le test a été effectué avec une console ECOS, qui possède un fonctionnement étrange sur le premier octet du premier module (capteurs 1:1 à 1:8) pour lequel je n’ai pas compris le fonctionnement plus qu’aléatoire. Donc, pour l’ECOS, je n’utilise pas le premier Detector du premier Gateway qui donc ne possède que 3 Detector * 15 capteurs = 45 capteurs (on commence donc les adresses S88 par 2:1), les autres Gateway possèdent par contre 4 Detector * 15 capteurs = 60 capteurs. Le banc a été réalisé avec 3 Gateway et 3 Detector (un sur chaque Gateway), les capteurs sont simulés par des boutons poussoirs, le tout a fonctionné sans problème.

Voici la configuration du banc de test :

//Gateway 1 
#include "CAN_S88_Gateway.h"
void setup() {Setup(1);}   
void loop() {Loop();}

//Detector1:2 (capteurs 2:1 à 2:15, Detector 1:1 non utilisé)
#include "CAN_S88_Detector.h"    
void setup() {Setup(1,2);}    
void loop() { Loop();}

//Gateway 2
#include "CAN_S88_Gateway.h"
void setup() {Setup(2);}   
void loop() {Loop();}

//Detector2:1 (capteurs 5:1 à 5:15)
#include "CAN_S88_Detector.h"    
void setup() {Setup(2,1);}    
void loop() { Loop();}

//Gateway 3
#include "CAN_S88_Gateway.h"
void setup() {Setup(3);}   
void loop() {Loop();}

Detector3:1 (capteurs 9:1 à 9:15)
#include "CAN_S88_Detector.h"    
void setup() {Setup(3,1);}    
void loop() { Loop();}

Voici une image du banc de test, bon il y a beaucoup fils, mais une fois mis sur des platines cela s’éclaircit énormément.

Banc de test

Et le résultat sur la console ECOS.

Ecran de l’ECOS

La bibliothèque MCP_CAN utilisée dans ce projet :

bibliothèque MCP_CAN du projet

Conclusion

La solution présentée ici permet de conserver l’interface S88 de la console et de déployer de façon distribuée et sans contrainte l’ensemble des capteurs de rétro signalisation avec le bus CAN. Elle permet également de fiabiliser les échanges, en limitant le bus S88 à quelques centimètres et en limitant grandement la longueur de câblage par les topologies permises par le bus CAN. Enfin dernier point, le coût de la réalisation est modique. La réalisation d’un detector ou d’un Gateway m’est revenue à 7€.