LPSPI Peripheral Driver Polling Mode Returns Busy

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

LPSPI Peripheral Driver Polling Mode Returns Busy

1,649 Views
jeffthompson
Contributor V

I'm using the MIMXRT1062 to talk to a device using SPI Master mode. I need to control the PCS line myself, as the device requires me to send it a 24-bit bit starting address for each data transfer, with PCS asserted continually during the address and data phase of each transfer. For simplicity, I've elected to use LPSPI_MasterTransferBlocking(). This technique worked quite well for me when I was using the K64F12 in a previous incarnation of the project.


Since this project needs to use the USB ports in Host CDC mode, I started with the host_cdc_bm example, added FreeRTOS, verified that worked, then added my SPI device code.


I'm working with the MIMRXT1060-EVK as a starting point, and using MCUXpresso 11 to configure LPSPI1 signals to go to the Arduino shield interface, where my SPI device attaches. I've supplied 0-ohm resistors for R278 – R281, to bring the signals I need to the Arduino shield.The LPSPI1 pins are set up with default values, except that I'm using GPIO1_IO24 as my software-controlled chip select.


Initially, the baud rate is set ro 10 MHz, but all the other peripheral settings are at their default values, as are the clocks. After I've successfully configured the SPI device, I'll increase the baud rate to 30 MHz. Again, this worked nicely for my K64F12 version.


I've verified that LPSPI_MasterInit is called with the correct peripheral base address, config, and clock frequency, and that my chip select asserts and de-#asserts.


The problem is that my first few calls to LPSPI_MasterTransferBlocking, but then it returns kStatus_LPSPI_Busy, which I've never had happen before. What is the best way to handle this? The SDK API Reference Manual does not give any guidance on this.

0 Kudos
7 Replies

1,279 Views
danielchen
NXP TechSupport
NXP TechSupport

Hi Jeff:

Could you please make a simple project to reproduce this issue with NXP evk board?

Regards

Daniel

0 Kudos

189 Views
Yaseer
Contributor I
MIMXRT1024 LPSPI FRAM Reading
Hi,

I working on MIMXRT1024 to read and write the FRAM(MB85RS4MT). I am able to write the FRAM. After 1st data reading from FRAM, CS pin is not de-assert automatically after clearing TCR[CONT& CONTC & TX].

when i reading FRAM, I writing 4 bytes data FRAM, and i waiting for data by setting LPSPI_TCR_TXMSK_MASK. i am able to read the data. but after reading, TCR[CONT& CONTC & TX] are cleared. But CS pin is not De-assert automatically.i attached my Reading code in here.

status_t LPSPI_MasterTransferBlockingTXRX(LPSPI_Type *base, lpspi_transfer_t *transfer,uint8_t type){

assert(transfer != NULL);

/* Check that LPSPI is not busy.*/
if ((LPSPI_GetStatusFlags(base) & (uint32_t)kLPSPI_ModuleBusyFlag) != 0U)
{
return kStatus_LPSPI_Busy;
}
LPSPI_Enable(base, false);
/* Check arguements */
if (!LPSPI_CheckTransferArgument(base, transfer, false))
{
return kStatus_InvalidArgument;
}

LPSPI_FlushFifo(base, true, true);
LPSPI_ClearStatusFlags(base, (uint32_t)kLPSPI_AllStatusFlag);

/* Variables */
bool isTxMask = false;
bool isByteSwap = ((transfer->configFlags & (uint32_t)kLPSPI_MasterByteSwap) != 0U);
uint8_t bytesEachWrite;
uint8_t bytesEachRead;
uint8_t *txData = transfer->txData;
uint8_t *rxData = NULL;
uint8_t dummyData = g_lpspiDummyData[LPSPI_GetInstance(base)];
uint32_t readData = 0U;
uint32_t txRemainingByteCount = 4;
bool isPcsContinuous ;



uint32_t rxRemainingByteCount ;
uint32_t wordToSend =
((uint32_t)dummyData) | ((uint32_t)dummyData << | ((uint32_t)dummyData << 16) | ((uint32_t)dummyData << 24);
/*The TX and RX FIFO sizes are always the same*/
uint32_t fifoSize = LPSPI_GetRxFifoSize(base);
uint32_t bytesPerFrame = ((base->TCR & LPSPI_TCR_FRAMESZ_MASK) >> LPSPI_TCR_FRAMESZ_SHIFT) / 8U + 1U;
/* No need to configure PCS continous if the transfer byte count is smaller than frame size */

uint32_t rxFifoMaxBytes = MIN(bytesPerFrame, 4U) * fifoSize;
uint32_t whichPcs = (transfer->configFlags & LPSPI_MASTER_PCS_MASK) >> LPSPI_MASTER_PCS_SHIFT;
uint32_t temp = (base->CFGR1 & LPSPI_CFGR1_PINCFG_MASK);
if(type==0){
txRemainingByteCount =4;// normal read and write
isPcsContinuous = true;
}
else{
txRemainingByteCount =1;
isPcsContinuous = (((transfer->configFlags & (uint32_t)kLPSPI_MasterPcsContinuous) != 0U) &&
(bytesPerFrame < transfer->dataSize));
}
#if SPI_RETRY_TIMES
uint32_t waitTimes;
#endif

/* Mask tx data in half duplex mode */
if (((temp == LPSPI_CFGR1_PINCFG(kLPSPI_SdiInSdiOut)) || (temp == LPSPI_CFGR1_PINCFG(kLPSPI_SdoInSdoOut))) &&
(txData == NULL))
{
isTxMask = true;
}

base->CFGR1 &= (~LPSPI_CFGR1_NOSTALL_MASK);
LPSPI_Enable(base, true);

/* Configure transfer control register. */
base->TCR = (base->TCR & ~(LPSPI_TCR_CONT_MASK | LPSPI_TCR_CONTC_MASK | LPSPI_TCR_RXMSK_MASK |
LPSPI_TCR_TXMSK_MASK | LPSPI_TCR_PCS_MASK)) |
LPSPI_TCR_PCS(whichPcs);

/*TCR is also shared the FIFO, so wait for TCR written.*/
if (!LPSPI_TxFifoReady(base))
{
return kStatus_LPSPI_Timeout;
}

/* PCS should be configured separately from the other bits, otherwise it will not take effect. */
base->TCR |= LPSPI_TCR_CONT(isPcsContinuous) | LPSPI_TCR_CONTC(isPcsContinuous)|
#ifdef SERIAL
LPSPI_TCR_WIDTH(0)
#else
LPSPI_TCR_WIDTH(1)
#endif
| LPSPI_TCR_RXMSK(NULL == rxData)/**/ ;

/*TCR is also shared the FIFO, so wait for TCR written.*/
if (!LPSPI_TxFifoReady(base))
{
return kStatus_LPSPI_Timeout;
}

if (bytesPerFrame <= 4U)
{
bytesEachWrite = (uint8_t)bytesPerFrame;
bytesEachRead = (uint8_t)bytesPerFrame;
}
else
{
bytesEachWrite = 4U;
bytesEachRead = 4U;
}

/*Write the TX data until txRemainingByteCount is equal to 0 */
while (txRemainingByteCount > 0U)
{
if (txRemainingByteCount < bytesEachWrite)
{
bytesEachWrite = (uint8_t)txRemainingByteCount;
}

/*Wait until TX FIFO is not full*/
#if SPI_RETRY_TIMES
waitTimes = SPI_RETRY_TIMES;
while ((LPSPI_GetTxFifoCount(base) == fifoSize) && (--waitTimes != 0U))
#else
while (LPSPI_GetTxFifoCount(base) == fifoSize)
#endif
{
}
#if SPI_RETRY_TIMES
if (waitTimes == 0U)
{
return kStatus_LPSPI_Timeout;
}
#endif

/* To prevent rxfifo overflow, ensure transmitting and receiving are executed in parallel */
if (((NULL == rxData) || (rxRemainingByteCount - txRemainingByteCount) < rxFifoMaxBytes))
{
if (isTxMask)
{
/* When TCR[TXMSK]=1, transfer is initiate by writting a new command word to TCR. TCR[TXMSK] is cleared
by hardware every time when TCR[FRAMESZ] bit of data is transfered.
In this case TCR[TXMSK] should be set to initiate each transfer. */
base->TCR |= LPSPI_TCR_TXMSK_MASK;
if (isPcsContinuous && (txRemainingByteCount == bytesPerFrame))
{
/* For the last piece of frame size of data, if is PCS continous mode(TCR[CONT]), TCR[CONTC] should
* be cleared to de-assert the PCS. Be sure to clear the TXMSK as well otherwise another FRAMESZ
* of data will be received. */
base->TCR &= ~(LPSPI_TCR_CONTC_MASK | LPSPI_TCR_CONT_MASK | LPSPI_TCR_TXMSK_MASK);
}
txRemainingByteCount -= bytesPerFrame;
}
else
{
if (txData != NULL)
{
wordToSend = LPSPI_CombineWriteData(txData, bytesEachWrite, isByteSwap);
txData += bytesEachWrite;
}
/* Otherwise push data to tx FIFO to initiate transfer */
//printf(" Read Send: %x",wordToSend);
LPSPI_WriteData(base, wordToSend);
txRemainingByteCount -= bytesEachWrite;
}
}

/* Check whether there is RX data in RX FIFO . Read out the RX data so that the RX FIFO would not overrun. */
if ((rxData != NULL) && (rxRemainingByteCount != 0U))
{
/* To ensure parallel execution in 3-wire mode, after writting 1 to TXMSK to generate clock of
bytesPerFrame's data wait until bytesPerFrame's data is received. */
while (isTxMask && (LPSPI_GetRxFifoCount(base) == 0U))
{
}
#if SPI_RETRY_TIMES
waitTimes = SPI_RETRY_TIMES;
while ((LPSPI_GetRxFifoCount(base) != 0U) && (--waitTimes != 0U))
#else
while (LPSPI_GetRxFifoCount(base) != 0U)
#endif
{
readData = LPSPI_ReadData(base);
if (rxRemainingByteCount < bytesEachRead)
{
bytesEachRead = (uint8_t)rxRemainingByteCount;
}

LPSPI_SeparateReadData(rxData, readData, bytesEachRead, isByteSwap);
rxData += bytesEachRead;

rxRemainingByteCount -= bytesEachRead;
}
#if SPI_RETRY_TIMES
if (waitTimes == 0U)
{
return kStatus_LPSPI_Timeout;
}
#endif
}
}


rxRemainingByteCount = transfer->dataSize;
rxData = transfer->rxData;

//base->TCR |= LPSPI_TCR_CONT(1) | LPSPI_TCR_CONTC(1) | LPSPI_TCR_TXMSK(1) | LPSPI_TCR_WIDTH(1);
base->TCR = (base->TCR & ~( LPSPI_TCR_RXMSK_MASK ));
base->TCR |= LPSPI_TCR_CONT(1) | LPSPI_TCR_CONTC(1)|
#ifdef SERIAL
LPSPI_TCR_WIDTH(0)
#else
LPSPI_TCR_WIDTH(1)
#endif
|LPSPI_TCR_TXMSK(1)/**/ ;

/* wait a little bit */
//int k=0;
//for (size_t i = 0; i < 1000000; i++);
/*TCR is also shared the FIFO, so wait for TCR written.*/
// if (!LPSPI_TxFifoReady(base))
// {
// return kStatus_LPSPI_Timeout;
// }

/*Read out the RX data in FIFO*/
if (rxData != NULL)
{ waitTimes = SPI_RETRY_TIMES;
//printf("\n rxRemainingByteCount:");
while (rxRemainingByteCount > 0U)
{

int b=0;
#if SPI_RETRY_TIMES

while ((LPSPI_GetRxFifoCount(base) != 0U) && (waitTimes != 0U))
#else
while (LPSPI_GetRxFifoCount(base) != 0U)
#endif
{

readData = LPSPI_ReadData(base);
if (rxRemainingByteCount < bytesEachRead)
{
bytesEachRead = (uint8_t)rxRemainingByteCount;
}

LPSPI_SeparateReadData(rxData, readData, bytesEachRead, isByteSwap);
rxData += bytesEachRead;

rxRemainingByteCount -= bytesEachRead;
// printf(" Rec : %X",readData);
if(rxRemainingByteCount==0){
break;
}
}
#if SPI_RETRY_TIMES
waitTimes--;
if (waitTimes == 0U)
{
return kStatus_LPSPI_Timeout;
}
#endif
}
}

if (isPcsContinuous && !isTxMask)
{
/* In PCS continous mode(TCR[CONT]), after write all the data in TX FIFO, TCR[CONTC] and TCR[CONT] should be
cleared to de-assert the PCS. Note that TCR register also use the TX FIFO. Also CONTC should be cleared when
tx is not masked, otherwise written to TCR register with TXMSK bit wet will initiate a new transfer. */
#if SPI_RETRY_TIMES
waitTimes = SPI_RETRY_TIMES;
while ((LPSPI_GetTxFifoCount(base) == fifoSize) && (--waitTimes != 0U))
#else
while (LPSPI_GetTxFifoCount(base) == fifoSize)
#endif
{
}
#if SPI_RETRY_TIMES
if (waitTimes == 0U)
{
return kStatus_LPSPI_Timeout;
}
#endif
base->TCR = (base->TCR & ~(LPSPI_TCR_CONTC_MASK | LPSPI_TCR_CONT_MASK));
}
return kStatus_Success;
}




Can u help me solve this issue?



yaseer msm.
0 Kudos

1,279 Views
dmarks_ls
Senior Contributor I

I see NXP hasn't chimed in on this one yet.  I've had a look at the LPSPI driver, and the only way it returns kStatus_LPSPI_Busy after a call to LPSPI_MasterTransferBlocking() is if MBF (module busy) is set in SR (status register).  But looking at LPSPI_MasterTransferBlocking(), it appears to ensure that the module won't be busy after a transfer; if rxData is provided, then it reads all expected data from the FIFO, or if rxData is null, it waits for the TCF (transfer complete) flag in SR to be set.  Per the datasheet, "In master mode when the LPSPI returns to idle state with the transmit FIFO empty, the Transfer Complete Flag will set."  So if TCF is set, then the module is idle, and thus MBF should be clear.

Can you post some code, i.e. how you're invoking the LPSPI SDK API?  It shouldn't matter that you're manually controlling the chip select; the LPSPI module doesn't care whether you route the PCS signal to the designated pin or not.  It should be valid to manually set your PCS low, call LPSPI_MasterTransferBlocking(), and then set PCS high.  Also, what are you seeing on your scope when one of these transfer attempts returns busy?  Are your PCS and SCK signals somehow getting out of sync?

(An aside: this is one thing I don't like about the FSL SPI API; there's no provision for doing a continuous transfer in pieces.  When accessing a memory device, it's common to send an address and then read/write data.  But their LPSPI driver assumes that the transfer will always be terminated at the conclusion.  And this aspect of their driver masks an erratum in their LPSPI peripheral.)

David R.

0 Kudos

1,279 Views
jeffthompson
Contributor V

David,

Thanks for the response. I've been able to reduce the delay to only about 5 uS. I've also reduced the "Delay between transfers [ns]", found in MCUXpresso ConfigTools Peripherals/LPSPI1/General Configuration/Master Mode, to 100 ns for my initial configuration of the peripheral device at 10 MHz; subsequent communication takes place at 30 MHz with a 34 ns "Delay between transfers settings". (The "PCS to SCK delay time" and "Last SCK to PCS delay time" I've set to 5 ns and 0 ns, respectively).

I wholeheartedly agree with you that a two-phase transfer, such as what I'm doing and which you mentioned, would be a nice addition to the LPSPI driver since, as you mentioned, this is a very common method of communicating with peripheral devices.

Here's my code for.reading my SPI device. The write operation is similar, except that the dummy address vyte is onot sent.

bool FtdiChipHostMemRead ( uint32_t ftdiChipAddr, uint8_t *memAddr, uint32_t byteCount )
{
   status_t lpspiStatus = kStatus_Fail;
   lpspi_transfer_t xfer;
   bool retval = false;

   // FIX: remove the delay, if possible
   SDK_DelayAtLeastUs( 5 );

   if( (byteCount <= FTDI_MAX_MEM_TXFR) && ((ftdiChipAddr + byteCount) <= (FTDI_MAX_ADDR + 1)) )
   {
      // The masking off of the upper bits of the address, to indicate a host memory read to FTDI Chip,
      // may be overkill, but since it's being masked anyway, why not?
      ftdiXfer.hdr.addr[0] = (ftdiChipAddr & 0x003f0000) >> 16;
      ftdiXfer.hdr.addr[1] = (ftdiChipAddr & 0x0000ff00) >> 8;
      ftdiXfer.hdr.addr[2] = (ftdiChipAddr & 0x000000ff);
      ftdiXfer.hdr.addr[3] = 0;

      GPIO_PinWrite( BOARD_INITPINS_FT813_CSn_GPIO, BOARD_INITPINS_FT813_CSn_PIN, 0 );

      xfer.txData = (uint8_t*) &ftdiXfer;
      xfer.rxData = NULL;
      xfer.dataSize = sizeof(ftdiXfer.hdr.addr);
      xfer.configFlags = kLPSPI_MasterPcs0 | kLPSPI_MasterPcsContinuous;

      // Write out the address whence cometh the data
      if( kStatus_Success == (lpspiStatus = LPSPI_MasterTransferBlocking( FT813_SPI_PERIPHERAL, &xfer )) )
      {
         xfer.txData = NULL;
         xfer.rxData= memAddr;
         xfer.dataSize = byteCount;
         xfer.configFlags = kLPSPI_MasterPcs0 | kLPSPI_MasterPcsContinuous;

         // FIX: remove the delay, if possible
         SDK_DelayAtLeastUs( 5 );

         // The data being read is sent directly to the caller's read address
         if( kStatus_Success == (lpspiStatus = LPSPI_MasterTransferBlocking( FT813_SPI_PERIPHERAL, &xfer )) )
         {
            retval = true;
         }
      }

      GPIO_PinWrite( BOARD_INITPINS_FT813_CSn_GPIO, BOARD_INITPINS_FT813_CSn_PIN, 1 );
   }

   // TODO: remove for production. Debug only.
   if( false == retval ) __asm("BKPT");

   return retval;
}

0 Kudos

1,279 Views
dmarks_ls
Senior Contributor I

Hmm.  I don't see anything obvious to suggest why you're getting kStatus_LPSPI_Busy prior to starting a new transfer.  However, given what you're experiencing, and how you're controlling PCS, maybe try a couple of different things.

1) Since you're manually controlling PCS, there's no need to set kLPSPI_MasterPcsContinuous for your transfers.  Try turning that off.  Reason I suggest that, there's an erratum regarding LPSPI and continuous transfers that can affect read operations.  But the erratum doesn't apply if the CONT bit is 0.

2) Just for giggles... set your transfer rate relatively low, say, 1 MHz, so that 1 clock time = 1 us.  Then play with the clock timing settings (LPSPI.CCR), and try maxing out CCR[SCKPCS]... that's the delay from the last clock edge to the rising edge of PCS.  In the NXP driver, that would be the lastSckToPcsDelayInNanoSec setting.  The max is 255 cycles, which at 1 MHz would be 255 us, so maybe try 250000 ns.  Then see if your 5 us delay is still adequate.  This is checking whether the LPSPI peripheral perhaps is signaling "transfer complete" when it hasn't yet de-asserted PCS (internally, anyway).

Also, if you haven't already, put a scope on the SPI bus lines to make sure your driver is actually doing what you expect it to do.  If you don't have an analyzer pod, the Saleae Logic Pro 8 is indispensable.

Sorry I can't offer a genuine solution.  Maybe one of the NXP engineers can suggest something?  Hui_Ma‌ ?

David R.

0 Kudos

1,279 Views
jeffthompson
Contributor V

Will controlling the chip select in this way adversely affect LPSPI transfers to the SPI device?

0 Kudos

1,279 Views
jeffthompson
Contributor V

I added a 1 tick delay—vTaskDelay(1)—between calls and no longer get the kStatus_LPSPI_Busy status. But, why? I shouldn't have to pace my LPSPI accesses this way.

0 Kudos