PIC18F4550 SPI

Introduction to SPI Communication Protocol

Serial Peripheral Interface (SPI) is a synchronous serial data protocol generally used for quick communication over short distances.

SPI is a synchronous protocol that allows a master device (usually microcontroller) to initiate communication with a slave device. It is a full-duplex communication protocol.

SPI uses separate clock and data lines with a select line to select the device with which we want to communicate.

SPI generally used to communicate quickly with devices like SD card, EEPROM chips, some of the TI ADCs, etc.

It can communicate with multiple slaves.

There are four basic pins used in PIC18F4550 SPI communication which are:

1. SCK (Serial Clock)

2. SDI (Serial Data In)

3. SDO (Serial Data Out)

4. SS (Slave Select)

General connection diagram of PIC18F4550 master and slave devices:

PIC18F4550 SPI Pins
PIC18F4550 SPI Pins

       1. SCK: (Serial Clock)

           The clock signal (SCK) is provided by the master to provide synchronization.

           Only the master device can control the clock line, SCK.

        2. SDI: (Serial Data In)

            SDI is the serial data input which carries data into the device.

        3. SDO: (Serial Data Out)

           This is the Serial data output signal. It carries data out of the device.

        4. SS: (Slave Select)

            It is a slave select signal. In master mode, we can use it to choose the device (slave) with which we wish to communicate.

            This is an active low signal, so a low on this line will indicate the SPI is active.

            When multiple slaves are used then GPIO pins of the microcontroller are used to select slave devices.

 

Working of PIC18F4550 SPI

SPI allows 8 bit of data to be synchronously transmitted and received simultaneously.

Data leaving the master is put on the SDO (serial data output) line and data entering the master enters via the SDI (serial data input) line.

A clock (SCK), is generated by the master device. It controls when and how quickly data is exchanged between the two devices.

Master select devices using the SS (slave select) line.

 

PIC18F4550 SPI Working
PIC18F4550 SPI Working

SSPSR: It is the shift register used for shifting data in or out. It is not user-accessible.

SSPBUF: It is the buffer register to which data bytes are written to or read from.

  • When the PIC18F4550 microcontroller wants to transmit the data, it will copy the data in the SSPBUF register (SSP Buffer) using a microcontroller.
  • Then that data is moved to the SSPSR i.e. SSP shift register which will shift that data out bit by bit on each clock event from master to the slave via SDO. It transmits or shifts MSB bit first.
  • While at the slave side the data is shifted in via SDI pin bit by bit using the same SSPSR register and moved to the SSPBUF once a byte of data has been exchanged between the two devices.
  • Then it should be necessary to read the data from SSPBUF at master before another write. Otherwise, the incoming data will be lost. This will indicate buffer overflow.
  • The master device transmits a clock and slave select signal. The slave device waits for these signals and uses them when processing the SPI data.

Note: When the master transmits data to the slave device, the slave also transmits the requested byte or flush byte simultaneously. So after data transmission via SSPBUF from master to slave, data should be read immediately to avoid overflow. It is also applicable for transmission from the slave device to master.

 

SPI Registers for PIC18F4550 

To configure PIC18F4550 for SPI communication protocol following two registers are used

SSPSTAT: SSP Status Register for SPI mode

SSP Status register for SPI mode
SSPSTAT Register

Bit 0 - BF: Buffer Full Status bit

       1 = Receive complete, SSPBUF is full.

       0 = Receive not complete, SSPBUF is empty.

Bit 6 - CKE: SPI Clock Select bit

When CKP =0

       0 = Data transmitted on the falling edge of SCK

       1 = Data transmitted on the rising edge of SCK

When CKP =1

       0 = Data transmitted on the rising edge of SCK

       1 = Data transmitted on the falling edge of SCK

Bit 7 - SMP: Sample bit

 SPI Master mode:

       1 = Input data sampled at end of data output time

       0 = Input data sampled at the middle of data output time

SPI Slave mode:

       SMP must be cleared when SPI is used in Slave mode.

Note: Other bits in this register are used for I2C protocol.

 

SSPCON1: SSP Control Register for SPI mode

SSP control Register for SPI mode
SSPCON1 Register 

 

Bits 3:0 - SSPM3: SSPM0: Master Synchronous Serial Port Mode Select bits

These bits are used to select Master or Slave mode and also a select clock.

SSPM3: SSPM0ModeClock / SS pin state
0000SPI Master modeFosc/4
0001SPI Master modeFosc/16
0010SPI Master modeFosc/64
0011SPI Master modeTMR2 output/2
0100SPI Slave modeSS enabled
0101SPI Slave modeSS disabled, used as I/O pin

Other combinations are used for I2C Protocol.

Bit 4 - CKP: Clock Polarity Select bit

       1= Idle state for the clock is a high level

       0 = Idle state for the clock is a low level

Bit 5 - SSPEN: Master Synchronous Serial Port Enable bit

       1 = Enables serial port and configures SCK, SDO, SDI, and SS as serial port pins.

       0 = Disables serial port and configures these pins as I/O port pins.

Bit 6 - SSPOV: Receive Overflow Indicator bit

SPI Slave mode:

       1 = A new byte is received while the SSPBUF register is still holding the previous data. In case of overflow, the data in SSPSR is lost. 

       0 = No overflow

Bit 7 - WCOL: Write Collision Detect bit (Transmit mode only)

        1 = The SSPBUF register is written while it is still transmitting the previous word

     (must be cleared through software)

        0 = No collision

 

Importance of CKP and CKE bits

CKP decides the polarity of a clock, which means, the idle state of the clock. SPI devices need to be programmed with the same polarity. Then both devices will send or receive data at the same time. But this data is meaningful or dummy depends on the application.

This leads to the following three chances,

  1. Master sends data, the slave sends dummy data
  2. Master sends dummy data, the slave sends data
  3. The Master sends data, the slave sends data.

CKE decides on which rising/falling edge data should be transmitted. But actual data is put or latch on the SDO line opposite to the edge of data transmission.

CKE and CKP together decide what mode of SPI is used for all SPI transfers.

SPI ModeCKPCKE
0,001
0,100
1,011
1,110

 

Following Timing diagram show actual SPI communication

SPI Timing Diagram

 

Programming PIC18F4550 SPI

Initialization:

Configure PORT pins used for SPI communication.

Also, disable SS pin i.e. SS=1.

Clear SSPIF.

Configure SSPSTAT register by setting bit SMP, CKE for data change at rising/falling edge of the clock, and make Buffer full (BF) = 0.

Initialize SSPCON register by setting bit SPEN =1 for SSP Enable. CKP indicates clock polarity.

Select mode of SPI master/slave mode and decide speed as you require using SSPM3: SSPM0 in SSPCON1 register.

Also de-multiplex all four pins SS, SDO, SDI, and SCK by using different registers i.e. ADCON0, ADCON1.

SPI_Master

void SPI_Init_Master()
{
    /* PORT definition for SPI pins*/    
    TRISBbits.TRISB0 =1;	/* RB0 as input(SDI) */
    TRISBbits.TRISB1=0;		/* RB1 as output(SCK) */
    TRISAbits.TRISA5=0;		/* RA5 as a output(SS') */
    TRISCbits.TRISC7=0;		/* RC7 as output(SDO) */

    /* To initialize SPI Communication configure following Register*/
    CS = 1;
    SSPSTAT=0x40;		/* Data change on rising edge of clk , BF=0*/
    SSPCON1=0x22;		/* Master mode,Serial enable,
				idle state low for clk, fosc/64 */ 
    PIR1bits.SSPIF=0;

    /* Disable the ADC channel which are on for multiplexed pin when
    used as an input */    
    ADCON0=0;			/* This is for de-multiplexed the 
				SCL and SDI from analog pins*/
    ADCON1=0x0F;		/* This makes all pins as digital I/O */
}

 

SPI_Slave

void SPI_Init_Slave()
{
    /* PORT definition for SPI pins*/    
    TRISBbits.TRISB0 = 1;	/* RB0 as input(SDI) */
    TRISBbits.TRISB1 = 1;	/* RB1 as output(SCK) */
    TRISAbits.TRISA5 = 1;	/* RA5 as a output(SS') */
    TRISCbits.TRISC7 = 0;	/* RC7 as output(SDO) */

    /* To initialize SPI Communication configure following Register*/
    CS = 1;
    SSPSTAT=0x40;		/* Data change on rising edge of clk , BF=0*/
    SSPCON1=0x24;		/* Slave mode,Serial enable, idle state 
				high for clk */ 
    PIR1bits.SSPIF=0;

    /* Disable the ADC channel which are on for multiplexed pin 
    when used as an input */    
    ADCON0=0;			/* This is for de-multiplexed the SCL
				and SDI from analog pins*/
    ADCON1=0x0F;		/* This makes all pins as digital I/O */    
}

 

Transmit Mode

  1. Copy data to the SSPBUF register.
  2. Wait for the SSPIF interrupt flag to set which is set after complete 1-byte transmission.
  3. Clear SSPIF
  4. Read the SSPBUF register immediately to flush data.
  5. Before transmission/writing of no. of bytes make SS active i.e. SS=0 and after complete transmission/writing disable it, SS=1.
void SPI_Write(unsigned char x)
{
    unsigned char data_flush;
    SSPBUF=x;			/* Copy data in SSBUF to transmit */
    while(!PIR1bits.SSPIF);	/* Wait for complete 1 byte transmission */
    PIR1bits.SSPIF=0;		/* Clear SSPIF flag */
    data_flush=SSPBUF;		/* Flush the data */
}

 

Receive mode

  1. Transmit flush byte by copying data to the SSPBUF register (optional).
  2. Wait for the SSPIF interrupt flag to set which is set after complete 1-byte is received.
  3. Read data from SSPBUF register and return SSPBUF.
unsigned char SPI_Read()
{    
    SSPBUF=0xff;		/* Copy flush byte in SSBUF */
    while(!PIR1bits.SSPIF);	/* Wait for complete 1 byte transmission */
    PIR1bits.SSPIF=0;
    return(SSPBUF);		/* Return received byte */   
}

 

Example 1

Let’s establish SPI communication between the PIC18F4550 microcontroller. One PIC will act as a master device and the other as a slave device.

By using this SPI communication, one PIC microcontroller will start counting continuously from 0-15 and transmit these values to another PIC microcontroller. Another PIC microcontroller will glow LED connected on PORT shows counting.

 

PIC18F4550 SPI Interfacing diagram

Master-Slave communication between Two PIC micrcontroller
Master-Slave communication between Two PIC18F4550 microcontroller

 

Program

SPI MASTER

/*
 * Interface Two PIC18F4550 microcontroller 
 * for communicating with each other using SPI Protocol
 * http://www.electronicwings.com
 */

#include <pic18f4550.h>
#include "Configuration_Header_File.h"

#define CS LATA5

void SPI_Write(unsigned char);
void SPI_Init_Master();
unsigned char SPI_Read();
void MSdelay(unsigned int);

void main() 
{
    int i;
    OSCCON = 0x72;		/* Use internal frequency 8 MHz */    
    INTCON2bits.RBPU=0;		/* Enable internal Pull-up of PORTB */
    SPI_Init_Master();		/* Initialize SPI communication */
    MSdelay(10);
    
    while(1)
    {
        CS = 0;
        for(i=0;i<=15;i++)	/* Start counter */
        {
            SPI_Write(i);	/* Send counter value to Slave */
            MSdelay(1000);
        }
        CS = 1;
        i=0;
    
    }
}

void SPI_Init_Master()
{
    /* PORT definition for SPI pins*/    
    TRISBbits.TRISB0 = 1;	/* RB0 as input(SDI) */
    TRISBbits.TRISB1 = 0;	/* RB1 as output(SCK) */
    TRISAbits.TRISA5 = 0;	/* RA5 as a output(SS') */
    TRISCbits.TRISC7 = 0;	/* RC7 as output(SDO) */

    /* To initialize SPI Communication configure following Register*/
    CS = 1;
    SSPSTAT=0x40;		/* Data change on rising edge of clk,BF=0*/
    SSPCON1=0x22;		/* Master mode,Serial enable,
				idle state low for clk, fosc/64 */ 
    PIR1bits.SSPIF=0;

    /* Disable the ADC channel which are on for multiplexed pin
    when used as an input */    
    ADCON0=0;			/* This is for de-multiplexed the SCL
				and SDI from analog pins*/
    ADCON1=0x0F;		/* This makes all pins as digital I/O */    
}

void SPI_Write(unsigned char x)
{
    unsigned char data_flush;
    SSPBUF=x;			/* Copy data in SSBUF to transmit */

    while(!PIR1bits.SSPIF);	/* Wait for complete 1 byte transmission */
    PIR1bits.SSPIF=0;		/* Clear SSPIF flag */
    data_flush=SSPBUF;		/* Flush the data */
}

unsigned char SPI_Read()
{    
    SSPBUF=0xff;		/* Copy flush data in SSBUF */
    while(!PIR1bits.SSPIF);	/* Wait for complete 1 byte transmission */
    PIR1bits.SSPIF=0;
    return(SSPBUF);		/* Return received data.*/   
}

/*************************Delay Function****************************/
void MSdelay(unsigned int val)	/* Delay of 1 ms for 8MHz Freq. */
{
     unsigned int i,j;
        for(i=0;i<val;i++)
            for(j=0;j<165;j++);
}

 

SPI SLAVE

/*
 * Interface Two PIC18F4550 microcontroller 
 * for communicating with each other using SPI Protocol
 * http://www.electronicwings.com
 */
#include <pic18f4550.h>
#include "Configuration_Header_File.h"

#define CS LATA5
#define LED LATD

void SPI_Write(unsigned char);
void SPI_Init_Slave();
unsigned char SPI_Read();
void MSdelay(unsigned int);


void main() 
{
    int i;
    
    TRISD =0;			/* PORT initialize as output */
    OSCCON = 0x72;		/* Use internal osc. frequency 8 MHz */    
    INTCON2bits.RBPU=0;		/* Enable internal Pull-up of PORTB */
    SPI_Init_Slave();		/* Initialize SPI communication as a slave */             
    
    while(1)
    {
     LED = SPI_Read();       
    }
}

void SPI_Init_Slave()
{
    /* PORT definition for SPI pins*/    
    TRISBbits.TRISB0 = 1;	/* RB0 as input(SDI) */
    TRISBbits.TRISB1 = 1;	/* RB1 as output(SCK) */
    TRISAbits.TRISA5 = 1;	/* RA5 as a output(SS') */
    TRISCbits.TRISC7 = 0;	/* RC7 as output(SDO) */

    /* To initialize SPI Communication configure following Register*/
    CS = 1;
    SSPSTAT=0x40;		/* Data change on rising edge of clk , BF=0*/
    SSPCON1=0x24;		/* Slave mode,Serial enable, idle state
				high for clk */ 
    PIR1bits.SSPIF=0;
    /* Disable the ADC channel which are on for multiplexed pin
    when used as an input */    
    ADCON0=0;			/* This is for de-multiplexed the SCL
				and SDI from analog pins*/
    ADCON1=0x0F;		/* This makes all pins as digital I/O */    
}

void SPI_Write(unsigned char x)
{
    unsigned char data_flush;
    SSPBUF=x;			/* Copy data in SSBUF to transmit */

    while(!PIR1bits.SSPIF);	/* Wait for complete 1 byte transmission */
    PIR1bits.SSPIF=0;		/* Clear SSPIF flag */
    data_flush=SSPBUF;		/* Flush received data */
}

unsigned char SPI_Read()
{    
    SSPBUF=0xff;		/* Copy flush data in SSBUF */
    while(!PIR1bits.SSPIF);	/* Wait for complete 1 byte transmission */
    PIR1bits.SSPIF=0;		/* Clear SSPIF flag */
    return(SSPBUF);		/* Return received data.*/   
}


/***************************Delay Function************************/
void MSdelay(unsigned int val)	/* Delay of 1 ms for 8MHz Frequency */
{
     unsigned int i,j;
        for(i=0;i<val;i++)
            for(j=0;j<165;j++);
}

Components Used

PIC18f4550
PIC18f4550
1
PICKit 4 MPLAB
PICKit 4 MPLAB
1

Downloads

PIC18F4550 SPI based Counter Project File Download
PIC18F455 SPI based Counter Simulation File Download
Ad