I am using SPI to write and read the data from slave board. I have two questions regarding this process:

Question 1. Why when I am trying to read 1.000.000 bytes through SPI using 1 message, it works slower than if I divide them into K messages?

I tried to play around difference between one huge message per transaction and multiple smaller messages (which in total gives us exactly the same sized buffers) and I observed that lesser the message size, faster SPI transaction speed.

Question 2. Is there a way to increase SPI transaction speed?

According to my tests, pure SPI transaction for 1.000.000 bytes divided into 500 packages is taking 380ms of working time.

Which means that I have 8.000.000 bits per 0.38 seconds => 20.55 Mbits/sec, which is less than 48Mbits/sec that it is supposed to be.

In addition here is some pieces of code that I used to compare and test:

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");

Any ideas?

OS: Linux 3.15.10-bone8 #1 Fri Sep 26 14:20:19 PDT 2014 armv7l GNU/Linux
CPU: Sitara AM3358 1GHz. According to this CPU, SPI clock max speed is 48MHz.

Code is written in C++ and using spidev.h library.

Basically the board is BeagleBone Black.

UPDATE 2: I have done lots of tests and figured out what is going on and what causes the problem.

The reason is gaps between byte readings. To be more specific lets review the code lines: read_xfer.rx_buf = read_buffer; read_xfer.len = ; read_xfer.speed_hz = 48000000; ioctl(fd, SPI_IOC_MESSAGE(1), &read_xfer);

If we place 100 (which is read 100 bytes) instead of we will see next picture on oscilloscope: Oscilloscope results As you may notice, it reads 8 bits then pause, then reads another 8 bits. So the problem is those gaps between reading the bytes. Here is some test results using oscilloscope:

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

According to results, I cannot read messages larger than 100 000 Bytes in one package, since the gaps will create a huge overhead.

ANOTHER QUESTION What causes those gaps and how to reduce them?

  • \$\begingroup\$ Maybe you could tell us what hardware and software you are using? Looks like some Linux board. In that case: does the documentation state that it can sustain 48 MHz SPI? \$\endgroup\$ Mar 10, 2015 at 21:10
  • \$\begingroup\$ I have updated information. Yes, both documentation and authors saying that 48MHz is supported. When I try to go higher SPI just throws an error. \$\endgroup\$
    – aka
    Mar 10, 2015 at 23:01
  • \$\begingroup\$ Might be good to attach an oscilloscope or logic analyzer and see what is going on at the wire level. \$\endgroup\$ Mar 10, 2015 at 23:10
  • \$\begingroup\$ I checked signals on Oscilloscope and saw that the clock frequency is 48MHz, which is good. But I still have different timings... Tomorrow I will check the gap between message signals. \$\endgroup\$
    – aka
    Mar 11, 2015 at 2:17
  • 2
    \$\begingroup\$ "saying that 48MHz is supported" is not exactly the same as saying that the corresponding throughput can be sustained (including the CPU work in the kernel & user mode). \$\endgroup\$ Mar 11, 2015 at 7:06

2 Answers 2

  1. Because the data is getting buffered somewhere with the larger packets?
  2. How is the SPI configured and to what is it connected?

As a general rule this kind of this is not best done this way. Are you actually using this code to do something or are you just trying to instrument the SPI capabilities? What else is running on the board and how is your kernel configured. What's the processor speed actually set to and what priority is your little app?

If you've got some kind of quasi realtime requirement then I'd suggest looking at

a) DMA (offload transmission from userland buffers)

b) Xenomai

c) (for the madly keen) the PRU

Hope this is not a homework question! Here are 2 links to get you started on A.

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

  • \$\begingroup\$ Thank you for answer, I will review it as soon as possible. I were able to improve speed upto ~28mbit/sec. I will write update on it soon. And no it is not homework question :) It is challenge I am facing at project. \$\endgroup\$
    – aka
    Jun 26, 2015 at 19:41

Your SPI buffer is configured to only send a certain number of bits at a time. It will divide your message into segments of the buffer size as you are seeing on the scope.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.