Pourquoi ne pas toujours utiliser DMA en faveur des interruptions avec UART sur STM32? [fermé]

9

Le mois dernier, j'ai passé beaucoup de temps à faire fonctionner UART (pour MIDI) avec une STM (STM32F103C8T6) en utilisant des interruptions, sans grand succès.

Cependant, ce soir, en utilisant DMA, cela a fonctionné assez rapidement.

Dans la mesure où je lis DMA est plus rapide et soulage le CPU, pourquoi ne pas toujours utiliser DMA en faveur des interruptions? D'autant plus que sur le STM32 il semble y avoir pas mal de problèmes.

J'utilise STM32CubeMx / HAL.

Michel Keijzers
la source
2
Pourquoi pas? C'est soit une question d'opinion, celle qui cherche à deviner quelle raison technique possible, ou de la même manière trop large, et donc pas une question qui appartient ici. Pour nommer un exemple aléatoire, DMA signifiera plus de latence dans la revendication des données, d'autant plus que vous n'obtenez aucun avantage réel à moins de lui permettre de rassembler plusieurs caractères. Souvent, cela peut être bien, parfois non.
Chris Stratton
6
Si obtenir des interruptions de travail a pris des semaines, c'est parce que vous avez mal abordé la tâche; faire fonctionner le DMA pourrait bien prendre plus de temps - c'est en fait une tâche plus complexe, donc la facilité apparente de la tâche plus complexe par rapport à la plus simple se résume probablement aux ressources que vous avez utilisées pour vous guider avec chacune, pas au mécanisme lui-même.
Chris Stratton
5
Ne présumez jamais que dma libère le cpu, parfois oui, le cpu continue, parfois non le processeur est figé pour contenir le bus pour le moteur dma. Trivial pour le faire avec une implémentation de bras, donc je ne peux pas simplement dire que toutes les armes sont de cette façon et que tous les x86 sont de cette façon ou quoi que ce soit, ce n'est pas si simple, vous devez toujours examiner la conception du système et peut-être faire un peu de piratage. La puce que vous avez peut très bien libérer le noyau du bras, ce n'est qu'un commentaire sur dma. En ce qui concerne votre question, cela n'a pas de sens que vous ne puissiez pas suivre et dma + int est probablement la solution complète si vous ne pouvez pas simplement interroger.
old_timer
5
Les interruptions sont assez banales sur le port série STM32F. Pourquoi ne postez-vous pas une question avec votre code afin que certains d'entre nous puissent essayer de repérer où vous vous trompez? Ce n'est jamais une bonne idée de pirater du code jusqu'à ce qu'il fonctionne sans comprendre quel était le problème sous-jacent.
Jon
7
À mon humble avis (pas si), c'est l'un des inconvénients de l'utilisation du Cube horrible et gonflé. Écrivez le logiciel à partir de zéro, vous apprendrez exactement comment fonctionne l'UART (parce que vous le devez), vous comprendrez beaucoup mieux le périphérique et à long terme, cela vous fera gagner beaucoup de temps.
DiBosco

Réponses:

24

Bien que DMA soulage le processeur et puisse ainsi réduire la latence des autres applications déclenchées par interruption fonctionnant sur le même cœur, il y a des coûts associés:

  • Il n'y a qu'une quantité limitée de canaux DMA et il y a des limites sur la façon dont ces canaux peuvent interagir avec les différents périphériques. Un autre périphérique sur le même canal peut être plus adapté à une utilisation DMA.

    Par exemple, si vous avez un transfert I2C en vrac toutes les 5 ms, cela semble être un meilleur candidat pour DMA qu'une commande de débogage occasionnelle arrivant sur UART2.

  • La mise en place et la maintenance de DMA est un coût en soi. (Normalement, la configuration de DMA est considérée comme plus complexe que la configuration d'un transfert par interruption normal par caractère, en raison de la gestion de la mémoire, du nombre de périphériques impliqués, de l'utilisation de DMA elle-même et de la possibilité que vous ayez besoin d'analyser les premiers caractères en dehors de DMA de toute façon, voir ci-dessous.)

  • Le DMA peut utiliser une puissance supplémentaire , car c'est encore un autre domaine du cœur qui doit être cadencé. D'un autre côté, vous pouvez suspendre le processeur pendant le transfert DMA, si le cœur le prend en charge.

  • Le DMA nécessite des tampons de mémoire pour fonctionner (à moins que vous ne fassiez du DMA périphérique à périphérique), il y a donc un coût de mémoire associé.

    (Le coût de la mémoire peut également être présent lors de l'utilisation d'interruptions par caractère, mais il peut également être beaucoup plus petit ou disparaître du tout si les messages sont interprétés immédiatement à l'intérieur de l'interruption.)

  • Le DMA produit une latence car le CPU n'est averti que lorsque le transfert est terminé / à moitié terminé (voir les autres réponses).

  • Sauf lors du streaming de données vers / depuis un tampon en anneau, vous devez savoir à l'avance la quantité de données que vous recevrez / envoyez.

    • Cela peut signifier qu'il est nécessaire de traiter les premiers caractères d'un message à l'aide d'interruptions par caractère: par exemple, lors de l'interface avec un XBee, vous devez d'abord lire le type et la taille du paquet, puis déclencher un transfert DMA dans un tampon alloué.

    • Pour d'autres protocoles, cela peut ne pas être possible du tout, s'ils n'utilisent '\n'que des délimiteurs de fin de message: par exemple, des protocoles basés sur du texte qui utilisent comme délimiteur. (Sauf si le périphérique DMA prend en charge la correspondance sur un caractère.)

Comme vous pouvez le voir, il y a beaucoup de compromis à considérer ici. Certains sont liés à des limitations matérielles (nombre de canaux, conflits avec d'autres périphériques, correspondance sur les caractères), certains sont basés sur le protocole utilisé (délimiteurs, longueur connue, tampons mémoire).

Pour ajouter quelques preuves anecdotiques, j'ai fait face à tous ces compromis dans un projet de loisir qui utilisait de nombreux périphériques différents avec des protocoles très différents. Il y avait quelques compromis à faire, principalement basés sur la question "combien de données est-ce que je transfère et à quelle fréquence vais-je le faire?". Cela vous donne essentiellement une estimation approximative de l'impact d'un simple transfert déclenché par interruption sur le processeur. J'ai donc donné la priorité au transfert I2C susmentionné toutes les 5 ms par rapport au transfert UART toutes les quelques secondes qui utilisait le même canal DMA. Un autre transfert UART se produisant plus souvent et avec plus de données d'autre part a la priorité sur un autre transfert I2C qui se produit plus rarement. Ce sont tous des compromis.

Bien sûr, l'utilisation de DMA présente également des avantages, mais ce n'est pas ce que vous avez demandé.

Jonas Schäfer
la source
Merci pour votre réponse détaillée. Le MIDI sera la partie la plus critique, donc je suppose que le DMA lui convient (bien que la vitesse soit faible: 31250 bauds). J'ai suffisamment de canaux DMA, plus tard, je vais utiliser un autre STM32 lors de l'utilisation de 4 USART. Je n'ai pas besoin de suspendre le CPU, car il aura une alimentation USB 5V, et je dois faire un traitement entre les messages (pour traiter les messages dans la boucle principale). J'ai un tampon de lecture de 256 octets et 256 octets de transmission. Je peux l'augmenter plus tard si nécessaire. Le STM32f103c8t6 a 20 Ko de RAM, le STM éventuel que j'utiliserai a 192 Ko.
Michel Keijzers
Et vous me donnez une très bonne idée de comment vous améliorer. Jusqu'à présent, je lis toujours 1 octet et vérifie en continu lorsqu'un message complet (MIDI) est reçu. Mais je peux lire le premier octet, et en fonction de cela, la taille est surtout connue et je peux demander le reste. Cela m'a coûté un autre petit tampon, mais ça va.
Michel Keijzers
La lecture d'octets uniques avec DMA est très inefficace. Pour une latence plus faible et une efficacité plus élevée, l'utilisation d'interruptions par caractère jusqu'à ce que vous connaissiez la taille, puis le passage à DMA serait favorable.
Jonas Schäfer
Eh bien, j'ai eu beaucoup de problèmes à utiliser les interruptions (sans DMA), je pense que j'utiliserai une réception DMA de 1 octet, et après cela, je sais à combien d'octets je vais m'attendre et je fais une demande DMA pour en obtenir plus.
Michel Keijzers
6
C'est probablement une erreur - vous devez corriger votre code d'interruption simple, sans DMA.
Chris Stratton
10

L'utilisation de DMA signifie généralement que vous ne prenez plus d'interruption sur chaque caractère, mais uniquement après qu'un "tampon plein" de caractères a été reçu (ou transmis). Cela augmente la latence du traitement de ces caractères - le premier caractère n'est traité qu'après réception du dernier caractère du tampon.

Cette latence peut être une mauvaise chose, en particulier dans une application sensible à la latence telle que le MIDI, où quelques ms ici et là peuvent ajouter de sérieux problèmes de jouabilité pour les performances live.

Dave Tweed
la source
Ce que je fais est de recevoir 1 octet à la fois (donc un tampon «DMA» de 1 octet) et après chaque rappel DMA de cet octet, de le stocker dans un tampon en anneau que je gère manuellement. Dans ma boucle principale, j'ai l'intention de vérifier les messages MIDI complets et de les traiter.
Michel Keijzers
3
Le DMA est généralement utilisé pour obtenir plusieurs octets et n'interrompre que lorsqu'ils ont tous été reçus. Interrompre après un seul octet est normal lorsque vous n'utilisez pas DMA, cela me fait donc me demander: à quoi sert la complication supplémentaire de l'utilisation de DMA pour cela?
Steve Melnikoff
5
@MichelKeijzers Alors ce que vous faites est à peu près exactement le même que dans les implémentations pilotées par interruption pure. Par conséquent, il n'y a aucun avantage à utiliser DMA dans ce cas et votre problème d'origine n'est probablement pas résolu par le DMA mais par votre réécriture de votre code (ISR, setup).
JimmyB
@JimmyB ... merci ... mais en raison de la réponse de Jonas ci-dessous, je vais faire une amélioration pour lire autant d'octets car le message est long. Je le sais après avoir reçu le premier octet (dans la plupart des cas). Il sera alors plus avantageux d'utiliser le DMA sur les interruptions.
Michel Keijzers
8

Le DMA ne remplace pas les interruptions - elles sont généralement utilisées ensemble! Si vous utilisez DMA pour envoyer des données via un UART, par exemple, vous avez toujours besoin d'une interruption pour vous dire quand l'envoi est terminé.

duskwuff -inactif-
la source
Certes, peut-être que sur le STM32, le mécanisme d'interruption (pur non DMA) est un peu maladroit par rapport au DMA direct.
Michel Keijzers
2
@duskwuff Pas vraiment; vous pouvez interroger pour voir quand le DMA est terminé, et vous voudrez peut-être le faire car l'une des principales raisons d'utiliser le DMA est de ne pas avoir à vous soucier du port série jusqu'à ce que votre programme soit dans un état où il peut agir sur le reçu Les données. Ou pour le DMA sortant, vous pouvez simplement interroger pour voir s'il est possible d'ajouter plus au tampon d'envoi.
Chris Stratton
1
@MichelKeijzers: IDK la puce spécifique, mais généralement l'alternative au DMA n'est pas littéralement des interruptions, elle est programmée IO (où vous utilisez des instructions CPU pour lire / écrire des données depuis / vers un registre d'E / S). Dans un gestionnaire d'interruption, vous feriez généralement une lecture, puis une autre au cas où un caractère arriverait pendant que vous lisez le premier, surtout si cela ne déclenche pas une autre interruption. Ou lisez jusqu'à ce qu'un tampon interne soit vide s'il existe un tel tampon. Évidemment, vous avez besoin de plus d'interruptions pour PIO et configurez-les différemment.
Peter Cordes
@ChrisStratton Bon point ... jusqu'à présent, je n'ai pas vérifié s'il est possible de transmettre, je transmets juste quelque chose, sans vérifier si c'est ok. Sinon sinon, je réessaye plus tard.
Michel Keijzers
@PeterCordes Il semble que le STM32 ait suffisamment d'interruptions pour le DMA et j'ai lu à chaque fois juste 1 octet. Même le STM32 le plus simple (F103c8t6) possède suffisamment de ports / interruptions DMA disponibles.
Michel Keijzers
5

L'utilisation de DMA introduit des questions et des défis intéressants au-delà de toutes les autres considérations relatives à l'utilisation des périphériques UART. Je vais vous donner quelques exemples: Supposons que votre uC se trouve sur un bus RS485 (ou autre) avec d'autres appareils. Il y a beaucoup de messages sur le bus, certains sont destinés à votre uC, d'autres non. Supposons en outre que ces voisins de bus parlent tous un protocole de données différent, ce qui implique que les longueurs de message sont différentes.

Certaines questions qui ne se posent que lors de l'utilisation de DMA sont:

  • quand dois-je interrompre?
    • Les DMA aiment vraiment interrompre uniquement lorsqu'ils ont transféré une quantité prédéfinie de données.
    • Que faites-vous si vous ne recevez jamais suffisamment de données pour déclencher une interruption DMA?
  • Que faire si vous ne recevez qu'un message partiel lorsque le DMA s'interrompt?
  • À quoi ressemblent vos tampons RX? Sont-ils linéaires ou circulaires?
    • Le DMA peut être un participant de tampon circulaire indiscipliné dans le sens où il n'obéit qu'à la frontière d'adresse, mais n'a aucun problème à passer au-delà des autres pointeurs du système de tampon circulaire.

Quoi qu'il en soit, juste matière à réflexion.

pgvoorhees
la source
Merci pour ces considérations. Actuellement, je reçois toujours 1 octet et le stocke dans un tampon en anneau, car en effet mes messages (MIDI) peuvent avoir des longueurs différentes et je ne sais pas ce que je reçois ensuite. Dans ma boucle principale, je vérifie les messages complets pour les traiter (et s'ils sont terminés, je les supprime du tampon en anneau). Donc, je recevrai toujours suffisamment de données (sauf si je manquerais des octets, je dois vérifier cela). Mon tampon RX ne fait que 1 octet, mais je le copie dans un tampon circulaire / circulaire. Je n'ai pas vérifié s'il est plein (besoin de l'ajouter).
Michel Keijzers
Hé, pas de soucis. Je suis sûr que votre application sera bien programmée. Comme d'autres l'ont mentionné, le DMA est génial, mais il n'est pas gratuit. Il introduit des considérations supplémentaires dans le système qui n'existent pas si vous pouvez vous en sortir sans l'utiliser.
pgvoorhees
eh bien j'espère, je suis encore débutant.
Michel Keijzers
3

Du côté de la réception (si je me souviens bien) DMA se termine soit sur une correspondance de caractères ou sur le nombre de terminaux. Certains protocoles et de nombreuses applications interactives ne s'intègrent pas facilement dans ce modèle et vous devez vraiment gérer les choses caractère par caractère. Les techniques DMA peuvent également être fragiles si la liaison de communication n'est pas fiable, la perte d'un seul caractère dans le flux peut facilement gâcher votre machine d'état DMA.

Dean Franks
la source
Je reçois en effet octet par octet et le copie manuellement dans un tampon en anneau pour le traiter plus tard.
Michel Keijzers
1

J'ai utilisé le STM32CubeMx / HAL sur quelques projets maintenant et j'ai constaté que le logiciel de gestion UART qu'il génère présente des défauts certains du côté de la réception.

Lors de la transmission, vous souhaiterez normalement envoyer un bloc de données ou une ligne de texte. Dans ce cas, vous savez à l'avance la durée du transfert de données et l'utilisation du DMA est donc une solution évidente. Vous obtenez une interruption une fois le transfert terminé et pouvez utiliser la fonction de rappel complet UART TX pour indiquer à votre code principal que la transmission est terminée et vous pouvez envoyer un autre bloc de données.

En ce qui concerne la réception des données, les fonctions fournies par ST supposent toutes que vous savez combien de caractères le périphérique émetteur vous donnera avant de commencer à envoyer. Normalement, cela n'est pas connu. La fonctionnalité d'interruption place les données reçues dans une mémoire tampon et indique uniquement qu'il y a des données disponibles lorsque le nombre prédéfini de caractères a été reçu. Si vous essayez d'utiliser le DMA ou d'interrompre la fonctionnalité pour recevoir des données en configurant des transferts séquentiels à un seul caractère, le temps de configuration de chacun d'eux signifie que vous perdrez des caractères à autre chose que les débits de données les plus lents (le débit en bauds que vous commencer à perdre des données dépendra de la vitesse d'horloge de votre processeur) et chargera le processeur de manière excessive, ne laissant aucun cycle d'instruction pour tout autre traitement

Pour contourner cela, j'ai écrit ma propre fonction de gestionnaire d'interruption qui stocke les données dans un petit tampon circulaire local et définit un décompte qui est lu par le code principal (un sémaphore de comptage RTOS) pour indiquer qu'il y a des données reçues prêtes. Le code principal peut alors collecter les données de ce tampon à loisir, peu importe s'il y a un certain retard dans la collecte des données à condition que le tampon local ne déborde pas avant que les données ne soient collectées.

uɐɪ
la source
Je fais exactement la même chose (je pense). Je lis un octet à la fois et le stocke dans un tampon cyclique, et j'ai l'intention de vérifier dans la boucle principale les messages complets. Peut être amélioré un peu cependant.
Michel Keijzers
Pensez-vous que je pourrais rencontrer le problème que la configuration du DMA à chaque fois surchargera mon processeur / caractères manquants à 31 250 bauds?
Michel Keijzers
1
Tant que vous configurez le DMA pour transférer un certain nombre de caractères à la fois, ce ne sera pas un problème. J'ai 4 UART exécutant 115200 et plus et I2C utilisant DMA sans problèmes. Les transmissions UART sont toutes ~ 20 octets ou plus. Le problème était d'utiliser DMA pour recevoir sur l'UART (processeur L4 à 80 MHz, 9600 bauds).
u
Actuellement, je le mets à 1 octet à la fois, mais je peux l'améliorer (en faisant le premier octet, etn puis vérifier combien d'octets supplémentaires sont nécessaires).
Michel Keijzers