Understanding SPI on the Raspberry Pi

I’ve been asked to include SPI (and I2C – more on that soon) support for the Raspberry Pi in my wiringPi… However because it’s hard to anticipate exactly what SPI devices you may connect up, it’s hard to provide something specific, so what I’ve done is provide some “helper” functions in a library and a guide on how to use SPI and what it’s all about…

SPI – Serial Peripheral Interface

SPI is an interface developed (or named?) by Motorola which is a Synchronous serial clocked, full-duplex master/slave bus….

That’s a mouthful. In essence, data is sent out synchronised (or locked) to a clock signal which is also sent out (so 2 wires), there is a separate wire for incoming data (3 wires, so data can be sent and received at the same time, so full-duplex) and a fourth wire to act as a “chip select” signal. Because of this, it’s sometimes called a 4-wire bus. The master/slave part indicates that any device on the bus can start a transmission to any other device on the bus. There can be multiple chip-select wires to talk to multiple devices on the same SPI bus.

The Raspberry Pi only implements master mode at this time and has 2 chip-select pins, so can control 2 SPI devices. (Although some devices have their own sub-addressing scheme so you can put more of them on the same bus)

One thing to remember about SPI is that to receive a byte, you need to send a byte. This may sound a little odd, but consider a device which accepts a command byte, then a data byte, then sends back a result byte… To make this happen, we need to send three bytes to the device, which will send back 2 dummy bytes, followed by the 3rd byte with data in it. For example, a GPIO expander chip (e.g. the MCP23S17 as used in the PiFace) where you send it a command (read register), then the data (the register number), then it sends back a return value (the register contents). So, when you send the command, it will send back a byte which you ignore, you send it the data, it sends back a byte which you also ignore, but to make it send the result, you need to send it a third byte which it will ignore!

Another example is the MCP3002 2-channel, 10-bit analog to digital converter chip as used the the Gertboard – that chip actually uses the SPI clock signal to drive its internal logic. The command sequence you send to it is only 4 bits long, but you get 10 bits of data back from it, starting one bit after the last command-bit… so you need to send it 2 bytes and read 2 bytes back from it, and in the 2 bytes back, the data starts at the 5th bit and extends for 10 bits, so to keep the clock going, you need to send 2 bytes, read 2 bytes then pick the 10 data bits out of the returning 16 bits…

So it’s not always obvious!

Installing the kernel driver

The Linux kernel in recent Raspberry Pi releases supports the SPI as a native device (no more bit-banging!) but it’s disabled by default, so we need to load the module before we can use the SPI device. Additionally we may need to change the permissions and/or ownerships of the files in /dev/ so that we can access them from out programs without needing to be root or run them with sudo.

The easiest way to do this is with the gpio program that’s part of wiringPi:

(Instructions to get and install wiringPi are here)

gpio load spi

That command will load the SPI driver and change ownership to the user running the command. Once we’ve done that, we can then run our SPI programs.

If you want to do it the hard/traditional way, then:

sudo modprobe spi_bcm2708
sudo chown `id -u`.`id -g` /dev/spidev0.*

Use the lsmod command to make sure the modules are loaded.

Using SPI

WiringPi provides a small library to help hide most of the issues dealing with opening and sending bytes to/from SPI devices, however if you need something that’s outside what this library can do, then you’ll need to write your own – however the library code is easy to follow, so you should be able to use that as a basis for your own code.

These are documented elsewhere, but in-essence:

if (wiringPiSPISetup (channel, 1000000) < 0)
  fprintf (stderr, "SPI Setup failed: %s\n", strerror (errno))

will get you going (channel is 0 or 1, the 1000000 is the speed – 1MHz here) and:

wiringPiSPIDataRW (buffer, size) ;

will then perform a concurrent transmit and receive of the contents of the buffer (unsigned chars) of the given size. Note that the buffer will be overwritten.

Do have a look at the gertboard.c library for examples of this code – in particular the analog read function, where it sends a command to the SPI A/D converter and reads back the data in the same transaction – then picks the data out of the return bytes.

Abusing SPI

Abusing is perhaps a harsh word here, but it’s possible to use the SPI bus for things that weren’t designed for SPI – one example is shift registers. Just use the clock and MOSI outputs, and write a byte at a time to the SPI device. Another use is the daisy-chained LED strings that are now available at a reasonable price – you can send up to 4096 bytes at a time which might represent a string of over 1000 LEDs, each accepting a 3-byte value, then pause (typically 500μS) to make the devices display the pattern.

SPI Speed?

An important aspect of SPI is that the clock can (usually) be driven at varying speeds – the dependence is usually on the peripheral device being driven. E.g. the analog to digital converter on the Gertboard must be driven at a speed between 1MHz and 3MHz – however the upper limit is the voltage it’s supplied with (3MHz max. at 5v), so it’s important to know in-advance what limitations of the devices you are talking to.

To experiment with timings, I wrote a little test program to see the effects of the different speeds – starting at 0.5Mb/sec going up to 62Mb/sec. I simply looped the MOSI and MISO pins together and checked that the data received was the same as the data sent.

The first thing I found was that the Pi stops sending at clock speeds over 62Mb/sec., and that in-reality 32Mbs is the upper limit of the SPI clock.

Next, I noticed that for some clock speeds, the data rate doesn’t change – this is due to the rounding issues when calculating the clock divider value. What I have observed is that the speeds available are:

  1. 0.5 MHz
  2. 1 MHz
  3. 2 MHz
  4. 4 MHz
  5. 8 MHz
  6. 16 MHz and
  7. 32 MHz.

You can see the progression, it’s powers of 2, and this reflects the clock divider values too.

The important thing to realise is that if you are clocking a part designed for a SPI clock rate of 25MHz, then setting the clock to 25MHz will really create a clock of 16MHz. There is no way to get an exact 25MHz clock speed. This probably isn’t important, and the reality of trying to send a 25MHz signal down a pair of wires or along a PCB track is that it’s highly likely to not work unless you take good care with the signal routing, shielding and so on.

Another observation was that the overheads in the Linux kernel increase with clock speed and the data throughput doesn’t quite increase linearly with clock speed. So with a clock at 1MHz we get a throughput of 0.108 MB/sec, at 2MHz it’s 0.214 MB/sec and it more or less doubles up to 16MHz (1.55MB/sec) but at 32Mb/sec clock, it’s barely 2.2MB/sec when you might expect it to be closer to 3MB/sec.

Finally, it’s worth while noting that the actual latency of calling the wiringPiSPIDataRW() function is rather high – an example I tested was sending 2 bytes at a time to the Gertboards digital to analog converter and rather than the 50,000 updates/sec I was expecting I was seeing barely half that, and a few experiments shows that it wasn’t that tied to the SPI clock frequency either.

Summary:

SPI is easy to use and can be fast. Be aware that communication is full duplex and data comes back at the same time as you send it, so be prepared to pick bits out of the return data – often at funny bit offsets, rather than byte offsets. The Pi can only directly drive 2 SPI channels, but some devices have their own sub-addressing scheme to allow for more on the same bus.

Comments

Understanding SPI on the Raspberry Pi — 50 Comments

  1. Thanks for this great article.

    With respect to “The Pi can only directly drive 2 SPI channels,”. You could expand this easily to 3 SPI channels using the 74HCT139 (http://www.datasheetcatalog.org/datasheet/philips/74HC_HCT139_CNV_2.pdf). Connecting /CS0 to A0 and /CS1 to A1. This will give you Y0, Y1 and Y2 for selecting three SPI slaves.

    However, I have noticed that the kernel driver only has 2 devices (/dev/spidev0.0 and /dev/spidev0.1).

    Is it possible to get a 3 channel support in the kernel driver and in wiringPi?

    Thanks. Regards, Arjan

  2. I’m trying to get to grips with this to ‘Abuse’ (!) SPI to run 6 x RGB LEDs connected to a WS2803. I need to be able to do 2 things – first, pull the clock signal low for > 500us, second , send a string of 18 bytes with the RGB values. (and then pull the clock low again to transfer the data to the registers). I don’t need to read any data back. I presume it’s OK to use wiringPiSPIDataRW to output the data, but I don’t want the buffer overwritten, and can’t see how to pull the clock low. Any assistance, example code, etc gratefully received! TIA…John

    • The SPI clock is normally low.
      So when you issue the wiringPiSPIDataRW () the clock pulses high then low for each bit transmitted. (18 * 8 = 144 clock pulses)

      After calling wiringPiSPIDataRW (), you need to then pause for at least 500uS – delayMicroseconds (550) ; at that point, the ws2803’s will copy the latched data into the PWM registers and change the LED brightnesses/colours.

      And (famous last words) that should be that…

      then you can load up another 18 bytes (6 RGB LEDs, I presume) and start again.

      The buffer will get overwritten though – probably with zeros, but it’ll be whatever noise appears on the MISO pin on the Pi. So you’d have to keep a working copy of the buffer, then use (e.g.) memcpy () to copy if from your working buffer to the tx/rx buffer. (However I suspect you’d want to change the data anyway – once you clock in those 18 bytes the LEDs remain static whith those values, you don’t need to keep refreshing them unless you change the values)

      -Gordon

      • Maybe is this a nice solution?

        Tie MISO and MOSI together because you do not use data coming back.
        So the buffer is “not” overwritten, hmmm, overwritten with the same data.
        But the data is the same, not changed!

        Is this a usable trick?

        • I think that might work well for the LED driver type displays where you send a big buffer of data to the LED chain and only want to modify a small portion of it each time.

          Not sure about devices which return data as you send it – such as ADCs, etc.

          -Gordon

  3. Thanks, Gordon, I’ll give it a go and report back in due course. I’ve just finished putting the parts onto a breadboard, so doing the coding is the next step!

  4. Hi Gordon – got it partly sorted, it needs 3 arguments to wiringPiSPIDataRW – channel, buffer, size. My LEDs now illuminate! However, the delayMicroseconds (550) doesn’t seem to be working, looks like the program stops there – if I comment it out, it gets to printing the ‘done’ message. Could I be missing a header file?

    • Hi Just catching up this morning..

      I was about to reply to your earlier one – yes, you need 3 parameters, but what I strongly suggest you do when compiling your code is to use the -Wall flag in GCC – That turns on all warnings – it would have flagged that up for you…. So:

      gcc -Wall -o project project.c -lwiringPi

      And yes, for the delyaMicroseconds – you need to include wiringPi.h and call one of the wiringPiSetup() functions – probably call wiringPiSetupSys() as that doesn’t need root privs. Although if you want to have accurate delayes of under 10uS then you need to call one of the other wiringPiSetup() functions and run it as root.

      This is an area where I need to put in a little more documentation… Working on it!

      -Gordon

      • Thanks Gordon, adding wiringPiSetupSys() looks to have got the delayMicroseconds function working.
        As far as documentation is concerned, it does seem to be scattered around a bit, I’ve had to find most of what I needed by using Google search! Most seems to be Arduino-based, and I haven’t been able to find complete documentation of all the R-Pi based functions and how to use them. Still, very grateful for your work which is much appreciated.
        Cheers…John

        • Glad your going now! I have an xmas project using those led driver chips too – didn’t think it would be too hard to get them going!

          -Gordon

          • I’m very impressed by the WS2803 – very fast, 18 channels of 8-bit control, cascadable up to a thousand or so – which is 6000 RGB LEDS!! – and a particularly nice feature is that the drive channels are selectably current-limited, so you only need 1 resistor for all the LEDs (I use 1k5 to set the current at around 18mA), no need for series resistors for each LED any more. Bargain device!

          • And driven by a Raspberry Pi – which probably gives you far more scope for patterns, designs and so on than an Arduino board.

            -Gordon

  5. Just in case you, or anyone else is interested, here’s my completed program :

    /*
    * SPItest3.c:
    * LED manipulation using wiringPi SPI functions
    * to control 6 x RGB LEDs via WS2803 chip
    * Ramps R, G and B for each LED until all are on full.
    * The program does 18 x 256 = 4609 writes to the chip buffer,
    * latching the output each time, and still manages it in a couple of seconds!

    */

    #include
    #include
    #include
    #include
    #include
    #include

    int main (void)
    {
    int channel=0 ;
    uint8_t i=0 ;
    uint16_t j=0 ;
    uint8_t k=0 ;
    uint8_t bufsize=18 ;
    uint16_t delaytime = 550 ;
    uint8_t outbuffer [18] ;
    uint8_t firstbuffer [] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} ;
    int cnt ;

    printf (“Raspberry Pi wiringPi SPI LED test program\n”) ;

    // set output SPI channel to 0 and speed to 8MHz

    if (wiringPiSPISetup (0,8000000) < 0)
    {
    fprintf (stderr, "Unable to open SPI device 0: %s\n", strerror (errno)) ;
    exit (1) ;
    }
    printf ("Starting\n") ;

    wiringPiSetupSys() ;

    // for each location in the buffer

    for (i==0;i!=bufsize;i++)
    {
    // go through each value in turn and load buffer location with LED level

    for (j==0;j!=256;j++)
    {
    firstbuffer[i] = j ;

    // load output buffer and send it out

    for (k==0;k!=bufsize;k++)
    {
    outbuffer [k] = firstbuffer [k] ;
    }

    k=0 ;

    wiringPiSPIDataRW (channel, outbuffer, bufsize) ;

    delayMicroseconds (delaytime) ;

    }

    j=0 ;

    }

    printf ("Done\n") ;

    return 0 ;
    }

    • You couldn’t share your circuit diagram with us could you 🙂 ? I’d be interested to see how you wired up the WS2803 to the PI. I have some chips myself which I am going to try and use.

      Which GPIO pins on the PI become the SPI pins when using the wiring PI example above?

      • Stuart – sorry about the delay, it’s been a couple of weeks since I last visited this site. The R-Pi SPI connections I used are the MOSI (GPIO 10) and SCLK (GPIO11) pins, and of course the ground pin. The WS2803 is pretty straightforward to use, just search for the datasheet and it should all be obvious – +5v power pin, power and data ground pin, one pin for the current-limiting resistor that then goes to ground, 2 pins for the SPI bus data and clock, 2 pins to cascade the signal to the next chip – all the rest are the LED connections.
        Don’t try to power the WS2803 from the R-Pi, even if you only use one – with all 6 LEDs on full white, it will take around 400mA, assuming a 1k5 resistor to limit the current to 18mA per LED colour channel, which is more than the R-Pi can safely supply.

        • Thanks John, got it all working today 🙂 I have noticed some inconsistencies with the timings etc, not sure why but I assume it might be because the raspberry is off doing some housekeeping. These WS2803’s are pretty good. Next step is to daisy chain the other 4 I have but I’ve got to solder up a lot of SMD leds first..

  6. For some reason, the ‘Includes’ aren’t shown correctly – they are :
    #include wiringPi.h
    #include stdio.h
    #include stdlib.h
    #include stdint.h
    #include errno.h
    #include string.h

    but with each included item bound by ‘less that’ and ‘greater than’ signs.

  7. Excellent article and example. I’m starting to use it now to communicate with a microchip PIC to get A/D samples. PIC will create regular jitter free timing for A/D and will buffer data until PI is ready. Transfers using SPI :). This article and wiringPI has really helped me get up and running fast. Thank you.

    I did spot a little error: please check your example code in the main article:

    wiringPiSPIDataRW (buffer, size) ;

    should read:

    wiringPiSPIDataRW (channel, buffer, size) ;

    Thanks again,
    Jonathan

  8. If I ever get my hands on a Raspberry Pi, I’d like to try interfacing it to some automotive sensors. These particular devices require either 16- or 32-bit transfers (i.e, 16 or 32 clocks while slave select remains asserted). Does the SPI driver natively support these word lengths, or is it fixed at eight bits?

    • From what I understand, the hardware supports an arbitary data length, but I’ve only ever told it to do 8-bit transfers, however, from my own observations of doing multi-byte transfers, the select stays low for the duration.

      However there does seem to be some clock pauses between each byte on the bus, but it seems to be 2-3 clock widths for each byte. It’s not affected multi-byte transfers to things like the D/A and A/D on the Gertboard, nor multi-byte transactions to GPIO extenders, or the multi-LED controller chips, etc.

      Have a look at the gertboard code in the wiringPi lib. which does 2 and 3 byte transfers for some examples.

      -Gordon

      • Thanks for the information. So long as slave select remains low throughout the transfer, delay between bytes within the transfer is of no consequence. Since the parts I’m looking to interface are bandwidth limited to 50 Hz or less, the sampling frequency doesn’t need to be very high.

        I received a message from Newark on Friday indicating that Raspberry Pi boards are available. Perhaps before too long I’ll be able to put this information to good use. Until then, I’ll take a look at the library files to see what I can learn.

  9. I am using these commands to load the driver on Debian Wheezy.
    sudo modprobe spi_bcm2708
    sudo chown `id -u`.`id -g` /dev/spidev0.*
    Everything works great. Thanks.
    The problem is everytime I reboot the drivers go away.
    How do I make the driver stay mounted between power ups?

    • Edit /etc/modules and put in it the 2 moduels you need – spidev and spi_bcm2708 then they’ll be loaded every time you boot.

      However the devices will only be accessible as root, so plan B might be to put the chmod command in /etc/rc.local

      chmod 1000.1000 /dev/spidev*

      If you have wiringPi installed, then

      gpio load spi

      will work and does the chmod for you, but only as a command typed – if you put that into /etc/rc.local, it will work, but the device entries will be owned by root.

      -Gordon

  10. Hello,

    thanks for the great library!
    I have a few questions:

    1)Can I read the state of the MISO pin with the digitalRead function while SPI is enabled, or is the pin blocked by the SPI logic?

    2)Is there a way to disable SPI again, to get back to the normal functions of the pins, for example to control tho MOSI pin?

    3)When SPI gets disabled, do the Pin modes stay in the state set by the spi logic, or do they get set back to standard (input I think)?

    Thanks,
    Daniel

    • Hm. I really don’t know to tell the truth. I’d expect that you can read the state of MISO while the driver is live – easy to test, load SIP, then connect a 1K resistor from MISO to +3.3v, gpio read 13, the connect it to ground and gpio read 13 again…

      I don’t know about 2 – I suspect if you unload the driver module, then pinMode() the pins it’ll be fine though.

      and again for (3) – My suspicion is that they stay in the last mode they were set in, so I’d want to use pinMode() after than anyway.

      Sorry for the rather vague answers though!

      Cheers,

      -Gordon

  11. happy new year and thank you very much indeed gordon for your highly appreciated work!

    i was wondering where the limitation of sending 4096 bytes (12bit) at a time comes from? is there a possibility to send up to 65536 bytes (16bit)?

    i am chatting spi with a screen, thus quite a few bytes need to be sent… thank you very much

    • I think it’s a kernel limitation and can only grab a 4K page at a time from userland into kernel space and into the SPI. I’ve tried with larger buffers myself and they fail. Nothing apepars to happen…
      Just split the data into 4K blocks and you’ll be fine.

      -Gordon

  12. Hello,

    Do you think there might be a problem using “wiringPi” from daemon process ?

    I wrote the test application that calls wiringPiSPISetup() and wiringPiSPIDataRW() as expected.

    While trying to run the very same code from the daemon process wiringPiSPISetup() returns 0.

    Driver has been loaded and works properly.

    The other difference between test program that works and the daemon program is that the daemon is in C++. I assume this does not change a thing. Just to provide all differences.

    Thanks in advance for your thoughts and suggestions.

    Regards

    • I can’t think of any issues running a program as a daemon using wiringPi. (I presume you mean by detatching from the controlling tty, etc.) wiringPi will output to stderr, but only at wiringPiSetup() if it fails to open the device.

      So make sure the SPI driver is loaded though – esepcially if you’re rebooting the Pi to run your daemon. you can put the modules in /etc/modules or execute /usr/local/bin/gpio load spi from the shell script that’s launching your daemon.

      -Gordon

  13. Hi
    do you have or do you know somebody having experience to connect RaspBerry with a RF module like RFM12?

    • I don’t know of anything offhand using that module. The only ones I’ve used are the ciseco xrf/urf modules. The URF ones plug into the usb and create a bridges rs232 type transmission.

      -Gordon

  14. I’m using wiringPi and it is very good. But I have problem with SPI. SPI is OK but my raspberry reading and sending data on rising edge of clock signal. I have to read and send data on falling edge. Because I’m using ADE7763 and this have active data on falling edge. It is possible change it? In “BCM2835 ARM Peripherals” page 22 and 23 is register IN RISING ain OUT RISING but I don’t know how change it. Thank you for your help.

    • You can change it without too much difficulty.

      You can edit wiringPiSPI.c and set spiMode to SPI_MODE_2 (near the top of the time),

      or call the ioctl() to change the mode. What you need to do is set the mode to mode 2 (I think, experiment!) by

      ioctl (fd, SPI_IOC_WR_MODE, &spiMode) ;

      where spiMode is uint8_t that contains SPI_MODE_2, and the ‘fd’ is the number returned from wiringPiSPISetup ()

      -Gordon

  15. I am trying to get the Raspberry to talk to an Aduino Uno without success. I cobbled something together in Python which worked after a fashion but it sometimes made nistakes or missed a transmision so I thought it might be the speed being used. Having failed to find out how to change the transmision speed in Python I tried your functions without success. The problem is that the Arduino program is waiting for the SPI transmition to finish before setting off an interrupt
    (ISR (SPI_STC_vect)), but this never happens.
    I assume that wiringPiSPIDataRW is setting the SS pin High after transmission but the Arduino is not seeing it. The same program “worked” ie saw the interupt in Python.

    • Tricky to debug… The wiringPiSPIDataRW() function is nothing more than a wrapper round the standard Linux system call, so what should happen is that the select pin goes LOW, then the data is clocked out, then the select pin goes HIGH.

      How many bytes are you trying to transsfer? (and what speed?)

      I’ve a funny feeling the ATmega interrupts after each byte, doesn’t it?

      And what mode did you initialise SPI on the ATmega in? You also need to make sure the SPI clock speed on the Pi is less than half F_CPU too… (but I guess you have that right though – if it worked in Python – assuming Python uses the standard Linux kernel driver and doesn’t bit-bang the hardware in some odd mode?)

      -Gordon

  16. I have resolved the issue after a whole day trying! Isnt electronics fun. After resolving my stupidity of using the wrong SS pin with your library (changed during experimentation) I got the same garbled result as with Python I tried changing modes and speeds (on both machines) and could not improve the result. Then I realized I was using a level shifter (TXB0108). All the examples on the internet just plugged them together) that might be causing some kind of delay so I played with the delay level without any noticeable effect. I have no idea what it does. So in desperation I swapped to another level shifter (BSS138) which I bought to use for I2C because for some reason the other chip will not work with I2C. The whole thing just worked straight away with your library and with Python.

    So either I have a duff chip, or I cannot solder (possible but I do not think so) or this chip cannot do the job it is advertised for. I have gone back to the supplier Proto-PIC to find out.

  17. More experimantation has found that although the BSS138 chip is working it is very susceptible to speed, I have tried to use the command byte

    c=SPI.transfer(val)

    in the Arduino interrupt loop instead of

    byte c = SPDR;

    I believe that the first command transfers val into the buffer to be sent back next transmission

    That is too slow and the corrupted data comes in
    I have reduced the speed to a supposed 10kHz to try to overcome this but to no avail.

    I think I need somebody who knows what they are doing rathe than me chasing my tail. Unless I can solve this I cannot transfer data from the Arduino to the Rasberry. I am begining to think I will write it myself in software. It would be slow but I only need a couple of bytes per second.

    alan

    • What about serial comms? I’ve used the Pi’s serial port to an Arduno in the past – if you use a standard USB cable to the Arduino from the Pi, you can power the Arduino and use the serial without any level shifters – 115200 baud works just fine – not as fast as SPI though…

      -Gordon

  18. PPS
    I have discovered that I can do

    SPDR=val;

    and that is fast enough.

    I have thought of the solution though tiredness and too much red wine
    mean I will try it tomorrow.

    Only send and receive one byte at a time rather than a phrase and wait some delay in between. Then the interrupt in the Arduino can keep up.

    ie some code in the Arduino like

    c=SPDR
    switch
    case A:
    SPDR=Avalue;
    case B:
    SPDR= Bvalue
    break;

    in the raspberry
    to get Avalue
    buff[0]=65;
    wiringPiSPIDataRW (chan,buff,1);
    buff[0]=99;
    sleep(5);
    res=wiringPiSPIDataRW (chan,buff,1);

    What a fudge!

    Alan