top of page

PIC18F4550 SPI

Updated: Feb 2

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


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


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


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


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: SSPM0

Mode

Clock / SS pin state

0000

SPI Master mode

Fosc/4

0001

SPI Master mode

Fosc/16

0010

SPI Master mode

Fosc/64

0011

SPI Master mode

TMR2 output/2

0100

SPI Slave mode

SS enabled

0101

SPI Slave mode

SS 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 Mode

CKP

CKE

0,0

0

1

0,1

0

0

1,0

1

1

1,1

1

0



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 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++);
}

 

4 views0 comments

Comments


bottom of page