SPI stands for Serial Peripheral Interface. Like UART and I2C, It is used to transfer data simply and quickly from one device to another. Most importantly, it has a faster data transfer rate than UART and I2C. We can use it to transmit and receive 8-bit data synchronous at the same time. Therefore, it supports full-duplex communication. SPI protocol consists of four lines such as SDO, SDI, SCK, and CS.
It is the popular embedded serial communication that is widely supported by many chip manufacture. It is considered as one of the fastest serial data transfer interfaces for the embedded system. Most of the time, it is used to communicate data to and from the micro-microcontroller.
SPI Communication Introduction
SPI is a synchronous protocol based on the master-slave communication type. The master device (usually a microcontroller ) controls slave devices. Because the slave device takes instructions from the master device. It allows transmission of data from a master device to one or more slave devices and from slave devices to master devices over short distances at high speeds (MHz). It consists of 4 fours but three-wire communication is also possible.
- MOSI/SDO: This line used to send data to a slave. It can be read as master-out salve-in
- MOSI /SDI: MOSI wire used to receive data from a slave. It is an input line for the master and output line for a slave.
- SCLK: Clock signal wire
- CS/SS: In case of a single master and multiple slave configuration, this wire used to select to which slave, master device wants to send data.
SPI bus interface connection is incorporated into many devices like ADC, DAC, and EEPROM. If you want to explore further on SPI communication protocol, read this complete guide:
SPI Communication Module PIC Microcontrollers
SPI is implemented in the PIC microcontrollers by a hardware module called the Synchronous Serial Port or the Master Synchronous Serial Port. This module is built into many PIC microcontrollers. This module also supports II2C communication that is a two-wire communication. It supports SPI serial communication between two or more devices at a high speed and is reasonably easy to implement.
Unlike I2C, we do not use slave address in serial peripheral interface communication. But we use a chip select pin to select a slave. In the case of multiple slaves, we need a chip select wire for every slave device.
PIC SPI communication module transmits/receives 8-bit data synchronously simultaneously. That means it supports full-duplex communication mode.
SPI Communication Bus Pins
SPI was originally started by Motorola Corp, but now this standard is adopted by many semi-conductor chip companies. It consists of a total of 4 pins:
- SDO: Serial data output from master and Input to slave [MOSI]
- SDI: Serial data input to master and output from slave [MISO]
- SCLK: Serial clock output from master [SCK] Input to slave [SCK]
- CE: Chip Enable or SS (Slave Select)
Only two pins SDI (Data in) and SDO (Data out) are for data transfer. This reduction of data pins reduces the package size and power consumption. SPI bus has the SCLK (serial clock) pin to synchronize the data transfer between two chips. It is generated by the master device and controls when data is sent and when it is read. The last pin of SPI bus is CE (Chip Enable) which is used to initiate or terminate the data transfer. When the CE signal goes low at a slave device, only that slave is accessed by SPI and will listen for SPI clock and data signals. These four pins make the SPI a 4-wire interface. In the 3-wire interface, we have SCLK, CE and only one single pin for data transfer.
SPI Communication Data Transfer
In connecting a device with an SPI bus to a microcontroller, we use the MCU as a master device and the SPI device acts as a slave. The clock signal is provided by the master to provide synchronization. MCU generates the SCLK which is fed to the SCLK pin of the SPI device. The SPI protocol uses SCLK to synchronize the transfer of one bit at a time and the MSB (Most significant bit) goes in first.
During transfer:
- CE must be high, CE=1.
- Address and data are transferred between MCU and slave in a group of 8 bits.
- The address byte is immediately followed by a Data byte.
- For Write: A7 bit of address byte is always 1
- For reading: A7 bit of address byte is always 0
SPI Data Transfer Modes
There are two modes of operation for SPI devices.
- Single byte write
- Multiple bytes write
Single-Byte Write of SPI communication:
The following are the steps to send data in a single-byte mode for SPI devices.
- Make CE=1 to begin writing.
- The 8-bit address is shifted one bit at a time with each edge of the clock.
- When all the 8 bits of the address are sent, the SPI device will expect to receive the data for the particular address location.
- The 8bit data is shifted with one-bit time with each edge of the clock.
- Make CE=0, which indicates the end of the write cycle.
SPI Multiple Burst Write
This mode loads consecutive locations. We just provide the address for the first location, followed by the data for that location. From then on consecutive bytes are written to that consecutive memory location. SPI device internally increments the address location as long as CE is high.
PIC Microcontroller SPI Module Registers
In this section, we will PIC18F452 microcontroller SPI module registers and their configuration settings. MSSP (Master synchronous serial port) module inside the Pic MCU supports the SPI protocol. Four registers are associated with SPI of the MSSP module of PIC18F452, which are:
- SSPBUF (synchronous serial port buffer Register)
- SSPCON1 (synchronous serial port control Register)
- SSPSTAT (synchronous serial port status Register)
- SSPSR ( Shift register but use cannot access it)
To transfer a byte of data, it is placed in SSPBUF. This register holds the byte received. It will then transfer the contents to the SSPSR. SSPSR is the synchronous shift register for the SPI module. It shifts data in and out of the device.
SPI SSPCON1 Register
The SSPCON1 register controls the SPI mode operation.
bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0 |
---|---|---|---|---|---|---|---|
WCOL | SSPOV | SSPEN | CKP | SSPM3 | SSPM2 | SSPM1 | SSPM0 |
- WCOL is a status flag that indicates if write collision error occurs.
SSPCON1bits.WCOL= 1; // 1 = The SSPBUF register is written while it is still transmitting the previous word SSPCON1bits.WCOL= 0; // No error
- SSPOV bit indicates an overflow error and this is useful in slave mode only.
- SSPEN bit (Synchronous Serial Port Enable) in the SSPCON1 register must be set “High” to allow the use of Pins of pic MCU for SPI protocol. To enable SPI port in MPLAB XC8, we configure SSPCON1 register like this:
SSPCON1bits.SSPEN=1 ; // Enables serial port and configures SCK, SDO, SDI, and SS as serial port pins SSPCON1bits.SSPEN=0 ; // Disables serial port and configures these pins as I/O port pins
- SSPM3: SSPM0 bits are used to select SPI port mode. We can select either master or slave mode with different clock frequency options.
SPI SSPSTAT register
bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0 |
---|---|---|---|---|---|---|---|
SMP | CKE | D/A | P | S | R/W | UA | BF |
- SMP bit7: This bit is used to sample data in master mode and this bit must be set to zero when SPI is used in slave mode.
SSPSTATbits.SMP=1; //input data sampled at the end of data output time SSPSTATbits.SMP=0; // input data sampled at the middle of data output time
- CKE: It selects clock edge for SPI ( positive edge or negative edge)
When CKP = 0:
SSPSTATbits.CKE = 1; // Data transmitted on rising edge of SCK SSPSTATbits.CKE = 0; // Data transmitted on falling edge of SCK
When CKP = 1:
SSPSTATbits.CKE = 1; //Data transmitted on the falling edge of SCK SSPSTATbits.CKE = 0; //Data transmitted on the rising edge of SCK
Buffer Full status bit:
- BF=1, receive complete, SSPBUF is full
- BF=0, receive not complete, SSPBUF is empty
Note: Bits from 1-5 of the SSPSTAT register are reserved for I2C communication only.
SPI communication with PIC microcontroller Example
In this section, we will see example codes to use SPI communication with pic microcontroller in master and slave mode. In the end, PIC to PIC SPI communication example will be discussed.
PIC SPI Module Master Mode Example
In this example, we will learn to use the SPI module as a master. For demonstration, we use an external EEPROM (25LC256) that communicates over SPI protocol. We will write data to EEPROM with the PIC18F452 microcontroller. After that, we will read from EEPROM and display its value on seven segment display as shown in this circuit diagram.
SPI Master Mode Code PIC18F452 Microcontroller
This code uses an SPI master mode of PIC18F452 microcontroller. It will write data to external EEPROM which acts as an SPI slave device. Firstly, it will write data to EEPROM. Secondly, it will read data from the same locations and display its value on the seven segment display. Make sure to add a configuration file in this code.
#include <xc.h> #define SCK RC3 //Serial Clock #define SDI RC4 //Serial Data In #define SD0 RC5 //Serial Data Out #define SS RA5 //Slave Select: Not used in this application #define SCK_dir TRISC3 #define SDI_dir TRISC4 #define SDO_dir TRISC5 #define SS_dir TRISA5 #define WREN 0x06 //Write enable #define WRDI 0x04 //Write Disable #define WRITE 0x02 #define READ 0x03 #define RDSR 0x05 //Read Status Register //----------------------Common Anode 7-Segment Codes---------------------------- char data[16] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x98,0x88,0x83,0xC6,0xA1,0x86,0x8E}; /* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F */ int address[16] = {0x0103,0x0B31,0x1032,0x0C23,0x6004,0xC0BF,0x10C6,0x0317,0x0AF8,0x0A19,0x102A,0x0CCB,0x1388,0x612D,0x501E,0x42AF}; /* Addresses are 16 bit(hence int data type) wide <ie 4 nibbles>(According to Datasheet info). NB: Chosen at Random */ //------------------------------------------------------------------------------ void EEPROM_write(int address,char data); char EEPROM_read(int address); char EEPROM_status(); char EEPROM_busy(); char EEPROM_wrEnable(); void delay(unsigned int nMilliseconds) { #define CYCLES_PER_MS 100 /* Number of decrement-and-test cycles. */ unsigned long nCycles = nMilliseconds * CYCLES_PER_MS; while (nCycles--); } void SPI_init() { SS_dir = 1; //Diselect chip initially SCK_dir = 0; SDI_dir = 1; SDO_dir = 0; SS_dir = 0; //SS_dir = 0; //Disable: Because operating in master mode SSPSTAT = 0x80; //SMP = 1: Sample bit. Input data sampled at end of data output time. CKE = 0: SPI Clock Edge Select bit CLEAR SSPCON1 = 0x30; //SSPEN = 1: Enable Serial Port. CKP = 1: Clock Polarity Select bit: Idle state for clock is a high level } //------------------------------------------------------------------------------ void SPI_write(char data) { SSPBUF = data; while(BF == 0); } //----------------------------------------------------------------------------- char SPI_read(char dummy) { SSPBUF = dummy; //Assign to clear while(BF == 0); return SSPBUF; } #include <xc.h> void main(void) { SPI_init(); TRISD = 0; PORTD = data[0]; for( int i = 0 ; i < 16 ; i++ ) EEPROM_write(address[i],data[i]); while(1) { for( int i = 0 ; i < 16 ; i++ ) { PORTD = EEPROM_read(address[i]); //Pass Address and Return value at that very address tp PORTD delay(1000); } } return; } void EEPROM_write(int address,char data) { while( EEPROM_busy() == 1 ); //Set write enable latch SS = 0; SPI_write(WREN); SS = 1; //Latch-Enabled. use isWriteEn() if you want to verify //Start Write sequence(only occurs when/if Write latch is Enanled) SS = 0; SPI_write(WRITE); SPI_write(address>>8); SPI_write(address); SPI_write(data); SS = 1; } //------------------------------------------------------------------------------ char EEPROM_read(int address) { while( EEPROM_busy() == 1 ); //Wait for previous Write/Read operations to finish SS = 0; SPI_write(READ); SPI_write(address>>8); //Upper Byte SPI_write(address); //Lower Byte char data = SPI_read(0); SS = 1; return data; } //------------------------------Used as a flag---------------------------------- char EEPROM_status() { SS = 0; SPI_write(RDSR); //Intsruction: Read Status register of the EEPROM char status = SPI_read(0) ; //Send a Dummy parameter to read. Thus ANY int number is valid SS = 1; return status; } //------------------------------------------------------------------------------ char EEPROM_busy() { char busy = EEPROM_status(); return (busy & 0x01); //LSB is Write-In-Process (WIP) bit. When Set, a write is in progress, when Clear, no write is in progress } //------------------------------------------------------------------------------ char EEPROM_wrEnable() { char isWrite = EEPROM_status(); return (isWrite & 0x02); //Bit 2 is the Write Enable Latch (WEL) bit and indicates the status of the write enable latch }
Simulation Result
PIC to PIC SPI Communication MPLAB XC8 Compiler
In this example, we use one pic microcontroller (PIC18F452) as a master and one as a slave. The master microcontroller sends an instruction to the slave PIC18F452 to control an LED. Make connections according to this circuit diagram:
- Connect SCK pin of master with SCK of slave pic microcontroller
- Connect SDO pin (master) to SDI (slave)
- Master input is a logic probe. When the user will press this probe, the master SPi device will send an instruction to the slave to control an LDE.
- Connect an LED with RB0 pin of slave pic microcontroller
Simulation Result
PIC SPI Master Code MPLAB XC8
Upload this code to the master-microcontroller and make sure to add configuration file in this code. You can generate configuration bits with mplab xc8 compiler.
#include <xc.h> #include "newxc8_header.h" // generated configuration bits header file void delay(unsigned int nMilliseconds) { #define CYCLES_PER_MS 100 /* Number of decrement-and-test cycles. */ unsigned long nCycles = nMilliseconds * CYCLES_PER_MS; while (nCycles--); } void SPI_init() { // this setting selects master mode with frequency fosc/4 SSPCON1bits.SSPM0 = 0; SSPCON1bits.SSPM1 = 0; SSPCON1bits.SSPM2 = 0; SSPCON1bits.SSPM3 = 0; // Enable SPI Port SSPCON1bits.SSPEN = 1; // Configure The Clock Polarity & Phase SSPCON1bits.CKP = 0; SSPSTATbits.CKE = 0; // Slew rate control enabled for High Speed mode SSPSTATbits.SMP = 0; // Set SPI pins as digital I/O TRISC5 = 0; // SDO -> Output TRISC4 = 1; // SDI -> Input TRISC3 = 0; // SCK -> Output //Uncommnent this line, if you want to send send over interrupt // SSPIE = 1; PEIE = 1; GIE = 1; } //------------------------------------------------------------------------------ void SPI_write(char data) { SSPBUF = data; while(BF == 0); } //----------------------------------------------------------------------------- void main(void) { SPI_init(); TRISB=0x00; while(1) { if(RB0) SPI_write(0x01); // if switch pressed, send 0x01 else SPI_write(0x02); // if switch is pressed pressed, send 0x02 } return; }
PIC SPI Slave Code MPLAB XC8
#include <xc.h> #include "newxc8_header.h" char data; void __interrupt() ISR(void) { if(SSPIF) // check if interrupt occurs due to SPI module { data = SSPBUF; // copy data from receive buffer into a global variable data SSPIF = 0; // reset flag interrupt for SPI module } } void delay(unsigned int nMilliseconds) { #define CYCLES_PER_MS 100 /* Number of decrement-and-test cycles. */ unsigned long nCycles = nMilliseconds * CYCLES_PER_MS; while (nCycles--); } void SPI_init() { // Set Spi Mode To Slave + SS Enabled SSPCON1bits.SSPM0 = 0; SSPCON1bits.SSPM1 = 0; SSPCON1bits.SSPM2 = 1; SSPCON1bits.SSPM3 = 0; // Enable The Synchronous Serial Port SSPCON1bits.SSPEN = 1; // Configure The Clock Polarity & Phase (SPI Mode Num. 1) SSPCON1bits.CKP = 0; SSPSTATbits.CKE = 0; // Clear The SMP Bit SSPSTATbits.SMP = 0; // Configure The IO Pins For SPI Master Mode TRISC5 = 0; // SDO -> Output TRISC4 = 1; // SDI -> Input TRISC3 = 1; // SCK -> Intput TRISA5 = 1; // SS -> Input // Enable Interrupts. Comment Them To Disable interrupts(NOT Recommended) SSPIE = 1; PEIE = 1; GIE = 1; } void main(void) { SPI_init(); TRISB = 0x00; while(1) { if (data==0x01) { RB0=1; //if received data = 0x01, turn on LED } else if (data==0x02) { RB0=0; //if received data = 0x02, turn off LED } } return; }
PIC to PIC SPI Communication Example
The main devices used in the circuit are the two 18F452 (one acts as a master and one as a slave) and LEDs. The 4 SPI control lines connect the two PICs together.
- SCLK: RC3 (master)to RC3 (slave)
- MOSI: RC5 (master) to RC4 (slave)
- MISO: RC4 (master) to RC5 (slave)
- SS:RA0 (master) to RA5 (slave)
7 LEDs will be used to display the data transferred to the SPI slave Pic MCU from the SPI master Pic MCU. The LEDs are connected to PORTB (RB0-RB6) of the slave Pic.
Code SPI communication with pic microcontroller
There will be two codes for this example. One for the SPI master and one for SPI slave.MPLAB code is given below for both devices.The master is sending commands and slave is receiving and displaying it on the LED output port. LEDs act like a binary counter, counting down to zero.
SPI communication Master Code:
void main(void){ unsigned x = 0xFF; unsigned mask = 0x80; TRISA = 0x00; TRISC = 0x00; PORTA = 0x03; OpenSPI(SPI_FOSC_16, MODE_10, SMPMID); while(1){ PORTAbits.RA0 = 0; //Slave Select putcSPI(x); PORTAbits.RA0 = 1; //Slave Select Delay10KTCYx(75); if(x == 0) x = 0xFF; else x--; } }
SPI communication Slave Code:
void main(void){ unsignedchar x; ADCON1 =0b00000110; //PORTA All Digital TRISA =0xFF; TRISB =0x00; PORTA =0x00; PORTB =0x00; TRISCbits.TRISC3 =1; //SCLK TRISCbits.TRISC4 =1; //MOSI OpenSPI(SLV_SSON, MODE_10, SMPMID); while(1){ while(!DataRdySPI()); x =getcSPI();//ReadSPI(); PORTB =(x>>1); Delay10KTCYx(5); } }
Other SPI communication tutorials:
Really helpful to understand for the beginners . Good work bro keep it up .
Can you help me? I need to make a protocol for micro controller communication over power lines.
Why suddenly everybody is getting interested in communication over power line?
Good explanation. I am confused about which PIC pin should I connect to SDI, SCLK and CS pins, if I want to use SPI available internally.
it is clearly shown on circuit diagram
Good explanation!, Can you help me sending scheme source for Isis simulation?
goode information ,i am interesting for this project canyou help me to simulate spi of a micro controller
pic 16f 876 with a thermocouple MAX6675 with this circuit in my e mail asmakhenifer@gmail.com
Thank you. It was useful.
I am interfacing 2 gb SD Card with PIC16F887, so is it possible to code for it without using FAT ….??? Kindly tell..
Would you write assembly code for same c code? thanks.
I wanted to know how SPI signals are doing and here on this site everything is clear for me clear. Thank You and good luck to everyone with programming and also in life <3
can you describe how to interface enc28j60 with pic32 microcontroller