Iskuri
Posts: 4
Joined: Mon Jun 25, 2012 7:57 pm

3-Wire SPI Interfacing

Wed Apr 18, 2018 1:01 pm

Hi All, I am currently working with an RF module that can only operate using Half Duplex (3-Wire) SPI. When I configure my Pi A to use 3-Wire mode, however, it seems to only send the Chip Enable and Clock data over the pins, completely ignoring the MOMI pin. The Pi works absolutely fine in standard Full Duplex mode so I was wondering if there's something I am doing wrong. Thanks.

Here's the configuration code:

Code: Select all

	mode |= SPI_3WIRE;
	/*
	 * spi mode
	 */
	ret = ioctl(fd, SPI_IOC_WR_MODE32, &mode);
	if (ret == -1)
		pabort("can't set spi mode");

	ret = ioctl(fd, SPI_IOC_RD_MODE32, &mode);
	if (ret == -1)
		pabort("can't get spi mode");

	/*
	 * bits per word
	 */
	ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
	if (ret == -1)
		pabort("can't set bits per word");

	ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
	if (ret == -1)
		pabort("can't get bits per word");

	/*
	 * max speed hz
	 */
	ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
	if (ret == -1)
		pabort("can't set max speed hz");

	ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
	if (ret == -1)
		pabort("can't get max speed hz");

Here's the transfer code:

Code: Select all

void transfer(unsigned char* tx, uint8_t txSize, unsigned char* rx, uint8_t rxSize) {
	int ret;

	int i = 0;
	/*
	printf("Tx: ");
	for(i = 0 ; i < txSize ; i++) {
		printf("%02x ",tx[i]);
	}
	printf("\n");
	*/
	/*
	struct spi_ioc_transfer tr = {
		.tx_buf = (unsigned long)tx,
		.rx_buf = (unsigned long)rx,
		.len = txSize+rxSize,
		.delay_usecs = delay,
		.speed_hz = speed,
		.bits_per_word = bits,
	};
	*/


	struct spi_ioc_transfer tr[2];
	memset(tr,0,sizeof(struct spi_ioc_transfer)*2);

	tr[0].tx_buf = (unsigned long)tx;
	tr[0].len = txSize;
	tr[0].rx_buf = 0;
	tr[0].delay_usecs = delay;
	tr[0].speed_hz = speed;
	tr[0].bits_per_word = bits;

	if(rxSize > 0) {
        	tr[1].tx_buf = 0;
        	tr[1].len = rxSize;
        	tr[1].rx_buf = (unsigned long)rx;
        	tr[1].delay_usecs = delay;
        	tr[1].speed_hz = speed;
        	tr[1].bits_per_word = bits;
	} else {

		ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr[0]);
		if (ret < 1) {
			pabort("can't send spi message");
		}

	}

	if(rxSize > 0) {

        	ret = ioctl(fd, SPI_IOC_MESSAGE(2), &tr);
        	if (ret < 1) {
        	        pabort("can't send spi message");
	        }

		printf("Rx: ");
		for(int i = 0 ; i < rxSize ; i++) {
			printf("%02x ",rx[i]);
		}
		printf("\n");
	}
}
And finally, my currently in use modules:

Code: Select all

Module                  Size  Used by
spidev                  7220  0
cfg80211              542242  0
rfkill                 21476  2 cfg80211
snd_bcm2835            23054  0
snd_pcm                89526  1 snd_bcm2835
snd_timer              22396  1 snd_pcm
snd                    59578  3 snd_timer,snd_bcm2835,snd_pcm
spi_bcm2835             7456  0
uio_pdrv_genirq         3718  0
uio                     9901  1 uio_pdrv_genirq
fixed                   3033  0
ip_tables              12395  0
x_tables               21417  1 ip_tables
ipv6                  399486  25

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

Re: 3-Wire SPI Interfacing

Wed Apr 18, 2018 1:33 pm

Is 3-wire operation supported by the Linux kernel SPI driver?

My pigpio userland driver has support for 3-wire operation on the main SPI device (3-wire is not supported by the auxiliary SPI).

Iskuri
Posts: 4
Joined: Mon Jun 25, 2012 7:57 pm

Re: 3-Wire SPI Interfacing

Wed Apr 18, 2018 2:00 pm

I believe it does, unless there is a weakness in the implementation. I initially attempted this with my Beaglebone Black which responded with an error message that 3-wire mode was unsupported when I tried to initialise it, however the Pi just accepts it and doesn't transmit any of the MOSI command. I will try giving it a go with the pigpio driver as well. Thanks!

Iskuri
Posts: 4
Joined: Mon Jun 25, 2012 7:57 pm

Re: 3-Wire SPI Interfacing

Wed Apr 18, 2018 3:56 pm

Are you sure PIGPIO works for 3-wire? I just attempted it and it has the same issue as standard spidev. Here's my code for initialising the thing:

Code: Select all

	if (gpioInitialise() < 0) {
		printf("Gpio init failed\n");
		return 1;
	}
	fd = spiOpen(0,speed,(1<<9));
	printf("Got fd: %d\n",fd);
EDIT: Ignore me, I had written my code wrong, you need to make sure to add the number of write bits:

Code: Select all

W is 0 if the device is not 3-wire, 1 if the device is 3-wire. Standard SPI device only. 

nnnn defines the number of bytes (0-15) to write before switching the MOSI line to MISO to read data. This field is ignored if W is not set. Standard SPI device only. 

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

Re: 3-Wire SPI Interfacing

Wed Apr 18, 2018 5:31 pm

Thinking about it you might need to send two segments to the Linux kernel SPI driver. The first with the bytes to write, the second with the bytes to read.

I'm assuming SPI supports a chain of SPI commands in one transaction like the I2C driver does.

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