6 votos

Temporización entre palabras en SPI

Estoy tratando de usar un dsPIC33F para generar una señal de datos que no debe ser interrumpida entre palabras. Debe ser un tren continuo de 240 bits. Estoy sincronizando el SPI usando DMA con palabras de 16 bits, pero estoy obteniendo huecos entre las palabras. ¿Hay algo que pueda hacer al respecto o estoy atascado con esto?

¡Aquí está lo de impar! Si lo hago usando una rutina de bucle escribiendo continuamente en el registro SPI, ¡no hay huecos! Así que el DMA está creando estos huecos de alguna manera. Estaría bien si pudiera reducir el uso de la CPU con DMA, ya que un alto uso de la CPU impide que se ejecuten otras tareas en segundo plano.

0 votos

Pon tu código.

0 votos

@Leon Mi código es largo, pero lo he pegado aquí para DMA: pastebin.com/FRzp77EN y aquí para los que no son DMA: pastebin.com/CF1QvaCB

0 votos

¿cuál es la velocidad de instrucción del PIC, cuál es la velocidad del reloj SPI y cuánto duran los intervalos?

3voto

RelaXNow Puntos 1164

Todavía no he utilizado el motor DMA de dsPIC, pero esto me parece sorprendente. Cuando dices "escribiendo continuamente", ¿quieres decir en un bucle esperando la bandera SPIxIF (o como se llame, no lo estoy buscando ahora)? Supongo que es la misma bandera que se utiliza para indicar al motor DMA que transfiera la siguiente palabra.

Si no estás esperando a SPIxIF, entonces estás metiendo datos en el buffer SPI y los bits de salida no son predecibles. Tal vez hay una brecha inherente entre las palabras. ¿Cuál es la tasa de reloj SPI en términos de ciclos de instrucción por reloj?

El SPI es intrínsecamente sincrónico, por lo que un pequeño espacio no debería causar problemas. A menos que estés tratando de usar el periférico SPI para otra cosa que no sea SPI, tu requerimiento no tiene sentido.

0 votos

Estoy usando SPI para generar datos de video para un OSD. Así que debe ser continuo o habrá espacios muy visibles entre los píxeles.

0 votos

Si el modo maestro tiene ese espacio entre palabras (es posible, pero no me convence), entonces puedes probar el modo esclavo en su lugar. Tendrías que alimentar tu propio reloj en el pin de reloj, pero los datos tendrían que salir sincronizados con este reloj. Con la selección de pines periféricos, podrías incluso manejar el pin de entrada del reloj SPI desde una salida PWM sin ninguna conexión externa al pin.

0 votos

Buena idea la de utilizar el modo esclavo. Ten en cuenta que la velocidad de reloj debe ser lo suficientemente lenta para dejar suficiente tiempo de latencia de interrupción para recargar el registro.

1voto

Alex Andronov Puntos 178

En el PIC18xx, el puerto SPI requiere un retraso entre el final de una palabra y el comienzo de la siguiente. IIRC, cuando se ejecuta el puerto SPI a una tasa de un bit por reloj de instrucción, el intento de alimentar más de un byte por doce relojes de instrucción causará la sobrecarga de transmisión y la pérdida de datos. Es bueno saber que no parece tener esa limitación en el dsPIC, pero mi conjetura sería que el DMA no se dispara hasta que el SPI está vacío, lo que termina imponiendo un retraso de un ciclo. No sé cómo funciona el DMA en el dsPIC, pero en algunos procesadores es posible usar un temporizador para operar el DMA; quizás podrías intentar ejecutar el DMA al puerto SPI cada 16 ciclos; si hay un retraso de un ciclo entre la escritura al puerto y el inicio de la transferencia, el segundo tick del temporizador llegaría un ciclo antes de que el puerto SPI hubiera disparado el segundo DMA. Una limitación de este enfoque es que, a menos que el puerto SPI tenga doble búfer, el DMA tendría que ejecutarse con la máxima prioridad. De lo contrario, si una transferencia se produjera tarde, la siguiente transferencia podría causar un desbordamiento del buffer.

0 votos

Los periféricos SPI son a menudo útiles para otros propósitos. En un PSOC, por ejemplo, utilizo un transmisor SPI para generar vídeo. El código está escrito para cargar el registro de transmisión una vez cada dieciséis ciclos sin comprobar si está listo (en ese procesador en particular, una prueba y una bifurcación tardarían 13 ciclos IIRC, así que no habría tiempo para que el código hiciera la prueba).

1voto

Jon Ericson Puntos 9703

En tu línea de escaneo interrumpe donde tienes:

// Then write first DMA words. Successive words requested by SPI module.
// TODO: offset DMA by one word, or we will get duplicates. OR, just write
// a null word first... Whichever is easiest/best.
SPI2BUF = scanline_level_out_dma[0];
SPI1BUF = scanline_mask_out_dma[0];

Sólo hay que poner DMAxCONbits.FORCE = 1 antes de habilitar el canal DMA y forzará el primer byte tan pronto como se habilite el canal DMA.

También puedes configurar tus canales de lectura ficticios en modo continuo (en lugar de un solo disparo) con un búfer de 1 palabra e iniciarlos cuando arranques e ignorar la interrupción, sin necesidad de volver a pensar en ellos después.

En tu ejemplo de DMA todavía tienes definidas las interrupciones SPI:

/**
 * _SPI1Interrupt: Primary ISR for SPI1.
 *
 * Only clears interrupt flag at the moment.
 */
void _MY_ISR _SPI1Interrupt()
{
        uint16_t nullread;
        nullread = SPI1BUF;
        nullread = SPI2BUF;
        IFS0bits.SPI1IF = 0;
}

Creo que esto todavía se llama cuando el DMA está habilitado a menos que deshabilites la interrupción de la CPU, es decir, tanto el controlador DMA como la CPU responden a la interrupción.

Esto parece repetir el trabajo del canal de lectura DMA y encima es lento (basta con usar una variable global en lugar de una de pila en un ISR como este). También puede estar bloqueando tu transferencia, no sé si el controlador DMA reiniciaría la interrupción mientras la CPU estuviera en un manejador para esa interrupción.

¡Aquí está lo de impar! Si lo hago usando una rutina de bucle continuamente escribiendo en el registro SPI, no hay ¡no hay huecos!

Esto es peligroso sin comprobar el SPIxIF, bien podrías estar garbando lo que sale y tus bytes de prueba harían que eso fuera realmente difícil de encontrar.

EDIT : Con tu reloj SPI siendo sólo 1/7 de tu reloj de instrucción necesitas ser muy cuidadoso en el manejador de interrupción de arriba para borrar SPI1IF ASAP o simplemente deshabilitarlo todo junto y dejar que el controlador DMA lo maneje. Para conseguir que no haya ningún hueco tienes menos de 7 ciclos de instrucción para sacar la siguiente palabra de la unidad SPI. Creo que tienes que deshabilitar completamente la intervención de la CPU y dejar que el controlador DMA se encargue de ello para tener alguna posibilidad, pero incluso entonces tienes 2 transferencias DMA y cualquier latencia de activación que haya desde el SPI -> DMA (es 1 reloj de instrucción para que el SPIxIF se dispare, por ejemplo). Así que va a estar cerca.

0voto

Sólo hay que poner DMAxCONbits.FORCE = 1 antes de habilitar el canal DMA y se forzará el primer byte en cuanto se habilite el canal DMA.

Estas dos líneas me han ahorrado 2 días de problemas. Estaba intentando una implementación de SPI basada en DMA en PIC32 MZ. Había habilitado el canal y luego forzado un inicio. A bajos relojes SPI de menos de 1 Mhz todo estaba bien. Una vez que probé con más de 2 mhz (Mi requerimiento es de 20 Mhz) empecé a tener el primer carácter solo repetido y el resto estaba bien. Ahora que cambié el orden y probé todo va bien. Sólo quería expresar mi agradecimiento.

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X