SPI slave DMA chip select restart

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

SPI slave DMA chip select restart

Jump to solution
1,715 Views
Daan_Amsterdam
Contributor I

Hi, I'm currently working on a project where my MCU (LPC55S06) has an SPI slave interface which is used for > 1 MBit/sec datatransfers. To decrease system load I am using the fsl_spi_dma.c / .h modules.

In my code I enable the chip-select deassert IRQ, initialize the SPI peripheral in slave mode with DMA enabled, and wait for the master to start a transfer.

The master (not shared in this code) is periodically sending 12 bytes of data (every 500 ms). From the slave, it initially receives the expected data ("ABCDEFGHIJKL", see slave example code below).

The slave will respond on the master deasserting the chip select by calling the 'spi_slave_deasserted_isr' function, setting the spi_dma_handle to idle, rxInProgress to false, aborting the DMA transfer (also tried without calling this function) and calling the 'spis_transfer_cb' (which is also used as the DMA interrupt function), directly restarting a new SPI slave transfer. (see 'spis_transfer_cb' function)

The master will after 500 ms send 12 bytes of data again,however, now the response which the master receives is as follows:

"\0\0\0\0\0\0\0\0ABCD" (8 x 0, then the ABCD).

This also happens the third, fourth, fifth, ... time.

When I reset my slave MCU, the master again received "ABCDEFGHIJKL", after which the problem gets reintroduced.

I've decided to take a look on my oscilloscope, also showing that only the first slave transfer is actually transmitting "ABCDEFGHIJKL", after which "\0\0\0\0\0\0\0\0ABCD" is reintroduced.
I'm looking for a logical explanation on why this is happening, however cannot find one yet. Anyone who could help me out?

Thanks in advance!

 

#include <fsl_dma.h>
#include <fsl_spi_dma.h>

#include "main.h"

#include "log.h"

#ifndef CONFIG_SPIM_FLEXCOMM
#define CONFIG_SPIM_FLEXCOMM 3
#endif

#ifndef CONFIG_SPIS_DMA
#define CONFIG_SPIS_DMA DMA0
#endif

#ifndef CONFIG_SPIS_RX_DMA_CHANNEL
#define CONFIG_SPIS_RX_DMA_CHANNEL  8
#endif

#ifndef CONFIG_SPIS_TX_DMA_CHANNEL
#define CONFIG_SPIS_TX_DMA_CHANNEL	9
#endif

#define SPI_VA(arg)				SPI ## arg
#define SPI_VA2(arg)            SPI_VA(arg)
#define SPI_IF_BASE				SPI_VA2(CONFIG_SPIM_FLEXCOMM)

static spi_dma_handle_t spis_handle;
static dma_handle_t spis_tx_dma_handle;
static dma_handle_t spis_rx_dma_handle;

static void (*usercallback)(size_t remainingbytes, status_t status, void * userdata);

static void spis_transfer_cb(SPI_Type *base, spi_dma_handle_t *handle, status_t status, void *userdata);

static void spi_slave_deasserted_isr(SPI_Type *base, spi_dma_handle_t *handle) {
//	LOG_INFO("Statusregister: %02x", base->STAT);
	if (base->STAT & SPI_STAT_SSD_MASK) {

		// Clear mask
		base->STAT ^= ~SPI_STAT_SSD_MASK;

		// Disable Slave Select deassert interrupt
		base->INTENCLR |= SPI_INTENCLR_SSDEN_MASK;

		// Clear FIFO's
		base->FIFOCFG |= SPI_FIFOCFG_EMPTYTX_MASK | SPI_FIFOCFG_EMPTYRX_MASK;
		base->FIFOSTAT |= SPI_FIFOSTAT_TXERR_MASK | SPI_FIFOSTAT_RXERR_MASK;

		// Empty txFIFO is confirmed. Disable IRQs and restore triggers values
		base->FIFOINTENCLR = SPI_FIFOINTENCLR_RXLVL_MASK | SPI_FIFOINTENCLR_TXLVL_MASK;
		base->FIFOTRIG     = (base->FIFOTRIG & (~(SPI_FIFOTRIG_RXLVL_MASK | SPI_FIFOTRIG_RXLVL_MASK))) |
		                         SPI_FIFOTRIG_RXLVL(0) | SPI_FIFOTRIG_TXLVL(0);

		// Set idle state and call user callback
		handle->state = (uint32_t)kStatus_SPI_Idle;
		handle->rxInProgress = false;
		SPI_MasterTransferAbortDMA(CONFIG_SPIS_DMA,  handle);
		if(handle->callback != NULL) {
			handle->callback(base, handle, handle->state, handle->userData);
		}

		// SPI_SlaveTransferAbortDMA(SPI_IF_BASE, handle);
		
		spis_transfer_cb(SPI_IF_BASE, &spis_handle, 0, spis_handle.userData);
	}
}

static void SPI_SlaveTransferSetCustomIRQHandler(SPI_Type *base, spi_dma_handle_t *handle) {
	FLEXCOMM_SetIRQHandler(base, (flexcomm_irq_handler_t) spi_slave_deasserted_isr, handle);
}

void spis_init(void (*callback)(size_t remainingbytes, status_t status, void * userdata), void * userdata) {
    spi_slave_config_t user_config;

    SPI_SlaveGetDefaultConfig(&user_config);
	user_config.polarity = kSPI_ClockPolarityActiveLow,
	user_config.phase = kSPI_ClockPhaseFirstEdge,
    user_config.sselPol = (spi_spol_t) kSPI_SpolActiveAllLow;

    SPI_SlaveInit(SPI_IF_BASE, &user_config);

    /* DMA init */
    DMA_Init(CONFIG_SPIS_DMA);

    /* configure channel/priority and create handle for TX and RX. */
    DMA_EnableChannel(CONFIG_SPIS_DMA, CONFIG_SPIS_TX_DMA_CHANNEL);
    DMA_EnableChannel(CONFIG_SPIS_DMA, CONFIG_SPIS_RX_DMA_CHANNEL);
    DMA_SetChannelPriority(CONFIG_SPIS_DMA, CONFIG_SPIS_TX_DMA_CHANNEL, kDMA_ChannelPriority0);
    DMA_SetChannelPriority(CONFIG_SPIS_DMA, CONFIG_SPIS_RX_DMA_CHANNEL, kDMA_ChannelPriority1);
    DMA_CreateHandle(&spis_tx_dma_handle, CONFIG_SPIS_DMA, CONFIG_SPIS_TX_DMA_CHANNEL);
    DMA_CreateHandle(&spis_rx_dma_handle, CONFIG_SPIS_DMA, CONFIG_SPIS_RX_DMA_CHANNEL);

    usercallback = callback;

    SPI_SlaveTransferSetCustomIRQHandler(SPI_IF_BASE,
			&spis_handle);

    SPI_SlaveTransferCreateHandleDMA(SPI_IF_BASE,
    		&spis_handle,
			spis_transfer_cb,
			userdata,
			&spis_tx_dma_handle,
			&spis_rx_dma_handle);

	// todo -> generate FLEXCOMM# using macro token concatenation
    (void)EnableIRQ(FLEXCOMM3_IRQn);
}

void spis_transfer(const void * txdata, void * rxdata, size_t n) {
	spi_transfer_t slave_transfer;
	slave_transfer.dataSize = n;
	slave_transfer.rxData = rxdata;
	slave_transfer.txData = (void*) txdata;

//	LOG_DEBUG_ARRAY("txdata?", txdata, 10);
	slave_transfer.configFlags = 0x0;

	// Enable Slave Select deassert interrupt
	SPI_IF_BASE->INTENSET |= SPI_INTENSET_SSDEN_MASK;

	if (kStatus_Success != SPI_SlaveTransferDMA(SPI_IF_BASE, &spis_handle, &slave_transfer))
	{
		LOG_ERROR("There is an error when start SPI_SlaveTransferDMA \r\n");
		return;
	}
}

static uint8_t mosi_data[26];

static void spis_transfer_cb(SPI_Type *base, spi_dma_handle_t *handle, status_t status, void *userdata)
{
	// directly start a new transfer
	spis_transfer("ABCDEFGHIJKLMNOPQRSTUVWXYZ",
				mosi_data,
				26);

	// todo -> dispatch event to task to process mosi-data

	//  size_t remaining = 0;
	//	SPI_SlaveTransferGetCountDMA(base, handle, &remaining);
	//	usercallback(remaining, status, userdata);
}

static void my_slave_example_code(void) {
	spis_init(NULL, NULL);

	// start the first transfer
	spis_transfer("ABCDEFGHIJKLMNOPQRSTUVWXYZ",
				mosi_data,
				26);

	for(;;) {
		vTaskDelay(pdMS_TO_TICKS(100));
	}
}

 

0 Kudos
Reply
1 Solution
1,687 Views
Daan_Amsterdam
Contributor I

Hi Alice, solved by aborting the DMA before clearing the SPI fifos. None of the demos in the SDK have an implementation where the slave aborts on the chip select signal.

View solution in original post

0 Kudos
Reply
2 Replies
1,688 Views
Daan_Amsterdam
Contributor I

Hi Alice, solved by aborting the DMA before clearing the SPI fifos. None of the demos in the SDK have an implementation where the slave aborts on the chip select signal.

0 Kudos
Reply
1,691 Views
Alice_Yang
NXP TechSupport
NXP TechSupport

Hello,

How about first test the spi_dma_slave demo under SDK?

 

BR

Alice

0 Kudos
Reply