SPI Communication with PIC microcontroller (original) (raw)

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.

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

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:

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:

SPI Data Transfer Modes

There are two modes of operation for SPI devices.

  1. Single byte write
  2. 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.

spi single byte writing

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.

spi multi byte writing

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:

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

SSPCON1bits.WCOL= 1; // 1 = The SSPBUF register is written while it is still transmitting the previous word SSPCON1bits.WCOL= 0; // No error

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

spi frequency selection bits in pic microcontroller

SPI SSPSTAT register

bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
SMP CKE D/A P S R/W UA BF

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

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:

Note: Bits from 1-5 of the SSPSTAT register are reserved for I2C communication only.

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 communication with external EEPROM using pic microcontroller

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:

PIC to PIC SPI communication with LED Control Example

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

spi communication with pic microcontroller circuit diagram

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.

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: