/** * * This function enables interrupts for the SPI device. If the Spi driver is used * in interrupt mode, it is up to the user to connect the SPI interrupt handler * to the interrupt controller before this function is called. If the Spi driver * is used in polled mode the user has to disable the Global Interrupts after * this function is called. If the device is configured with FIFOs, the FIFOs are * reset at this time. * * @param InstancePtr is a pointer to the XSpi instance to be worked on. * * @return * - XST_SUCCESS if the device is successfully started * - XST_DEVICE_IS_STARTED if the device was already started. * * @note None. * ******************************************************************************/ int XSpi_Start(XSpi *InstancePtr) { u32 ControlReg; Xil_AssertNonvoid(InstancePtr != NULL); Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY); /* * If it is already started, return a status indicating so. */ if (InstancePtr->IsStarted == XIL_COMPONENT_IS_STARTED) { return XST_DEVICE_IS_STARTED; } /* * Enable the interrupts. */ XSpi_IntrEnable(InstancePtr, XSP_INTR_DFT_MASK); /* * Indicate that the device is started before we enable the transmitter * or receiver or interrupts. */ InstancePtr->IsStarted = XIL_COMPONENT_IS_STARTED; /* * Reset the transmit and receive FIFOs if present. There is a critical * section here since this register is also modified during interrupt * context. So we wait until after the r/m/w of the control register to * enable the Global Interrupt Enable. */ ControlReg = XSpi_GetControlReg(InstancePtr); ControlReg |= XSP_CR_TXFIFO_RESET_MASK | XSP_CR_RXFIFO_RESET_MASK | XSP_CR_ENABLE_MASK; XSpi_SetControlReg(InstancePtr, ControlReg); /* * Enable the Global Interrupt Enable just after we start. */ XSpi_IntrGlobalEnable(InstancePtr); return XST_SUCCESS; }
/** * * Transfers the specified data on the SPI bus. If the SPI device is configured * to be a master, this function initiates bus communication and sends/receives * the data to/from the selected SPI slave. If the SPI device is configured to * be a slave, this function prepares the data to be sent/received when selected * by a master. For every byte sent, a byte is received. * * This function/driver operates in interrupt mode and polled mode. * - In interrupt mode this function is non-blocking and the transfer is * initiated by this function and completed by the interrupt service routine. * - In polled mode this function is blocking and the control exits this * function only after all the requested data is transferred. * * The caller has the option of providing two different buffers for send and * receive, or one buffer for both send and receive, or no buffer for receive. * The receive buffer must be at least as big as the send buffer to prevent * unwanted memory writes. This implies that the byte count passed in as an * argument must be the smaller of the two buffers if they differ in size. * Here are some sample usages: * <pre> * XSpi_Transfer(InstancePtr, SendBuf, RecvBuf, ByteCount) * The caller wishes to send and receive, and provides two different * buffers for send and receive. * * XSpi_Transfer(InstancePtr, SendBuf, NULL, ByteCount) * The caller wishes only to send and does not care about the received * data. The driver ignores the received data in this case. * * XSpi_Transfer(InstancePtr, SendBuf, SendBuf, ByteCount) * The caller wishes to send and receive, but provides the same buffer * for doing both. The driver sends the data and overwrites the send * buffer with received data as it transfers the data. * * XSpi_Transfer(InstancePtr, RecvBuf, RecvBuf, ByteCount) * The caller wishes to only receive and does not care about sending * data. In this case, the caller must still provide a send buffer, but * it can be the same as the receive buffer if the caller does not care * what it sends. The device must send N bytes of data if it wishes to * receive N bytes of data. * </pre> * In interrupt mode, though this function takes a buffer as an argument, the * driver can only transfer a limited number of bytes at time. It transfers only * one byte at a time if there are no FIFOs, or it can transfer the number of * bytes up to the size of the FIFO if FIFOs exist. * - In interrupt mode a call to this function only starts the transfer, the * subsequent transfer of the data is performed by the interrupt service * routine until the entire buffer has been transferred.The status callback * function is called when the entire buffer has been sent/received. * - In polled mode this function is blocking and the control exits this * function only after all the requested data is transferred. * * As a master, the SetSlaveSelect function must be called prior to this * function. * * @param InstancePtr is a pointer to the XSpi instance to be worked on. * @param SendBufPtr is a pointer to a buffer of data which is to be sent. * This buffer must not be NULL. * @param RecvBufPtr is a pointer to a buffer which will be filled with * received data. This argument can be NULL if the caller does not * wish to receive data. * @param ByteCount contains the number of bytes to send/receive. The * number of bytes received always equals the number of bytes sent. * * @return * -XST_SUCCESS if the buffers are successfully handed off to the * driver for transfer. Otherwise, returns: * - XST_DEVICE_IS_STOPPED if the device must be started before * transferring data. * - XST_DEVICE_BUSY indicates that a data transfer is already in * progress. This is determined by the driver. * - XST_SPI_NO_SLAVE indicates the device is configured as a * master and a slave has not yet been selected. * * @notes * * This function is not thread-safe. The higher layer software must ensure that * no two threads are transferring data on the SPI bus at the same time. * ******************************************************************************/ int XSpi_Transfer(XSpi *InstancePtr, u8 *SendBufPtr, u8 *RecvBufPtr, unsigned int ByteCount) { u32 ControlReg; u32 GlobalIntrReg; u32 StatusReg; u32 Data = 0; u8 DataWidth; /* * The RecvBufPtr argument can be NULL. */ Xil_AssertNonvoid(InstancePtr != NULL); Xil_AssertNonvoid(SendBufPtr != NULL); Xil_AssertNonvoid(ByteCount > 0); Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY); if (InstancePtr->IsStarted != XIL_COMPONENT_IS_STARTED) { return XST_DEVICE_IS_STOPPED; } /* * Make sure there is not a transfer already in progress. No need to * worry about a critical section here. Even if the Isr changes the bus * flag just after we read it, a busy error is returned and the caller * can retry when it gets the status handler callback indicating the * transfer is done. */ if (InstancePtr->IsBusy) { return XST_DEVICE_BUSY; } /* * Save the Global Interrupt Enable Register. */ GlobalIntrReg = XSpi_IsIntrGlobalEnabled(InstancePtr); /* * Enter a critical section from here to the end of the function since * state is modified, an interrupt is enabled, and the control register * is modified (r/m/w). */ XSpi_IntrGlobalDisable(InstancePtr); ControlReg = XSpi_GetControlReg(InstancePtr); /* * If configured as a master, be sure there is a slave select bit set * in the slave select register. If no slaves have been selected, the * value of the register will equal the mask. When the device is in * loopback mode, however, no slave selects need be set. */ if (ControlReg & XSP_CR_MASTER_MODE_MASK) { if ((ControlReg & XSP_CR_LOOPBACK_MASK) == 0) { if (InstancePtr->SlaveSelectReg == InstancePtr->SlaveSelectMask) { if (GlobalIntrReg == TRUE) { /* Interrupt Mode of operation */ XSpi_IntrGlobalEnable(InstancePtr); } return XST_SPI_NO_SLAVE; } } } /* * Set the slave select register to select the device on the SPI before * starting the transfer of data. */ XSpi_SetSlaveSelectReg(InstancePtr, InstancePtr->SlaveSelectReg); /* * Set the busy flag, which will be cleared when the transfer * is completely done. */ InstancePtr->IsBusy = TRUE; /* * Set up buffer pointers. */ InstancePtr->SendBufferPtr = SendBufPtr; InstancePtr->RecvBufferPtr = RecvBufPtr; InstancePtr->RequestedBytes = ByteCount; InstancePtr->RemainingBytes = ByteCount; DataWidth = InstancePtr->DataWidth; /* * Fill the DTR/FIFO with as many bytes as it will take (or as many as * we have to send). We use the tx full status bit to know if the device * can take more data. By doing this, the driver does not need to know * the size of the FIFO or that there even is a FIFO. The downside is * that the status register must be read each loop iteration. */ StatusReg = XSpi_GetStatusReg(InstancePtr); while (((StatusReg & XSP_SR_TX_FULL_MASK) == 0) && (InstancePtr->RemainingBytes > 0)) { if (DataWidth == XSP_DATAWIDTH_BYTE) { /* * Data Transfer Width is Byte (8 bit). */ Data = *InstancePtr->SendBufferPtr; } else if (DataWidth == XSP_DATAWIDTH_HALF_WORD) { /* * Data Transfer Width is Half Word (16 bit). */ Data = *(u16 *)InstancePtr->SendBufferPtr; } else if (DataWidth == XSP_DATAWIDTH_WORD){ /* * Data Transfer Width is Word (32 bit). */ Data = *(u32 *)InstancePtr->SendBufferPtr; } XSpi_WriteReg(InstancePtr->BaseAddr, XSP_DTR_OFFSET, Data); InstancePtr->SendBufferPtr += (DataWidth >> 3); InstancePtr->RemainingBytes -= (DataWidth >> 3); StatusReg = XSpi_GetStatusReg(InstancePtr); } /* * Start the transfer by no longer inhibiting the transmitter and * enabling the device. For a master, this will in fact start the * transfer, but for a slave it only prepares the device for a transfer * that must be initiated by a master. */ ControlReg = XSpi_GetControlReg(InstancePtr); ControlReg &= ~XSP_CR_TRANS_INHIBIT_MASK; XSpi_SetControlReg(InstancePtr, ControlReg); /* * If the interrupts are enabled as indicated by Global Interrupt * Enable Register, then enable the transmit empty interrupt to operate * in Interrupt mode of operation. */ if (GlobalIntrReg == TRUE) { /* Interrupt Mode of operation */ /* * Enable the transmit empty interrupt, which we use to * determine progress on the transmission. */ XSpi_IntrEnable(InstancePtr, XSP_INTR_TX_EMPTY_MASK); /* * End critical section. */ XSpi_IntrGlobalEnable(InstancePtr); } else { /* Polled mode of operation */ /* * If interrupts are not enabled, poll the status register to * Transmit/Receive SPI data. */ while(ByteCount > 0) { /* * Wait for the transfer to be done by polling the * Transmit empty status bit */ do { StatusReg = XSpi_GetStatusReg(InstancePtr); } while ((StatusReg & XSP_SR_TX_EMPTY_MASK) == 0); /* * A transmit has just completed. Process received data * and check for more data to transmit. Always inhibit * the transmitter while the transmit register/FIFO is * being filled, or make sure it is stopped if we're * done. */ ControlReg = XSpi_GetControlReg(InstancePtr); XSpi_SetControlReg(InstancePtr, ControlReg | XSP_CR_TRANS_INHIBIT_MASK); /* * First get the data received as a result of the * transmit that just completed. We get all the data * available by reading the status register to determine * when the Receive register/FIFO is empty. Always get * the received data, but only fill the receive * buffer if it points to something (the upper layer * software may not care to receive data). */ StatusReg = XSpi_GetStatusReg(InstancePtr); while ((StatusReg & XSP_SR_RX_EMPTY_MASK) == 0) { Data = XSpi_ReadReg(InstancePtr->BaseAddr, XSP_DRR_OFFSET); if (DataWidth == XSP_DATAWIDTH_BYTE) { /* * Data Transfer Width is Byte (8 bit). */ if(InstancePtr->RecvBufferPtr != NULL) { *InstancePtr->RecvBufferPtr++ = (u8)Data; } } else if (DataWidth == XSP_DATAWIDTH_HALF_WORD) { /* * Data Transfer Width is Half Word * (16 bit). */ if (InstancePtr->RecvBufferPtr != NULL){ *(u16 *)InstancePtr->RecvBufferPtr = (u16)Data; InstancePtr->RecvBufferPtr += 2; } } else if (DataWidth == XSP_DATAWIDTH_WORD) { /* * Data Transfer Width is Word (32 bit). */ if (InstancePtr->RecvBufferPtr != NULL){ *(u32 *)InstancePtr->RecvBufferPtr = Data; InstancePtr->RecvBufferPtr += 4; } } InstancePtr->Stats.BytesTransferred += (DataWidth >> 3); ByteCount -= (DataWidth >> 3); StatusReg = XSpi_GetStatusReg(InstancePtr); } if (InstancePtr->RemainingBytes > 0) { /* * Fill the DTR/FIFO with as many bytes as it * will take (or as many as we have to send). * We use the Tx full status bit to know if the * device can take more data. * By doing this, the driver does not need to * know the size of the FIFO or that there even * is a FIFO. * The downside is that the status must be read * each loop iteration. */ StatusReg = XSpi_GetStatusReg(InstancePtr); while(((StatusReg & XSP_SR_TX_FULL_MASK)== 0) && (InstancePtr->RemainingBytes > 0)) { if (DataWidth == XSP_DATAWIDTH_BYTE) { /* * Data Transfer Width is Byte * (8 bit). */ Data = *InstancePtr-> SendBufferPtr; } else if (DataWidth == XSP_DATAWIDTH_HALF_WORD) { /* * Data Transfer Width is Half * Word (16 bit). */ Data = *(u16 *)InstancePtr-> SendBufferPtr; } else if (DataWidth == XSP_DATAWIDTH_WORD) { /* * Data Transfer Width is Word * (32 bit). */ Data = *(u32 *)InstancePtr-> SendBufferPtr; } XSpi_WriteReg(InstancePtr->BaseAddr, XSP_DTR_OFFSET, Data); InstancePtr->SendBufferPtr += (DataWidth >> 3); InstancePtr->RemainingBytes -= (DataWidth >> 3); StatusReg = XSpi_GetStatusReg( InstancePtr); } /* * Start the transfer by not inhibiting the * transmitter any longer. */ ControlReg = XSpi_GetControlReg(InstancePtr); ControlReg &= ~XSP_CR_TRANS_INHIBIT_MASK; XSpi_SetControlReg(InstancePtr, ControlReg); } } /* * Stop the transfer (hold off automatic sending) by inhibiting * the transmitter. */ ControlReg = XSpi_GetControlReg(InstancePtr); XSpi_SetControlReg(InstancePtr, ControlReg | XSP_CR_TRANS_INHIBIT_MASK); /* * Select the slave on the SPI bus when the transfer is * complete, this is necessary for some SPI devices, * such as serial EEPROMs work correctly as chip enable * may be connected to slave select */ XSpi_SetSlaveSelectReg(InstancePtr, InstancePtr->SlaveSelectMask); InstancePtr->IsBusy = FALSE; }
/** * * This function does a minimal test on the Spi device and driver as a design * example. The purpose of this function is to illustrate the device slave * functionality in interrupt mode. This function receives data from a master and * prints the received data. * * @param SpiInstancePtr is a pointer to the instance of Spi component. * @param SpiDeviceId is the Device ID of the Spi Device and is the * XPAR_<SPI_instance>_DEVICE_ID value from xparameters.h. * * @return XST_SUCCESS if successful, otherwise XST_FAILURE. * * @note This function contains an infinite loop such that if the Spi * device doesn't receive any data or if the interrupts are not * working, it may never return. * ******************************************************************************/ static int SpiSlaveIntrExample(XSpi *SpiInstancePtr, u16 SpiDeviceId) { XSpi_Config *ConfigPtr; int Status; u32 Count; xil_printf("\r\nEntering the Spi Slave Interrupt Example.\r\n"); xil_printf("Waiting for data from SPI master\r\n"); /* * Initialize the SPI driver so that it's ready to use, specify the * device ID that is generated in xparameters.h. */ ConfigPtr = XSpi_LookupConfig(SpiDeviceId); if (ConfigPtr == NULL) { return XST_FAILURE; } Status = XSpi_CfgInitialize(SpiInstancePtr, ConfigPtr, ConfigPtr->BaseAddress); if (Status != XST_SUCCESS) { return XST_FAILURE; } /* * Connect the SPI driver to the interrupt subsystem such that * interrupts can occur. This function is application specific. */ Status = SetupInterruptSystem(SpiInstancePtr); if (Status != XST_SUCCESS) { return XST_FAILURE; } /* * Setup the handler for the SPI that will be called from the interrupt * context when an SPI status occurs, specify a pointer to the SPI * driver instance as the callback reference so the handler is able to * access the instance data. */ XSpi_SetStatusHandler(SpiInstancePtr,SpiInstancePtr,(XSpi_StatusHandler) SpiHandler); /* * The SPI device is a slave by default and the clock phase and polarity * have to be set according to its master. In this example, CPOL is set * to active low and CPHA is set to 1. */ Status = XSpi_SetOptions(SpiInstancePtr, XSP_CLK_PHASE_1_OPTION | XSP_CLK_ACTIVE_LOW_OPTION); if (Status != XST_SUCCESS) { return XST_FAILURE; } /* * Start the SPI driver so that the device is enabled. */ XSpi_Start(SpiInstancePtr); /* * Enable the DTR half-empty interrupt while transfering more than * FIFO_DEPTH number of bytes in slave mode, so that the Tx FIFO * is never empty during a transfer. If the Tx FIFO is empty during * a transfer, it results in master receiving invalid data. */ XSpi_IntrEnable(SpiInstancePtr, XSP_INTR_TX_HALF_EMPTY_MASK); /* * Initialize the write buffer with pattern to write, initialize the * read buffer to zero so it can be verified after the read. */ Test = 0x50; for (Count = 0; Count < BUFFER_SIZE; Count++) { WriteBuffer[Count] = (u8)(Count + Test); ReadBuffer[Count] = 0; } /* * Transmit data as a slave, when the master starts sending data. */ TransferInProgress = TRUE; Status = XSpi_Transfer(SpiInstancePtr, WriteBuffer, ReadBuffer, BUFFER_SIZE); if (Status != XST_SUCCESS) { return XST_FAILURE; } /* * Wait till the transfer is complete. */ while (TransferInProgress == TRUE); /* * Print all the data received from the master. */ xil_printf("\r\nReceived data is:\r\n"); for (Count = 0; Count < BUFFER_SIZE; Count++) { xil_printf("0x%x \r\n", ReadBuffer[Count]); } xil_printf("\r\nExiting the Spi Slave Interrupt Example.\r\n"); return XST_SUCCESS; }