STM32 USART con DMA: ¿qué interrupciones usar?

Estoy trabajando en el firmware de un STM32F103 que se comunica a través de RS232 a 115200 baudios con un controlador de motor. El controlador del motor (un Copley Xenus XTL ) funciona con un protocolo de "hablar cuando se le habla". Estoy usando la interfaz de programación ASCII en los documentos vinculados. El STM32 siempre envía el mismo comando ("g r0x18") para sondear un registro, y el controlador del motor responde con un número variable (~4-10 bytes) de caracteres terminados con un retorno de carro ("v 12345", donde el número de dígitos es variable). Tengo un código para analizar la respuesta y extraer un valor numérico de ella. Una vez que se analiza la respuesta, el comando de sondeo de registro debe transmitirse nuevamente al controlador del motor. El STM32 también lee un canal ADC sobre DMA en modo circular en segundo plano.

Me gustaría implementar esto usando el controlador DMA para hacer que todo no bloquee tanto como sea posible, pero estoy un poco confundido en cuanto a qué interrupciones debo usar y cuándo se activan. Sin usar el controlador DMA, el código de análisis actualmente reside en la interrupción USART RXNE. Supongamos que transmito un comando y el controlador del motor comienza a responder. Creo que la interrupción RXNE se dispara por cada byte recibido, pero ¿qué pasa con la interrupción completa de la transferencia DMA? ¿Hay alguna diferencia funcional en este caso entre usar la interrupción DMA TC y la interrupción USART RXNE?

"el controlador del motor responde con un número variable (~4-10 bytes) de caracteres terminados por un retorno de carro". -- Creo que excluye cualquier uso razonable de DMA para esto. Deberá ejecutar un código personalizado después de cada byte recibido para detectar si la transmisión está completa; el controlador DMA no ayudará aquí. Continúe usando el RXNE, pero tal vez reduzca el ISR para que solo acepte un byte, colóquelo en su búfer, verifique si es CR y, si lo es, configure alguna señal para su posterior procesamiento/análisis.
the parsing code currently resides in the USART RXNE interruptUna mejor manera sería usar solo la interrupción RX para colocar el byte actual en un búfer circular. En el bucle principal, compruebe si el búfer contiene un mensaje completo y analice ese mensaje.
Además, 115 kbaudios son aproximadamente 11 kbytes/s, lo que significa que a un reloj de CPU de 66 MHz apenas hay más de una IRQ cada 6000 ciclos de CPU; eso debería ser alrededor del 1% de la carga de la CPU, y solo mientras se reciben datos.
Es más difícil ayudar con solo una parte de la información. ¿Podría publicar un enlace al controlador del motor y su protocolo? Indique también exactamente qué comandos está enviando al controlador del motor. Estoy de acuerdo con los dos comentarios, a. 1% de sobrecarga de CPU no es mucho, y b. sacar el código de análisis de la rutina de servicio de interrupción. La rutina de servicio de interrupción debe ser lo más breve posible para minimizar el bloqueo. Además, se parece más a una transferencia DMA cuando todo lo que hace es almacenar bytes en la RAM. Esperaría reducir la carga usando DMA, pero es más complejo.
Si bien DMA probablemente no sea necesario, es posible que pueda usarlo configurando una transferencia para la máxima duración posible y dejando que se detenga cuando se quede sin datos, luego use un temporizador configurado cuando envió el comando para detener el DMA y procesar los resultados. Si puede cambiar el firmware del controlador del motor, puede poner primero el recuento de bytes que se enviarán y hacer que solo una interrupción UART RX lo capture y programe el DMA. O tal vez podría enviar una condición de interrupción para terminar el mensaje y disparar una interrupción para evaluar el resultado.
@gbulmer Agregué la información sobre el controlador del motor. Parece que puedo estar haciendo la pregunta equivocada. Nunca antes había trabajado con un controlador DMA en un micro, por lo que sería razonable publicar "no use DMA para esto" como respuesta.
@m.Alin A better way would be to only use the RX interrupt to put the current byte in a circular buffer.: esto solo es cierto si 1. necesita responder a ciertos eventos dentro de un marco de tiempo finito y 2. no tiene las instalaciones para interrupciones anidadas . Dado que está en un STM32, tiene un NVIC con múltiples prioridades de interrupción, por lo que es completamente aceptable tener una lógica significativa en los controladores de interrupciones.

Respuestas (2)

Puede configurar DMA para que funcione en modo cíclico, y con un búfer razonablemente grande puede extraer caracteres con la frecuencia que desee con una pollfunción simple. Simplemente almacene el índice del último carácter recuperado y use el registro DMA para verificar cuántos caracteres tiene que recibir hasta que se renueve (el registro AFAIR tiene NDTRen su nombre), y procese los caracteres recibidos desde la última llamada, luego actualice el índice del último carácter recuperado.

El uso de DMA descrito lo hace pollindependiente del contexto desde el que lo llama, siempre que evite la preferencia. Luego puede usar la interrupción RX para activar llamadas de sondeo, o puede hacerlo en otro contexto (por ejemplo, con algunos eventos periódicos).

Esto es generalmente eficiente si los marcos son lo suficientemente largos, la tasa de baudios es grande y la latencia de interrupción es mayor que el tiempo de recepción de un solo carácter.

Pero si los datos se reciben con la suficiente lentitud, puede salirse con la suya procesándolos char por char de la manera actual, y el uso de DMA puede ser excesivo.

Como señaló @Chris Stratton, también puede configurar DMA para una transmisión única y esperar el tiempo suficiente, cambiar el protocolo o usar otra señal como "fin de transmisión", y luego procesar el cuadro en el búfer DMA.

"Entonces puede usar la interrupción de RX para activar la (s) llamada (s) de sondeo" Es una buena manera, pero ¿alguna vez ha intentado hacer algo como en la familia STM32F? Lo he intentado y hay un problema. Cuando activo el modo DMA escribiendo DMAR en USART_CR3, las interrupciones RX no vienen. Hay una pequeña nota al respecto en su RefMan: "Si se usa DMA para la recepción, no habilite el bit RXNEIE". Eso significa que no use las interrupciones RX. Desafortunadamente, parece que es imposible.

Deberías usar interrupciones DMA. Estoy usando STM32F02 con bibliotecas periféricas STM32 STL, pero la idea es más o menos la misma para F01:

  • Inicializa tu UART
  • Inicialice DMA (¡no olvide proporcionar la señal CLK para el periférico ANTES de llamar a DMA_Init!)
  • Configure las interrupciones de NVIC y DMA, y habilite DMA de la siguiente manera:

 NVIC_InitTypeDef NVIC_InitStructure;
 //Enable DMA1 channel IRQ Channel
 //Note: maybe in your implementation you don't have DMAx_Streamy, look for channel/ stream configurations in your microcontroller manual

 NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream1_IRQn; // This could be different in your implementation

 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
 NVIC_Init(&NVIC_InitStructure);

 // Enable DMA1 Channel Transfer Complete interrupt
 DMA_ITConfig(DMA1_Stream1, DMA_IT_TC, ENABLE);

 USART_DMACmd(ACCESSORY_UART, USART_DMAReq_Rx, ENABLE);
 DMA_Cmd(DMA1_Stream1, ENABLE);

La función USART_DMACmd une UART con DMA, y creo que esa es su respuesta. De esta forma, el DMA copiará un byte al puntero que proporcionaste en la configuración cada vez que se dispare un evento RXNE, pero interrumpiendo SÓLO cuando se dispare un evento Transfer Complete (DMA_IT_TC), llamando a la función DMA correspondiente según el canal configurado. / arroyo.