User avatar
mike808
Posts: 21
Joined: Mon Jun 03, 2013 12:55 am

SPI communication

Sun Nov 03, 2013 3:32 pm

I have a SPI device that I'd like to communicate with using the PI. I was planning on using spidev API to do so but had a few questions since I'm a newbie to SPI. Looking at some arduino code, I see methods like:

Code: Select all

void Adafruit_ILI9340::writecommand(uint8_t c) {
  CLEAR_BIT(dcport, dcpinmask);
  //digitalWrite(_dc, LOW);
  CLEAR_BIT(clkport, clkpinmask);
  //digitalWrite(_sclk, LOW);
  CLEAR_BIT(csport, cspinmask);
  //digitalWrite(_cs, LOW);

  spiwrite(c);

  SET_BIT(csport, cspinmask);
  //digitalWrite(_cs, HIGH);
}


void Adafruit_ILI9340::writedata(uint8_t c) {
  SET_BIT(dcport,  dcpinmask);
  //digitalWrite(_dc, HIGH);
  CLEAR_BIT(clkport, clkpinmask);
  //digitalWrite(_sclk, LOW);
  CLEAR_BIT(csport, cspinmask);
  //digitalWrite(_cs, LOW);
  
  spiwrite(c);

  //digitalWrite(_cs, HIGH);
  SET_BIT(csport, cspinmask);
} 
Using spidev, do I need to set and clear bits when writing to the SPI bus or is that already handled for me? Also, it's used like this:

Code: Select all

  writecommand(0xCB);  
  writedata(0x39); 
  writedata(0x2C); 
  writedata(0x00); 
  writedata(0x34); 
  writedata(0x02); 
 
  writecommand(0xF7);  
  writedata(0x20);
What is the data following the command and how does that differ from writing the command itself when using the SPI bus?

My goal here is to understand more about SPI and to use an SPI device with the raspberry PI instead of just the arduino.

Thanks everyone!

halhertani
Posts: 40
Joined: Sun Aug 19, 2012 5:02 am
Location: Ottawa, ON Canada
Contact: Website

Re: SPI communication

Mon Nov 04, 2013 5:03 pm

With I2C, each slave device has an address and the master chip (microcontroller or RPi) must first send the address of the device of interest to initiate communication with it. In SPI there's no such slave device address. Instead, every SPI slave device has an active low chip(slave) select pin that must be asserted (pulled low) before the master initiates communication with the slave. The chip select pin must then be de-asserted when communication is complete. If there are multiple slave devices on the bus, the master device should only pull one of them low at a time to initiate communication with that particular device...this is how slave devices are accessed(addressed) in SPI without any fancy addressing scheme.

On the raspberry pi (w Raspbian) if you enable SPI, and run a "ls /dev/spi*" command you'll see two files; i) "/dev/spidev0.0" & ii) "/dev/spidev0.1". This first number after spidev "0.*" indicates the spi device number and the second number is the chip select number. This basically means that there's only one spi device on the RPi and it can connect to a maximum of two slave devices because there are only two chip selects "0.0" & "0.1".

When using userspace spidev under Linux you do not need to assert chip select pins individually. For example to interface an MCP3008 SPI ADC, simply connect:

MCP3008 -------- RPi
SI -------- MOSI
SO -------- MISO
SCK -------- SCLK
&
CS -------- CE0

The CE0 pin on the RPi is mapped to the "/dev/spidev0.0" entry in the "/dev" directory. So to access that particular device simply open() "/dev/spidev0.0" , initialize it and start reading and writing.

Similarly let's say you want to connect an MCP23S17 SPI GPIO expander but this time to the "/dev/spidev0.1" entry in the "/dev" directory. Connect:

MCP23S17 ---------- RPI
SI ---------- MOSI
SO ---------- MISO
SCK ---------- SCLK
&
CS ---------- CE1

The fact that CE1 on the RPI is connected to the chip select pin of the MCP23S17 means that in order to communicate with the chip you must open & initialize the "/dev/spidev0.1" entry in the /dev directory.

Also when setting up & reading/writing the SPI peripheral using spidev under Linux, you should not configure the SPI peripheral by writing bits to the SPI configuration registers like you would on a microcontroller/Arduino. You simply need to use the spidev api which involves using some special structures, ioctl()s and open()s (& sometimes read()s and writes()s). Under Linux "everything is accessed as a file". This is also true when accessing the I2C peripheral (via i2cdev) and GPIO via (sysfs) under Linux.

the SPI class that i created was poorly named "mcp3008Spi"(http://hertaville.com/2013/07/24/interf ... i-using-c/) but you could use it to access any SPI device simple instantiate and SPI object
mcp3008Spi a2d("/dev/spidev0.0", SPI_MODE_0, 1000000, 8);

This instantiates an SPI object called "a2d" connected to /dev/spidev0.0 (CE0 pin on the RPi) using SPI mode (0,0), with a speed of 1Mbps and sending 8 bits at a time.

Then to simply read/write data:

a2d.spiWriteRead(data, sizeof(data) );

where data is an array of bytes of any size that could be transmitted and received. If you only want to read bytes you could always put dummy data into the array. The received bytes will be saved in data after the method returns (data is passed by reference).

I also highly recommend taking a look at the WiringPi library which can do SPI communication using a more robust & easy to use API.


Hope this helps! good luck.

Return to “C/C++”