¿Aumentar la velocidad de transacción de SPI?

Estoy usando SPI para escribir y leer los datos de la placa esclava. Tengo dos preguntas con respecto a este proceso:

Pregunta 1. ¿Por qué cuando estoy tratando de leer 1.000.000 bytes a través de SPI usando 1 mensaje, funciona más lento que si los divido en K mensajes?

Traté de jugar con la diferencia entre un mensaje enorme por transacción y varios mensajes más pequeños (lo que en total nos da exactamente el mismo tamaño de búfer) y observé que a menor tamaño del mensaje, mayor velocidad de transacción SPI.

Pregunta 2. ¿Hay alguna forma de aumentar la velocidad de transacción de SPI?

Según mis pruebas, la transacción SPI pura de 1.000.000 de bytes divididos en 500 paquetes requiere 380 ms de tiempo de trabajo.

Lo que significa que tengo 8.000.000 bits por 0,38 segundos => 20,55 Mbits/seg, que es menos de 48 Mbits/seg que se supone que es.

Además, aquí hay algunos fragmentos de código que usé para comparar y probar:

device = "/dev/spidev1.0";
mode = SPI_MODE_0;
bits = 8;
speed = 48000000;

fd = open(device, O_RDWR);

memset(read_buffer, 0, sizeof(read_buffer));

mass_xfer[0].tx_buf = (unsigned long)tx1;

mass_xfer[0].len = 2;
mass_xfer[0].speed_hz = speed;

// sz - amount of bytes within one SPI message

// n - amount of messages in one SPI transaction

unsigned long sz = 2000, i, n = (nbytes + sz - 1) / sz;
for (i = 0; i < n; i++) {
    mass_xfer[i + 1].rx_buf = (unsigned long)read_buffer + i * sz;
    mass_xfer[i + 1].len = min(sz, nbytes - i * sz);
    mass_xfer[i + 1].speed_hz = speed;
}

timestamp_t t0 = get_timestamp();
ret = ioctl(fd, SPI_IOC_MESSAGE(n + 1), mass_xfer);
// ret = ioctl(fd, SPI_IOC_MESSAGE(1), &read_xfer); // When I tried to read using one message, time was more than 600ms
timestamp_t t1 = get_timestamp();
printf(" SPI READ: %.2f (%d) %d\n", (t1 - t0)/1000.0L, n + 1, SPI_MSGSIZE(n + 1)); // After this output, for 1.000.000 bytes I am receiving 380.xx ms
if (ret < 1)
    pabort("can't send spi message");

¿Algunas ideas?

ACTUALIZACIÓN 1:
SO: Linux 3.15.10-bone8 #1 viernes 26 de septiembre 14:20:19 PDT 2014 armv7l GNU/Linux
CPU: Sitara AM3358 1GHz. De acuerdo con esta CPU, la velocidad máxima del reloj SPI es de 48 MHz.

El código está escrito en C++ y usa la biblioteca spidev.h.

Básicamente la tabla es BeagleBone Black .

ACTUALIZACIÓN 2: Hice muchas pruebas y descubrí qué está pasando y qué causa el problema.

La razón son las brechas entre las lecturas de bytes. Para ser más específicos, revisemos las líneas de código: read_xfer.rx_buf = read_buffer; read_xfer.len = ; read_xfer.speed_hz = 48000000; ioctl(fd, SPI_IOC_MESSAGE(1), &read_xfer);

Si colocamos 100 (que se lee 100 bytes) en lugar de, veremos la siguiente imagen en el osciloscopio: Resultados del osciloscopiocomo puede notar, lee 8 bits, luego hace una pausa y luego lee otros 8 bits. Entonces, el problema son esos espacios entre la lectura de los bytes. Aquí hay algunos resultados de prueba usando un osciloscopio:

  • bytes_amount = gap_width
  • 10 bytes = 480ns
  • 50 bytes = 410ns
  • 100 bytes = 200 ns
  • 50 000 bytes = 200 ns
  • 70 000 bytes = 350 ns

Según los resultados, no puedo leer mensajes de más de 100 000 bytes en un paquete, ya que las brechas crearán una sobrecarga enorme.

OTRA PREGUNTA ¿Qué provoca esas brechas y cómo reducirlas?

¿Tal vez podría decirnos qué hardware y software está utilizando? Parece una placa de Linux. En ese caso: ¿la documentación indica que puede soportar SPI de 48 MHz?
Tengo información actualizada. Sí, tanto la documentación como los autores dicen que se admite 48MHz. Cuando trato de subir un SPI más alto, solo arroja un error.
Podría ser bueno conectar un osciloscopio o un analizador lógico y ver qué sucede a nivel del cable.
Revisé las señales en el osciloscopio y vi que la frecuencia del reloj es de 48 MHz, lo cual es bueno. Pero todavía tengo tiempos diferentes... Mañana revisaré la brecha entre las señales de los mensajes.
"decir que se admite 48 MHz" no es exactamente lo mismo que decir que se puede mantener el rendimiento correspondiente (incluido el trabajo de la CPU en el kernel y el modo de usuario).
Revisé la frecuencia del reloj y, como puede ver en la imagen de arriba, es de 48 MHz y tarda 180 ns en transmitir 8 bits. Por favor, revisa las actualizaciones.

Respuestas (2)

  1. ¿Porque los datos se almacenan en algún lugar con los paquetes más grandes?
  2. ¿Cómo se configura el SPI ya qué se conecta?

Como regla general, este tipo de esto no se hace mejor de esta manera. ¿Realmente está usando este código para hacer algo o simplemente está tratando de instrumentar las capacidades de SPI? Qué más se está ejecutando en la placa y cómo está configurado su kernel. ¿Cuál es la velocidad del procesador configurada realmente y qué prioridad tiene su pequeña aplicación?

Si tiene algún tipo de requisito casi en tiempo real, le sugiero que mire

a) DMA (transmisión de descarga de los búferes de la zona de usuario)

b) Xenomai

c) (para los locos entusiastas) la PRU

¡Espero que esta no sea una pregunta de tarea! Aquí hay 2 enlaces para que comiences con A.

https://stackoverflow.com/questions/3333959/mapping-dma-buffers-to-userspace https://groups.google.com/forum/#!topic/beagleboard/UPbU2WoVzVI

Gracias por la respuesta, lo revisaré lo antes posible. Pude mejorar la velocidad hasta ~28mbit/seg. Escribiré una actualización pronto. Y no, no es una pregunta de tarea :) Es un desafío al que me enfrento en el proyecto.

Su búfer SPI está configurado para enviar solo una cierta cantidad de bits a la vez. Dividirá su mensaje en segmentos del tamaño del búfer como está viendo en el alcance.