fsr
Posts: 88
Joined: Wed Jan 13, 2016 2:29 am

spi_bcm2835 & 3wire mode support

Wed Jan 13, 2016 2:42 am

Hi,

I've encountered and error which I am unable to resolve and was wondering if anyone can shed some light on it.

According to the kernel sources the spi_bcm2835 module supports wire mode https://github.com/raspberrypi/linux/bl ... -bcm2835.c

Code: Select all

#define BCM2835_SPI_MODE_BITS	(SPI_CPOL | SPI_CPHA | SPI_CS_HIGH \
				| SPI_NO_CS | SPI_3WIRE)
I can set the flag without error but an actual transfer fails with errno 0x16 'Invalid Parameter'.

Here's my current config - have I missed something obvious?

Code: Select all

fraser@rpi:~/src/ref $ uname -a
Linux rpi 4.1.13-v7+ #826 SMP PREEMPT Fri Nov 13 20:19:03 GMT 2015 armv7l GNU/Linux

fraser@rpi:~/src/ref $ grep spi /boot/config.txt
dtparam=spi=on

fraser@rpi:~/src/ref $ lsmod | grep spi
spi_bcm2835             7216  0 

fraser@rpi:~/src/ref $ grep spi /etc/group
spi:x:999:pi,fraser

fraser@rpi:~/src/ref $ ls -l /dev/spi*
crw-rw---- 1 root spi 153, 0 Jan 13 13:11 /dev/spidev0.0
crw-rw---- 1 root spi 153, 1 Jan 13 13:11 /dev/spidev0.1

fraser@rpi:~/src/ref $ ./spidev_test -D /dev/spidev0.0 
spi mode: 0
bits per word: 8
max speed: 500000 Hz (500 KHz)

00 00 00 00 00 00 
00 00 00 00 00 00 
00 00 00 00 00 00 
00 00 00 00 00 00 
00 00 00 00 00 00 
00 00 00 00 00 00 
00 00 

fraser@rpi:~/src/ref $ ./spidev_test -D /dev/spidev0.0 -3
spi mode: 16
bits per word: 8
max speed: 500000 Hz (500 KHz)
can't send spi message: Invalid argument
Aborted

User avatar
joan
Posts: 14935
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: spi_bcm2835 & 3wire mode support

Wed Jan 13, 2016 10:55 am

SPI_CS_HIGH and SPI_NO_CS sound like incompatible flags. Are you sure it's complaining about 3-wire mode?

I see nothing specifying the number of bytes to be sent before switching to 3-wire to receive. How does the interface work?

fsr
Posts: 88
Joined: Wed Jan 13, 2016 2:29 am

Re: spi_bcm2835 & 3wire mode support

Wed Jan 13, 2016 12:41 pm

joan wrote:SPI_CS_HIGH and SPI_NO_CS sound like incompatible flags.
They are modes the driver can support. The BCM2835_SPI_MODE_BITS just a list of capabilities for spidev (which actually creates the /dev node). see line 783 of the spi-bcm2835.c when this list is given to spidev as part of the initialiation.

I'm only setting 3wire with the -3 as per:
./spidev_test -D /dev/spidev0.0 -3
you'll se the output of that command shows the mode is = 16 ( = 0x10 = just SPI_3WIRE )
(FYI the spidev_test program is part of the kernel documentation https://www.kernel.org/doc/Documentatio ... dev_test.c I'm just using it as it rules out anything bodgy I may have done :)
joan wrote:I see nothing specifying the number of bytes to be sent before switching to 3-wire to receive. How does the interface work?
ah ok, thats a thought.
by default the spi messages are full duplex. I thought (assumed!) with 3wire mode enabled the driver would be smart enough to do the sending and then do the receiving - perhaps not. And yes good point, how does it know how many bytes to transfer in each direction? a message only has one length. The spidev.h does vaguely mention half duplex so I'll explore that a little and see where it takes me.

cheers,
F.

User avatar
joan
Posts: 14935
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: spi_bcm2835 & 3wire mode support

Wed Jan 13, 2016 12:54 pm

For reference I drive the device directly and implement 3-wire mode. See http://abyz.co.uk/rpi/pigpio/cif.html#spiOpen

fsr
Posts: 88
Joined: Wed Jan 13, 2016 2:29 am

Re: spi_bcm2835 & 3wire mode support

Thu Jan 14, 2016 12:23 am

FYI I think I have sussed it out now.
I still need to do some testing but I can at least send and receive messages without triggering errors now.

For future reference:
The messages need to be constructed in a particular way when 3wire mode is active.

This way does not work (I assume as it implies a full duplex transaction which can't be done in 3wire mode):

Code: Select all

ioctl(fd, SPI_IOC_RD_MODE32, 0x10); //enable 3wire mode

struct spi_ioc_transfer tr;  //structure to hold message

tr.tx_buf = (unsigned long)tx; //pointer to send buffer 
tr.rx_buf = (unsigned long)rx; //pointer to receive buffer 
tr.len = len;  //size of the buffers

ioctl(fd, SPI_IOC_MESSAGE(1), &tr); //results in errno0x16 invalid argument
This way is correct as it implies a half duplex transaction

Code: Select all

ioctl(fd, SPI_IOC_RD_MODE32, 0x10); //enable 3wire mode

struct spi_ioc_transfer tr[2];  //structure to hold messages

tr[0].tx_buf = (unsigned long)tx; //pointer to send buffer 
tr[0].rx_buf = 0;
tr[0].len = len;  //size of the transmit buffer

tr[1].tx_buf = 0;
tr[1].rx_buf = (unsigned long)rx; //pointer to receive buffer 
tr[1].len = len;  //size of the receive buffer

ioctl(fd, SPI_IOC_MESSAGE(2), &tr); //no error
Also note it would appear the spidev_test.c sample that is part of the kernel documentation does not work in 3wire mode and so can't be considered a true test.

Return to “Interfacing (DSI, CSI, I2C, etc.)”