Bug in SPI driver for LPC845 - SPI_MasterTransferBlocking

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Bug in SPI driver for LPC845 - SPI_MasterTransferBlocking

4,747 Views
tim99
Contributor III

If you call the function SPI_MasterTransferBlocking with one of the flags kSPI_EndOfFrame, kSPI_EndOfTransfer or kSPI_ReceiveIgnore. the next time you call the SPI_MasterTransferBlocking without these flags, they will still be set.

The reason for this behavior is the line in the function SPI_MAsterTransferBlocking:

/* Read datawidth and ssel info from TXCTL. */

tx_ctrl = base->TXCTL & (SPI_TXCTL_LEN_MASK | SPI_TXCTL_RXIGNORE_MASK | SPI_TXCTL_EOF_MASK | SPI_TXCTL_EOT_MASK |

kSPI_SselDeAssertAll);

According to the comment, the intention is to keep the datawitdh and ssel, but all the above mentioned flags/bits are also read. To get the behavior as I would expect I need to change the line to:

tx_ctrl = base->TXCTL & (SPI_TXCTL_LEN_MASK | kSPI_SselDeAssertAll);

An example of when the function fails

/*Start Transfer*/

xfer.txData = Obuf;

xfer.rxData = tmp;

xfer.dataSize = LengthO;

xfer.configFlags = 0;

/* Transfer data in polling mode. */

SPI_MasterTransferBlocking(SPI_0_PERIPHERAL, &xfer);

 

/*Start Transfer*/

xfer.txData = NULL;

xfer.rxData = Ibuf;

xfer.dataSize = LengthI;

xfer.configFlags = kSPI_EndOfTransfer | kSPI_EndOfFrame;

/* Transfer data in polling mode. */

SPI_MasterTransferBlocking(SPI_0_PERIPHERAL, &xfer);

/*Start Transfer*/

xfer.txData = Obuf;

xfer.rxData = tmp;

xfer.dataSize = LengthO;

xfer.configFlags = 0;

/* Transfer data in polling mode. */

SPI_MasterTransferBlocking(SPI_0_PERIPHERAL, &xfer);

 

/*Start Transfer*/

xfer.txData = NULL;

xfer.rxData = Ibuf;

xfer.dataSize = LengthI;

xfer.configFlags = kSPI_EndOfTransfer | kSPI_EndOfFrame;

/* Transfer data in polling mode. */

SPI_MasterTransferBlocking(SPI_0_PERIPHERAL, &xfer);

The first call works as expected and the SSEL line is kept active. The second call works as expected where the SSEL line is set to inactive after the transmission. The third call fails where the SSEL line is set to inactive after the call as if the the flags EndOfTransfer and EndOfFrame were still set.

Please confirm if this is a bug or if I am using the driver wrong.

Tags (2)
8 Replies

4,450 Views
oliver1
Contributor I

Hi all,

FYI, the problem in SPI_MasterTransferBlocking() is still there in version 2.7.0 of the SDK. It can be solved with the following patch:

--- SDK-2.7.0-LPC845/devices/LPC845/drivers/fsl_spi.c.old 2020-01-08 02:27:28.000000000 +0100
+++ SDK-2.7.0-LPC845/devices/LPC845/drivers/fsl_spi.c 2020-03-05 08:02:35.408384641 +0100
@@ -460,8 +460,7 @@
 
     remainingBytes = xfer->dataSize;
     /* Read datawidth and ssel info from TXCTL. */
-    tx_ctrl   = base->TXCTL & (SPI_TXCTL_LEN_MASK | SPI_TXCTL_RXIGNORE_MASK | SPI_TXCTL_EOF_MASK | SPI_TXCTL_EOT_MASK |
-                             (uint32_t)kSPI_SselDeAssertAll);
+    tx_ctrl   = base->TXCTL & (SPI_TXCTL_LEN_MASK | (uint32_t) kSPI_SselDeAssertAll);
     dataWidth = ((tx_ctrl & SPI_TXCTL_LEN_MASK) >> SPI_TXCTL_LEN_SHIFT);
 
     /* Set end of frame configuration. */

The problem in SPI_WriteConfigFlags() mentioned by joevignola‌ is fixed.

 

Best regards

Oliver

0 Kudos

4,450 Views
joevignola
Contributor III

I actually found two bugs in the spi SDK driver.  The first one is not clearing the config flags between transactions as explained in the original post.  I was able to overcome this by adjusting the registers before calling SPI_MasterTransferBlocking function.

// Clear any config flags from a previous transmission
SPI0->TXCTL = (SPI0->TXCTL & ~(SPI_TXCTL_EOT_MASK | SPI_TXCTL_EOF_MASK | SPI_TXCTL_RXIGNORE_MASK));

if (SPI_MasterTransferBlocking(SPI0, &xfer) == kStatus_Success)

The second problem is with the SPI_WriteConfigFlags function.  Using an OR operation only allows the adding of control bits.  There are no API calls to actually clear the configuration bits.

static inline void SPI_WriteConfigFlags(SPI_Type *base, uint32_t configFlags)
{
base->TXCTL |= (configFlags & (SPI_TXCTL_EOT_MASK | SPI_TXCTL_EOF_MASK | SPI_TXCTL_RXIGNORE_MASK));
}

Are these API functions tested?  The SDK examples are so trivial they really are useless and the documentation is just a bunch of function prototypes.

Joe.

0 Kudos

4,450 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, Friend,

I suggest you rewrite the xfer.configFlags variable with new value when you want to keep /CS low during the transfer period.

for example, pls try to use the code, pls have try.

BR

XiangJun Rong

/*Start Transfer*/

xfer.txData = Obuf;

xfer.rxData = tmp;

xfer.dataSize = LengthO;

/* Transfer data in polling mode. */

SPI_MasterTransferBlocking(SPI_0_PERIPHERAL, &xfer);

 

/*Start Transfer*/

 

xfer.txData = NULL;

xfer.rxData = Ibuf;

xfer.dataSize = LengthI;

xfer.configFlags = kSPI_EndOfTransfer | kSPI_EndOfFrame;

/* Transfer data in polling mode. */

SPI_MasterTransferBlocking(SPI_0_PERIPHERAL, &xfer);

 

/*Start Transfer*/

xfer.txData = Obuf;

xfer.rxData = tmp;

xfer.dataSize = LengthO;

xfer.configFlags& = ~(kSPI_EndOfTransfer | kSPI_EndOfFrame);         //Rong write to reset the end of frame and end of transfer bits

/* Transfer data in polling mode. */

SPI_MasterTransferBlocking(SPI_0_PERIPHERAL, &xfer);

 

/*Start Transfer*/

 

xfer.txData = NULL;

xfer.rxData = Ibuf;

xfer.dataSize = LengthI;

xfer.configFlags = kSPI_EndOfTransfer | kSPI_EndOfFrame;

xfer.configFlags& = ~(kSPI_EndOfTransfer | kSPI_EndOfFrame);         //Rong write to reset the end of frame and end of transfer bits

/* Transfer data in polling mode. */

SPI_MasterTransferBlocking(SPI_0_PERIPHERAL, &xfer);

0 Kudos

4,450 Views
tim99
Contributor III

Hi Xiangjun and thanks for your reply,

Just tried your code, but the result was the same. It seems to me that you can use the xfer.configFlags to set bits in the SPI Transmitter Control register, but there is no way to clear them.

0 Kudos

4,450 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, Tim99,

After you clear kSPI_EndOfTransfer | kSPI_EndOfFrame bits with the following lines for xfer.configFlags

xfer.configFlags& = ~(kSPI_EndOfTransfer | kSPI_EndOfFrame)

If you debug the following code SPI_TransferHandleIRQInternal(), especially, set a break point on the line:

tx_ctrl |= (handle->configFlags & (uint32_t)kSPI_FrameDelay) ? (uint32_t)kSPI_FrameDelay : 0;

dose the Bit21 EOF is cleared are set?

BR

XiangJun Rong

static void SPI_TransferHandleIRQInternal(SPI_Type *base, spi_master_handle_t *handle)
{
    uint32_t tx_ctrl = 0, last_ctrl = 0, tmp32;
    bool loopContinue;
    uint32_t fifoDepth;
    /* Get flexcomm instance by 'base' param */
    uint32_t instance = SPI_GetInstance(base);

    /* check params */
    assert((NULL != base) && (NULL != handle) && ((NULL != handle->txData) || (NULL != handle->rxData)));

    fifoDepth = SPI_FIFO_DEPTH(base);
    /* select slave to talk with */
    tx_ctrl |= (SPI_DEASSERT_ALL & SPI_ASSERTNUM_SSEL(handle->sselNum));
    /* set width of data */
    tx_ctrl |= SPI_FIFOWR_LEN(handle->dataWidth);
    /* delay for frames */
    tx_ctrl |= (handle->configFlags & (uint32_t)kSPI_FrameDelay) ? (uint32_t)kSPI_FrameDelay : 0;
    /* end of transfer */
    last_ctrl |= (handle->configFlags & (uint32_t)kSPI_FrameAssert) ? (uint32_t)kSPI_FrameAssert : 0;
    do
    {
        loopContinue = false;

        /* rxFIFO is not empty */
        if (base->FIFOSTAT & SPI_FIFOSTAT_RXNOTEMPTY_MASK)
        {
            tmp32 = base->FIFORD;
            /* rxBuffer is not empty */
            if (handle->rxRemainingBytes)
            {
                /* low byte must go first */
                *(handle->rxData++) = tmp32;
                handle->rxRemainingBytes--;
                /* read 16 bits at once */
                if (handle->dataWidth > kSPI_Data8Bits)
                {
                    *(handle->rxData++) = tmp32 >> 8;
                    handle->rxRemainingBytes--;
                }
            }
            /* decrease number of data expected to receive */
            handle->toReceiveCount -= 1;
            loopContinue = true;
        }

        /* - txFIFO is not full
         * - we cannot cause rxFIFO overflow by sending more data than is the depth of FIFO
         * - txBuffer is not empty or the next 'toReceiveCount' data can fit into rxBuffer
         */
        if ((base->FIFOSTAT & SPI_FIFOSTAT_TXNOTFULL_MASK) && (handle->toReceiveCount < fifoDepth) &&
            ((handle->txRemainingBytes) ||
             (handle->rxRemainingBytes >= SPI_COUNT_TO_BYTES(handle->dataWidth, handle->toReceiveCount + 1))))
        {
            /* txBuffer is not empty */
            if (handle->txRemainingBytes)
            {
                /* low byte must go first */
                tmp32 = *(handle->txData++);
                handle->txRemainingBytes--;
                /* write 16 bit at once */
                if (handle->dataWidth > kSPI_Data8Bits)
                {
                    tmp32 |= ((uint32_t)(*(handle->txData++))) << 8U;
                    handle->txRemainingBytes--;
                }
                /* last transfer */
                if (!handle->txRemainingBytes)
                {
                    tx_ctrl |= last_ctrl;
                }
            }
            else
            {
                tmp32 = ((uint32_t)s_dummyData[instance] << 8U | (s_dummyData[instance]));
                /* last transfer */
                if (handle->rxRemainingBytes == SPI_COUNT_TO_BYTES(handle->dataWidth, handle->toReceiveCount + 1))
                {
                    tx_ctrl |= last_ctrl;
                }
            }
            /* send data */
            tmp32        = tx_ctrl | tmp32;
            base->FIFOWR = tmp32;
            /* increase number of expected data to receive */
            handle->toReceiveCount += 1;
            loopContinue = true;
        }
    } while (loopContinue);
}

0 Kudos

4,450 Views
tim99
Contributor III

Hi Xiangjun,

I don't think we have the same SDK. I my SDK (2.6.0, LPCXpresso845) there is no function called SPI_TransferHandleIRQInternal().

0 Kudos

4,450 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, Tim99,

Sorry for the delay, I took a long festival.

Can you share your code here so that I can check if the function exists or not.

BR

Xiangjun Rong

0 Kudos

4,450 Views
tim99
Contributor III

Hi Xiangjun,

This can very easily be replicated with the SDK example (SDK_2.6.0 LPCXpresso845MAX). If you run the example lpcxpresso845max_spi_polling_master, you will get the result as shown in image

SPI1.jpg

If you just add the two lines as shown below to the file spi_polling_master.c, you will get the following result

SPI2.jpg

SSEL line is being set high for each byte during the second transfer

------ From spi_polling_master.c------>

/*Start Transfer*/
xfer.txData = txBuffer;
xfer.rxData = rxBuffer;
xfer.dataSize = sizeof(txBuffer);
xfer.configFlags = kSPI_EndOfTransfer | kSPI_EndOfFrame;
/* Transfer data in polling mode. */
SPI_MasterTransferBlocking(EXAMPLE_SPI_MASTER, &xfer);

------>  Added a second transmission to demonstrate the problem.
xfer.configFlags = 0; // Using ~(kSPI_EndOfTransfer | kSPI_EndOfFrame) will not work either
SPI_MasterTransferBlocking(EXAMPLE_SPI_MASTER, &xfer);

You can also observe that the EOT and EOF bits are still set in the SPI register. There is nothing that can be done with xfer.configFlags to reset EOT or EOF

I think it is better if you download the SDK instead of me sending you the files because my motivation for this post is to verify if this is a bug in the SDK, and if so, hopefully NXP will fix it for the next version of the SDK

0 Kudos