FTDI Chip Select – Removing Time Gaps Before Chip Select is Disabled

dllftdispi

When using FTDI FT2232H in SPI mode, there are relative short time gaps (~2 ms) between positions where clocks are stopped and chip select is disabled.
But if data transmission have high speed rate, in this case gaps may influence.

Is there some way either remove this time gaps or make it shorter?

Time gaps on the SPI timing diagram

Best Answer

If you are using the MPSSE-SPI lib you mostlikely made acquaintance with the "INFRA_SLEEP(2)" call within the SPI_ToggleCS function. You can either remove it completely and recompile the lib or you could apply the patch posted below and compile then. I already reached out for the FTDI support and they confirmed that this sleep is there for preventing timing problems (maybe for reeeeeally slow ICs?). But to my experience even without this sleep the MPSSE-SPI will never be the first choice for (continuous) high throughput because every sequence is sent and the further execution halted until the MPSSE finished sending out the SPI sequence. If one needs to revive the received data additional delay is added as the corresponding USB commands and data has to be sent back and forth. So if you are looking for high throughput data one has to stick to the modes with FIFO support in my experience. Minor headsup: if you want to go for the fast opto isolated serial interface be aware that the maximum data rate for single port use with a FSCLK of ~50MHz is "only" ~24 Mbits which is roughly half the expected value. Reason for this is that FSCTS is held low for several clk cycles before it goes high again. The cause behind this is unclear by now. Maybe one has to put both ports to fast opto to get the full bandwidth or so.

patch:

     LibMPSSE/TopLayer/SPI/inc/ftdi_spi.h |  11 +++
 LibMPSSE/TopLayer/SPI/src/ftdi_spi.c | 179 +++++++++++++++++++++++++++++++----
 2 files changed, 170 insertions(+), 20 deletions(-)

diff --git a/LibMPSSE/TopLayer/SPI/inc/ftdi_spi.h b/LibMPSSE/TopLayer/SPI/inc/ftdi_spi.h
index 1b6f34d..fa950ef 100644
--- a/LibMPSSE/TopLayer/SPI/inc/ftdi_spi.h
+++ b/LibMPSSE/TopLayer/SPI/inc/ftdi_spi.h
@@ -137,6 +137,17 @@ FTDI_API void Cleanup_libMPSSE(void);
 FTDI_API FT_STATUS SPI_ChangeCS(FT_HANDLE handle, uint32 configOptions);
 FTDI_API FT_STATUS SPI_ToggleCS(FT_HANDLE handle, bool state);

+FTDI_API FT_STATUS SPI_ToggleCS_D(FT_HANDLE handle, bool state, uint32 CSReleaseDelayInMS);
+FTDI_API FT_STATUS SPI_Read_D(FT_HANDLE handle, uint8 *buffer,
+   uint32 sizeToTransfer, uint32 *sizeTransfered, uint32 options, uint32 CSReleaseDelayInMS);
+FTDI_API FT_STATUS SPI_Write_D(FT_HANDLE handle, uint8 *buffer,
+   uint32 sizeToTransfer, uint32 *sizeTransfered, uint32 options, uint32 CSReleaseDelayInMS);
+FTDI_API FT_STATUS SPI_ReadWrite_D(FT_HANDLE handle, uint8 *inBuffer,
+   uint8 *outBuffer, uint32 sizeToTransfer, uint32 *sizeTransferred,
+   uint32 transferOptions, uint32 CSReleaseDelayInMS);
+FTDI_API FT_STATUS SPI_IsBusy_D(FT_HANDLE handle, bool *state, uint32 transferOptions, uint32 CSReleaseDelayInMS);
+FTDI_API FT_STATUS SPI_ToggleCS_D(FT_HANDLE handle, bool state, uint32 CSReleaseDelayInMS);
+
 /******************************************************************************/


diff --git a/LibMPSSE/TopLayer/SPI/src/ftdi_spi.c b/LibMPSSE/TopLayer/SPI/src/ftdi_spi.c
index 62085eb..ea64127 100644
--- a/LibMPSSE/TopLayer/SPI/src/ftdi_spi.c
+++ b/LibMPSSE/TopLayer/SPI/src/ftdi_spi.c
@@ -319,6 +319,32 @@ FTDI_API FT_STATUS SPI_CloseChannel(FT_HANDLE handle)
    return status;
 }

+/*!
+* \brief Reads data from a SPI slave device
+*
+* This function reads the specified number of bits or bytes from the SPI device
+*
+* \param[in] handle Handle of the channel
+* \param[in] *buffer Pointer to buffer to where data will be read to
+* \param[in] sizeToTransfer Size of data to be transfered
+* \param[out] sizeTransfered Pointer to variable containing the size of data
+*          that got transferred
+* \param[in] transferOptions This parameter specifies data transfer options
+*              if BIT0 is 0 then size is in bytes, otherwise in bits
+*              if BIT1 is 1 then CHIP_SELECT line will be enabled at start of transfer
+*              if BIT2 is 1 then CHIP_SELECT line will be disabled at end of transfer
+*
+* \return Returns status code of type FT_STATUS(see D2XX Programmer's Guide)
+* \sa
+* \note
+* \warning
+*/
+FTDI_API FT_STATUS SPI_Read(FT_HANDLE handle, uint8 *buffer,
+   uint32 sizeToTransfer, uint32 *sizeTransferred, uint32 transferOptions)
+{
+   return SPI_Read_D(handle, buffer, sizeToTransfer, sizeTransferred, transferOptions, 2);
+}
+
 /*!
  * \brief Reads data from a SPI slave device
  *
@@ -331,16 +357,18 @@ FTDI_API FT_STATUS SPI_CloseChannel(FT_HANDLE handle)
  *         that got transferred
  * \param[in] transferOptions This parameter specifies data transfer options
  *             if BIT0 is 0 then size is in bytes, otherwise in bits
- *             if BIT1 is 1 then CHIP_SELECT line will be enables at start of transfer
+ *             if BIT1 is 1 then CHIP_SELECT line will be enabled at start of transfer
  *             if BIT2 is 1 then CHIP_SELECT line will be disabled at end of transfer
+ * \param[in] CSReleaseDelayInMS delays release of CS line for given time in ms. Pass 0 to deactivate.
  *
  * \return Returns status code of type FT_STATUS(see D2XX Programmer's Guide)
  * \sa
  * \note
  * \warning
  */
-FTDI_API FT_STATUS SPI_Read(FT_HANDLE handle, uint8 *buffer,
-   uint32 sizeToTransfer, uint32 *sizeTransferred, uint32 transferOptions)
+FTDI_API FT_STATUS SPI_Read_D(FT_HANDLE handle, uint8 *buffer,
+   uint32 sizeToTransfer, uint32 *sizeTransferred, uint32 transferOptions,
+   uint32 CSReleaseDelayInMS)
 {
    FT_STATUS status;
    //uint32 i;
@@ -357,7 +385,7 @@ FTDI_API FT_STATUS SPI_Read(FT_HANDLE handle, uint8 *buffer,
    if(transferOptions & SPI_TRANSFER_OPTIONS_CHIPSELECT_ENABLE)
    {
        /* Enable CHIPSELECT line for the channel */
-       status = SPI_ToggleCS(handle,TRUE);
+       status = SPI_ToggleCS_D(handle,TRUE,CSReleaseDelayInMS);
        CHECK_STATUS(status);
    }

@@ -429,7 +457,7 @@ FTDI_API FT_STATUS SPI_Read(FT_HANDLE handle, uint8 *buffer,
    if(transferOptions & SPI_TRANSFER_OPTIONS_CHIPSELECT_DISABLE)
    {
        /* Disable CHIPSELECT line for the channel */
-       status = SPI_ToggleCS(handle,FALSE);
+       status = SPI_ToggleCS_D(handle,FALSE,CSReleaseDelayInMS);
        CHECK_STATUS(status);
    }
    UNLOCK_CHANNEL(handle);
@@ -442,6 +470,32 @@ FTDI_API FT_STATUS SPI_Read(FT_HANDLE handle, uint8 *buffer,
    return status;
 }

+/*!
+* \brief Writes data to a SPI slave device
+*
+* This function writes the specified number of bits or bytes to the SPI device
+*
+* \param[in] handle Handle of the channel
+* \param[in] *buffer Pointer to buffer from containing the data
+* \param[in] sizeToTransfer Size of data to be transfered
+* \param[out] sizeTransfered Pointer to variable containing the size of data
+*          that got transferred
+* \param[in] transferOptions This parameter specifies data transfer options
+*              if BIT0 is 0 then size is in bytes, otherwise in bits
+*              if BIT1 is 1 then CHIP_SELECT line will be enabled at start of transfer
+*              if BIT2 is 1 then CHIP_SELECT line will be disabled at end of transfer
+*
+* \return Returns status code of type FT_STATUS(see D2XX Programmer's Guide)
+* \sa
+* \note
+* \warning
+*/
+FTDI_API FT_STATUS SPI_Write(FT_HANDLE handle, uint8 *buffer,
+   uint32 sizeToTransfer, uint32 *sizeTransferred, uint32 transferOptions)
+{
+   return SPI_Write_D(handle, buffer, sizeToTransfer, sizeTransferred, transferOptions, 2);
+}
+
 /*!
  * \brief Writes data to a SPI slave device
  *
@@ -454,16 +508,18 @@ FTDI_API FT_STATUS SPI_Read(FT_HANDLE handle, uint8 *buffer,
  *         that got transferred
  * \param[in] transferOptions This parameter specifies data transfer options
  *             if BIT0 is 0 then size is in bytes, otherwise in bits
- *             if BIT1 is 1 then CHIP_SELECT line will be enables at start of transfer
+ *             if BIT1 is 1 then CHIP_SELECT line will be enabled at start of transfer
  *             if BIT2 is 1 then CHIP_SELECT line will be disabled at end of transfer
+ * \param[in] CSReleaseDelayInMS delays release of CS line for given time in ms. Pass 0 to deactivate.
  *
  * \return Returns status code of type FT_STATUS(see D2XX Programmer's Guide)
  * \sa
  * \note
  * \warning
  */
-FTDI_API FT_STATUS SPI_Write(FT_HANDLE handle, uint8 *buffer,
-   uint32 sizeToTransfer, uint32 *sizeTransferred, uint32 transferOptions)
+FTDI_API FT_STATUS SPI_Write_D(FT_HANDLE handle, uint8 *buffer,
+   uint32 sizeToTransfer, uint32 *sizeTransferred, uint32 transferOptions,
+   uint32 CSReleaseDelayInMS)
 {
    FT_STATUS status;
    ChannelConfig *config=NULL;
@@ -485,7 +541,7 @@ FTDI_API FT_STATUS SPI_Write(FT_HANDLE handle, uint8 *buffer,
    if(transferOptions & SPI_TRANSFER_OPTIONS_CHIPSELECT_ENABLE)
    {
        /* enable CHIPSELECT line for the channel */
-       status = SPI_ToggleCS(handle,TRUE);
+       status = SPI_ToggleCS_D(handle,TRUE,CSReleaseDelayInMS);
        CHECK_STATUS(status);
    }

@@ -552,7 +608,7 @@ FTDI_API FT_STATUS SPI_Write(FT_HANDLE handle, uint8 *buffer,
    if(transferOptions & SPI_TRANSFER_OPTIONS_CHIPSELECT_DISABLE)
    {
        /* disable CHIPSELECT line for the channel */
-       status = SPI_ToggleCS(handle,FALSE);
+       status = SPI_ToggleCS_D(handle,FALSE,CSReleaseDelayInMS);
        CHECK_STATUS(status);
    }
    UNLOCK_CHANNEL(handle);
@@ -566,6 +622,36 @@ FTDI_API FT_STATUS SPI_Write(FT_HANDLE handle, uint8 *buffer,
 }


+/*!
+* \brief Reads and writes data from/to a SPI slave device
+*
+* This function transfers data in both directions between a SPI master and a slave. One bit is
+* clocked out and one bit is clocked in during every clock.
+*
+* \param[in] handle Handle of the channel
+* \param[in] *inBuffer Pointer to buffer to which data read will be stored
+* \param[in] *outBuffer Pointer to buffer that contains data to be transferred to the slave
+* \param[in] sizeToTransfer Size of data to be transferred
+* \param[out] sizeTransfered Pointer to variable containing the size of data
+*          that got transferred
+* \param[in] transferOptions This parameter specifies data transfer options
+*              if BIT0 is 0 then size is in bytes, otherwise in bits
+*              if BIT1 is 1 then CHIP_SELECT line will be enabled at start of transfer
+*              if BIT2 is 1 then CHIP_SELECT line will be disabled at end of transfer
+*
+* \return Returns status code of type FT_STATUS(see D2XX Programmer's Guide)
+* \sa
+* \note
+* \warning
+*/
+FTDI_API FT_STATUS SPI_ReadWrite(FT_HANDLE handle, uint8 *inBuffer,
+   uint8 *outBuffer, uint32 sizeToTransfer, uint32 *sizeTransferred,
+   uint32 transferOptions)
+{
+   return SPI_ReadWrite_D(handle, inBuffer, outBuffer, sizeToTransfer, sizeTransferred, transferOptions, 2);
+}
+
+
 /*!
  * \brief Reads and writes data from/to a SPI slave device
  *
@@ -580,17 +666,18 @@ FTDI_API FT_STATUS SPI_Write(FT_HANDLE handle, uint8 *buffer,
  *         that got transferred
  * \param[in] transferOptions This parameter specifies data transfer options
  *             if BIT0 is 0 then size is in bytes, otherwise in bits
- *             if BIT1 is 1 then CHIP_SELECT line will be enables at start of transfer
+ *             if BIT1 is 1 then CHIP_SELECT line will be enabled at start of transfer
  *             if BIT2 is 1 then CHIP_SELECT line will be disabled at end of transfer
+ * \param[in] CSReleaseDelayInMS delays release of CS line for given time in ms. Pass 0 to deactivate.
  *
  * \return Returns status code of type FT_STATUS(see D2XX Programmer's Guide)
  * \sa
  * \note
  * \warning
  */
-FTDI_API FT_STATUS SPI_ReadWrite(FT_HANDLE handle, uint8 *inBuffer,
+FTDI_API FT_STATUS SPI_ReadWrite_D(FT_HANDLE handle, uint8 *inBuffer,
    uint8 *outBuffer, uint32 sizeToTransfer, uint32 *sizeTransferred,
-   uint32 transferOptions)
+   uint32 transferOptions, uint32 CSReleaseDelayInMS)
 {
    FT_STATUS status;
    ChannelConfig *config=NULL;
@@ -617,7 +704,7 @@ FTDI_API FT_STATUS SPI_ReadWrite(FT_HANDLE handle, uint8 *inBuffer,
    if(transferOptions & SPI_TRANSFER_OPTIONS_CHIPSELECT_ENABLE)
    {
        /* enable CHIPSELECT line for the channel */
-       status = SPI_ToggleCS(handle,TRUE);
+       status = SPI_ToggleCS_D(handle,TRUE,CSReleaseDelayInMS);
        CHECK_STATUS(status);
    }

@@ -742,7 +829,7 @@ FTDI_API FT_STATUS SPI_ReadWrite(FT_HANDLE handle, uint8 *inBuffer,
    if(transferOptions & SPI_TRANSFER_OPTIONS_CHIPSELECT_DISABLE)
    {
        /* disable CHIPSELECT line for the channel */
-       status = SPI_ToggleCS(handle,FALSE);
+       status = SPI_ToggleCS_D(handle,FALSE,CSReleaseDelayInMS);
        CHECK_STATUS(status);
    }
    UNLOCK_CHANNEL(handle);
@@ -752,6 +839,25 @@ FTDI_API FT_STATUS SPI_ReadWrite(FT_HANDLE handle, uint8 *inBuffer,
 }


+/*!
+* \brief Read the state of SPI MISO line
+*
+* Reads the logic state of the SPI MISO line without clocking the bus
+*
+* \param[in] handle Handle of the channel
+* \param[out] *state State of the line
+* \return Returns status code of type FT_STATUS(see D2XX Programmer's Guide)
+* \sa
+* \note This function may be used for applications that needs to poll the state of
+*      the MISO line to check if the device is busy
+* \warning
+*/
+FTDI_API FT_STATUS SPI_IsBusy(FT_HANDLE handle, bool *state)
+{
+   return SPI_IsBusy_D(handle, state, SPI_TRANSFER_OPTIONS_CHIPSELECT_ENABLE | SPI_TRANSFER_OPTIONS_CHIPSELECT_DISABLE, 2);
+}
+
+
 /*!
  * \brief Read the state of SPI MISO line
  *
@@ -759,13 +865,17 @@ FTDI_API FT_STATUS SPI_ReadWrite(FT_HANDLE handle, uint8 *inBuffer,
  *
  * \param[in] handle Handle of the channel
  * \param[out] *state State of the line
+* \param[in] transferOptions This parameter specifies CS handling options
+*              if BIT1 is 1 then CHIP_SELECT line will be enabled before read
+*              if BIT2 is 1 then CHIP_SELECT line will be disabled after read
+ * \param[in] CSReleaseDelayInMS delays release of CS line for given time in ms. Pass 0 to deactivate.
  * \return Returns status code of type FT_STATUS(see D2XX Programmer's Guide)
  * \sa
  * \note This function may be used for applications that needs to poll the state of
  *     the MISO line to check if the device is busy
  * \warning
  */
-FTDI_API FT_STATUS SPI_IsBusy(FT_HANDLE handle, bool *state)
+FTDI_API FT_STATUS SPI_IsBusy_D(FT_HANDLE handle, bool *state, uint32 transferOptions, uint32 CSReleaseDelayInMS)
 {
    FT_STATUS status=FT_OTHER_ERROR;
    uint32 noOfBytes=0,noOfBytesTransferred=0;
@@ -773,7 +883,13 @@ FTDI_API FT_STATUS SPI_IsBusy(FT_HANDLE handle, bool *state)

    FN_ENTER;
    /*Enable CS*/
-   SPI_ToggleCS(handle, TRUE);
+   if(transferOptions & SPI_TRANSFER_OPTIONS_CHIPSELECT_ENABLE)
+   {
+       /* enable CHIPSELECT line for the channel */
+       status = SPI_ToggleCS_D(handle,TRUE,CSReleaseDelayInMS);
+       CHECK_STATUS(status);
+   }
+
    /*Send command to read*/
    buffer[noOfBytes++]=MPSSE_CMD_GET_DATA_BITS_LOWBYTE;
    buffer[noOfBytes++]=MPSSE_CMD_SEND_IMMEDIATE;
@@ -793,7 +909,12 @@ FTDI_API FT_STATUS SPI_IsBusy(FT_HANDLE handle, bool *state)
        *state=TRUE;

    /*Disable CS*/
-   SPI_ToggleCS(handle, FALSE);
+   if(transferOptions & SPI_TRANSFER_OPTIONS_CHIPSELECT_DISABLE)
+   {
+       /* disable CHIPSELECT line for the channel */
+       status = SPI_ToggleCS_D(handle,FALSE,CSReleaseDelayInMS);
+       CHECK_STATUS(status);
+   }
    FN_EXIT;
    return status;
 }
@@ -1160,6 +1281,23 @@ FT_STATUS SPI_DisplayList(void)
    return status;
 }

+/*!
+* \brief Toggles the state of the CS line
+*
+* This function turns ON/OFF the chip select line associated with the channel
+*
+* \param[in] handle Handle of the channel
+* \param[in] state TRUE if CS needs to be set, false otherwise
+* \return Returns status code of type FT_STATUS(see D2XX Programmer's Guide)
+* \sa
+* \note
+* \warning
+*/
+FT_STATUS SPI_ToggleCS(FT_HANDLE handle, bool state)
+{
+   return SPI_ToggleCS_D(handle, state, 2);
+}
+
 /*!
  * \brief Toggles the state of the CS line
  *
@@ -1167,12 +1305,13 @@ FT_STATUS SPI_DisplayList(void)
  *
  * \param[in] handle Handle of the channel
  * \param[in] state TRUE if CS needs to be set, false otherwise
+ * \param[in] CSReleaseDelayInMS delays release of CS line for given time in ms. Pass 0 to deactivate.
  * \return Returns status code of type FT_STATUS(see D2XX Programmer's Guide)
  * \sa
  * \note
  * \warning
  */
-FT_STATUS SPI_ToggleCS(FT_HANDLE handle, bool state)
+FT_STATUS SPI_ToggleCS_D(FT_HANDLE handle, bool state, uint32 CSReleaseDelayInMS)
 {
    ChannelConfig *config=NULL;
    bool activeLow;
@@ -1183,7 +1322,7 @@ FT_STATUS SPI_ToggleCS(FT_HANDLE handle, bool state)
    uint8 value, oldValue, direction;

    FN_ENTER;
-   if(!state)
+   if(!state && CSReleaseDelayInMS)
    {
        INFRA_SLEEP(2);
    }