In this article, let's try to understand how the serial peripheral interface (SPI) protocol works, which can be used to transfer data between digital electronics components. It is useful to connect various digital sensors and other peripheral modules with a microcontroller unit. As the name implies, it is a serial communication protocol. However, unlike UART, this is synchronous communication, and hence, SPI requires a clock signal. When communicating, one device acts as the master which provides the clock signal for the receiving, i.e., slave devices. The clock channel is named as SCK. We can have one master device, and one or more slave devices. The master device has to have dedicated set of channels, each connected to a particular slave device. By enabling the relevant dedicated channel, the master notifies that it wishes to communicate with the relevant slave device. These dedicated channels are called chip select or slave select (SS) channels. If there is only one slave device connected to the master device, there will be only one SS channel from the master to the slave. If there are two slave devices connected to the master, there will be two different SS pins from the master going to the two slave devices separately.
The data output from the master device is named as master-out slave-in (MOSI) channel, which is connect to the MOSI channels of all the slave devices. Similarly, the data input to the master device is named as master-in slave-out (MISO) channel, which similarly connects to the MISO pins of all the slave devices. This means, the data channels MOSI and MISO are shared about the master and all the slaves. Which slave is communicating with the master as a given moment is decided by the enabling of relevant SS channel by the master. The following diagram from Wikipedia nicely illustrates a scenario where a master device is connected to three slave devices:
Figure: SPI Connectivity between a master and three slaves (source: https://en.wikipedia.org/wiki/Serial_Peripheral_Interface) |
Let's see the SPI protocol in action in a microcontroller module, particularly in an Arduino Uno device. In this scenario, our Arduino device is going to act as the master device. For the time being, we will not use a slave device since the action in MISO and MOSI are basically similar, except that they are taking data in the two directions separately. In our case, since we are only going to have a master device, we can observe some data in the MISO line when the master is sending some data out. The following is the code that we are programming the Arduino Uno device with. In this program, we are using SPI.h library from the Arduino library for SPI communication. Since we are using the pin 10 of the Arduino as the SS pin, we set it to OUTPUT mode. When initiating communication, we pull the SS pin LOW before sending data, which will go out from the MISO pin of the Arduino. After data transmission, we can set the SS pin HIGH again to signify that we are done. During the time period of SS pin is LOW, the master can both write data out (through MOSI) and also read data from the slave (through MISO) at the same time. In our case, we are only sending data out from the master.
Figure: The Arduino program to be run on the master device. |
Now, it is time to wire the hardware for our observation. The MOSI, MISO, and SCK pins are hardware-defined pins. That means, we don't have a choice but to use the predefined pins of the Arduino device: MOSI is pin 11, MISO is pin 12, and SCK is pin 13. The number of SS pins we need depends on the number of slave devices we have. So, it is not hardwired. From our program code, we have set pin 10 to act as SS. When sending data from the master using SPI.h library, we can use SPI.transfer() function to specify the data we are sending. In our example code, we are sending the hexadecimal value 0xAA from the master which converts to 10101010 in binary. In order to observe SPI behaviour, let's connect the logic analyser to the SPI pins as follows:
Figure: Arduino's SPI pins connected to the logic analyser. |
Channel 0 --> 10 (SS)
Channel 1 --> 11 (MOSI)
Channel 2 --> 12 (MISO)
Channel 3 --> 13 (SCK)
The following picture illustrates the Arduino Uno device pins tapped by the logic analysers channel probes.
Now, we can observe the signals going through the relevant SPI pins of the Arduino device as captured by the logic analyser. The following screenshot illustrates the captured data by the logic analyser. Channel 0 indicates that the SS pin has been pulled LOW to enable data communication between the master and slave. When this was done, the master has immediately started sending a clock signal through the SCK pin, which is captured on Channel 3 of the logic analyser. While the clock signal is present, at every HIGH position of the clock, the MOSI and MISO channels can transmit data bits --- HIGH for 1 and LOW for 0 as data bits. In our case, the master was sending the bit pattern 10101010. Therefore, we observe the 10101010 pattern in the MOSI pin, which is captured by Channel 1 of the logic analyser. The MISO pin as captured by Channel 2 remains idle as we don't have a slave device to send anything back to the master.
Figure: Logic analyser's view of the data being transferred through SPI interface. |
That's it about the SPI protocol. In contrast to the UART protocol, SPI has the advantage of connecting multiple devices together. A UART device only has Tx and Rx pins to send and receive data from only one other device. SPI achieves the capability to have more than two devices by adding an extra pin (SS) to select the intended recipient of a transmission. The cost of this is that the master device has to posses extra digital output pins as many as the number of slave devices. If there are 5 slave devices, the master should have 10 SS pins, in addition to the MISO, MOSI, and SCK pins.