How to Implement SPI Using PIC18F4550

The Serial Peripheral Interface (SPI) is a high speed, synchronous, serial communication standard. This communication protocol is basically a Master ā€“ Slave implementation where the master device controls the clock based on which the slave devices operate. The master communicates with a slave or a number of slaves in a system through the SPI bus.

The SPI bus requires a minimum of three wires including SDO (Serial Data Out), SDI (Serial Data Input) and SCK (Serial Clock). Since it is a master controller system the SDO is also called MOSI (Master Output Slave Input) and the SDI is also called MISO (Master Input Slave Output).

The SPI is a full-duplex high speed communication protocol. The master and slave can transmit and receive data at the same time. The master is the device which generates clock for all these data transmissions.

Read Ā PIC microcontroller tutorialĀ based on PIC18F4550 microcontrollers in which one of the microcontroller acts as a slave transmitter and the other acts as master receiver.

With the help of an LCD, this particular project demonstrates the complete data transfer between a master and slave when both the master and slave are transmitting and receiving the data at the same time.

In PIC18F4550 microcontroller the hardware implementation for the SPI interface can be viewed as a simple SISO (Serial-In-Serial-Out) Shift register controlled by a Clock.

How to Implement SPI Using PIC18F4550The above diagram is a single buffer implementation of the SPI hardware. The data is shifted into the buffer from the slave device through the SDI (MISO) and the data is shifted out of the buffer to the slave through the SDO (MOSI). The only difference in the hardware configuration of a master and slave device is the direction of the clock. For a master device the clock is always output and for a slave device the clock is always input.

The Clock unit can generate the clock required for the data transmission. In case of a master device the clock is generated by the device and all other devices in the SPI bus send or receive data to the master based on this clock. When the microcontroller is configured as a slave it can only accept the clock from a master device.

On each clock generated or received one bit in the SISO register is shifted from the SDI to SDO. After the eighth clock since the communication starts, one byte of data has been shifted out through the SDO and one byte of data has been shifted in through the SDI. Now the SISO has an entire byte received from the slave while transmitting a byte to the slave.

In PIC18F4550 the Serial Communication registers are not dedicated for the SPI only, but it can be configured for IIC communication also. The entire module is called Master Synchronous Serial Port (MSSP) module. There are a few registers available in the PIC18F4550 which helps to configure the MSSP as an SPI master or slave module. The major registers associated with the SPI communication in the PIC18F4550 are SSPBUF, SSPSTAT and SSPCON1 register.

SSPBUF

The Synchronous Serial Port Buffer (SSPBUF) register is the register which holds the SPI data which needed to be shifted out to the slave or which has been shifted in from the slave. In case of a slave device this register holds the SPI data which needed to be shifted out to the master or which has been shifted in from the master.

SSPSTAT

The Synchronous Serial Port Status (SSPSTAT) register is the register which holds the status of the SPI module. The first two bits and the last bit of this register are only dedicated for the SPI communication.

The SMP bit decides when the master should sample the data, the CKE decides when to transmit data based on the clock transition and the value of BF indicates whether the SSPBUF is full or not (reception complete or not).

SSPCON1

The Synchronous Serial Port Control 1(SSPCON1) register is the register which is dedicated register for the control of SPI only. All the bits of these register are significant and should be carefully set them.

WCOL bit indicates whether a Write collision has been occurred or not, the SSPOV indicates whether an overflow occurred in SSPBUF or not, SSPEN is the bit which is used to enable or disable the SPI module. The CKP bit is used to decide which are the idle state and the active state of the clock. The bits SSPM3 to SSPM0 is used to set the device as a master or slave with required clock frequency for master and SS enabled or disabled for the slave.

The registers are explained with more details in PIC microcontroller

READ AND WRITE OPERATIONS

Whenever the master or slave needs to transmit data, it can simply write the data to the SSPBUF. When the master writes the data to the SSPBUF the clock will get generated automatically and with each clock the data from the SSPBUF is shifted out through the SDO pin bit by bit. When the slave writes into the SSPBUF the data remains there until the master generates a clock which will shift the data out from the slaveā€™s SSPBUF through its SDO pin.

Apart from simply writing into the SSPBUF for data transmission, the master or slave can also read the received data from the SSPBUF. The master and slave should write or read from the SSPBUF in a particular manner only. Read PIC microcontrollerĀ tutorial to know more.

TIPS FOR CODING:

The SPI protocol is implemented in different devices in different manner and hence when it comes to the coding one should carefully study all the hardware details regarding the SPI implementation in the device for which the coding should be done. Care should be taken especially on the clock frequency, SPI pinā€™s multiplexed arrangement, clock polarity settings, use of SS pin and code performance etc. To have clear understanding of code writing visit PIC Microcontroller tutorial.

CODE DETAILS

The code includes a few functions for initializing the SPI module, sending and receiving the SPI data etc. The details of the functions are given below;

Voidspi master_init ( void )

This function is used to initialize the SPI module as a master with the required clock frequency. It also set the clock polarity and when to sample input data and to transmit output data regarding the clock transition states. It also sets the SPI pins as input or output as required by the master. It can also disable all other modules multiplexed into the SPI pins.

Voidspi slave_init ( void )

This function is used to initialize the SPI module as a slave with SS pin enabled. It also set the clock polarity and when to sample input data and to transmit output data regarding the clock transition states. It also sets the SPI pins as input or output as required by the slave. It can also disable all other modules multiplexed into the SPI pins.

unsignedĀ  char spi data ( unsigned char tx_data )

This function can send a data byte which it takes as the argument and return the received data byte. The master first write the data and then wait for the data to complete transmission and then read the received data while the slave first wait till all the data bits has been received, and then reads the data followed by a data write.

The functions are explained with more details in PIC microcontrollerĀ tutorial.

MASTER AND SLAVE AS TRANSMITTER AND RECEIVER

The arrangement below shows how the master transmits some meaningful data to the slave which the slave can receive. The data which the master transmits is displayed in the first line of the 16Ɨ2 LCD. The slave then transmits the same data which it has received back to the master which is then displayed in the second line of LCD. In this way the master is doing transmission and reception and the slave is also performing transmission and reception. Since the PIC18F4550 is used as master and slave the capabilities of the SPI module of the PIC18F4550 is exploited to the maximum in this project. The following figure can make the arrangement details more clear;

//####################### For Master ####################//
#include <p18f4550.h>
//======================= chip config ===================//
#pragma config PLLDIV = 1
#pragma config CPUDIV = OSC4_PLL6
#pragma config FOSC = INTOSC_HS
#pragma config USBDIV = 1
#pragma config IESO = OFF
#pragma config PWRT = OFF
#pragma config BOR = OFF
#pragma config VREGEN = OFF
#pragma config WDT = OFF
#pragma config WDTPS = 32768
#pragma config CCP2MX = ON
#pragma config PBADEN = OFF
#pragma config LPT1OSC = OFF
#pragma config MCLRE = ON
#pragma config STVREN = ON
#pragma config LVP = OFF
#pragma config ICPRT = OFF
#pragma config XINST = OFF
#pragma config DEBUG = OFF
#pragma config WRTD = OFF
//======================= chip config ===================//
Ā How to Implement SPI Using PIC18F4550 Schematic
//LCD Control pins
#define rs PORTBbits.RB4
#define rw PORTBbits.RB3
#define en PORTBbits.RB2
//LCD Data pins
#define lcdport PORTD
#define lcd_port_dir TRISD
void lcd_clear ( void );
void lcd_2nd_line ( void );
void lcd_1st_line ( void );
void lcd_ini ( void );
void dis_cmd ( unsigned char cmd_value );
void dis_data ( unsigned char data_value );
void lcdcmd ( unsigned char cmdout );
void lcddata ( unsigned char dataout );
void delay_ms ( int delay );
void spi_master_init ( void );
unsigned char spi_data ( unsigned char tx_data );
unsigned char spi_read_data;
void main()
{
unsigned int i = 0, a = 3, b = 3;
unsigned char data1 [] = ā€œEngineersGarageā€;
unsigned char data2 [] = ā€ Ā  Ā SPI demo Ā  ā€œ;
unsigned char data3 [] = ā€œEG LABSā€;
OSCCON = 0x72; // set CPU Frequency as 8 MHz
lcd_ini (); // LCD initialization
//========================= start up display on LCD ================================//
while ( data1 [i] != ā€˜\0ā€™ )
{
dis_data ( data1 [i] );
delay_ms ( 200 );
i++;
}
i = 0;
lcd_2nd_line ();
while ( data2 [i] != ā€˜\0ā€™ )
{
dis_data ( data2 [i] );
delay_ms ( 200 );
i++;
}
i = 0;
delay_ms ( 3000 );
lcd_clear ();
dis_data ( ā€˜Mā€™ );
dis_data ( ā€˜:ā€™ );
dis_data ( ā€˜ ā€˜ );
lcd_2nd_line ();
dis_data ( ā€˜Sā€™ );
dis_data ( ā€˜:ā€™ );
dis_data ( ā€˜ ā€˜ );
//========================= start up display on LCD ================================//
spi_master_init (); // initialize SPI module
while ( data3 [i] != ā€˜\0ā€™ )
{
// show the data about to send on first line of LCD //
dis_cmd ( 0x80 + a );
delay_ms ( 1 );
dis_data ( data3 [i] ); // show the data about to send on first line of LCD //
spi_read_data = spi_data ( data3 [i] ); // send and receive the data
// show the received data on the second line of LCD //
dis_cmd ( 0xC0 + b );
delay_ms ( 1 );
dis_data ( spi_read_data );
// show the received data on the second line of LCD //
a ++;
b ++;
i ++;
}
//========== send a demo data to get the final data from the slave =========//
// show the data about to send on first line of LCD //
dis_cmd ( 0x80 + a );
delay_ms ( 1 );
dis_data ( 0xFF );
// show the data about to send on first line of LCD //
spi_read_data = spi_data ( 0xFF ); // send demod data and receive the last data
// show the received data on the second line of LCD //
dis_cmd ( 0xC0 + b );
delay_ms ( 1 );
dis_data ( spi_read_data );
// show the received data on the second line of LCD //
//========== send a demo data to get the final data from the slave =========//

About The Author

Ibrar Ayyub

I am an experienced technical writer holding a Master's degree in computer science from BZU Multan, Pakistan University. With a background spanning various industries, particularly in home automation and engineering, I have honed my skills in crafting clear and concise content. Proficient in leveraging infographics and diagrams, I strive to simplify complex concepts for readers. My strength lies in thorough research and presenting information in a structured and logical format.

Follow Us:
LinkedinTwitter