Jump to main content

##

# 3.12.10 Multi Channel Serial Peripheral Interface (MCSPI)

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

MCSPI master mode

In MCSPI 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.

MCSPI slave mode

MCSPI 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 MCSPI 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 MCSPI 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 MCSPI slave to MCSPI master. This option can be enabled to provide an indication to the MCSPI master on when the MCSPI slave will be ready to respond. In a typical implementation, to read data from MCSPI slave, the MCSPI master asserts the chip select line and then sends a MCSPI 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 MCSPI master must then wait for the MCSPI slave to frame the response by waiting on the busy line. Once the MCSPI slave drives the busy signal to ready state, the MCSPI 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 MCSPI master.

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

MCSPI Master in blocking (peripheral interrupt disabled) mode

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

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

        /* MCSPI Write Read */
        MCSPI1_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 );
    }

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

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

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

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

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

        /* Register callback function   */
        MCSPI1_CallbackRegister(APP_MCSPI_Callback, 0);

        /* MCSPI Write Read */
        MCSPI1_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 */
    			}
    		}
    	}

    }

MCSPI slave mode

This example uses the MCSPI peripheral library in slave mode and allows reading and writing data from/to its internal buffer by a MCSPI master. The MCSPI 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 MCSPI slave asserts the Busy line to indicate to the MCSPI master that it is busy. Once ready, the MCSPI slave de-asserts the Busy line. Once the MCSPI slave is ready, the MCSPI 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 MCSPI 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 MCSPIEventHandler(uintptr_t context )
    {
        if (MCSPI1_ErrorGet() == MCSPI_SLAVE_ERROR_NONE)
        {
            appData.nBytesRead = MCSPI1_Read(APP_RxData, MCSPI1_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);
                        MCSPI1_Write(APP_TxData, appData.nBytesReadRequest);
                    }
                    break;
            }

            if (appData.status.busy == 0)
            {
                /* Indicate to MCSPI Master that slave is ready for data transfer */
                MCSPI1_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:

                    MCSPI1_CallbackRegister(MCSPIEventHandler, (uintptr_t) 0);

                    /* Wait for instructions from MCSPI 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 MCSPI Master that slave is ready for data transfer */
                    MCSPI1_Ready();

                    appData.state = APP_STATE_IDLE;

                    break;

                case APP_STATE_IDLE:
                    break;

                default:
                    break;
            }
        }
    }

## Library Interface

MCSPI peripheral library provides the following interfaces:

Functions

Name | Description | Master (blocking/interrupt disabled) mode | Master (non-blocking/interrupt enabled) mode | Slave mode
--- | --- | --- | --- | ---
MCSPIx_Initialize | Initializes MCSPI module of the device | Yes | Yes | Yes
MCSPIx_TransferSetup | Configure MCSPI operational parameters at run time | Yes | Yes | No
MCSPIx_WriteRead | Write and Read data on MCSPI peripheral | Yes | Yes | No
MCSPIx_Write | Writes data to MCSPI peripheral | Yes | Yes | Yes
MCSPIx_Read | Reads data on the MCSPI peripheral in master mode. Reads data from the PLIB's internal buffer to the application buffer in MCSPI slave mode | Yes | Yes | Yes
MCSPIx_CallbackRegister | Allows application to register a callback with the PLIB | No | Yes | Yes
MCSPIx_IsBusy | Returns transfer status of MCSPI | No | Yes | Yes
MCSPIx_IsTransmitterBusy | Returns transfer status of MCSPI | Yes | No | No
MCSPIx_ReadCountGet | Returns the number of bytes pending to be read out from the PLIB's internal buffer | No | No | Yes
MCSPIx_ReadBufferSizeGet | Returns the size of the PLIB's internal receive buffer | No | No | Yes
MCSPIx_WriteBufferSizeGet | Returns the size of the PLIB's internal transmit buffer | No | No | Yes
MCSPIx_ErrorGet | Returns the error status of MCSPI transfer | No | No | Yes
MCSPIx_Ready | Drives the MCSPI slave busy line to ready (not busy) state | No | No | Yes
MCSPIx_ChipSelectSetup | Enables the specified hardware chip select | Yes | Yes | No

Data types and constants

Name | Description | Master (blocking/interrupt disabled) mode | Master (non-blocking/interrupt enabled) mode | Slave mode
--- | --- | --- | --- | ---
MCSPI_CLOCK_PHASE | Enum | Identifies MCSPI Clock Phase Options | Yes | Yes
MCSPI_CLOCK_POLARITY | Enum | Identifies MCSPI Clock Polarity Options | Yes | Yes
MCSPI_DATA_BITS | Enum | Identifies MCSPI bits per transfer | Yes | Yes
MCSPI_CHIP_SELECT | Enum | Enumerator constants for MCSPI hardware chip select | Yes | Yes
MCSPI_TRANSFER_SETUP | Struct | Data structure containing the MCSPI parameters which can be changed at run time | Yes | Yes
MCSPI_CALLBACK | Typedef | Defines the data type and function signature for the MCSPI peripheral callback function | No | Yes
MCSPI_SLAVE_CALLBACK | Typedef | Pointer to a MCSPI Call back function when MCSPI is configued in slave mode | No | No
MCSPI_SLAVE_ERROR | Macros and Typedef | Defines the macros and typedef associated with MCSPI slave mode errors | No | No