1.27.22 1.34.20 1.35.13 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 MHC. 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 |
SPIx_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 |
---|---|---|---|---|
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_CHIP_SELECT | Enum | Enumerator constants for SPI hardware chip select | 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_CALLBACK | Typedef | Pointer to a SPI Call back function when SPI is configued in slave mode | No | No |
SPI_SLAVE_ERROR | Macros and Typedef | Defines the macros and typedef associated with SPI slave mode errors | No | No |