I have modified the SDK sample "lpspi_transfer_s32k144" from S32SDK_S32K14x_EAR_0.8.4 to use DMA instead of interrupts. This involved adding a EDMA_DRV_Init() to the mainline and using components inspector to add two new DMA channels: #1 for LPSPI0_TX and #2 for LPSPI0_RX. These are used by the master.
This works, but eventually I just want to send from master to slave -- my eventual slave device will have no data to send back. So I don't need or want that LPSPI0_RX DMA channel.
However, if I set the master's RX to "No DMA" in component inspector, the master stops sending on SPI completely. No activity shows up on the scope on any of the SPI lines.
Similarly if I put the RX DMA back but change the line:
LPSPI_DRV_MasterTransferBlocking(SEND, &masterDataSend, &masterDataReceive, BUFFER_SIZE, TIMEOUT);
...to have NULL in place of &masterDataReceive indicating I expect to receive nothing for the master, again the SPI just stops working. Nothing in the driver returns any errors; the SPI lines simply do nothing.
The same code works fine I switch back to using interrupts on master.
What's the trick to getting DMA just on SPI master TX to behave?
Ok, there seems to be problems with transfers >16 bytes (i.e. more than the SPI FIFO capacity) with both Interrupt and DMA modes when there is no Rx buffer (half duplex, i.e. NULL for receiveBuffer in the LPSPI_DRV_MasterTransferBlocking() call). In both cases it seems to be unhandled receive overruns.
To get Interrupt driven half duplex working I had to make a change to
S32SDK_S32K14x_EAR_0.8.4/platform/drivers/src/lpspi/lpspi_master_driver.c .
In LPSPI_DRV_MasterIRQHandler(), I replaced the existing code:
if(LPSPI_GetStatusFlag(base,LPSPI_RX_DATA_FLAG) && (rxCount != (uint16_t)0))
{
LPSPI_DRV_ReadRXBuffer(instance);
}
...with this:
if (rxCount) {
if (LPSPI_GetStatusFlag(base,LPSPI_RX_DATA_FLAG))
{
LPSPI_DRV_ReadRXBuffer(instance);
}
} else {
/* No RX wanted so clear RX FIFO -- Pontus Hedman 20171031 */
LPSPI_SetFlushFifoCmd(base, false, true);
}
That seemed to take care of IRQ driven code. Meanwhile, to get DMA half duplex with >16 byte transfers working, I had to change LPSPI_DRV_MasterStartTransfer() in the same file. I replaced the existing code:
LPSPI_SetIntMode(base,LPSPI_RECEIVE_ERROR , true);
...with this conditional code:
/* Catch Rx errors only if we actually want to receive -- Pontus Hedman 20171031 */
if (receiveBuffer) {
LPSPI_SetIntMode(base, LPSPI_RECEIVE_ERROR , true);
}
In addition, in my main() of the lpspi_transfer_s32k144 sample project I had to turn on NOSTALL so the SPI doesn't choke when transferring more than 16 bytes (FIFO capacity):
/* Don't stall on Rx FIFO overrun on large DMAs; don't care in half duplex */
LPSPI_Disable(g_lpspiBase[SEND]);
g_lpspiBase[SEND]->CFGR1 |= LPSPI_CFGR1_NOSTALL(1);
LPSPI_Enable(g_lpspiBase[SEND]);
I don't know if this is all the best or even correct solution, but it seems to work. I'm not keen on the NOSTALL since it can cause Tx underruns, but from testing I haven't seen any.
Feedback appreciated...
Hi,
Thanks for your feedback.
We know that is a problem when LPSPI is used in half duplex because overflow interrupt is triggered and transfer fails.
On EAR 0.9.1 this issue will be fixed.
Best regards,
Razvan