--- title: "Communication CAN" date: "30-12-2022" link: "https://hackmd.io/WtYb7Yl2RWy5PWPDJITdcA" tags: EVOLUTEK --- [TOC] # Introduction Lorsque nous avons décidé de faire évoluer l'architecture électronique des robots afin de la moderniser et de la rendre modulable, il a été question d'incorporer un bus de communication central dans le robot afin de pouvoir facilement échanger des informations entre les différents composants du robot. Notre choix s'est porté sur le **bus CAN**. Ce document va vous présenter ce qu'est le **bus CAN** et de quelle façon nous allons l'utiliser pour communiquer au sein du robot. # Présentation du bus CAN Le **bus CAN** est un bus de données **série bidirectionnel half-duplex** très présent dans le domaine de l'automobile. Il est aussi présent dans l'aéronautique. ![](https://i.imgur.com/C67esfI.png) Chaque appareil connecté sur le bus est appelé **nœud**. Chaque nœud peut communiquer avec tous les autres et il n'y a pas de **Master** en charge de cadencer le bus. La longeur maximale du bus est déterminée par la vitesse utilisée, plus la vitesse est élevée (1Mbit/s max), plus la longeur est réduite. ![](https://i.imgur.com/R33yBsG.png) Chaque **nœud** est connecté au bus par deux signaux, **CANH** et **CANL** qui sont deux signaux différentiels. Ces deux signaux doivent être connecté à un **transciever** qui sera en charge de la conversion pour l'appareil. À Evolutek, nous utilisons un [MCP2551](https://ww1.microchip.com/downloads/en/devicedoc/20001667g.pdf) de chez Microchip. Le bus a besoin d'être rebouclé à chaque extrémité par des résistances de 120 Ω. Une **trame CAN** suit ce format : ![](https://i.imgur.com/jMRsqLu.png) * SOF : bit dominant indiquant le début de trame * Champ d'abritrage (12 ou 32 bits) : identifiant de la trame * Champ de commande (6 bits) : stocke le mode de la trame et la taille du champ de données * Champ de données (jusqu'à 64 bits) : permet de contenir des données * Champ de CRC (16 bits) : stocke le CRC de la trame * ACK (2 bits) : champ de confirmation de la reception de la trame * EOF (7 bits) : fin de trame Pour en savoir plus : [Bus CAN - Wikipedia](https://en.wikipedia.org/wiki/CAN_bus). # Topologie du réseau CAN du robot Voici un schéma du **réseau CAN** minimum qui sera mis en place dans le robot : ![](https://i.imgur.com/XazcNYF.png) Ce réseau se composera de plusieurs éléments distincs : * **Asserv** (*Slave*) : carte en charge de l'asservissement du robot * **Acts** (*Slave*): carte en charge des actionneurs du robot * **Raspberry Pi** (*Master*) : mini ordinateur en charge des décisions dans le robot * **Debug** (*Observer*) : port de debug du réseau CAN du robot permettant de se connecter via un dongle USB * **MDB** (*Slave*) : carte en charge de la détection des robots dans le mât de balise On peut observer trois types de nœud : * **Master** : Appareil contrôlant l'ensemble des **Slaves** du réseau * **Slave** : Appareil obéissant aux ordres du **Master** * **Observer** : Appareil permettant d'observer le bus CAN et d'intervenir pour du debug Un **Slave** pourrait être amené à échanger des informations avec un autre **Slave**. ## Fonctionnement du master Le **Master** a pour rôle de contrôler la communication sur le **réseau CAN** et de vérifier le bon fonctionnement de l'ensemble des **Slaves**. Ses tâches sont multiples : * Configurer les **Slaves** * Vérifier que les **Slaves** sont toujours en running (via leurs heartbeats) * Emettre un heartbeat en continu * Envoyer des commandes aux **Slaves** * Traiter les messages envoyés par les **Slaves** ## Fonctionnement d'un slave Un **Slave** sur le réseau CAN suit un fonctionnement que l'on peut résumer via une machine à état : ![](https://i.imgur.com/Fibl02u.png) * *Init* : Le **Slave** fait son init minimale * *Wait for reset* : Le **Slave** attend de recevoir un reset de la part du **Master** * *Config* : le **Slave** reçoit sa configuration nécessaire de la part du **Master** * *Run* : le **Slave** execute sa boucle de fonctionnement classique tant qu'il reçoit un **heartbeat** de la part du master (timeout à définir) * *Timeout* : si el **Slave** ne reçoit plus de **heartbeat** de la part du **Master**, il retourne en *Wait for reset* > Un **Salve** ne va rien emettre sur le **bus CAN** tant que le **Master** ne l'a pas configuré. # Description du protocole FIXME ## Identifiant des trames L'identifiant des trames CAN (29 bits de long) sera composé de : | Trame | Priority | Message type | Message id | Dst board type | Dst Board id | Src board type | Src board id | Tracking | |:--------------:|:--------:|:------------:|:----------:|:--------------:|:------------:|:--------------:|:------------:|:--------:| | Index | 0 | 1 | 3 | 9 | 13 | 17 | 21 | 25 | | Nombre de bits | 1 | 2 | 6 | 4 | 4 | 4 | 4 | 4 | | Min-Max | 0-1 | 0-3 | 0-63 | 0-15 | 0-15 | 0-15 | 0-15 | 0-15 | ## Description des champs ### Priority (2 bits): Ces deux bits permettent de définir la priorité du message : * 0x00 : Priorité haute * 0x01 : Priorité basse ### Message type (2 bits) : Ce champ indique le type de message : * 0x00 : Error * 0x01 : Status * 0x02 : Command * 0x03 : Heartbeat & Ack ### Message id (8 bits) : Ce champ indique l'id de message. FIXME : liens vers listes des ids ### Board type (5 bits) : Ce champ indique le type de carte auquel on s'adresse : * 0x0 : Toutes les cartes * 0x01 : Raspberry Pi * 0x02 : Mât de balise * 0x03 : Actuators * 0x04 : Asserv ### Board id (5 bits): Ce champ indique l'identifiant de la carte à laquelle on s'adresse. À noter que 0x0 est reservé pour s'adresser à toutes les cartes du même type. ### Tracking (7 bits): Ce champ permet de stocker le numéro de l'échange en cours entre deux cartes. Chaque appareil possède son propre numéro de tracking. Ce numéro est incrémenté par un appareil à chaque nouvel échange. Une fois le maximum atteint (0x7F), le numéro de tracking repasse à 0x0. ## Modèles d'échanges La communication CAN se fera via trois modèles d'échanges distincts : * Les **commandes simples** * Les **commandes multiples** * Les **Status** indépendants / **Emergencies** / **Heartbeats** > En cas de **broadcast** (par type ou id) de commandes / status, les noeuds réceptrices ne doivent pas **acknowledge** les messages. ### Commande simple Ce type d'échange suit un déroulement simple : * Une noeud émet une commande vers une autre noeud * La noeud qui reçoit la commande acknowledge * La noeud peut émettre en retour un ou plusieurs status * La noeud qui reçoit les status doit les acknowledge > Seulement le **Master** à la possibilité d'envoyer des commandes en **broadcast** à l'ensemble du réseau. ![](https://i.imgur.com/5jQAqa9.png) ### Commandes multiples Ce type d'échange est similaire au précédent à la différence près que la noeud émetteur va envoyer un ensemble de commande l'une après l'autre. ![](https://i.imgur.com/jl23hHH.png) ### Status indépendant / Emergency / Heartbeat Ce type d'échange permet à un noeud de pouvoir envoyer une information à un autre noeud ou à l'ensemble du réseau. ![](https://i.imgur.com/f2vQvXK.png) > A noter que le **Heartbeat** n'est pas à acknowledge. # Configuration du réseau FIXME ## Bus EVO Afin de pouvoir facilement rajouter des modules dans le robot, un bus dédié a été créé. Ce nouveau bus, nommée *bus EVO* incoropore : * Des tensions logiques (12V, 5V et 3.3V) * Le *bus CAN* * Des signaux de contrôles (RESET et /AU) Pinout du connecteur : ![](https://i.imgur.com/isKqBlK.png) Le connecteur pour interconnecter les cartes est un WR-MM 10 pins de chez Würth Elektronik ([Lien](https://www.we-online.com/en/components/products/MM_1_27_SMT_FEMALE_CONNECTOR_WITH_POLARIZATION_69036728XX76#690367281076)). Ce bus permettra aux modules connectés de pouvoir intéragir avec le reste du robot. ### Carte DBE Une carte permettant de facilement se connecter au Bus Evo a été réalisée. Cette carte permet de facilement alimenter une carte et s'y connecter via un bus CAN. La carte DBE incorpore des boutons pour simulter le comportement du Reset et de l'AU du robot. ![](https://hackmd.io/_uploads/rkE6Xd40h.png) ## Dongle USB Nous utilisons un Dongle USB **CANine** (https://tinymovr.com/products/can-bus-adapter?variant=42038628286617)() pour nous connecter sur le **bus CAN** et pouvoir débugger. Ce dongle peut-être configuré pour activer la resistance de terminaison mais aussi alimenté le bus en **5V**. ### Prérequis Le dongle a besoin d'avoir le driver **slcan** activé. ### Configuration Pour configurer le dongle pour avoir accès au réseau **CAN** : ``` $ dmesg | grep tty // permet d'identifier le numéro de device $ sudo slcand -o -c -s6 /dev/ttyACMn can0 // Avec n de ACMn le numéro de device $ sudo ip link set up can0 $ candump can0 // Pour tester ``` ## STM32 (CAN natif) Presque tous les **STM32** possède au moins un **bus CAN** en natif. ### Prérequis Vérifier que la stm32 que vous manipulez possède bien un bus can fonctionnel et en vérifier les pins. ### Configuration Pour configurer votre stm32 pour le can, allez dans votre .ioc dans la catégorie "Pinout & Configuration" et cherchez "can". Activez-le. Ensuite, allez dans la sous-catégorie "NVIC Settings" et activez l'interrupt RX0 si vous voulez pouvoir recevoir des messages. ![](https://cdn.discordapp.com/attachments/423923693194969090/1095789280074403900/Capture_decran_2023-04-12_211247.png) Éventuellement, vous pouvez vérifier que les pins soient les bons et que le baud rate (trouvable dans la sous-catégorie "Parameter Settings") soit bien à 500 000 bits/s mais normalement CubeMX s'en charge très bien tous seul de ça. ### Filtres Les filtres sont une part très importante du protocole CAN. En effet, ils permettent de ne pas recevoir les messages ne nous concernant pas sans surcharger le processeur. Un filtre CAN est essentiellement composé de deux choses: un ID et un masque. Comme nous utilisons les identifiants étendus, l'id fait 29 bits et le masque s'appliquant dessus fait aussi 29 bits. Le masque est une valeur qui s'applique bit à bit sur l'ID ce qui signifie que lorsqu'une requête est reçue, chaque filtre va comparer les bits de son id dont le bit équivalent du masque est à 1 à celui de même emplacement dans l'id de la requête reçue. En d'autres termes, on cherche à ce que: (id_filter && mask_filter) == (id_received && mask_filter) ## Raspberry Pi (Linux avec CAN via SPI) Les cartes **Raspberry Pi** ne possède pas de **CAN** natif, il faut donc l'interfacer via un contrôleur. Sur nos robots, nous interfaçons la **Raspberry Pi** avec le **bus CAN** via un contrôleur **SPI-CAN**, un **MCP2515** de chez **Microchip** (https://ww1.microchip.com/downloads/en/DeviceDoc/MCP2515-Stand-Alone-CAN-Controller-with-SPI-20001801J.pdf) couplé à un **transciever CAN MCP2551** (https://www.microchip.com/en-us/product/MCP2551). Il est possible de trouver des cartes d'interface pour tester la communication comme celle-ci : https://www.amazon.fr/AZDelivery-r%C3%A9cepteur-protocole-d%C3%A9veloppement-compatible/dp/B086TXSFD8/ref=sr_1_1_sspa. ### Prérequis Aucun ### Configuration Le **Linux** embarqué sur la **Raspberry Pi** propose déjà un driver via le bus **SPI** pour configurer le contrôleur et ajouter un **bus CAN** directement comme bus réseau. Pour cela, il suffit d'éditer le fichier */boot/config.txt* (avec précaution) et rajouter : ``` dtparam=spi=on dtoverlay=spi-bcm2835-overlay dtoverlay=mcp2515-can0,oscillator=16000000,interrupt=25 ``` Il suffit ensuite de rédemarrer la carte. Pour vérifier que tout s'est bien passé, vous pouvez faire : ``` $ dmesg | grep -i spi $ dmesg | grep -i can ``` Pour configurer le **bus CAN** de la carte, il suffit de : ``` $ sudo ip link set can0 type can bitrate 500000 $ sudo ip link set can0 up $ candump can0 // Pour tester ``` Pour que le *bus CAN* soit configuré au démarrage, il suffit de rajouter dans */etc/network/interfaces* : ``` auto can0 iface can0 can static bitrate 250000 ``` > Marche pas faut refaire les deux commandes sudo ip à chaque passage ## Virtuel (Linux) Linux permet de facilement setup un **bus CAN** virtuel. ### Prérequis Aucun ### Configuration Pour configurer un **bus CAN** virtuel : ``` $ modprobe vcan $ sudo ip link add dev vcan0 type vcan $ sudo ip link set up vcan0 $ candump vcan0 // Pour tester ``` > Comment on set la vitesse ? # Ressources utiles * Explication détaillée du **bus CAN** : https://en.wikipedia.org/wiki/CAN_bus * Connecteur du **BUS EVO** : https://www.we-online.com/en/components/products/MM_1_27_SMT_FEMALE_CONNECTOR_WITH_POLARIZATION_69036728XX76#690367281076 * Dongle USB **CANine** : https://tinymovr.com/products/can-bus-adapter?variant=42038628286617 * Documentation **CANine** : https://canine.readthedocs.io/en/latest/canine.html#hardware-configuration * Repo Github du Dongle : https://github.com/tinymovr/CANine * **Can-utils** : https://github.com/linux-can/can-utils * **MCP2515** : https://ww1.microchip.com/downloads/en/DeviceDoc/MCP2515-Stand-Alone-CAN-Controller-with-SPI-20001801J.pdf * **MCP2551** : https://www.microchip.com/en-us/product/MCP2551 * Carte d'interface **CAN** : https://www.amazon.fr/AZDelivery-r%C3%A9cepteur-protocole-d%C3%A9veloppement-compatible/dp/B086TXSFD8/ref=sr_1_1_sspa # Idées intéressantes * Le slave pourrait envoyer un status au démarrage pour indiquer qu'il est prêt à être reset / config * Comment gérer la partie bootloader ? * Avoir un câble de debug pour le dongle USB sur le bus EVO