Auteur Sujet: Le bus CAN  (Lu 80529 fois)

Jean-Luc

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 1713
    • Voir le profil
Le bus CAN
« le: février 03, 2015, 04:16:53 pm »
Nous avions démarré en interne une discussion sur le bus CAN qui a débouché sur la réalisation d'un module permettant d'expérimenter le bus sur un Arduino. La carte réalisée est visible sur le fil voisin : BreakoutBoard CAN. Ce développement a été entrepris car les shields CAN sont mystérieusement hors de prix (20€ et plus). 

Le bus CAN est déjà utilisé dans notre hobby. Il y a tout d'abord le MERG (Model Electronic Railway Group). Organisation anglaise qui a choisi le CAN pour interconnecter les modules qu'elle développe. L'explication est donnée ici : http://www.merg.org.uk/merg_resources/cbus.php

Ensuite il y a Zimo qui a choisi le bus CAN comme bus de rétro signalisation : http://www.zimo.at/web2010/aboutus/highlights_EN.htm

Enfin il y a CAN Digital Bahn : http://www.can-digital-bahn.com/news.php

J'utilise moi même le CAN sur mon réseau (2 bus en fait, l'un à 125kb et l'autre à 700kb+)

Des informations concrètes suivront sur le site éditorial.
« Modifié: février 04, 2015, 01:28:06 pm par Jean-Luc »
Cordialement

railyRabbit

  • Newbie
  • *
  • Messages: 29
    • Voir le profil
Re : Le bus CAN
« Réponse #1 le: mai 16, 2015, 09:47:37 pm »
J'ai un doute à lire les différents post : le bus CAN intègre-t-il un système de validation / renvoi des ordres si nécessaire ?
Que se passe-t-il si l'information transmise est corrompue en chemin, ou si le destinataire ne la reçoit pas (buffer plein, pb transitoire sur la connexion...) ?

DDEFF

  • Hero Member
  • *****
  • Messages: 760
    • Voir le profil
Re : Le bus CAN
« Réponse #2 le: juillet 08, 2015, 01:48:53 pm »
Jean-Luc est pas mal occupé en ce moment.
Je peux néanmoins répondre à tes questions : oui, le bus CAN s'occupe de tout. Il a plusieurs niveaux de sécurité et renvoie les messages si nécessaire.
Je n'ai pas pu tester encore, mais c'est ce qui se fait de mieux comme bus.
"Ce n'est pas le puits qui est trop profond, c'est ta corde qui est trop courte" (proverbe chinois)

Dominique

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 3014
  • 100% Arduino et N
    • Voir le profil
Re : Le bus CAN
« Réponse #3 le: juillet 29, 2015, 08:55:22 am »
J'ai fait quelques tests entre un UNO et un MEGA:

J'ai réalisé un banc de test avec un Mega qui émet des messages CAN sur 4 IDs et 4 tailles différents toutes les 25ms ( variable en fait, avec une limite basse à 10ms) et un Uno qui les reçoit sous IT, les compte et renvoie les compteurs (message de 4 octets) au Mega.
Pour garantir la réception, les messages sont récupères dans l'IRQ du CAN dans un buffer circulaire de 256 octets et traites 1 par 1 dans la LOOP qui est assez rapide pour suivre sans problème. Le buffer ne se sature jamais.

Apparemment il faut respecter 2 règles:
- mettre le moins de traitement possible dans l'ISR, juste un flag à monter,
- vider complètement le buffer du MCP2515 à chaque LOOP si le flag est monté.

Le UNO qui teste combine le CAN, l'I2C et un timer1 pour un Watchdog. Il compte les messages reçus, les affiche et renvoie un message avec ses compteurs toutes les secondes.

J'ai mis en place le maximum de précautions:
- la surveillance de l'IRQ du 2515, avec vidage forcé si plus d'IRQ depuis 1 seconde (ça peut servir à surveiller la vie des autres modules)
- la surveillance des overflows
- un Watchdog sur la LOOP avec réinit du CAN si la librairie se bloque.

Pour le moment ça tient à l'aise les 40 messages par seconde (4 messages de taille différents repères toutes les 25 ms.)

En augmentant la cadence, je vois que ça tient encore à 100 messages par seconde.

Mais il y a quelques pertes tout de même : 6/1000 à 40 messages/sec, 17/1000 à 100 messages/sec, 1/1000 à 20 messages/sec.

Il faut donc utiliser des messages Accusés de Réception au niveau application, notamment pour confirmer l'exécution d'une commande
Cordialement,
Dominique

Dominique

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 3014
  • 100% Arduino et N
    • Voir le profil
Re : Le bus CAN
« Réponse #4 le: juillet 29, 2015, 05:42:10 pm »
Mise à jour :

La cause des pertes de message résidait dans le test de non réception (plus d'IRQ pendant 1seconde) dont le traitement consistait à vider le tampon du 2515 pour lui permettre de générer à nouveau un IRQ.
En supprimant ce vidage forcé, les pertes de messages ont disparu !

Néanmoins, avec toutes les tortures qui je lui fais subir, il reste une perte de 1 message sur 10.000 environ.
Cordialement,
Dominique

Dominique

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 3014
  • 100% Arduino et N
    • Voir le profil
Re : Le bus CAN
« Réponse #5 le: août 19, 2015, 07:36:04 pm »
J'irai même jusqu'à dire que je prépare un article pour la mise en oeuvre "à coup sûr" du bus CAN, avec la carte Locoduino qui fonctionne à merveille !

J'ai réalisé ma carte de commande des aiguilles et des dételeurs, + quelques capteurs (RFID, infrarouge et Hall notamment), ainsi qu'une carte de test pour valider tous les scenarii de communication.

A bientôt pour lire le résultat ...
Cordialement,
Dominique

DDEFF

  • Hero Member
  • *****
  • Messages: 760
    • Voir le profil
Re : Le bus CAN
« Réponse #6 le: août 22, 2015, 12:44:59 pm »
Salut Dominique,
J'attends le résultat avec impatience !  :D
Mon programme fait tous les calculs en 6 ms
A 300 km/h, un TGV fait environ 1 cm/10 ms au 1/87.
Je pense qu'il ne faut pas dépasser 10 ms pour récupérer les infos du réseau via le CAN.
"Ce n'est pas le puits qui est trop profond, c'est ta corde qui est trop courte" (proverbe chinois)

Dominique

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 3014
  • 100% Arduino et N
    • Voir le profil
Re : Le bus CAN
« Réponse #7 le: août 22, 2015, 04:11:03 pm »
Bonjour Denis,

Effectivement un TGV à 300 km/h ça fait pas loin d'1 mètre par seconde au 1/87eme.
Comme ton TGV mesure pas loin d'1m de longueur en HO, la longueur de tes cantons (supérieure au train le plus long) fait donc au moins 1 m.
Donc tu auras au maximum une détection de passage par seconde pour ce TGV. Et vu que c'est un TGV a pleine vitesse, tu lui donneras la priorité la plus haute pour les traitements.

Si ton traitement total dure 6 ms, il reste 94 ms pour piloter les trains, les aiguilles, les signaux, le décor et même le café du chef de gare.

Bon, blague à part, un seul Arduino pourrait faire le job s'il n'y a pas trop de trains en mouvement.

Maintenant, avec le CAN a 500 kb/s , les temps de transmission seront largement inférieurs au temps de détection et au temps de traitement (tes 6 ms).

De toute façon le problème ne peut pas se poser de façon aussi simple : Il y a des tas de temps de latence à compter comme, par exemple, le temps d'exécution d'un changement d'aiguille qui nécessite d'attendre l'accusé d'exécution (la fin de l'impulsion de commande du moteur dans mon cas). Imagines des enclenchements multi-aiguilles !.

Cela veut dire que chaque cycle de détection/action devra prendre plusieurs tours de LOOP dans le Central, et que l'organisation du logiciel multiprocesseur deviendra une affaire sérieuse ;)

Je reviendrai sur cette question intéressante un peu plus tard car ça concerne aussi l'architecture logicielle.

Cordialement,
Dominique

Dominique

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 3014
  • 100% Arduino et N
    • Voir le profil
Re : Le bus CAN
« Réponse #8 le: août 22, 2015, 06:20:06 pm »
Voici une "recette de cuisine" pour la mise en oeuvre rapide du bus CAN.

Au dela de l'installation et de la mise en oeuvre de la carte CAN Locoduino, j'ai mis au point une gestion de mémoire tampon circulaire dans laquelle les messages sont enregistrés aussitôt que possible, et exploités ensuite tranquillement par la fonction LOOP.
Comme cela, on est certain de ne rien perdre !

Le matériel se compose d'une carte Arduino (par exemple ici un Mega2560) et d'une carte CAN Locoduino.
On commence par relier à la carte CAN les broches du bus SPI du Mega : 50 (MISO), 51 (MOSI), 52 (SCK), 53 (SS : chip select CAN), ainsi que le +5V et le 0V (Gnd).
Ajoutons une liaison entre la broche INT (interruption) de la carte CAN et la broche 2 (Interruption 0) de l'Arduino.

Il faut aussi télécharger une bibliothèque qui se trouve ici : https://github.com/Seeed-Studio/CAN_BUS_Shield
Puis il faut placer le dossier téléchargé dans le dossier des autres bibliothèques (voir l'article "Installer un bibliothèque").

Ensuite on peut placer ces 2 lignes en tête de programme :

#include <SPI.h>                 // pour la bibliotheque CAN
#include "mcp_can.h"             // bibliotheque CAN

Puis il faut créer l'objet CAN comme le permet la bibliothèque :

// variables globales pour l'interface CAN
MCP_CAN CAN(53);                // Definition du CS (chip select) pin 53 (SS du bus SPI)
unsigned char Flag_Recv = 0;    // variable d'échange avec l'interruption IRQ

Ainsi, on le voit, qu'une variable "Flag_Recv" qui servira à faire savoir à la LOOP qu'un ou plusieurs messages sont arrivés "sous interruption".

Cette variable est positionnée par la routine d'interruption suivante :

/*
 *  ISR CAN (Routine de Service d'Interruption)
 *  le flag IRQ monte quand au moins un message est reçu
 *  le flag IRQ ne retombe QUE si tous les messages sont lus
 */

void MCP2515_ISR()
{
     Flag_Recv = 1;
}

Après avoir placé ces lignes de code en tête de programme, abordons le SETUP dans lquel on insère les lignes suivantes :

  /* -----------------------------------------------------
  *                       SETUP
  * -----------------------------------------------------
  */

  /////////////// INIT CAN /////////////////
 
START_INIT:

  if(CAN_OK == CAN.begin(CAN_500KBPS))       // initialisation du can bus : baudrate = 500k
  {
    Serial.println(F("CAN BUS init ok!"));
  }
  else
  {
    Serial.println(F("CAN BUS init echec !"));
    Serial.println(F("Init CAN BUS a nouveau"));
    delay(200);
    goto START_INIT;
  }


On comprend bien ici que l'instruction CAN.begin(baudrate) démarre l'interface, avec un compte-rendu "CAN_OK", sinon cela se répète car, à ce stade de l'initialisation (SETUP), si le bus CAN ne démarre pas, il est inutile d'aller plus loin.

Personnellement je n'ai jamais vu d'échec sauf si la carte CAN n'est pas (ou est mal) branchée .

Puis il faut "attacher" l'interruption 0 à la routine MCP2515_ISR() précédente :

  attachInterrupt(0, MCP2515_ISR, FALLING); // interrupt 0 (pin 2)

Enfin on définit les filtres CAN qui limiteront les messages reçus à seulement ceux qui intéressent cette réalisation :

  /*
   * set mask & filter
   */
   
  CAN.init_Mask(0, 0, 0x3ff);               // there are 2 mask in mcp2515, you need to set both of them
  CAN.init_Mask(1, 0, 0x3ff);               // a preciser
   
  CAN.init_Filt(0, 0, 0x03);                // Reception possible : Id 03 (hex)
  CAN.init_Filt(1, 0, 0x04);                // Reception possible : Id 04 (hex)
  CAN.init_Filt(2, 0, 0x30);                // Reception possible : Id 30 (hex)
  CAN.init_Filt(3, 0, 0x40);                // Reception possible : Id 40 (hex)
  CAN.init_Filt(4, 0, 0x31);                // Reception possible : Id 31 (hex)
  CAN.init_Filt(5, 0, 0x41);                // Reception possible : Id 41 (hex)

Le SETUP ayant mis en place tous les acteurs, la LOOP peut commencer son travail répétitif !

/*-----------------------------------------------------
 *                        LOOP
 *-----------------------------------------------------                       
 */

void loop()
{

  if (Flag_Recv)  {
    Flag_Recv = 0;  // Flag MCP2515 pret pour un nouvel IRQ
    CAN_recup();    // récupération  et traitement du ou des messages CAN reçus
  }


et la fonction CAN_recup() est là :

// Message recu
byte IdR;                       // Id pour la routine CAN_recup()
unsigned char lenR = 0;         // Longueur "    "       "
unsigned char bufR[8];          // buffer reception      "
// Message emis
unsigned char bufS[4];          // buffer emission

// Memoire circulaire pour le stockage rapide des messages recus
unsigned char _Circule[256];    // recepteur circulaire des messages CAN sous IT
int _indexW, _indexR, _Ncan;    // index d'ecriture et lecture, nb d'octets a lire
byte _CANoverflow = 0;          // flag overflow (buffer _Circule plein)

/*
 * Routine de récuperation des messages CAN dans la memoire circulaire _Circule
 * appelee par LOOP lorsque Flag_Recv = 1;
 */
 
void CAN_recup()
{
  unsigned char len = 0;                // nombre d'octets du message
  unsigned char buf[8];                 // message
  unsigned char Id;                     // Id

  while (CAN_MSGAVAIL == CAN.checkReceive())  {
    CAN.readMsgBuf(&len, buf);          // read data, len: data length, buf: data buf
    Id = CAN.getCanId();
    if ((_Ncan+len+2) < sizeof(_Circule))  { // il reste de la place dans _Circule
      _Circule[_indexW] = Id;           // enregistrement de Id
      _indexW++;
      _Ncan++;
      if (_indexW == sizeof(_Circule))  {_indexW = 0;}
      _Circule[_indexW] = len;          // enregistrement de len
      _indexW++;
      _Ncan++;
      if (_indexW == sizeof(_Circule))  {_indexW = 0;}
      for (byte z = 0; z<len; z++)  {
        _Circule[_indexW] = buf[z];      // enregistrement du message
        _indexW++;
        _Ncan++;
        if (_indexW == sizeof(_Circule))  {_indexW = 0;}
      }
    } else {
      _CANoverflow = 1;  // depassement de la capacite de Circule
    }
  }
}


Dans Loop, récupérer un message, en parfaite indépendance de leur réception se fait ainsi :

  // traitement d'un seul message par loop dans la memoire circulaire _Circule
   
  if (_Ncan > 2)  {                 // messages dans _Circule : au moins 3 bytes
    _Ncan--;
    RId = _Circule[_indexR];        // recup Id
    _indexR++;
    if (_indexR == sizeof(_Circule))  {_indexR = 0;}
    _Ncan--;
    Rlen = _Circule[_indexR];       // recup longueur
    _indexR++;
    if (_indexR == sizeof(_Circule))  {_indexR = 0;}
    if (_dumpCan)  {               // _dumpCan est un flag qui permet de "sortir" les messages ou non
      Serial.print("CAN id ");
      Serial.print(RId);
      Serial.print(", data ");
    }
    for (int k = 0; k < Rlen; k++)  {
      _Ncan--;
      Rbuf[k] = _Circule[_indexR];  // recup octets message
      _indexR++;
      if (_indexR == sizeof(_Circule))  {_indexR = 0;}
      if (_dumpCan)  { 
      Serial.print("0x");
      Serial.print(Rbuf[k], HEX);
      }
    } // le message est dans les globales RId, Rlen et Rbuf[..]
    Serial.println();
 

Voilà que ce je dois expliquer…

Cordialement,
Dominique

DDEFF

  • Hero Member
  • *****
  • Messages: 760
    • Voir le profil
Re : Le bus CAN
« Réponse #9 le: août 23, 2015, 09:53:21 am »
Formidable !!
J’essaierai bien, mais je fais partie de ceux qui n'ont pas eu les breakouts de Jean-Luc  :'(
Je pense que si on utilise un DUE, on peut éviter de mettre des F(...).
C'est très clair et bien expliqué. Merci.

Concernant mes "temps TGV", je pense surtout aux aiguilles que je gère en PRS, avec détection du train pour chaque aiguille.
Donc, des zones de 20 cm en HO.
Mais tu as raison, il faut adapter l'architecture du programme pour que ce soient les interruptions qui mettent à jour les différentes "tables".
Ce que je note, c'est que ce n'est pas le chrono qui va nous bloquer.
"Ce n'est pas le puits qui est trop profond, c'est ta corde qui est trop courte" (proverbe chinois)

Dominique

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 3014
  • 100% Arduino et N
    • Voir le profil
Re : Le bus CAN
« Réponse #10 le: août 23, 2015, 04:06:15 pm »
J'ai démarré la rédaction de l'article 130.
Cordialement,
Dominique

DDEFF

  • Hero Member
  • *****
  • Messages: 760
    • Voir le profil
Re : Le bus CAN
« Réponse #11 le: août 23, 2015, 08:24:47 pm »
Je pense que j'ai compris l'essentiel de ton programme et ça me rassure.
J'ai près de 60 aiguilles et donc, ça fera 2 octets à traiter, mais ça ne change rien à la logique.
L'idée de la mémoire circulaire est bonne, à mon avis. Voir sa taille en fonction du réseau, si nécessaire.
Vivement que je puisse avoir des breakouts !!
Ce sera avec un DUE et je vais réfléchir a l'intégration de tout ça dans mon programme.
Mais l'horizon se dégage  :D
Merci Dominique
"Ce n'est pas le puits qui est trop profond, c'est ta corde qui est trop courte" (proverbe chinois)

Dominique

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 3014
  • 100% Arduino et N
    • Voir le profil
Re : Le bus CAN
« Réponse #12 le: août 23, 2015, 09:52:39 pm »
Sur le fil d'à coté (BoB CAN), le cas du DUE est abordé. Il ne faut y ajouter que le 2551 car un contrôleur est déjà intégré. La carte CAN Locoduino peut se connecter à un DUE via les connecteurs ad'hoc. Mais je n'ai pas encore essayé.

Ça m'intéresse et j'ai déjà 2 DUE qui attendent.

On peut, bien-entendu, envisager une petite production de cartes en accord avec Jean-Luc, et en le déchargeant des tâches ingrates, notamment l'appro des composants. Il faudra de toute façon en faire un minimum.
Cordialement,
Dominique

Jack56

  • Newbie
  • *
  • Messages: 10
    • Voir le profil
Re : Le bus CAN
« Réponse #13 le: novembre 28, 2015, 02:46:57 pm »
Bonjour,

Tout d’abord j’espère avoir posté ma question dans le bon fil.

J’ai vu sur le net des cartes SPI-CAN mais avec des quartz à 8 MHz ou 20 MHz du coup en regardant le code de la CAN_BUS_Shield j’ai remarqué qu’il y avait des constantes (MCP_16MHz_200kBPS_CFG1, MCP_16MHz_200kBPS_CFG2, …) qui permettent de configurer les registres du MCP2515 CNF1, CHF2 et CNF3. Cependant ces constantes sont déterminées pour une FOSC = 16 MHz.

J’ai essayé de trouver la logique pour adapter ces constantes à d’autres fréquences, mais j’avoue ne pas en avoir trouvée. J’ai bien sûr regardé le datasheet du 2515, mais j’avoue avoir du mal avec les subtilités de l’anglais. :-[

Y-a-t-il une logique pour déterminer BRP, PRSEG, PHSEG1, PHSEG2 en fonction(FOSC, BitRate) afin de pouvoir paramétrer correctement les registres CNF1, CNF2 et CNF3.

Merci d'avance

Dominique

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 3014
  • 100% Arduino et N
    • Voir le profil
Re : Le bus CAN
« Réponse #14 le: novembre 28, 2015, 03:05:49 pm »
Bonjour Jack,

Cette carte : "MCP2515 CAN Bus Module TJA1050"

http://www.ebay.fr/itm/311379482437

contient un quartz à 8Mhz
Vous pouvez le dessouder (il faut une bonne tresse à dessouder) et mettre un quartz de 16 MHz à la place.

Je l'ai testé et ça marche avec la bibliothèque CAN préconisée.

Cordialement
Dominique
Cordialement,
Dominique