Image of an arrow

RS-485 sur BeagleBone : coup d’oeil sur l’UART d’OMAP

Avatar

pproulx

imageIl y a quelques semaines, j’ai publié un billet démontrant l’importance d’une solution en temps réel pour le support du standard RS-485 sur BeagleBone. Bien qu’il soit possible d’éliminer d’emblée la solution existante de l’UART d’OMAP (le type d’UART présent et supporté sur le SoC des BeagleBone) tel que mentionné dans l’article, j’ai quand même décidé d’y jeter un coup d’oeil.

Je me suis intéressé à la façon dont les auteurs ont décidé d’implémenter le support RS-485, nécessairement du côté pilote, ce support remontant tout juste à la mi-août. Puis, j’ai trouvé un petit bogue de performance qui concerne les fins de transmissions qui représentent une étape cruciale dans l’implémentation du protocole RS-485. J’ai donc soumis un correctif au noyau de Linux; il a été accepté la semaine passée.

Ce billet assez technique devrait donc intéresser les ingénieurs avides du noyau Linux et du standard RS-485.

Analyse du code

En lisant rapidement la nouvelle version de omap-serial.c après l’ajout du support RS-485, j’ai remarqué que plusieurs propriétés concernant RS-485 peuvent maintenant être ajoutées aux noeuds des UART d’OMAP. Parmi celles-ci, rs485-rts-delay a retenu mon attention : en passant en diagonale dans le code, j’ai vu des appels à mdelay qui utilisent les valeurs de cette propriété (un délai avant de transmettre et un après une transmission).

J’ai d’abord pensé qu’il s’agissait là d’une stratégie pour s’assurer d’une transmission complètement terminée : attendre un délai fixé d’avance une fois une interruption de tampon circulaire (FIFO) d’envoi vide reçue. Mais en scrutant plus attentivement le code, j’ai trouvé qu’il s’agit d’un temps d’attente supplémentaire, à ajouter avant et après une transmission, alors que la fin de transmission est connue d’une autre façon. On peut donc fixer les deux valeurs de rs485-rts-delay à 0 pour atteindre un délai de redressement le plus court possible. En fait, les valeurs de cette propriété sont directement données aux membres delay_rts_before_send et delay_rts_before_send d’une instance de struct serial_rs485, une structure définie par le sous-système sériel en soi.

Le signal de direction des données est assuré par une ligne GPIO réservée par le pilote.

En somme, voici le mécanisme que j’ai décelé lorsque le support RS-485 est activé :

  • Lorsque le sous-système sériel de Linux demande au pilote de commencer une transmission, celui-ci active les interruptions de FIFO d’envoi (ou interruptions THR, pour Transmitter Holding Register). Il active également le signal GPIO de direction des données pour prendre le bus RS-485.
  • L’UART émet une interruption THR tant et aussi longtemps que sa FIFO d’envoi contient 32 caractères ou moins.
  • Lorsque le pilote traite une interruption THR, il ajoute jusqu’à 16 nouveaux caractères, montant potentiellement le niveau de cette FIFO à 48.
  • Si le pilote, lors d’une interruption THR, n’a plus rien à fournir à l’UART (aucun nouveau caractère fourni par l’utilisateur), il vérifie si la transmission est terminée (un bit de statut est dédié à cette condition), et :
    • si la transmission n’est pas terminée, il laisse l’interruption THR activée;
    • si la transmission est terminée, il désactive l’interruption THR et désactive le signal GPIO de direction des données afin de relâcher le bus RS-485.

En se rappelant que l’UART émet une interruption THR aussitôt que sa FIFO d’envoi contient 32 caractères, on constate que le pilote se trouve à scruter le bit indiquant la fin d’une transmission, et ce pendant la durée de transmission de 33 caractères (puisqu’aussitôt que la FIFO d’envoi atteint 32 caractères, c’est qu’il y en a un autre en cours de transmission). En effet, en ne désactivant pas l’interruption, elle est relancée aussitôt puisque la FIFO d’envoi est en train de se vider. À 9600 bauds (avec 1 stop bit et sans parité), on parle d’un blocage d’environ 34 ms pendant lesquelles le microprocesseur ARM est en constant traitement d’interruptions à chaque fin de transmission, évènement qui arrive souvent dans les applications typiques de RS-485, tel que les systèmes d’automation industrielle.

Vérifions ces suppositions physiquement.

Préparation d’un premier test

Dans mon fichier DTS, j’ai spécifié aucun délai additionnel avant et après une transmission. J’ai aussi demandé l’activation de RS-485 dès le chargement du pilote. Cette activation peut aussi se faire par la suite du côté utilisateur avec un appel à ioctl. Comme un premier UART (nommé UART0) est utilisé pour la console, je teste avec l’UART1 (la puce AM335x comprend 6 UART identiques) :

uart1: serial@48022000 {
    pinctrl-names = "default";
    pinctrl-0 = <&uart1_pins>;

    /* Aucun délai supplémentaire avant/après transmission */
    rs485-rts-delay = ;

    /* Broche 17 de GPIO1 pour direction des données */
    rts-gpio = <&gpio1 17 GPIO_ACTIVE_HIGH>;

    /* Broche 16 de GPIO1 pour tester */
    test-gpio = <&gpio1 16 GPIO_ACTIVE_HIGH>;

    /* Activer RS-485 */
    linux,rs485-enabled-at-boot-time;

    status = "okay";
};

Le symbole uart1_pins fait référence à cette configuration de multiplexage des broches :

uart1_pins: pinmux_uart1_pins {
    pinctrl-single,pins = <         0x180 (PIN_INPUT_PULLUP | MUX_MODE0)    /* uart1_rxd */         0x184 (PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* uart1_txd */         0x044 (PIN_OUTPUT_PULLDOWN | MUX_MODE7) /* gpio1_17 */         0x040 (PIN_OUTPUT_PULLDOWN | MUX_MODE7) /* gpio1_16 */     >;
};

La broche 17 du contrôleur GPIO1 est disponible sur le port d’extension P9, juste à côté de la sortie Tx de l’UART1, sur le BeagleBone blanc que j’utilise pour tester (broches 23 et 24 du port d’extension). Afin de vérifier la fréquence des interruptions de FIFO d’envoi, j’ai également ajouté au tout une autre sortie GPIO (broche 16 de GPIO1, donc broche 15 du port d’extension) qui émettra une très courte pulsation à chaque appel à transmit_chars (qui est appelée lors d’une interruption THR).

Expansion Header P9 Pinout Table

J’utilise l’analyseur logique Saleae Logic pour vérifier avec précision la sortie de l’UART et le délai de redressement RS-485. Voici mon installation :

Installation de test

La sonde orange est branchée sur la sortie Tx de l’UART tandis que la sonde rouge est connectée au signal de direction des données RS-485. La sonde bleue analyse les pulsations d’interruption THR.

Maintenant, du côté utilisateur sur le BeagleBone :

# stty -F /dev/ttyO1 115200 cs8
# while true; do
    echo -n '0123456789abcdef0123456789abcdefWXYZ0123456789abcdef0123456789abcdef' > /dev/ttyO1
  done

Le temps requis d’une itération à l’autre pour ouvrir, paramétrer et fermer le port /dev/ttyO1 est suffisant pour bien délimiter chaque petite transmission. La partie WXZY de la transmission sépare ici le début de la transmission des 32 derniers caractères.

Premier test

Avec le logiciel de Saleae, je fais une lecture de 5 millions d’échantillons à 12 MHz :

Salae Logic result diagram

On remarque donc le comportement mentionné plus haut. Au tout début d’une transmission, trois interruptions ont lieu très rapidement :

    • La première ajoute 16 caractères à la FIFO d’envoi vide de l’UART. Celui-ci continue d’émettre une interruption THR parce que sa FIFO d’envoi contient moins de 33 caractères. De plus, il commence immédiatement la transmission du premier caractère, faisant passer la FIFO d’envoi à 15 caractères.
    • La deuxième ajoute encore 16 caractères, ce qui monte le niveau de la FIFO d’envoi à 31 caractères. Encore une fois, la FIFO d’envoi contient 32 caractères ou moins, donc l’UART continue l’émission d’interruption THR.
    • Enfin, la troisième ajoute 16 autres caractères et l’UART n’a plus faim pour l’instant : le compte est à 47 caractères.

La FIFO d’envoi doit alors se vider de 15 caractères avant l’émission de la prochaine interruption, ce qu’on observe avec les caractères 1 à f. Aussitôt que le caractère f est transféré de la FIFO d’envoi au registre à décalage de transmission, la FIFO passe à 32 caractères, d’où la pulsation constatée avant la transmission de f.

Ce cycle se répète et à un certain moment, seulement 4 caractères (à cause de l’ajout des 4 caractères WXYZ) peuvent être ajoutés parce que le tampon du côté pilote n’a plus rien à fournir. Ces caractères sont les cdef qui seront transmis en dernier. Lors de la prochaine interruption, avant la transmission de Z, le tampon du pilote est vide et celui-ci entre dans la boucle de scrutage mentionnée plus haut pendant la durée de transmission de 33 caractères. Les gros rectangles blancs à la fin représentent en réalité un torrent d’interruptions, plus facilement visibles lorsqu’on se rapproche :

Salae Logic result diagram

Puis, le dernier bit est transmis. Cette condition étant remplie, le pilote désactive enfin les interruptions THR et le signal GPIO de direction des données RS-485.

Comme vous pouvez le voir sur la capture d’écran ci-haut, le délai de redressement RS-485 est plutôt intéressant (beaucoup plus court que la durée d’un caractère). En scrutant constamment l’état de fin de transmission grâce au relancement de la fonction gérant l’interruption, le pilote arrive à obtenir un très petit délai entre la fin effective d’une transmission et la mise à jour du signal GPIO de direction des données.

Amélioration de omap-serial.c

Le comportement du pilote d’UART montré ci-haut peut être amélioré. L’UART d’OMAP est compatible 16C750, un type d’UART bien connu, mais il ajoute un registre de contrôle supplémentaire, SCR (Supplementary Control Register), dont le bit TMEMPTYCTLIT est particulièrement intéressant si activé : THR interrupts is generated when TX FIFO and TX shift register are empty. C’est exactement l’évènement qui nous intéresse.

Évidemment, on ne veut pas être interrompu seulement à la fin d’une transmission lorsque notre tampon du côté pilote n’est pas vide : la transmission doit être continue, sans avoir de « trou ». Il suffit donc d’activer TMEMPTYCTLIT lorsque nous recevons une interruption THR (la FIFO d’envoi a atteint le seuil) et que le pilote n’a plus rien à fournir à l’UART, ce qui signifie qu’une fin de transmission est imminente. Dans ce cas, il faut également éviter de désactiver les interruptions THR. La prochaine interruption THR devrait donc nous signaler que la transmission est complète, auquel cas nous pouvons désactiver les interruptions THR en toute quiétude.

Résultat? Constatez la différence :

Salae Logic result diagram

Si, pendant la transmission des 32 derniers caractères, une nouvelle transmission est demandée au pilote, TMEMPTYCTLIT est désactivé et cela provoque instantanément une interruption THR puisque le niveau de la FIFO d’envoi est sous les 33 caractères. Ce comportement est désirable afin d’éviter un trou entre deux transmissions consécutives indépendantes.

Bien que l’UART d’OMAP semble supporter le standard RS-485 avec un délai de redressement respectable ici, il faut garder en tête que ce n’est pas une solution temps réel pour autant. Le temps de redressement dépend toujours de la disponibilité du pilote et n’est donc aucunement garanti. Si plusieurs autres interruptions plus prioritaires sont en cours de traitement, celle du pilote de l’UART d’OMAP subira une latence plus importante et le délai de redressement RS-485 sera nécessairement plus grand.

 

  1. Bonjour
    suite a la lecture de votre article j’ai des questions a vous poser
    car je dois configuré en Pic 18F en langage C
    Merci de votre aide

Comments are closed.


Articles similaires

Image of an arrow

Savoir-faire Linux est fière d’annoncer la sortie de la version v2.3.0 de l’extension officielle du Projet Yocto pour VS Code. Lisez l’article complet en anglais. Liens et ressources Pour en savoir plus sur cette ambitieuse extension du Projet Yocto pour VS Code : Téléchargez l’extension depuis le magasin VS Code Parcourez le code, signalez des […]

Savoir-faire Linux est fière d’annoncer la sortie de la version v2.2.0 de l’extension officielle du Projet Yocto pour VS Code. Cette version majeure offre de nouvelles fonctionnalités très demandées par la communauté ! Parmi les nouveautés, la possibilité de gérer plusieurs configurations BitBake dans le même espace de travail VS Code, ou encore l’analyse des […]

[L’introduction est en français, le reste du texte en anglais]   L’économie d’énergie a toujours été une préoccupation majeure dans les systèmes embarqués, puisque par définition, ils peuvent avoir des contraintes énergétiques. Bien sûr, aujourd’hui, l’économie d’énergie est toujours au cœur des discussions. L’économie d’énergie est toujours un ensemble de compromis. En termes de disponibilité […]

L’équipe de Savoir-faire Linux est fière d’annoncer la sortie de la version v2.1 de l’extension du Projet Yocto pour VS Code. Le projet Yocto, avec le financement du Sovereign Tech Fund, a entrepris d’étendre et de moderniser cette technologie pour les cinq années à venir. Au sein de cette initiative, Savoir-faire Linux s’est vu confier […]

Thumbnail image

[L’introduction est en français, le reste du texte en anglais] TL;DR Les systèmes audio utilisent souvent des périphériques asynchrones, qui ont besoin d’être synchronisés avec du ré-échantillonnage. Nos mesures montrent que la charge CPU du ré-échantillonnage peut atteindre presque 30% d’un cœur sur un SoC i.MX8M Nano. Utiliser l’endpoint de feedback de l’USB gadget UAC2 […]