Jump to main content

##

# 3.18.15 Serial Peripheral Interface (SPI)

The SPI PLIB can be configured in master or slave mode.

SPI master mode

In SPI master mode, the PLIB can be configured to run in blocking mode or non-blocking mode. In blocking mode the peripheral interrupt is disabled and the transfer API blocks until the transfer is complete. In non-blocking mode, the peripheral interrupt is enabled. The transfer API initiates the transfer and returns immediately. The transfer is completed from the peripheral interrupt. Application can either use a callback to get notified when the transfer is complete or can use the IsBusy API to check the completion status.

SPI slave mode

SPI slave mode enables peripheral interrupt for data transfers. The PLIB uses internal receive and transmit buffers. The receive buffer is used to hold the data received from the SPI master while the application data to be transmitted out is copied into the transmit buffer. The size of the transmit and receive buffers is configurable in MCC. Application must register a callback with the PLIB to receive event notifications. A callback is given when the chip select is de-asserted by the SPI master. The application must read out the received data in the callback, thereby clearing the PLIB's internal RX buffer.

The PLIB optionally supports busy signalling from SPI slave to SPI master. This option can be enabled to provide an indication to the SPI master on when the SPI slave will be ready to respond. In a typical implementation, to read data from SPI slave, the SPI master asserts the chip select line and then sends a SPI packet informing the slave about the memory address to read from and the number of bytes to read and then de-asserts the chip select line. The SPI master must then wait for the SPI slave to frame the response by waiting on the busy line. Once the SPI slave drives the busy signal to ready state, the SPI master can start reading the actual data by asserting the chip select line and sending dummy writes for the number of bytes to read. Finally, after the intended bytes are read, the chip select must be de-asserted by the SPI master.

If the busy signalling is not enabled, the SPI master must wait for sufficient duration to allow the SPI slave to become ready with the response.

SPI Master in blocking (peripheral interrupt disabled) mode

    // Following code demonstrates SPI self loopback with the PLIB configured in blocking mode
    uint8_t txData[]  = "SELF LOOPBACK FOR SPI!";
    uint8_t rxData[sizeof(txData)];

    int main ( void )
    {
        /* Initialize all modules */
        SYS_Initialize ( NULL );

        /* SPI Write Read */
        SPI1_WriteRead(&txData[0], sizeof(txData), &rxData[0], sizeof(rxData));

        /* Compare received data with the transmitted data */
        if ((memcmp(txData, rxData, sizeof(txData)) == 0))
        {
            /* Pass: Received data is same as transmitted data */
        }
        else
        {
            /* Fail: Received data is not same as transmitted data */
        }

        while ( true )
        {
        }

        /* Execution should not come here during normal operation */

        return ( EXIT_FAILURE );
    }

SPI Master in non-blocking (peripheral interrupt enabled) mode

    // Following code demonstrates SPI self loopback with the PLIB configured in non-blocking mode

    uint8_t txData[]  = "SELF LOOPBACK FOR SPI!";
    uint8_t rxData[sizeof(txData)];
    volatile bool transferStatus = false;

    /* This function will be called by SPI PLIB when transfer is completed */
    void APP_SPI_Callback(uintptr_t context )
    {
        transferStatus = true;
    }

    int main ( void )
    {
        /* Initialize all modules */
        SYS_Initialize ( NULL );

        /* Register callback function   */
        SPI1_CallbackRegister(APP_SPI_Callback, 0);

        /* SPI Write Read */
        SPI1_WriteRead(&txData[0], sizeof(txData), &rxData[0], sizeof(rxData));

    	while (1)
    	{
    		/* Perform other tasks here ...*/

    		/* Check if transfer has completed */
    		if(transferStatus == true)
    		{
    			/* Compare received data with the transmitted data */
    			if(memcmp(txData, rxData, sizeof(txData)) == 0)
    			{
    				/* Pass: Received data is same as transmitted data */
    			}
    			else
    			{
    				/* Fail: Received data is not same as transmitted data */
    			}
    		}
    	}

    }

SPI slave mode

This example uses the SPI peripheral library in slave mode and allows reading and writing data from/to its internal buffer by a SPI master. The SPI master writes data by sending a write command followed by two bytes of memory address followed by the data to be written.

< WR_CMD > < ADDR_MSB > < ADDR_LSB > < DATA0 > ... < DATA n >

The SPI slave asserts the Busy line to indicate to the SPI master that it is busy. Once ready, the SPI slave de-asserts the Busy line. Once the SPI slave is ready, the SPI master reads the data by sending read command followed by two bytes of memory address and the number of bytes to read.

< RD_CMD > < ADDR_MSB > < ADDR_LSB > < NUM_BYTES >

The SPI slave responds by sending the data at the requested memory address.

    /*
     * Protocol:
     * Master:     [WR_CMD ADDR1 ADDR0 DATA0 DATA1 .. DATAN] ... ... ... ... ... ... [RD_CMD ADDR1 ADDR0] [DUMMY DUMMY .. DUMMY]
     * Slave:      [DUMMY  DUMMY DUMMY DUMMY DUMMY .. DUMMY] ... ... ... ... ... ... [DUMMY  DUMMY DUMMY] [DATA0 DATA1 .. DATAN]
     * BUSY PIN:    ----------------------------------------------------------------
     * ____________|                                                                |____________________________________________
     *
     * WR_CMD = 0x02
     * RD_CMD = 0x03
     * BUSY   = 0x01
     * READY  = 0x00
     */

    typedef enum
    {
    	APP_STATE_INITIALIZE,
        APP_STATE_READ,
        APP_STATE_WRITE,
        APP_STATE_IDLE,

    } APP_STATES;

    /* Commands */
    #define APP_CMD_WRITE                       0x02
    #define APP_CMD_READ                        0x03

    #define APP_MEM_BUFFER_SIZE                 512
    #define APP_RX_BUFFER_SIZE                  256
    #define APP_TX_BUFFER_SIZE                  256

    typedef struct
    {
        uint8_t busy        :1;
        uint8_t reserved    :7;
    }STATUS;

    typedef struct
    {
        volatile APP_STATES          state;
        volatile STATUS              status;
        volatile uint8_t             nBytesRead;
        volatile uint8_t             nBytesToWrite;
        volatile uint8_t             nBytesReadRequest;
        volatile uint16_t            memAddr;
    }APP_DATA;

    APP_DATA appData;

    uint8_t APP_MemoryBuffer[APP_MEM_BUFFER_SIZE] =
    {
        0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
        0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
        0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
        0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
        0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
        0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,
        0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,
        0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,
        0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
        0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
        0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,
        0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,
        0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,
        0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,
        0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,
        0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff,

        0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
        0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
        0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
        0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
        0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
        0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,
        0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,
        0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,
        0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
        0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
        0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,
        0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,
        0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,
        0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,
        0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,
        0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff
    };

    uint8_t APP_RxData[APP_RX_BUFFER_SIZE];
    uint8_t APP_TxData[APP_TX_BUFFER_SIZE];

    void delay(uint32_t count)
    {
        uint32_t i;

        // 1 loop roughly provides 1us delay at 32MHz CPU frequency
        for (i = 0; i < count; i++)
        {
            asm("NOP");asm("NOP");asm("NOP");asm("NOP");asm("NOP");asm("NOP");
            asm("NOP");asm("NOP");asm("NOP");asm("NOP");asm("NOP");asm("NOP");
            asm("NOP");asm("NOP");asm("NOP");asm("NOP");asm("NOP");asm("NOP");
            asm("NOP");asm("NOP");asm("NOP");asm("NOP");asm("NOP");asm("NOP");
        }
    }

    void SPIEventHandler(uintptr_t context )
    {
        if (SPI1_ErrorGet() == SPI_SLAVE_ERROR_NONE)
        {
            appData.nBytesRead = SPI1_Read(APP_RxData, SPI1_ReadCountGet());

            switch(APP_RxData[0])
            {
                case APP_CMD_WRITE:
                    if (appData.status.busy == 0)
                    {
                        appData.status.busy = 1;
                        appData.memAddr = ((APP_RxData[1] << 8) | (APP_RxData[2]));
                        appData.nBytesToWrite = (appData.nBytesRead - 3);
                        appData.state = APP_STATE_WRITE;
                    }
                    break;

                case APP_CMD_READ:

                    appData.memAddr = ((APP_RxData[1] << 8) | (APP_RxData[2]));
                    appData.nBytesReadRequest = APP_RxData[3];

                    if ((appData.memAddr + appData.nBytesReadRequest) <= APP_TX_BUFFER_SIZE)
                    {
                        memcpy(APP_TxData, &APP_MemoryBuffer[appData.memAddr], appData.nBytesReadRequest);
                        SPI1_Write(APP_TxData, appData.nBytesReadRequest);
                    }
                    break;
            }

            if (appData.status.busy == 0)
            {
                /* Indicate to SPI Master that slave is ready for data transfer */
                SPI1_Ready();
            }
        }
    }

    int main ( void )
    {
        /* Initialize all modules */
        SYS_Initialize ( NULL );

        appData.state = APP_STATE_INITIALIZE;

        while(1)
        {
            /* Check the application's current state. */
            switch (appData.state)
            {
                case APP_STATE_INITIALIZE:

                    SPI1_CallbackRegister(SPIEventHandler, (uintptr_t) 0);

                    /* Wait for instructions from SPI master */
                    appData.state = APP_STATE_IDLE;

                    break;

                case APP_STATE_WRITE:

                    /* Adding delay to simulate busy condition */
                    delay(1000);

                    /* Copy received data into Application memory buffer */
                    if ((appData.memAddr + appData.nBytesToWrite) <= APP_MEM_BUFFER_SIZE)
                    {
                        memcpy(&APP_MemoryBuffer[appData.memAddr], &APP_RxData[3], appData.nBytesToWrite);
                    }

                    appData.status.busy = 0;

                    /* Indicate to SPI Master that slave is ready for data transfer */
                    SPI1_Ready();

                    appData.state = APP_STATE_IDLE;

                    break;

                case APP_STATE_IDLE:
                    break;

                default:
                    break;
            }
        }
    }

## Library Interface

SPI peripheral library provides the following interfaces:

Functions

Name | Description | Master (blocking/interrupt disabled) mode | Master (non-blocking/interrupt enabled) mode | Slave mode
--- | --- | --- | --- | ---
SPIx_Initialize | Initializes SPI module of the device | Yes | Yes | Yes
SPIx_TransferSetup | Configure SPI operational parameters at run time | Yes | Yes | No
SPIx_WriteRead | Write and Read data on SPI peripheral | Yes | Yes | No
SPIx_Write | Writes data to SPI peripheral | Yes | Yes | Yes
SPIx_Read | Reads data on the SPI peripheral in master mode. Reads data from the PLIB's internal buffer to the application buffer in SPI slave mode | Yes | Yes | Yes
SPIx_CallbackRegister | Allows application to register a callback with the PLIB | No | Yes | Yes
SPIx_IsBusy | Returns transfer status of SPI | No | Yes | Yes
SPIx_ReadCountGet | Returns the number of bytes pending to be read out from the PLIB's internal buffer | No | No | Yes
SPIx_ReadBufferSizeGet | Returns the size of the PLIB's internal receive buffer | No | No | Yes
SPIx_WriteBufferSizeGet | Returns the size of the PLIB's internal transmit buffer | No | No | Yes
SPIx_ErrorGet | Returns the error status of SPI transfer | No | No | Yes
SPIx_Ready | Drives the SPI slave busy line to ready (not busy) state | No | No | Yes

Data types and constants

Name | Description | Master (blocking/interrupt disabled) mode | Master (non-blocking/interrupt enabled) mode | Slave mode
--- | --- | --- | --- | ---
SPI_CLOCK_PHASE | Enum | Identifies SPI Clock Phase Options | Yes | Yes
SPI_CLOCK_POLARITY | Enum | Identifies SPI Clock Polarity Options | Yes | Yes
SPI_DATA_BITS | Enum | Identifies SPI bits per transfer | Yes | Yes
SPI_TRANSFER_SETUP | Struct | Data structure containing the SPI parameters which can be changed at run time | Yes | Yes
SPI_CALLBACK | Typedef | Defines the data type and function signature for the SPI peripheral callback function | No | Yes
SPI_SLAVE_ERROR | Macros and Typedef | Defines the macros and typedef associated with SPI slave mode errors | No | No
SPI_SLAVE_CALLBACK | Typedef | Pointer to a SPI Call back function when SPI is configued in slave mode | No | No