- 1 The Serial Peripheral Interface
- 1.1 Introducing SPI
- 1.2 Typical connections
- 1.3 Communication diagram
- 1.4 Clock Frequency, polarity and phase
- 2 Common uses
- 3 Using SPI in ChibiOS/HAL
- 3.1 Enabling SPI driver and its peripherals
- 3.2 The SPI configuration
- 3.3 Most used APIs and their usage
- 3.4 PAL configuration
- 4 Our circuit
- 4.1 Introducing the Shift Registers
- 4.2 Schematic
- 4.3 Bill of Materials
- 5 Demos explained
- 5.1 Step 0
- 5.2 Step 1
- 5.3 Step 2
- 6 Project used in this tutorial
1 The Serial Peripheral Interface
1.1 Introducing SPI
Communicating by a serial bus instead of a parallel one could be very useful when we need to send out a big number of words or fixing a constraint on wires. The Serial Peripheral Interface bus, also known as SPI bus, is a synchronous, serial communication peripheral which communicates in full duplex mode using a master-slave architecture with a single master.
That means we have always a single master and one or more slaves. The communications is bi-directional (i.e. data flows from master to slave but also from slave to master). Since this bus is synchronous, the master (that handles and manages the communication) provides also a clock on a specific line (known as SCK or CLK) so the data is send-out/captured on a clock edge.
Both master and slave send-out and capture 1 bit per clock period. Using other words for each clock period master send and receive a bit and the same happens for slave: to do this are required two separated line (MISO and MOSI).
As there may be more than a slave, the master needs for a slave selector and this is implemented using an additional line for each slave. In this way only the slave having its select line lowered actually communicates with master. Because of that this line is know as Slave Select (SS) as well as Chip Select (CS) (or also as nCS, nSS where n means negate). Nothing has been said on data size as it depends on hardware. However, most common word sizes are 8-bit, 12-bit or 16-bit.
1.2 Typical connections
The SPI bus requires only three wires and additional one for each slave device:
- SCK or Serial Clock;
- MOSI or Master Output Slave Input;
- MISO or Master Input Slave Output;
- CS or Chip Select (one for each slave, also known as SS, nCS or nSS);
There are two way to connect a master with more than a slave: the independent mode and the cooperative mode.Fig.1 – SPI independent slaves connection (on the left), SPI chain connection (on the right).
Using the first mode, every slave can be selected independently, the second one use a single Chip Select and only the first device receives data directly from master. Indeed, other devices are connected as cascade. This kind of connection is used to extend functionalities of certain devices and not every device is designed for this purpose. Even if this is not completely clear at this point, we suggest to do not worry about any incomprehensions since we will make some examples about daisy chain connection later in this tutorial.
1.3 Communication diagram
Let’s make a recap of concept already discussed trying to realize what they mean. We said that SPI is a full-duplex bus and this means that every communication provides a data exchange on MISO and MOSI. Since SPI is a single master/multiple slave the communication starts lowering the CS related to the slave which is interested by the current operation. After master provides some clock pulses equal in number to the number of bits of the word to be exchanged. When communication ends usually master pulls-up the CS. Note that if more than a word are exchanged usually the CS is kept low for the entire communication.
1.4 Clock Frequency, polarity and phaseFig.2 – SPI 8-bit exchange conceptual timing diagram.
Usually, master has a wide range of selectable clock frequencies. Another important information about the SPI is Clock polarity and phase (known as CPOL and CPHA). Master must be able to operate in every condition since usually slaves are designed to be as cheap as possible with a simple circuitry and they have a fixed CPOL and CPHA. This means we will almost always adapt master configurations depending on slave time diagram. Because of that is very important to be able to understand the value of CPOL and CPHA starting from a time diagram.
Clock Polarity specifies if clock signal is normally high or normally low, so if CPOL is equal to 0 clock is normally low and vice-versa. Clock Phase specifies if data is captured on first or second clock edge, so if CPHA is equal to 0 data is captured on first clock edge of a period and vice-versa (Note CPHA doesn’t specify if data is captured on rising or falling edge).
Let’s see some timing diagrams to look better into this. In what follows we are sending 8 bits and more precisely 0x55 (i.e. 0b01010101) with different CPOL and CPHA to discuss differences.
In this case CPOL is 0 and we can see this looking the yellow waveform before and after the communication occurrence. Data is aligned with first clock edge so CPHA is also 0.
Fig.4 – SPI oscilloscope screenshot when CPOL is low and CPHA is high.
In this case CPOL is still 0, indeed waveform is still low before and after the communication occurrence. Now data is aligned on second clock edge so this time CPHA is 1.
Fig.5 – SPI oscilloscope screenshot when CPOL is high and CPHA is low.
Now CPOL is 1 and the yellow waveform before and after the communication occurrence is high. Data is aligned with first clock edge so CPHA is 0.
Fig.6 – SPI oscilloscope screenshot when both CPOL and CPHA are high.
In this case should be clear that CPOL is 1 and CPHA is 1.
2 Common uses
The SPI is widely used in embedded systems as is a fast and reliable communication bus, it has an higher throughput compared to I2 bus, it is full-duplex and it is not limited to any maximum clock speed, enabling potentially high speed. It is possible find on the market a big number of devices that are addressable through the SPI bus.
It is also very interesting the capabilities that we can reach coupling the SPI bus with one ore more Shift Registers (here and out SR) connected in Daisy Chain. Indeed some SR can receive a serial input and shift out parallel data. In this way we can use four wire to address potentially an unlimited number of outputs.
3 Using SPI in ChibiOS/HAL
3.1 Enabling SPI driver and its peripherals
Like the already discussed PAL driver, to use the SPI we need to enable the related subsystem switch in halconf.h.
Furthermore, on STM32, SPI peripheral has more than a driver and we have to enable those who want to use editing mcuconf.h.
Note that, once we have set STM32_SPI_USE_SPI1 to TRUE a object related to SPI driver 1 (SPID1) becomes available.
3.2 The SPI configuration
We will now introduce the SPI configuration object postponing its use. Anyway, its type is SPIConfig. What follows is the one used in our demonstrations:
As said, this object is hardware depending. Looking at file “os/hal/platforms/STM32/SPIv1/spi_lld.h”, we could find out some additional information on this object:
In ChibiOS a driver configuration usually contains some fields common for every MCU and some others depending on the hardware. Here we have four fields of which the first three are common for every MCU and the fourth is specific of STM32F4. So we have:
- end_cb, that is a pointer to a callback occurring when an operation is complete (like when finishing send out words). Choosing NULL callback is avoided;
- ssport, that is the port of the Chip Select used during this communication;
- sspad, that is the pad of the Chip Select used during this communication;
- cr1, that is most likely the most important. Indeed, it is value assigned to SPI Control Register 1. You could found more information about this register in Reference Manual looking for “SPI_CR1”.;
For the record, STM32 has two versions of the SPI driver and in this case we are using the driver SPIv1. Contrariwise, STM32F3 uses the driver SPIv2, indeed, in its SPIConfig there is another field related to the SPI Control Register 2 (not available on the STM32F4).Fig.7 – SPI Control Register 1 on a STM32.
CR1 is a 16-bit register controlling SPI configuration. In ChibiOS we could found bit masks helping to compose that value. (SPI_CR1_BR_0 is one of them). In this case we had configured SPI for sending out 8-bit length word with CPOL = 0, CPHA = 0 and frequency clock of fpclk/4, managing Chip Select software. Note that fpclk depends on clock tree configuration and in this case is 84/2 MHz. We had chosen this configuration reading shift register data-sheet.
3.3 Most used APIs and their usage
In the previous tutorial we said that
In ChibiOS, every driver (except PAL) must be started before to use it.
Indeed, the SPI implementation provides two APIs to start and stop the driver: spiStart() and spiStop(). Every APIs (related to the SPI driver) receive as first parameter a pointer to the SPIDriver object. While the spiStop() requires only this pointer, the spiStart() requires also a pointer to the already introduced SPIConfig object. Indeed, we configure a certain SPIDriver on spiStart() and to reconfigure this driver we have to stop it using spiStop() and restart it using a new configuration. In a scenario like this we will have a single SPIDriver and more than a SPIConfig.
An SPI bus usually exchanges words between a master and a slave and there are more APIs to perform this operation. We can classify these APIs in two categories: the blocking and non-blocking APIs. Here we have to spend some words to avoid misunderstandings:
- The blocking APIs just suspend the calling thread, and this thread becomes ready when the operation is completed. This means that (according to thread priorities) the thread can be resumed and it proceeds with operations.
- The non-blocking APIs doesn’t suspend the calling thread and it proceeds with operations.
For the record, some functions could be non-blocking since in ChibiOS the SPI driver internally uses the DMA so after the DMA initialization the CPU can be used to perform other operations. In both cases when the operation completes the driver implementation can launch a callback that could be very useful when we are using the non-blocking API. So, about APIs we have spiExchange() and spiStartExchange() that are respectively the blocking and the non-blocking functions that performs an exchange. Both require as parameters a pointer to the SPIDriver, the number of word to exchange, a pointer to a transmission buffer and a pointer to a receiver buffer. Note that the declaration of the transmission and the receiver buffer is a task assigned to the user since this is depending on the application.
Some peripheral like that from STM32 can manage Chip Select in hardware and in software. When hardware managed, the Chip Select is automatically pulled down by the peripheral when communications starts and pulled up when the SPI is disabled. When we use it in software mode the Chip Select must be managed using some APIs and they are spiSelect() and spiUnselect(). In the last case the Chip Select mode can be chosen acting on SPIConfig (more precisely acting on Control Registers). In hardware mode instead, Chip Select is fixed by internal hardware connections.
We can also ignore the transmission buffer or the received buffer. For that reason alternative APIs are: spiStartSend(), spiSend(), spiStartReceive() e spiReceive().
Concluding, when the same peripheral is contended by more threads there are two additional APIs that allow to acquire and to release the bus. In this way when bus is acquired by a thread, another one which tries to acquire the peripheral is queued until the bus is released. These APIs are spiAcquireBus() and spiReleaseBus(). An example of contending usage of the SPI could be found under the official ChibiOS/HAL demo tree.
A common blocking usage is presented in the demo we are proposing by the end of this tutorial.
3.4 PAL configuration
The SPI is internally connected to GPIO through the Alternate Function (Already explained in the previous tutorial). When we use the Chip Select in hardware mode we have to use the line connected through the Alternate Function, otherwise, in software mode, we can use any PIN configuring it as Output Pushpull and setting the port and pin of the Chip Select in the SPIConfig. We can choose the configuration of the PINs acting on board.h and board.c files or using the PAL API palSetPadMode():
4 Our circuit
4.1 Introducing the Shift RegistersFig.8 – Internal principle schematic of a Shift Register 74HC595.
A Shift Register is a cascade of Flip-Flops sharing the same clock. If a bit stream is placed at the input of this cascade on each clock period this bit stream is shifted forward. Considering every Flip-Flop output a SR can realize a serial to parallel converter.
In this case we are using an 8-bit serial in/parallel out SR to drive 8 LED sending a word. Connecting more than a SR as a cascade (or like a Daisy Chain) we can obtain a circuitry equivalent to a single SR having a bigger length.
SRs are very cheap and can be more complicated. We have used the well known 74HCT595. This is its datasheet:
Datasheet of 74HCT595
Indeed, this SR has a chain of Flip-Flops used to shift data in (see Fig.8) and some additional Flip-Flops used as Storage. The chain is synchronized by SHCP (on Low to High transition), the Storage group is synchronized by STCP (on Low to High transition). Furthermore, the shift chain can be reset by MR (when lowered) and the Storage output is enabled by OE (when lowered).
4.2 SchematicFig.9 – Meeting SPI schematic.
Connecting OE to GND and MR to VDD the Output is always enabled and the shift chain is never reset. The shift chain could be synchronized by SPI clock, but most important we don’t need to clock the Storage group on each SPI clock period: we actually need to store data only in the end of the communication and this requires a single LOW to HIGH transition when communication is completed. This can be easily achieved connecting the STCP to the Chip Select. Note that if Chip Select remains HIGH the Shift Chain is enabled anyway.
Since the 74HCT595 is not designed for LED driving, we connect it on the low side. This way, when an output line is high, related LED is off and no current flow though SR.
Q7′ (i.e. serial output) is connected to MOSI: in this way we could connect more SR as cascade. We could connect the MISO with the last SR output completing the Daisy Chain connection but in this case of study this is useless.
4.3 Bill of Materials
For each module needs:
- 1x 50mm x 70mm proto-board
- 8x Blue LED
- 8x 100ohm 5% Resistors
- 1x M74HC595B1
- 1x dip16 socket
- 2x polarized connector header 5 pin
On STNucleo-side you could use a proto-shield. If you are planning to connect a big number of LEDs consider an external power supply, otherwise you will overload on-board voltage regulator destroying it.
5 Demos explained
5.1 Step 0
In what follow we will implement a binary counter using a single SR and the SPI peripheral. This is made using two threads more than main that actually print the current transmission buffer on the serial. These threads works together: while the first one send out continuously a buffer over the SPI, the other update that buffer increasing its value. For the record, here the buffer is just a single 8-bit word (i.e. a uint8_t variable) and as spiSend() requires a buffer (i.e. a pointer to uint8_t), we have to pass the address of the variable.
5.2 Step 1
This is a little variance of the previous step. The only big difference is that here we send out two words instead of one to address two SR.
5.3 Step 2
In this last step the real variance is that we are another additional thread. So we have the main that prints information on the serial, a thread that send out a buffer over the SPI, a thread that updates a counter (that is the counter related to light game) and another one that according to this counter updates properly the buffer in order to create different light games.
6 Project used in this tutorial
This code has been tested using ChibiOS/RT 3.0.x under ChibiStudio preview 14. If you are using newest version of ChibiOS something could be changed. Let us know if code is not more working.
RT-STM32F401RE-NUCLEO-SPI-Step0 RT-STM32F401RE-NUCLEO-SPI-Step1 RT-STM32F401RE-NUCLEO-SPI-Step2
You may also be interested in