Estoy generando una serie compleja de pulsos en un STM32F103, esencialmente como se describe en la nota de la aplicación ST AN4776 Libro de cocina del temporizador de uso general , sección 5.3. Como resumen rápido, eso significa que estoy usando el modo de ráfaga DMA del temporizador para transferir nuevos valores a ARR, RCR y CCR1 después de cada evento de actualización del temporizador. Se proporciona una descripción más detallada al final de la pregunta, en caso de que no esté familiarizado con la nota de la aplicación.
Mi problema está relacionado con el ancho de banda DMA: los pulsos que genero pueden, en principio, estar espaciados arbitrariamente, incluido solo 1 ciclo de reloj (el temporizador tiene un preescalador de 1). Por supuesto, en este caso, el DMA no puede llegar a tiempo para transferir los datos para el siguiente pulso, y habrá fallas en la salida. Dado que mi aplicación puede tolerar pequeños errores de tiempo infrecuentes, preproceso mis pulsos de modo que el intervalo de pulso más pequeño sea un mínimo fijo (estoy usando 96 tics de reloj en este momento) para darle una oportunidad al DMA. Sin embargo, sigo teniendo fallas que creo que se deben a la DMA, y aumentar el tiempo mínimo incluso a números muy grandes no parece estar ayudando.
La parte clave de la oración anterior es "... Creo que ...". Así que me gustaría encontrar una manera de saber con seguridad si el DMA ha perdido su transferencia o no, preferiblemente algo que pueda dejar en el código siempre ejecutándose, de modo que encuentre fallas muy poco frecuentes.
Lo que he pensado/probado hasta ahora es:
Por supuesto, las fallas que veo podrían deberse a un error en el código que genera los búferes que el DMA envía a los temporizadores. Pero esa es exactamente la razón por la que me gustaría saber con seguridad si al DMA le faltan algunas transferencias o no, para saber si estoy buscando un error en mi código o no. El código en sí pasa un conjunto razonablemente completo de pruebas unitarias, por lo que el error sería sutil si estuviera allí.
Entonces, ¿alguna idea para verificar si mi problema se debe a fallas de DMA o no?
Estoy generando una serie de pulsos, cada uno determinado por la duración de la fase de "encendido" (ancho de pulso) y el tiempo (en tictac del reloj) hasta el siguiente pulso. En términos de estructuras de datos
struct pulse {
/*
* These are an image of the timer registers.
* We don't use repeats but it must be there
* since the DMA transfers it anyway
*
* TODO: support other capture/compare channels than 1
*/
uint32_t length; //ARR
uint32_t repeats; //RCR, we don't use this
uint32_t pulsewidth; //CCR1
};
//Check that pulse is of the correct type for the DMA stream
static_assert(std::is_pod<pulse>::value, "pulse must be POD");
static_assert(sizeof(pulse) == 12, "pulse must not be packed");
pulse pulseArray[MAX_PULSES];
Entonces, el modo ráfaga de TIM8 (y correspondientemente, TIM1) nos permite configurar el siguiente esquema:
pulseArray
el registro TIM8_DMAR "Dirección DMA para transferencia completa", en modo circular (por lo que, por supuesto, tengo que seguir llenándolo con nuevos datos de manera circular similar)length
, repeats
y pulsewidth
, y el modo ráfaga dirige estos a los registros TIM8_ARR, TIM8_RCR y TIM8_CCR1 . Dado que hemos habilitado la precarga tanto en el temporizador (TIM8_CR1 bit 7) como en el canal de comparación (TIM8_CCMR1 bit 3), los datos se transfieren al registro de precarga y se activan en la próxima actualización (es decir, cuando se completa el pulso actual). )Las Figuras 30 y 33 en la nota de la aplicación son muy esclarecedoras para comprender lo anterior.
Y ahora puedo exponer el problema con un poco más de detalle: suponga length
que es, por ejemplo, 1. Luego, la cantidad de ciclos de reloj disponibles para que el DMA transfiera el siguiente pulso (que en realidad está 2 posiciones por delante en el búfer debido a la precarga registros, pero eso no es importante aquí) es uno (suponiendo que el preescalador del temporizador es 1, que es en este caso). Obviamente, es imposible que el DMA transfiera 3 palabras de 16 bits en un ciclo de reloj y, por lo tanto, se repiten los valores del ciclo anterior. Llamemos a eso una "falla de DMA", a falta de un término mejor.
Por otro lado, debe haber un mínimo length
tal que durante cualquier pulso más largo que ese, el DMA tendrá tiempo para transferir todos los datos. Desafortunadamente, este mínimo length
depende de los horarios exactos de los autobuses, otro tráfico DMA y sus prioridades, por lo que es realmente difícil determinar esa longitud con lápiz y papel.
Por lo tanto, me gustaría encontrar una manera de detectar, con la mayor certeza posible, que no ha ocurrido una "falla de DMA", para poder ajustar mi mínimo length
y, al mismo tiempo, asegurarme de que se produzcan otros problemas técnicos. que estoy viendo no se deben a una "falla de DMA".
Usas DMA en modo circular; ¿Cómo se determina el tiempo de actualización de los registros? Como solo usa 1 búfer, no hay un tiempo tan trivial que no cause fallas. Solo se acercará más a estar libre de fallas al aumentar la precisión del tiempo, pero nunca exactamente libre de fallas en términos de probabilidad.
Hay una función de almacenamiento en búfer doble fácil de usar en los dispositivos stm32f4. Pero en los dispositivos stm32f1, hay elementos para hacerlo manualmente. Tiene los disparadores de interrupción de media transferencia y fin de transferencia allí en el periférico DMA. Diseñe el búfer circular como dos conjuntos de registros, como con cada interrupción, intercambie el puntero del búfer que asumió que DMA lo lea y que la CPU lo actualice.
Cuando tiene un conjunto complejo de problemas de tiempo superpuestos, su mejor opción es configurar un hipervisor, un "jefe de jefes". Está creando un semáforo maestro de 4 fases que repite la cuenta 0-3 sin fin.
Los llamo Fase A, B, C, D. Actúan como habilitadores para que se ejecuten subprocesos específicos. Linux y Android tienen esto incorporado. Lo he usado en LabView y MPLAB. Cualquier proyecto complejo tiene un reloj de 4 fases para cronometrar eventos. Un RTC de facto.
En el peor de los casos, algún fragmento de código tiene que esperar su turno, pero no tiene conflictos cuando se ejecuta.
Etapas:
A. Leer el registro de la ejecución anterior, luego configurar comprobaciones condicionales y preestablecer/restablecer valores cruciales. Ventana abierta para que los ISR se ejecuten solo durante la fase A.
B. Ejecute el código determinista crucial a continuación. Lea cualquier resultado de ISR. Esto determina parte de lo que se ejecuta a continuación, según los ciclos de reloj consumidos y la prioridad. Los controladores de errores se ejecutan primero.
C. Ejecute el código principal en función de los resultados de las fases A y B. Esto incluye reanudar o iniciar los modos de ráfaga DMA, ahora que tiene intervalos de tiempo basados en el tiempo de ejecución de la fase C. Escriba el código para que verifique las banderas de DMA antes de que se agote el tiempo de espera de la fase C.
D. Detener las transferencias DMA. Compruebe si hay errores. Compruebe los temporizadores de 'sombra'. Compruebe si hay sobreejecuciones o si hay tiempo para una ráfaga DMA corta para completar un paquete. Haga CRC y cualquier indicador de error, ahora que tiene un intervalo de tiempo para ellos. Aquí es donde encontraría un error de DMA no coincidente frente a un contador de sombras, etc.
Deje un código numérico o de registro para el estado de aprobación/rechazo y si está bien que la fase B ejecute la ruta normal o un controlador de errores.
jms
Timo
0___________
Timo
pgvoorhees
Tony Estuardo EE75