SPI woes

I’m looking for help from someone much more knowledgeable than me, or my BBB is likely to go flying out the window!

I am running Debian Jessie 8.2 (BBB-eMMC-flasher-debian-8.2-lxqt-4gb-armhf-2015-09-20-4gb) on a BBB Rev C, with Kernel 4.0.8 PREEMPT RT.

I have spidev1.0 enabled, currently connected with an SPI device (a MS5611 barometer, if curious). In running my code, sometimes my SPI command goes out, sometimes nothing goes out (all channels are flat-lined). I would say only about 1/3 of the commands actually go out. It is very repeatable (exactly the same behavior every time the code runs), and depends on what I compile in my code (e.g., if I add a printf statement, different SPI commands will fail).

Here is an example from a logic analyzer. SPI is in mode 3, command is 0xA2, exactly what I wanted to see. I slowed the clock down to 100 kHz even though the barometer is capable of 20 MHz, just to make sure the timing wasn’t too fast for the BBB. (Note: the reply is supposed to be 2 bytes, but clock stops after 1 byte, so clearly I need to figure out what is going on there. However, at least the right command is going out the door.)

When it doesn’t work, the ioctl command returns -1 and the logic analyzer shows that all of the signals are flatline. Not even the clock or CS lines! This is what happens at least 2 out of 3 times, maybe more.

Here is a snippet from the code, in case someone spots an error. In this case, Tx would have been 0xA2, and length would be 2.

transfer.tx_buf = (unsigned long) send; //the buffer for sending data
transfer.rx_buf = (unsigned long) receive; //the buffer for receiving data
transfer.len = length; //the length of buffer
transfer.speed_hz = 100000; //the speed in Hz
transfer.bits_per_word = 8; //bits per word
transfer.delay_usecs = 0; //delay in us

// send the SPI message (all of the above fields, inc. buffers)
int status = ioctl(fd, SPI_IOC_MESSAGE(1), &transfer);


So… HELP!! Does anyone have a reasonable idea on what might be happening, or how I can fix it? I’ve wasted more than 24 solid hours trying to debug this, to no avail. I’m counting on you experts out there…

Thanks (in advance)!!

#include <errno.h>

. . .

int status = ioctl(fd, SPI_IOC_MESSAGE(1), &transfer);
if(status == -1)
/* Google the error messege - Pay close attention to stackoverflow.com posts */
printf(“error: %s\n”, strerror(errno));

. . .


William, thank you so much! That put me on the right track. The error I got was “invalid argument”. The ioctl was within a function. Not really sure what the exact problem was, but taking it out of the function fixed the problem. Perhaps it was recasting the &transfer address, which is sometimes 8 bit, 16, or 24 bit depending on the command. Anyway, seems to be working now!

printf() debugging at it’s best :wink: You’re welcome hehe

You need to set the tx_nbits and rx_nbits fields of the spi_ioc_transfer struct
to something valid. Zero will do.

If you are using a stack variable (which your code snippet implied), the tx_nbits
and rx_nbits fields are likely random garbage.

You might just be getting lucky now.

— from include/uapi/linux/spi/spidev.h

struct spi_ioc_transfer {
__u64 tx_buf;
__u64 rx_buf;
__u32 len;
__u32 speed_hz;
__u16 delay_usecs;
__u8 bits_per_word;
__u8 cs_change;
__u8 tx_nbits;
__u8 rx_nbits;
__u16 pad;

— from include/linux/spi/spi.h


  • struct spi_transfer - a read/write buffer pair
  • @tx_buf: data to be written (dma-safe memory), or NULL
  • @rx_buf: data to be read (dma-safe memory), or NULL
  • @tx_dma: DMA address of tx_buf, if @spi_message.is_dma_mapped
  • @rx_dma: DMA address of rx_buf, if @spi_message.is_dma_mapped
  • @tx_nbits: number of bits used for writing. If 0 the default
  • (SPI_NBITS_SINGLE) is used.
  • @rx_nbits: number of bits used for reading. If 0 the default
  • (SPI_NBITS_SINGLE) is used.

— from drivers/spi/spi.c

2013 static int __spi_validate(struct spi_device *spi, struct spi_message message)
2014 {

2087 /
check transfer tx/rx_nbits:
2088 * 1. check the value matches one of single, dual and quad
2089 * 2. check tx/rx_nbits match the mode in spi_device
2090 /
2091 if (xfer->tx_buf) {
2092 if (xfer->tx_nbits != SPI_NBITS_SINGLE &&
2093 xfer->tx_nbits != SPI_NBITS_DUAL &&
2094 xfer->tx_nbits != SPI_NBITS_QUAD)
2095 return -EINVAL;
2096 if ((xfer->tx_nbits == SPI_NBITS_DUAL) &&
2097 !(spi->mode & (SPI_TX_DUAL | SPI_TX_QUAD)))
2098 return -EINVAL;
2099 if ((xfer->tx_nbits == SPI_NBITS_QUAD) &&
2100 !(spi->mode & SPI_TX_QUAD))
2101 return -EINVAL;
2102 }
2103 /
check transfer rx_nbits */
2104 if (xfer->rx_buf) {
2105 if (xfer->rx_nbits != SPI_NBITS_SINGLE &&
2106 xfer->rx_nbits != SPI_NBITS_DUAL &&
2107 xfer->rx_nbits != SPI_NBITS_QUAD)
2108 return -EINVAL;
2109 if ((xfer->rx_nbits == SPI_NBITS_DUAL) &&
2110 !(spi->mode & (SPI_RX_DUAL | SPI_RX_QUAD)))
2111 return -EINVAL;
2112 if ((xfer->rx_nbits == SPI_NBITS_QUAD) &&
2113 !(spi->mode & SPI_RX_QUAD))
2114 return -EINVAL;
2115 }

Better would be to first zero-initialize the entire struct using memset. Then you set the non-zero fields.

Thanks Scott and Emile! I am now zero initializing the structure. Everything seems to be working now. Quite a learning experience! Watching the io on the logic analyzer has definitely helped me understand how this all works. Another issue I mentioned in my original post was that I wasn't getting two bytes on the rx like I was expecting. Now I have figured out that len needs to be 3, not 2 (one for the outgoing byte and two for the return). Anyway, thanks again, all is good!