How to enable SPI Controller Bidirectional mode (SPI_3WIRE)?


9 posts
by notro » Thu Jan 17, 2013 2:56 pm
The Pi datasheet[1] states that the SPI controller supports Bidirectional mode.
The problem is that I can't find how to enable this.
All I find in the datasheet is the REN (Read Enable) register which decides the direction of the bus line.
I need this so I can read from a 8-bit LCD module (Sainsmart 1.8").

Anybody know how to enable this?


[1] BCM2835 ARM Peripherals, 10.2.2 Bidirectional mode, page 149
http://www.raspberrypi.org/wp-content/u ... herals.pdf

Side note:
I have made a patch for spi-bcm2708.c that enables 9-bit LoSSI write mode, which I needed for a LCD module (Adafruit 2.2").
LoSSi mode by the way is also bidirectional.
I will make a pull request to github.com/raspberrypi/linux when I get both modes working.
Posts: 484
Joined: Tue Oct 16, 2012 6:21 pm
Location: Norway
by bgreat » Thu Jan 17, 2013 4:24 pm
The LCD Module itself is write only. You can not read the content of the display.

If you are attempting to use the SD card feature, then you will need to wire the SD SPI data and clock lines in parallel with the LCD SPI lines, then use the remaining SPI chip select plus the SPI input for the card.

Possible connection (Sainsmart signal names from quick web search):
Code: Select all

For LCD:
  Raspberry Pi        Sainsmart
  ============        =========
  RP CE0,  pin 24 --> pin 7, CS : Chipselect for LCD,
  RP MOSI, pin 19 --> pin 4, SDA : LCD Data for SPI
  RP SCLK, pin 23 --> pin 3, SCL : SCLK for TFT Clock

For SD:
  Raspberry Pi        Sainsmart
  ============        =========
  RP CE1,  pin 26 --> pin 11, CS (SD-CS) : Chipselect for TF Card
  RP SCLK, pin 23 --> pin 9,  SCLK (SD-Clock): SPI Clock
  RP MOSI, pin 19 --> pin 10, MOSI (SD-DI) : SPI Master out Slave in
  RP MISO, pin 21 --> pin 8,  MISO (SD-DO) : SPI Master in Slave out

The Raspberry Pi CEx lines determine which peripheral responds to the SPI clock and data. There is no equivalent MISO connection for the LCD for reading. The current connection for the RS/DC and RES lines for the LCD remain as you would already have them. As shown, /dev/spidev0.1 would access the SD card while /dev/spidev0.0 is used by the LCD.

A read on SPI occurs simultaneously with the write. There is no special setup required when using the spidev drivers. The method for returning the read buffer depends on the program language being used. For Python spidev module, readbytes() returns the read results for a specified count. xfer() and xfer2() write a buffer and return the read buffer.

Enjoy!
Bill
User avatar
Posts: 235
Joined: Mon Jan 23, 2012 2:09 pm
by notro » Thu Jan 17, 2013 5:56 pm
The Sainsmart LCD module[1] uses a ST7735R display controller.
Looking at the datasheet[2] it states that the SDA pin is used as input and output.
According to http://www.whence.com/rpi/ there is no levelshifter that would mess up this functionality.

What I'm after is reading RDDID as described in: 10.1.3 RDDID (04h): Read Display ID

This bidirectional behaviour also applies to the hx8340bn controller used by the Adafruit 2.2" display.
But in this case the read functionality is built into the SPI controller on the rPi through the special LoSSI mode.
Writing 0x04 to the display controller receives 3 bytes back into the FIFO.
I haven't been able to make this work, because a levelshifter on the LCD module blocks the reading, and all I get is the last bit written (00h or FFh).

By the way I'm doing this from a kernel module (framebuffer driver), not using spidev.

Wikipedia has some words on this type of SPI bus[3].
In the Linux SPI framework[4], mode bit SPI_3WIRE is used to ask for this.


From st7735r datasheet:

8.3 Serial interface characteristics (3-line serial)
Timing diagram

9.4 Serial interface
[..]
SDA (serial data input/output)

9.4.2 Read Functions
The read mode of the interface means that the micro controller reads register value from the driver. To achieve read
function, the micro controller first has to send a command (read ID or register command) and then the following byte is
transmitted in the opposite direction. After that CSX is required to go to high before a new command is send (see the below
figure). The driver samples the SDA (input data) at rising edge of SCL, but shifts SDA (output data) at the falling edge of
SCL. Thus the micro controller is supported to read at the rising edge of SCL.
After the read status command has been sent, the SDA line must be set to tri-state no later than at the falling edge of SCL of
the last bit.

[1] Sainsmart 1.8 SPI LCD Module
http://www.sainsmart.com/arduino-compat ... tmega.html

[2] Sitronix ST7735R datasheet
http://www.adafruit.com/datasheets/ST7735R_V0.2.pdf

[3] SPI Three-wire serial buses
http://en.wikipedia.org/wiki/Serial_Per ... rial_buses

[4] definition of struct spi_device
http://lxr.free-electrons.com/source/in ... h?a=sh#L70
Posts: 484
Joined: Tue Oct 16, 2012 6:21 pm
Location: Norway
by bgreat » Thu Jan 17, 2013 7:01 pm
Interesting. I did not have that information.

Using the Python spidev module, you set SPI to three wire mode using the 'threewire' boolean. Looking at the code, this calls the spidev ioctl with SPI_3WIRE set. Walking the kernel sources should let you see what register configuration this requires.

For the Sainsmart display, is the mode jumper accessible for enabling 3-wire SPI mode? If so, I may get one of these to experiment with myself.

Enjoy!
Bill
User avatar
Posts: 235
Joined: Mon Jan 23, 2012 2:09 pm
by notro » Thu Jan 17, 2013 7:33 pm
Looking at the code, this calls the spidev ioctl with SPI_3WIRE set. Walking the kernel sources should let you see what register configuration this requires.

The spidev ioctl then calls spi_setup()[1] in spi.c which calls bcm2708_spi_setup() which calls bcm2708_setup_state in spi-bcm2708.c[2].
My problem is that the spi-bcm2708.c controller driver doesn't support the SPI_3WIRE mode bit. This is what I want to fix. But I don't know which register to set to enable this on the rPi SPI Controller hardware.

For the Sainsmart display, is the mode jumper accessible for enabling 3-wire SPI mode?

The enabling has to happen on the master side (ie. for this discussion the SPI controller hardware).
The display is already able to do this. Writing a 04h command, sets the display controller in a mode where it awaits 24 clock cycles to send data back. For this to work the SPI controller must change its MOSI (MasterOutSlaveIn) pin to input.
The rPi datasheet calls the pin MIMO (MasterInMasterOut) in this mode. Wikipedia calls it SISO (SlaveInSlaveOut). Both pointing to the fact that the data flows both ways.


[1] definition of spi_setup()
http://lxr.free-electrons.com/source/dr ... pi.c#L1156

[2] spi-bcm2708.c
https://github.com/raspberrypi/linux/bl ... -bcm2708.c
Posts: 484
Joined: Tue Oct 16, 2012 6:21 pm
Location: Norway
by bgreat » Thu Jan 17, 2013 11:16 pm
Reading the LCD controller data sheet, it appears to be operating in bidirectional 4-wire mode and not 3-wire mode. You should be able to set the REN bit to read per 10.2.2 of the Broadcom Raspberry Pi document. This will tristate the data pin while you generate a dummy transaction to clock in the read data to the FIFO.

Enjoy!
Bill
User avatar
Posts: 235
Joined: Mon Jan 23, 2012 2:09 pm
by notro » Fri Jan 18, 2013 12:06 am
You should be able to set the REN bit to read per 10.2.2 of the Broadcom Raspberry Pi document.

Thank you so much, I have read that section a couple of times, thinking I didn't understanding what it said. What tricked me was that REN is 0x1 on reset, meaning it is in receive mode.
Also the wording
read enable if you are using bidirectional mode.

from the REN description, implied to me that I needed to set this mode somewhere else.
But in the driver code, CS is set to zero before the individual bits are applied, so REN is zero there.
I will try setting this bit when reading in the driver and see what happens.

Reading the LCD controller data sheet, it appears to be operating in bidirectional 4-wire mode and not 3-wire mode.

Yes, you're right about this one too. I picked the wrong section when writing the post. It should have been 8.4 Serial interface characteristics (4-line serial)

Again, thanks for helping me out.
Posts: 484
Joined: Tue Oct 16, 2012 6:21 pm
Location: Norway
by bgreat » Fri Jan 18, 2013 1:49 am
Looking forward to seeing how this works out. Please update this thread if you make some progress.

Enjoy!
Bill
User avatar
Posts: 235
Joined: Mon Jan 23, 2012 2:09 pm
by notro » Sun Jan 20, 2013 6:29 pm
I tried adding this patch, but it didn't work. Very hard to look more into this without a SPI protocol analyzer.
Dropping this for now, but Bus Pirate by Sparkfun may come in handy if I decide to try again later:

Code: Select all
diff --git a/drivers/spi/spi-bcm2708.c b/drivers/spi/spi-bcm2708.c
index 200b49e..00f21fe 100644
--- a/drivers/spi/spi-bcm2708.c
+++ b/drivers/spi/spi-bcm2708.c
@@ -323,6 +324,11 @@ static int bcm2708_process_transfer(struct bcm2708_spi *bs,

        cs = stp->cs | SPI_CS_INTR | SPI_CS_INTD | SPI_CS_TA;

+       if ((spi->mode & SPI_3WIRE) && bs->rx_buf) {
+               dev_dbg(&spi->dev, "Enabling SPI_CS_REN\n");
+               cs |= SPI_CS_REN;
+       }
+
        bcm2708_wr(bs, SPI_CLK, stp->cdiv);
        bcm2708_wr(bs, SPI_CS, cs);

@@ -502,7 +508,7 @@ static int __devinit bcm2708_spi_probe(struct platform_device *pdev)
        }

        /* the spi->mode bits understood by this driver: */
-       master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_NO_CS;
+       master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_NO_CS | SPI_3WIRE;

        master->bus_num = pdev->id;
        master->num_chipselect = 3;
d
Posts: 484
Joined: Tue Oct 16, 2012 6:21 pm
Location: Norway