AnsweredAssumed Answered

RT1050 LPSPI last bit not completing in continuous mode

Question asked by David Rodgers on Apr 16, 2019
Latest reply on Dec 2, 2019 by Hui_Ma

TL;DR - On the RT1050 LPSPI peripheral, when continuous mode is active (TCR.CONT == 1), the last SCK edge of a transfer does not complete, which means the last byte does not arrive in the RX FIFO, which means that when implementing a SPI driver API, nothing can continue after a read transfer, because the read request cannot be closed out without sending at least one more byte into the FIFO, and clearing CONT would de-assert PCSx and terminate the transfer.


So, I've been writing my own C++ LPSPI driver, using the FSL driver and other code as examples.  The base class for my SPI driver consists of two methods:

  • Begin(cs) -- Establishes what chip select is being driven and sets up the transfer.
  • Xfer(txdata, rxdata, length, is_final) -- Transmits and/or receives X number of bytes, marking whether this is the final transfer before PCSx is deasserted.


Xfer() can be called multiple times, for instance when doing a write-then-read as is typical of most SPI devices.  E.g.

/* Select PCS0. */
/* Send command buffer, keep PCS0 active. */
lpspi.Xfer(cmd_buffer, nullptr, cmd_length, false);
/* Read data buffer, deassert PCS0 when done. */
lpspi.Xfer(nullptr, data_buffer, data_length, true);


Now, while it's not typical, there should be no reason why one could not do two or more consecutive reads, perhaps because of a limited buffer size or other considerations.  Like this:

lpspi.Xfer(cmd_buffer, nullptr, cmd_length, false);
int cycle_count = 4;
for (int i = 0; i < cycle_count; ++i) {
lpspi.Xfer(nullptr, data_buffer, data_length, (i == (cycle_count - 1)));
/* Process the data buffer. */


However, THIS IS NOT POSSIBLE with the NXP LPSPI peripheral, based on my own testing and one other forum post on this exact topic involving a Kinetis KL28 LPSPI.  Here's a screencap of a 20-byte transfer, with CONT == 0:

LPSPI transfer, TCR.CONT == 0


CONT == 0 obviously won't work for a SPI device access, with PCSx de-asserting between every byte.  But here's the same transfer with CONT == 1 (the only change made in the code):

LPSPI transfer, TCR.CONT == 1


You can see that the last half-clock doesn't appear on SCK; the clock stays high, and PCSx stays asserted because I haven't written to TCR yet.  This results in the last (zero) byte not arriving in the RX FIFO, which causes my driver to stall because the expected 20 bytes are never fully received.  If I write TCR with both CONT and CONTC set to 1, this doesn't change (I tried it, SCK remains stuck in the middle of the final bit).  And if I write TCR with CONTC set to 0, then yes, the lask SCK edge appears and the last byte arrives in the RX FIFO, but this also de-asserts PCSx and terminates the transfer.


I consider this to be a bug/erratum in the LPSPI peripheral, because it unnecessarily constrains the behavior of the software using it.  My SPI driver cannot perform another transfer following a read while keeping PCSx asserted; performing a read requires that it be the last transfer before de-asserting PCSx.  I've worked around this in my driver by doing two things:

  • Rather than always trying to receive N bytes from the RX FIFO after sending N bytes, I keep a "garbage byte" counter that goes up each time a byte is written with no read expected; the counter is decremented each time a byte is read from the RX FIFO, but the counter is not required to go to zero if the write is not a final transfer; the garbage count carries over to the subsequent transfer.
  • If the application requests a read (i.e. rxdata != nullptr) and is_final is false, the LPSPI driver returns an error because it cannot complete the request.


It's a crummy workaround, but it at least allows me to maintain a consistent API for my drivers.  Unless NXP has a proper workaround that allows me to get that last byte into the RX FIFO without terminating the transfer or shoving an extra byte into the TX FIFO, then I guess this isn't really a question post, more of an "engineer beware" post.  Thanks for reading.