¿Por qué no usar siempre DMA a favor de las interrupciones con UART en STM32? [cerrado]

9

Pasé el mes pasado mucho tiempo haciendo que UART (para MIDI) trabaje con un STM (STM32F103C8T6) usando interrupciones, sin mucho éxito.

Sin embargo, esta noche usando DMA funcionó bastante rápido.

Dado que, por lo que he leído, DMA es más rápido y alivia la CPU, ¿por qué no usar siempre DMA a favor de las interrupciones? Sobre todo porque en el STM32 parece haber algunos problemas.

Estoy usando STM32CubeMx / HAL.

Michel Keijzers
fuente
2
Por qué no? Esa es una cuestión de opinión, una que busca adivinar cuál es la razón técnica posible, o de la misma manera demasiado amplia, y por lo tanto no es una pregunta que pertenece aquí. Para nombrar un ejemplo aleatorio, DMA significará más latencia al reclamar los datos, especialmente porque no obtienes ningún beneficio real a menos que permitas que reúna varios caracteres. A menudo eso podría estar bien, a veces no.
Chris Stratton
66
Si conseguir interrupciones en el trabajo tomó semanas, es porque abordó la tarea de manera incorrecta; hacer que DMA funcione bien podría llevar más tiempo: en realidad es una tarea más compleja, por lo que la aparente facilidad de la tarea más compleja sobre la más simple probablemente se reduce a los recursos que usó para la orientación con cada uno, no el mecanismo en sí.
Chris Stratton
55
Nunca asuma que dma libera la CPU, a veces sí, la CPU continúa, a veces no el procesador está congelado para sostener el bus para el motor dma. Trivial para hacer esto con una implementación de brazo, así que no puedo decir que todos los brazos son de esta manera y todos los x86 son de esa manera o lo que sea, no es tan simple, siempre debe examinar el diseño del sistema y tal vez hacer un poco de pirateo. El chip que tiene puede liberar el núcleo del brazo, esto es solo un comentario sobre dma. En cuanto a su pregunta, no tiene sentido que no pueda mantenerse al día y es probable que dma + int sea la solución completa si no puede sondear.
old_timer
55
Las interrupciones son bastante triviales en el puerto serie STM32F. ¿Por qué no publica una pregunta con su código para que algunos de nosotros podamos tratar de detectar dónde se equivoca? Nunca es una buena idea piratear código hasta que funcione sin comprender cuál era el problema subyacente.
Jon
77
En mi (no tan) humilde opinión, este es uno de los inconvenientes de usar el horrible y hinchado Cubo. Escriba el software desde cero, aprenderá exactamente cómo funciona el UART (porque tiene que hacerlo), comprenderá el periférico mucho mejor y, a la larga, le ahorrará mucho tiempo.
DiBosco

Respuestas:

24

Si bien DMA alivia la CPU y, por lo tanto, puede reducir la latencia de otras aplicaciones controladas por interrupciones que se ejecutan en el mismo núcleo, hay costos asociados con ella:

  • Solo hay una cantidad limitada de canales DMA y hay limitaciones sobre cómo esos canales pueden interactuar con los diferentes periféricos. Otro periférico en el mismo canal puede ser más adecuado para el uso de DMA.

    Por ejemplo, si tiene una transferencia masiva de I2C cada 5 ms, este parece ser un mejor candidato para DMA que un comando de depuración ocasional que llega a UART2.

  • Configurar y mantener DMA es un costo en sí mismo. (Normalmente, la configuración de DMA se considera más compleja que la configuración de la transferencia controlada por interrupción normal por carácter, debido a la administración de memoria, a la mayor cantidad de periféricos involucrados, al uso de DMA en las interrupciones y a la posibilidad de que necesite analizar los primeros caracteres fuera de DMA de todos modos, ver más abajo)

  • DMA puede usar energía adicional , ya que es otro dominio del núcleo que necesita ser sincronizado. Por otro lado, puede suspender la CPU mientras la transferencia de DMA está en progreso, si el núcleo lo admite.

  • DMA requiere memorias intermedias para trabajar (a menos que esté haciendo DMA periférico a periférico), por lo que hay algunos costos de memoria asociados con él.

    (El costo de la memoria también puede estar presente cuando se usan interrupciones por carácter, pero también puede ser mucho más pequeño o desaparecer si los mensajes se interpretan de inmediato dentro de la interrupción).

  • DMA produce una latencia porque la CPU solo recibe una notificación cuando la transferencia se completa / medio completa (consulte las otras respuestas).

  • Excepto cuando se transmiten datos hacia / desde un búfer en anillo, debe saber de antemano cuántos datos recibirá / enviará.

    • Esto puede significar que es necesario procesar los primeros caracteres de un mensaje usando interrupciones por carácter: por ejemplo, al interactuar con un XBee, primero debe leer el tipo y el tamaño del paquete y luego activar una transferencia DMA en un búfer asignado.

    • Para otros protocolos, esto puede no ser posible si solo usan delimitadores de fin de mensaje: por ejemplo, protocolos basados ​​en texto que se usan '\n'como delimitadores. (A menos que el periférico DMA admita la coincidencia en un personaje).

Como puede ver, hay muchas compensaciones a considerar aquí. Algunos están relacionados con limitaciones de hardware (número de canales, conflictos con otros periféricos, coincidencia de caracteres), algunos se basan en el protocolo utilizado (delimitadores, longitud conocida, memorias intermedias).

Para agregar alguna evidencia anecdótica, me enfrenté a todas estas compensaciones en un proyecto de pasatiempo que utilizaba muchos periféricos diferentes con protocolos muy diferentes. Hubo algunas compensaciones que hacer, principalmente basadas en la pregunta "¿cuántos datos estoy transfiriendo y con qué frecuencia voy a hacer eso?". Básicamente, esto le brinda una estimación aproximada del impacto de la transferencia simple impulsada por interrupción en la CPU. Por lo tanto, le di prioridad a la transferencia I2C antes mencionada cada 5 ms sobre la transferencia UART cada pocos segundos que usaba el mismo canal DMA. Otra transferencia UART que ocurre con más frecuencia y con más datos, por otro lado, tiene prioridad sobre otra transferencia I2C que ocurre con menos frecuencia. Todo es una compensación.

Por supuesto, usar DMA también tiene ventajas, pero eso no es lo que pediste.

Jonas Schäfer
fuente
Gracias por tu respuesta detallada. MIDI será la parte más crítica, así que supongo que DMA es adecuado para él (aunque la velocidad es baja: 31250 baudios). Tengo suficientes canales DMA, luego usaré otro STM32 cuando use 4 USART. No necesito suspender la CPU, ya que tendrá una alimentación USB de 5V, y necesito hacer el procesamiento entre los mensajes (para procesar los mensajes en el bucle principal). Tengo una lectura de 256 bytes y un búfer de transmisión de 256 bytes. Puedo aumentarlo más tarde si es necesario. El STM32f103c8t6 tiene 20 KB de RAM, el STM eventual que usaré tiene 192 KB.
Michel Keijzers
Y me das una muy buena idea de cómo mejorar. Hasta ahora, siempre leo 1 byte y verifico continuamente cuando se recibe un mensaje completo (MIDI). Pero puedo leer el primer byte y, dependiendo de eso, se conoce principalmente el tamaño y puedo pedir el resto. Esto me costó otro pequeño buffer pero está bien.
Michel Keijzers
La lectura de bytes individuales con DMA es muy ineficiente. Para una latencia más baja y una mayor eficiencia, usar interrupciones por carácter hasta que sepa el tamaño y luego cambiar a DMA sería favorable.
Jonas Schäfer
Bueno, tuve muchos problemas al usar interrupciones (sin DMA), creo que usaré una recepción DMA de 1 byte, y después de eso sé cuántos bytes esperaré y haré una solicitud DMA para obtener más.
Michel Keijzers
66
Probablemente sea un error: debe corregir su código de interrupción simple, sin DMA.
Chris Stratton
10

El uso de DMA generalmente significa que ya no estás interrumpiendo cada personaje, sino solo después de que se haya recibido (o transmitido) un "búfer lleno" de caracteres. Esto aumenta la latencia de procesamiento de esos caracteres: el primer carácter no se procesa hasta después de que se haya recibido el último carácter en el búfer.

Esta latencia puede ser algo malo, especialmente en una aplicación sensible a la latencia como MIDI, donde unos pocos ms aquí y allá pueden sumar problemas serios de jugabilidad para actuaciones en vivo.

Dave Tweed
fuente
Lo que hago es recibir 1 byte a la vez (es decir, un búfer 'DMA' de 1 byte) y después de cada devolución de llamada DMA de ese byte, almacenarlo en un búfer en anillo que manejo manualmente. En mi ciclo principal, tengo la intención de verificar si hay mensajes MIDI completos y procesarlos.
Michel Keijzers
3
DMA se usa generalmente para obtener múltiples bytes, y solo se interrumpe cuando se han recibido. Interrumpir después de solo un byte es normal cuando no se usa DMA, por lo que me pregunto: ¿cuál es el punto en la complicación adicional de usar DMA para eso?
Steve Melnikoff
55
@MichelKeijzers Entonces, lo que haces es exactamente lo mismo que harías en implementaciones impulsadas por interrupciones. Por lo tanto, no hay ningún beneficio en el uso de DMA en este caso y su problema original probablemente no se resuelva con DMA sino con la reescritura de su código (ISR, configuración).
JimmyB
@JimmyB ... gracias ... sin embargo, debido a la respuesta de Jonas a continuación, haré una mejora para leer tantos bytes como el mensaje es largo. Sé esto después de recibir el primer byte (en la mayoría de los casos). De lo que se beneficiará más usar DMA sobre interrupciones.
Michel Keijzers
8

DMA no es un sustituto de las interrupciones, ¡generalmente se usan juntas! Si está utilizando DMA para enviar datos a través de un UART, por ejemplo, aún necesita una interrupción para indicarle cuándo se completa el envío.

duskwuff -inactive-
fuente
Es cierto, tal vez solo en el STM32 el mecanismo de interrupción (puro no DMA) es un poco torpe en comparación con el DMA directo.
Michel Keijzers
2
@duskwuff No realmente; puede sondear para ver cuándo termina el DMA, y es posible que desee hacerlo porque uno de los motivos clave para usar DMA es no tener que molestarse con el puerto serie hasta que su programa esté en un estado en el que pueda actuar sobre lo recibido datos. O para DMA saliente, simplemente puede sondear para ver si es posible agregar más al búfer de envío.
Chris Stratton
1
@MichelKeijzers: IDK el chip específico, pero por lo general la alternativa a DMA no es literalmente interrupciones, es IO programada (donde se usan instrucciones de CPU para leer / escribir datos desde / hacia un registro de E / S). En un controlador de interrupciones, normalmente haría una lectura, y luego quizás otra en caso de que entrara un personaje mientras leía el primero, especialmente si eso no desencadenará otra interrupción. O lea hasta que un búfer interno esté vacío si existe dicho búfer. Obviamente, necesita más interrupciones para PIO y configurarlas de manera diferente.
Peter Cordes
@ChrisStratton Buen punto ... hasta ahora no he verificado si es posible transmitir, simplemente transmito algo, sin verificar si está bien. Probablemente si no, lo intento más tarde.
Michel Keijzers
@PeterCordes Parece que el STM32 tiene suficientes interrupciones para DMA y leo cada vez solo 1 byte. Incluso el STM32 más simple (F103c8t6) tiene suficientes puertos / interrupciones DMA disponibles.
Michel Keijzers
5

El uso de DMA presenta algunas preguntas y desafíos interesantes más allá de todas las demás consideraciones del uso periférico UART. Le daré algunos ejemplos: suponga que su uC está sentada en un bus RS485 (o lo que sea) con otros dispositivos. Hay muchos mensajes en el bus, algunos están destinados a su uC, otros no. Además, suponga que todos estos vecinos del bus hablan un protocolo de datos diferente, lo que implica que las longitudes de los mensajes son diferentes.

Algunas preguntas que solo surgen cuando se usa DMA son:

  • cuando lo interrumpo?
    • A los DMA solo les gusta interrumpir cuando transfieren una cantidad preestablecida de datos.
    • ¿Qué haces si nunca recibes suficientes datos para activar una interrupción DMA?
  • ¿Qué sucede si solo recibe un mensaje parcial cuando el DMA interrumpe?
  • ¿Cómo son sus amortiguadores RX? ¿Son lineales o circulares?
    • DMA puede ser un participante de búfer circular indisciplinado en el sentido de que solo obedece el límite de la dirección, pero no tiene ningún problema para superar los otros punteros en el sistema de búfer circular.

De todos modos, solo comida para pensar.

pgvoorhees
fuente
Gracias por esas consideraciones. Actualmente, siempre recibo 1 byte y lo guardo en un búfer en anillo, ya que de hecho mis mensajes (MIDI) pueden tener diferentes duraciones y no sé cuál será el siguiente. En mi bucle principal, verifico si hay mensajes completos para procesarlos (y si está completo, los elimino del búfer en anillo). Por lo tanto, siempre recibiré suficientes datos (a menos que pierda bytes, tengo que verificar eso). Mi búfer RX es de solo 1 byte, pero lo copio a un búfer circular / en anillo. No hice verificaciones si está lleno (es necesario agregarlo).
Michel Keijzers
Oye, no te preocupes. Estoy seguro de que su aplicación estará bien programada. Como otros mencionaron, DMA es genial, pero no es gratis, es todo. Introduce consideraciones adicionales en el sistema que no existen si puedes escapar sin usarlo.
pgvoorhees
bueno, espero, todavía soy un principiante.
Michel Keijzers
3

En el lado de recepción (según recuerdo), DMA termina en una coincidencia de caracteres o en el recuento de terminales. Algunos protocolos y muchas aplicaciones interactivas no encajan fácilmente en este modelo y realmente necesita manejar las cosas carácter por carácter. Las técnicas de DMA también pueden ser frágiles si el enlace de comunicaciones no es confiable, perder un solo carácter en la transmisión puede dañar fácilmente su máquina de estado de DMA.

Dean Franks
fuente
De hecho, recibo byte a byte y lo copio manualmente a un búfer en anillo para procesarlo más tarde.
Michel Keijzers
1

He utilizado el STM32CubeMx / HAL en un par de proyectos ahora y he descubierto que el software de manejo de UART que genera tiene defectos claros en el lado de recepción.

En la transmisión, normalmente querrá enviar un bloque de datos o una línea de texto. En este caso, sabe de antemano cuánto dura la transferencia de datos y, por lo tanto, usar el DMA es una solución obvia. Obtiene una interrupción una vez que se completa la transferencia y puede usar la función de devolución de llamada completa de UART TX para indicar a su código principal que la transmisión está completa y puede enviar otro bloque de datos.

Cuando se trata de la recepción de datos, todas las funciones proporcionadas por ST suponen que usted sabe cuántos caracteres le dará el dispositivo emisor antes de que comience a enviar. Normalmente esto no se sabe. La funcionalidad de interrupción coloca los datos recibidos en un búfer y solo indica que hay datos disponibles cuando se ha recibido el número predefinido de caracteres. Si intenta utilizar la funcionalidad DMA o de interrupción para recibir datos configurando transferencias secuenciales de un solo carácter, el tiempo de configuración para cada uno de estos significará que perderá caracteres en cualquier otra cosa que no sean las velocidades de datos más lentas (la velocidad en baudios que obtendrá comenzar a perder datos dependerá de la velocidad del reloj del procesador) y cargará el procesador en exceso, sin dejar ciclos de instrucciones para ningún otro procesamiento

Para solucionar esto, he escrito mi propia función de controlador de interrupciones que almacena los datos en un pequeño búfer circular local y establece un conteo que es leído por el código principal (un semáforo de conteo RTOS) para indicar que hay datos recibidos listos. El código principal puede recopilar los datos de este búfer en su tiempo libre, no importa si hay algún retraso en la recopilación de datos siempre que el búfer local no se desborde antes de que se recopilen los datos.

uɐɪ
fuente
Yo hago exactamente lo mismo (creo). Leo 1 byte a la vez y lo guardo en un búfer cíclico, y tengo la intención de verificar en el bucle principal los mensajes completos. Sin embargo, se puede mejorar un poco.
Michel Keijzers
¿Crees que podría encontrarme con el problema de que configurar el DMA cada vez sobrecargará mi procesador / caracteres faltantes a 31,250 baudios?
Michel Keijzers
1
Siempre que configure el DMA para transferir una cantidad de caracteres a la vez, esto no será un problema. Tengo 4 UART con 115200 y superior e I2C usando DMA sin problemas. Las transmisiones UART son ~ 20 bytes o más. El problema era usar DMA para recibir en el UART (procesador L4 a 80MHz, 9600baudios).
u
Actualmente lo configuro en 1 byte a la vez, pero puedo mejorarlo (haciendo el primer byte y luego comprobando cuántos bytes adicionales se necesitan).
Michel Keijzers