With reference to the Arduino Uno, Mega2560, Leonardo and similar boards:
- How does SPI work?
- How fast is SPI?
- How do I connect between a master and a slave?
- How do I make an SPI slave?
Please note: This is intended as a reference question.
With reference to the Arduino Uno, Mega2560, Leonardo and similar boards:
Please note: This is intended as a reference question. |
|||
|
Introduction to SPIThe Serial Peripheral Interface Bus (SPI) interface is used for communication between multiple devices over short distances, and at high speed. Typically there is a single "master" device, which initiates communications and supplies the clock which controls the data transfer rate. There can be one or more slaves. For more than one slave, each one has its own "slave select" signal, described later. SPI signalsIn a full-blown SPI system you will have four signal lines:
When multiple slaves are connected to the MISO signal they are expected to tri-state (keep at high impedance) that MISO line until they are selected by Slave Select being asserted. Normally Slave Select (SS) goes low to assert it. That is, it is active low. Once a particular slave is selected it should configure the MISO line as an output so it can send data to the master. This image shows the way that data is exchanged as one byte is sent: Note that three signals are outputs from the master (MOSI, SCK, SS) and one is an input (MISO). TimingThe sequence of events is:
Note that:
Because data is sent and received on the same clock pulse it is not possible for the slave to respond to the master immediately. SPI protocols usually expect the master to request data on one transmission, and get a response on a subsequent one. Using the SPI library on the Arduino, doing a single transfer looks like this in code:
Sample codeExample of sending only (ignoring any incoming data):
Wiring for output-only SPIThe above code (which sends only) might be used to drive an output serial shift register. These are output-only devices, so we don't need to worry about any incoming data. In their case the SS pin might be called the "store" or "latch" pin. Examples of this are the 74HC595 serial shift register, and various LED strips, just to mention a couple. For example, this 64 pixel LED display driven by a MAX7219 chip: In this case you can see that the board maker has used slightly different signal names:
Most boards will follow a similar pattern. Sometimes DIN is just DI (Data In). Here is another example, this time a 7-segment LED display board (also based on the MAX7219 chip): This uses exactly the same signal names as the other board. In both of these cases you can see that the board only needs 5 wires to it, the three for SPI, plus power and ground. Clock phase and polarityThere are four way you can sample the SPI clock. The SPI protocol allows for variations on the polarity of the clock pulses. CPOL is clock polarity, and CPHA is clock phase.
These are illustrated in this graphic: You should refer to the datasheet for your device to get the phase and polarity correct. There will usually be a diagram which shows how to sample the clock. For example, from the datasheet for the 74HC595 chip: As you can see the clock is normally low (CPOL = 0) and it is sampled on the leading edge (CPHA = 0) so this therefore is SPI mode 0. You can change the clock polarity and phase in code like this (choose one only, of course):
This method is deprecated in versions 1.6.0 onwards of the Arduino IDE. For recent versions you change the clock mode in the
Data orderThe default is most-significant bit first, however you can tell the hardware to process the least significant bit first like this:
Again, this is deprecated in versions 1.6.0 onwards of the Arduino IDE. For recent versions you change the bit order in the
SpeedThe default setting for SPI is to use the system clock speed divided by four, that is, one SPI clock pulse every 250 ns, assuming a 16 MHz CPU clock. You can change the clock divider by using
Where "divider" is one of:
The fastest rate is "divide by 2" or one SPI clock pulse every 125 ns, assuming a 16 MHz CPU clock. This would therefore take 8 * 125 ns or 1 µs to transmit one byte. This method is deprecated in versions 1.6.0 onwards of the Arduino IDE. For recent versions you change the transfer speed in the
However empirical testing shows that it is necessary to have two clock pulses between bytes, so the maximum rate at which bytes can be clocked out is 1.125 µs each (with a clock divider of 2). To summarize, each byte can be sent at a maximum rate of one per 1.125 µs (with a 16 MHz clock) giving a theoretical maximum transfer rate of 1/1.125 µs, or 888,888 bytes per second (excluding overhead like setting SS low and so on). Connecting to ArduinoArduino UnoConnecting via digital pins 10 to 13: Connecting via the ICSP header: Arduino Atmega2560Connecting via digital pins 50 to 52: You can also use the ICSP header, similar to the Uno above. Arduino LeonardoThe Leonardo and Micro do not expose the SPI pins on the digital pins, unlike the Uno and Mega. Your only option is to use the ICSP header pins, as illustrated above for the Uno. Multiple slavesA master can communicate with multiple slaves (however only one at a time). It does this by asserting SS for one slave and de-asserting it for all the others. The slave which has SS asserted (usually this means LOW) configures its MISO pin as an output so that slave, and that slave alone, can respond to the master. The other slaves ignore any incoming clock pulses if SS is not asserted. Thus you need one additional signal for each slave, like this: In this graphic you can see that MISO, MOSI, SCK are shared between both slaves, however each slave has its own SS (slave select) signal. ProtocolsThe SPI spec does not specify protocols as such, so it is up to individual master/slave pairings to agree on what the data means. Whilst you can send and receive bytes simultaneously, the received byte cannot be a direct response to the sent byte (as they are being assembled simultaneously). So it would be more logical for one end to send a request (eg. 4 might mean "list the disk directory") and then do transfers (perhaps just sending zeros outwards) until it receives a complete response. The response might terminate with a newline, or 0x00 character. Read the datasheet for your slave device to see what protocol sequences it expects. How to make an SPI slaveThe earlier example shows the Arduino as the master, sending data to a slave device. This example shows how the Arduino can be a slave. Hardware setupConnect two Arduino Unos together with the following pins connected to each other:
On the Arduino Mega, the pins are 50 (MISO), 51 (MOSI), 52 (SCK), and 53 (SS). In any case, MOSI at one end is connected to MOSI at the other, you don't swap them around (that is you do not have MOSI <-> MISO). The software configures one end of MOSI (master end) as an output, and the other end (slave end) as an input. Master example
Slave example
The slave is entirely interrupt-driven, thus it can doing other stuff. The incoming SPI data is collected in a buffer, and a flag set when a "significant byte" (in this case a newline) arrives. This tells the slave to get on and start processing the data. Example of connecting master to slave using SPIHow to get a response from a slaveFollowing on from the code above which sends data from an SPI master to a slave, the example below shows sending data to a slave, having it do something with it, and return a response. The master is similar to the example above. However an important point is that we need to add a slight delay (something like 20 microseconds). Otherwise the slave doesn't have a chance to react to the incoming data and do something with it. The example shows sending a "command". In this case "a" (add something) or "s" (subtract something). This is to show that the slave is actually doing something with the data. After asserting slave-select (SS) to initiate the transaction, the master sends the command, followed by any number of bytes, and then raises SS to terminate the transaction. A very important point is that the slave cannot respond to an incoming byte at the same moment. The response has to be in the next byte. This is because the bits which are being sent, and the bits which are being received, are being sent simultaneously. Thus to add something to four numbers we need five transfers, like this:
First we request action on number 10. But we don't get a response until the next transfer (the one for 17). However "a" will be set to the reply to 10. Finally we end up sending a "dummy" number 0, to get the reply for 42. Master (example)
The code for the slave basically does almost everything in the interrupt routine (called when incoming SPI data arrives). It takes the incoming byte, and adds or subtracts as per the remembered "command byte". Note that the response will be "collected" next time through the loop. This is why the master has to send one final "dummy" transfer to get the final reply. In my example I am using the main loop to simply detect when SS goes high, and clear the saved command. That way, when SS is pulled low again for the next transaction, the first byte is considered the command byte. More reliably, this would be done with an interrupt. That is, you would physically connect SS to one of the interrupt inputs (eg, on the Uno, connect pin 10 (SS) to pin 2 (an interrupt input), or use a pin-change interrupt on pin 10. Then the interrupt could be used to notice when SS is being pulled low or high. Slave (example)
Example output
Logic analyzer outputThis shows the timing between sending and receiving in the above code: New functionality in IDE 1.6.0 onwardsVersion 1.6.0 of the IDE has changed the way SPI works, to an extent. You still need to do
When you are done communicating with the slave, you call
Why use SPI?
This is an excellent question. My answers are:
Both methods have their place. I2C lets you connect many devices to a single bus (two wires, plus ground) so it would be the preferred choice if you needed to interrogate a substantial number of devices, perhaps fairly infrequently. However the speed of SPI could be more relevant for situations where you need to output rapidly (eg. a LED strip) or input rapidly (eg. an ADC converter). References
|
|||||||||||||||||||||
|
Thank you for your interest in this question.
Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).
Would you like to answer one of these unanswered questions instead?