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.
Hi Jeff:
Could you please make a simple project to reproduce this issue with NXP evk board?
Regards
Daniel
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.
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;
}
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.
Will controlling the chip select in this way adversely affect LPSPI transfers to the SPI device?
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.