Pasé mucho tiempo el mes pasado haciendo que UART (para MIDI) funcione 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 bastantes problemas.
Estoy usando STM32CubeMx/HAL.
Si bien DMA alivia la CPU y, por lo tanto, puede reducir la latencia de otras aplicaciones impulsadas por interrupciones que se ejecutan en el mismo núcleo, existen costos asociados con esto:
Solo hay una cantidad limitada de canales DMA y existen 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 I2C masiva cada 5 ms, parece 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 normal de la transferencia impulsada por interrupciones por carácter, debido a la administración de memoria, más periféricos involucrados, DMA que usa interrupciones y 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 DMA está en curso, si el núcleo lo admite.
DMA requiere búferes de memoria para funcionar (a menos que esté haciendo DMA de periférico a periférico), por lo que hay un costo de memoria asociado.
(El costo de la memoria también puede estar ahí 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 está completa o medio completa (consulte las otras respuestas).
Excepto cuando transmite datos hacia/desde un búfer de anillo, necesita saber de antemano cuántos datos recibirá/enviará.
Esto puede significar que es necesario procesar los primeros caracteres de un mensaje utilizando interrupciones por carácter: por ejemplo, al interactuar con un XBee, primero leería el tipo y el tamaño del paquete y luego activaría una transferencia DMA en un búfer asignado.
Para otros protocolos, esto puede no ser posible en absoluto, si solo usan delimitadores de fin de mensaje: por ejemplo, protocolos basados en texto que usan '\n'
como delimitador. (A menos que el periférico DMA admita la coincidencia en un carácter).
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, búferes de memoria).
Para agregar alguna evidencia anecdótica, me enfrenté a todas estas compensaciones en un proyecto de pasatiempo que usó muchos periféricos diferentes con protocolos muy diferentes. Había que hacer algunas concesiones, principalmente basadas en la pregunta "¿cuántos datos estoy transfiriendo y con qué frecuencia voy a hacerlo?". Básicamente, esto le brinda una estimación aproximada del impacto de la transferencia simple impulsada por interrupciones 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 son compensaciones.
Por supuesto, usar DMA también tiene ventajas, pero eso no es lo que pediste.
El uso de DMA generalmente significa que ya no está tomando una interrupción en cada carácter, sino solo después de que se haya recibido (o transmitido) un "búfer lleno" de caracteres. Esto aumenta la latencia del procesamiento de esos caracteres: el primer carácter no se procesa hasta que se recibe 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 serios problemas de reproducción para presentaciones en vivo.
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, todavía necesita una interrupción para saber cuándo se completa el envío.
El uso de DMA presenta algunas preguntas y desafíos interesantes más allá de todas las demás consideraciones del uso de periféricos UART. Le daré algunos ejemplos: suponga que su uC está sentado 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 de 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 alimento para el pensamiento.
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 estropear fácilmente su máquina de estado de DMA.
He usado el STM32CubeMx/HAL en un par de proyectos ahora y descubrí que el software de manejo de UART que genera tiene deficiencias definidas en el lado de recepción.
Al transmitir, normalmente querrá enviar un bloque de datos o una línea de texto. En este caso, usted sabe de antemano cuánto dura la transferencia de datos, por lo que 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 asumen que usted sabe cuántos caracteres le dará el dispositivo de envío 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 función DMA o de interrupción para recibir datos mediante la configuración de transferencias secuenciales de un solo carácter, el tiempo de configuración para cada uno de estos significará que perderá caracteres a cualquier velocidad que no sea la de datos más lenta (la velocidad en baudios que empezar a perder datos dependerá de la velocidad de reloj de su procesador) y cargará el procesador en exceso, sin dejar ciclos de instrucción para ningún otro procesamiento
Para evitar 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 lee el código principal (un semáforo de conteo RTOS) para indicar que hay datos recibidos listos. Luego, el código principal puede recopilar los datos de este búfer cuando lo desee, sin importar 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.
harry svensson
michel keijzers
chris stratton
chris stratton
Alaska
viejo contador de tiempo
viejo contador de tiempo
viejo contador de tiempo
michel keijzers
Jon
michel keijzers
dibosco
michel keijzers
michel keijzers