Problema de retraso de bytes SPI consecutivos FT232HL FTDI

Tengo un problema con el ic FTDI FT232HL.

La aplicación de Windows envía datos al chip a través de USB y el chip envía los datos con un canal SPI.

Verifiqué con un analizador lógico, los bytes se envían correctamente y el reloj SPI coincide con la configuración. Sin embargo, entre cada byte, hay un retraso de 64uS, lo que significa que no importa qué tan alto sea el reloj SPI, la transferencia de datos toma minutos en lugar de segundos.

Me imaginé que tal vez jugar con channelConf.LatencyTimer ayudaría, pero no muestra ninguna diferencia sin importar el valor utilizado (10, 128, 255), el retraso permanece 64uS entre bytes consecutivos.

Debe haber algo que arreglar porque hay numerosos ejemplos de personas que alcanzan altas tasas de transferencia. Además, la demora entre bytes debería ser una configuración en alguna parte.

He usado código de muestra proporcionado con sample-dynamic.c El flujo de bytes se envía con una sola llamada a p_SPI_Write() con una longitud total de 2048 bytes. Probé otra longitud (256, 8192, etc.) sin cambios. Aquí está la configuración utilizada:

channelConf.ClockRate = 5000*1000; 
channelConf.LatencyTimer= 10; 
channelConf.configOptions = SPI_CONFIG_OPTION_MODE0| SPI_CONFIG_OPTION_CS_DBUS3/*|*/ ;
channelConf.Pin = 0x00000000; /* FinalVal-FinalDir-InitVal-InitDir (for dir: 0=in, 1=out) */

SO: Windows7 X64 Compiler: GCC Library y código de: http://www.ftdichip.com/Support/SoftwareExamples/MPSSE/LibMPSSE-SPI.htm

FYI: Me puse en contacto con el soporte de FTDI, me pidieron que actualizara las bibliotecas a la última (lo cual hice), luego no brindaron más soporte.

Cualquier ayuda apreciada. Gracias.

¿Cómo estás entregando los datos a los conductores? ¿En qué tamaño de trozos? Los periféricos USB pueden ralentizarse si solo se mueve un byte o unos pocos por cuadro. En los primeros días, antes de que la gente entendiera los problemas, agregar un convertidor serie USB a algo que funcionaba bien en un puerto serie de bus local podría romper por completo su usabilidad. Eso a menudo se entiende ahora, pero SPI puede implicar la coordinación con otras señales como selecciones, por lo que es fácil imaginarse obstaculizado por el encuadre del bus e incapaz de aprovechar la tasa de datos teórica que se aplicaría a transferencias más grandes.
Hola. Como está escrito en el mensaje, no importa cuántos bytes se envíen a la vez, el retraso es el mismo. Normalmente envío 2048 bytes por llamada p_SPI_Write(). Además, uso el modo maestro spi, solo escritura, no debería haber ningún apretón de manos involucrado. Gracias.
¿Está seguro de que el lado USB está enviando datos lo suficientemente rápido? ¿Por qué está mirando solo el lado SPI de su puente?
No tengo idea de qué tan rápido el USB transmite los datos. Compruebo el lado SPI porque ese es el final de la línea y puedo verificar fácilmente con el analizador lógico. De todos modos, no veo cómo la transferencia USB podría ser más lenta que 16 KByte/seg. Escuché de los muchachos de FTDI, sugieren que no use su biblioteca (pero no dijeron claramente que su biblioteca tenía errores). Todavía me sorprende que algunas personas puedan alcanzar altas velocidades de transferencia desde el primer momento y usando la lib provista. Empieza a ser demasiado complicado para un simple trabajo de transferencia USB<>SPI.
Estoy en la misma situación. El SPI_ToggleCS() definitivamente es lento debido a la suspensión, pero después de haberlo refactorizado a solo llamadas Ft_Write, todavía hay un largo retraso entre lo que se ve en el analizador lógico y el pin Cs que baja al reloj + datos de alrededor de 100us en mi caso. Muy molesto y frustrado!
Me puse en contacto con FTDI y tuve algo de apoyo. Después de haber escrito la lógica usando las funciones FT_Write () de nivel inferior, todavía veía un retraso de> 60us desde CS bajo hasta la transmisión de datos. Me preguntaba y confirmaron que el problema tiene que ver con los marcos de transmisión de datos USB. Debe empaquetar los mensajes en el mismo marco de datos a través del bus USB para obtener el ancho de banda USB requerido, es decir, cree un solo búfer para contener sus comandos CS y los búferes de datos y luego use FT_Write para enviar. Si sabe lo suficiente sobre USB, puede asegurarse de maximizar cada marco de datos para obtener un ancho de banda completo.

Respuestas (4)

Por lo general, trabajo con el chip FT2232H, pero saqué un chip FT232HQ solo para poder verificar este problema que estaba teniendo. Es el mismo chip que el chip FT232HL que tiene, solo que en un paquete QFN en lugar de un QFP.

Intenté recrear el problema que describes, pero no pude hacerlo exactamente. Así es como se veía en mi analizador lógico cuando emitía 6 bytes a la vez a una velocidad de reloj de 5 MHz. Hay un pequeño retraso entre bytes, pero no tan grande como 64us.

Ejemplo de transmisión multibyte de 5 MHz

Aquí hay algunas cosas para comprobar.

  • El temporizador de latencia realmente no debería importar porque es simplemente un tiempo de espera antes de que el USB envíe un paquete incompleto. Lo tengo configurado en 255, pero a menudo para cosas sensibles al tiempo, lo tengo más bajo (2 -10)
  • Pruebe primero con una velocidad de reloj más lenta solo para probar y asegurarse de que el dispositivo se esté comunicando correctamente (supongo que lo hizo, solo estoy agregando esto en caso de que alguien más no lo haya intentado todavía).
  • Las direcciones de los pines no importan, excepto para GPIO, ya que la biblioteca SPI las sobrescribe.
  • En lugar de p_SPI_Write(), use SPI_Write(). Si realiza una sola llamada, agregue el chip apropiado, seleccione las banderas de activación y desactivación (consulte a continuación). Si realiza varias llamadas, asegúrese de agregar las banderas de selección de chip a la primera y última llamada de la serie.
  • Asegúrese de pasar el número de bytes para transmitir y establezca la SPI_TRANSFER_OPTIONS_SIZE_IN_BYTESbandera.
  • Si se trata de un diseño de placa personalizado, o si compró una placa adaptadora FT232H con descuento, asegúrese de que tenga la frecuencia de reloj del sistema correcta. Ese retraso entre bytes se basa en cuánto tarda el chip en mover el siguiente byte desde su búfer interno a los registros de desplazamiento de salida. Si el reloj del chip es lento, esto se mostrará en las frecuencias más altas como una brecha más grande entre los bytes. Tenga en cuenta que para SPI no importa si el reloj se estira un poco aquí y allá, ya que se basa en los bordes de la señal del reloj (leer en un borde, propagarse en el otro). Pruebe el cristal o el chip de cristal CMOS y asegúrese de que esté recibiendo 12 MHz.

Aquí hay un código de ejemplo rápido sobre cómo enviar múltiples bytes de datos en caso de que ayude.

uint32 sizeToTransfer = 0;
uint32 sizeTransfered = -1;
uint8 buffer[256]; //Must be large enough for what you are sending.
FT_STATUS status;

//add data
buffer[sizeToTransfer++] = 0x20; //First data byte (can be what you need)
/*
 * More bytes added....
 */
buffer[sizeToTransfer++] = 0x00; //Last data byte (can be what you need)

status = SPI_Write(*handle, buffer, sizeToTransfer, &sizeTransfered,
    SPI_TRANSFER_OPTIONS_SIZE_IN_BYTES |
    SPI_TRANSFER_OPTIONS_CHIPSELECT_ENABLE |
    SPI_TRANSFER_OPTIONS_CHIPSELECT_DISABLE);

//Don't forget to check status. It should equal FT_OK if everything went well

Descargo de responsabilidad: utilizo el FT2232H y no estoy 100 % seguro de si todas las declaraciones se pueden transferir a este caso.

Que yo sepa, ftdi mpsse spi no está realmente optimizado para un alto rendimiento de datos. Esto se vuelve obvio si uno mira más de cerca la fuente. En la versión actual (revisada el 24.04.2019) hay una llamada INFRA_SLEEP(2) dentro de la función SPI_ToggleCS que provoca un retraso de 2 ms cada vez que cambia el estado de la línea CS. Entonces, si necesita pulsar CS para cada palabra, está condenado. Incluso sin la llamada de suspensión, la latencia USB probablemente ralentizará severamente las cosas. Incluso sin esta llamada, el rendimiento máximo cae por debajo de las expectativas durante mis pruebas. Supongo que la razón de esto es la arquitectura de mpsse en sí. Así que, personalmente, recomendaría seguir con los protocolos admitidos de forma nativa como UART, opto rápido o FIFO para aplicaciones de alto rendimiento si es posible.

Por curiosidad (si este caso aún está abierto): ¿comprobó también si había diferentes controladores? ¿Puedes observar el mismo comportamiento para una PC diferente también?

Detrás de cada llamada a libMPSSE hay una llamada a FT_Write() que en realidad escribe un marco de datos en un USB de alta velocidad. Al usar las funciones de la biblioteca, por lo tanto, está restringido por la capa de comunicación USB (según lo informado por el soporte de FTDI).

La solución para obtener la máxima velocidad es bajar el nivel y almacenar los comandos en un gran FT_Write() para que los datos se empaqueten en un marco de transmisión USB:

{
Add to buffer: GPIO Write for CS lo
Add to buffer: Clock N bytes command
Add to buffer: N bytes of data
Add to buffer: GPIO write for CS hi
Add to buffer: Send immediate command (0x87) – only if you read bytes from the LTC device, not if you only write them
++ Repeat for other commands as much as possible for your use-case …
}
FT_Write of the above buffer

Solo para hacerle saber que creo que los controladores FTDI FT232HL tienen errores porque los errores desaparecen/aparecen según el concentrador/marca USB que se usa y la longitud del cable USB y la forma en que la computadora está "preparada". Al final compré una computadora específica y me quedé con ella para la producción en masa.

Algunos ayudantes:

  1. Tiempo de llamadaBeginPeriod(4); antes de cargar los controladores FTDI ayuda mucho, solo tiene que verificar después de que se configuró el período de tiempo porque a veces Windows no puede actualizar el período (por cualquier motivo). Consulte el siguiente código para verificar si timeBeginPeriod(4) funcionó.

    t1d=reloj(); BITBANG_usleep(5*1000);//5ms t2d=reloj(); tgastado=(((float)t2d-(float)t1d) / (CLOCKS_PER_SEC/1000)); if((int)tgastado > 8){ falló, ¡salga o vuelva a intentarlo! }

    void BITBANG_usleep(unsigned int usdelay) { clock_t t1,t2; if(usdelay<16*1000)//16ms {t1=reloj(); while(1){t2=reloj(); if(((t2-t1)/ (CLOCKS_PER_SEC/(1000)))>usdelay/1000){break;}}} else {usleep(usdelay);} }

  2. Use un cable corto y eventualmente use un HUB USB en el camino, mejora en gran medida la estabilidad, puede funcionar directamente durante cientos de horas sin problemas.

Espero que ayude, no es la mejor solución, ¡pero funcionó para mí!