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.
uart
dma
stm32cubemx
stm32f103c8t6
Michel Keijzers
fuente
fuente
Respuestas:
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.
fuente
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.
fuente
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.
fuente
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:
De todos modos, solo comida para pensar.
fuente
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.
fuente
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.
fuente