Unable to properly setup DMA channels for SPI operation

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
user_1669321
Level 5
Level 5
100 replies posted 50 replies posted 25 replies posted

Hi,

I need to setup my DMA transfers, but I'm not used to the X loops/Y loops nomenclature of the PSoC 6.

What I want is:

One SPI_TX_BUF_EMPTY interrupt to trigger one byte transfer by the TX DMA channel, for up to 512 bytes, then deactivate the channel.

One SPI_RX_BUF_NOT_EMPTY interrupt to trigger one byte transfer by the RX DMA channel, for up to 512 bytes, then deactivate the channel.

For this, I have setup:

SPI RX FIFO level = 0, rx output connected to RX_DMA input trigger

SPI TX FIFO level = 1, tx output connected to TX_DMA input trigger

DMA_RX

Trigger input type: One transfer per trigger

Trigger deactivation and triggering: Retrigger immediately (pulse trigger)

Data element size: Byte

Src and Dest width: byte to byte

X loop size = 1          // will be changed dynamically

X loop src inc = 0

X loop dst inc = 1

Y loop size = 1

Y loop src inc = 0

Y lop dst inc = 0

DMA_TX

Trigger input type: One transfer per trigger

Trigger deactivation and triggering: Retrigger immediately (pulse trigger)

Data element size: Byte

Src and Dest width: byte to byte

X loop size = 1          // will be changed dynamically

X loop src inc = 1      // will be changed dynamically

X loop dst inc = 0

Y loop size = 1

Y loop src inc = 0

Y lop dst inc = 0

I need to have a variable transaction size, thus I have the two following functions:

int8_t SpiDma_Write(uint8_t *buf, uint16_t len) {

    if (len == 0) {

        return -1;

    }

    NVIC_EnableIRQ(SPI_TX_DMA_isr_cfg.intrSrc);

    SPI_TX_DMA_SetXloopDataCount(&SPI_TX_DMA_Descriptor, len);

    SPI_TX_DMA_SetXloopSrcIncrement(&SPI_TX_DMA_Descriptor, 1);

    SPI_TX_DMA_Start((const void *) buf, (const void *) SPI_HW->TX_FIFO_WR);

    return 0;

}

int8_t SpiDma_Read(uint8_t *buf, uint16_t len) {

    if (len == 0) {

        return -1;

    }

    NVIC_EnableIRQ(SPI_RX_DMA_isr_cfg.intrSrc);

    SPI_TX_DMA_SetXloopDataCount(&SPI_TX_DMA_Descriptor, len);

    SPI_RX_DMA_SetXloopDataCount(&SPI_RX_DMA_Descriptor, len);

    SPI_TX_DMA_SetXloopSrcIncrement(&SPI_TX_DMA_Descriptor, 0);

    SPI_RX_DMA_Start((const void *) SPI_HW->RX_FIFO_RD, (const void *) buf);

    SPI_TX_DMA_Start((const void *) &_spiReadToken, (const void *) SPI_HW->TX_FIFO_WR);

    return 0;

}

Conceptually, this makes sense to me. I change the X loop length, then start the transaction. For the read, I remove the increment of the tx, as I'm just sending a dummy variable. However, in Cy_DMA_Descriptor_SetXloopSrcIncrement(), I fail the assert:

CY_ASSERT_L1(CY_DMA_SINGLE_TRANSFER != Cy_DMA_Descriptor_GetDescriptorType(descriptor));

I have tried using the Y loop to trigger "len" X loops of 1, but then I fail another assert.

Also, it seems that the max transfer length is of 256 elements. How can I deal with transfers of 512 bytes?

Thank you,

Fred

0 Likes
1 Solution

My error was that I forgot the ampersand before SPI_HW->TX_FIFO_WR.

SPI_TX_DMA_Start((const void *) &_spiReadToken, (const void *) SPI_HW->TX_FIFO_WR);

instead of

SPI_TX_DMA_Start((const void *) &_spiReadToken, (const void *) &SPI_HW->TX_FIFO_WR);

View solution in original post

5 Replies
RodolfoGL
Employee
Employee
250 solutions authored 250 sign-ins 5 comments on KBA

Every time you access any peripheral FIFO, you have to setup to be a WORD, even though the FIFO generates 8-bit data.

Therefore, the RX DMA should be WORD to BYTE and the TX DMA should be BYTE to WORD.

Thanks, Rodolfo.

Although this doesn't remedy my problem, it is an error that I missed.

0 Likes

It seems that setting the X loop and Y loop sizes to 256 in the Top Design fixes the problem. Setting them to 1 was causing the CY_ASSERT fail. By setting them to a high value in the Top Design, I can then use SPI_xx_DMA_SetXloopDataCount as I need.

However, I'm now unable to trigger the DMA interrupts. I also added an SPI_DONE interrupt, but that doesn't get triggered as well. I tried changing the interrupt of the DMA channels to trigger on every element transfer instead of on completion of descriptor chain, but that didn't do anything. The interrupts just don't get triggered.

To get over the maximum value of 256 elements, I managed a scheme that uses 2 descriptors chained together. Here's my code, is there anything wrong with it?

main.c

void main (void) {

    Cy_SysDisableCM4();

    SystemInit();

    __enable_irq(); /* Enable global interrupts. */

    SpiDma_Config();

    SpiDma_Sleep();

    CyDelay(1000);

    SpiDma_Start();

    uint8_t buffer[16] = {0x00};

    while(SpiDma_Read(buffer, 16) == 0) {    // We wait for either -1 or 1

        /* some stuff */

    }

}

Configuration

int8_t SpiDma_Config(void) {

    SPI_Start();

    SPI_Disable();

   

    SPI_HW->INTR_M_MASK = SCB_INTR_M_SPI_DONE_Msk;

    Cy_SysInt_Init(&SPI_isr_cfg, &_Spi_SpiDoneIsr);

    NVIC_DisableIRQ(SPI_isr_cfg.intrSrc);

   

    _SpiDma_ConfigRxDma();

    _SpiDma_ConfigTxDma();

   

    return 0;

}

int8_t _SpiDma_ConfigRxDma (void) {

    Cy_SysInt_Init(&SPI_RX_DMA_isr_cfg, &_SpiDma_RxDoneIsr);

    NVIC_DisableIRQ(SPI_RX_DMA_isr_cfg.intrSrc);

    Cy_DMA_Channel_SetInterruptMask(SPI_RX_DMA_HW, SPI_RX_DMA_DW_CHANNEL, SPI_RX_DMA_INTR_MASK);

    SPI_RX_DMA_Init();

    SPI_RX_DMA_Stop();

    return 0;

}

int8_t _SpiDma_ConfigTxDma (void) {

    Cy_SysInt_Init(&SPI_TX_DMA_isr_cfg, &_SpiDma_TxDoneIsr);

    NVIC_DisableIRQ(SPI_TX_DMA_isr_cfg.intrSrc);

    Cy_DMA_Channel_SetInterruptMask(SPI_TX_DMA_HW, SPI_TX_DMA_DW_CHANNEL, SPI_TX_DMA_INTR_MASK);

    SPI_TX_DMA_Init();

    SPI_TX_DMA_Stop();

    return 0;

}

int8_t SpiDma_Start(void) {

    SPI_Start();

    NVIC_EnableIRQ(SPI_isr_cfg.intrSrc);

    return 0;

}

int8_t SpiDma_Sleep(void) {

    SPI_Disable();

    SPI_TX_DMA_Stop();

    SPI_RX_DMA_Stop();

    NVIC_DisableIRQ(SPI_RX_DMA_isr_cfg.intrSrc);

    NVIC_DisableIRQ(SPI_TX_DMA_isr_cfg.intrSrc);

    NVIC_DisableIRQ(SPI_isr_cfg.intrSrc);

    return 0;

}

Read functions

int8_t SpiDma_Read(uint8_t *buf, uint16_t len) {

    if ((len == 0) || (len > 512)) {    // transactions over 512 bytes are not supported right now

        return -1;

    }

    if (!_rxDmaDone) {

        return 0;

    }

    if (_rxDmaStatus) {

        int8_t status = _rxDmaStatus;

        _rxDmaStatus = 0;

        return status;

    }

    _rxDmaDone = 0;

    _rxDmaStatus = 0;

    SPI_ClearRxFifo();

    SPI_ClearTxFifo();

    _SpiDma_ConfigRxTransaction(buf, len);

    NVIC_EnableIRQ(SPI_RX_DMA_isr_cfg.intrSrc);

    SPI_RX_DMA_Start((const void *) SPI_HW->RX_FIFO_RD, (const void *) buf);

    SPI_TX_DMA_Start((const void *) &_spiReadToken, (const void *) SPI_HW->TX_FIFO_WR);

    return 0;

}

void _SpiDma_ConfigRxTransaction(uint8_t *buf, uint16_t len) {

    // Max transfer length per X loop is 256

    if (len <= 256) {

        // TX

        SPI_TX_DMA_SetXloopSrcIncrement(&SPI_TX_DMA_First256BytesDescriptor, 0);

        SPI_TX_DMA_SetXloopDataCount   (&SPI_TX_DMA_First256BytesDescriptor, len);

        SPI_TX_DMA_SetNextDescriptor(NULL);  // Only run one descriptor

       

        // RX

        SPI_RX_DMA_SetXloopDataCount(&SPI_RX_DMA_First256BytesDescriptor, len);

        SPI_RX_DMA_SetNextDescriptor(NULL);  // Only run one descriptor

    } else {

        // TX

        SPI_TX_DMA_SetXloopSrcIncrement(&SPI_TX_DMA_First256BytesDescriptor, 0);

        SPI_TX_DMA_SetXloopSrcIncrement(&SPI_TX_DMA_Last256BytesDescriptor , 0);

       

        SPI_TX_DMA_SetXloopDataCount(&SPI_TX_DMA_First256BytesDescriptor,     256);

        SPI_TX_DMA_SetXloopDataCount(&SPI_TX_DMA_Last256BytesDescriptor , len-256);

       

        SPI_TX_DMA_SetSrcAddress(&SPI_TX_DMA_Last256BytesDescriptor, (const void *) &_spiReadToken);

        SPI_TX_DMA_SetDstAddress(&SPI_TX_DMA_Last256BytesDescriptor, (const void *) SPI_HW->TX_FIFO_WR);

       

        SPI_TX_DMA_SetNextDescriptor(&SPI_TX_DMA_Last256BytesDescriptor);

       

        // RX       

        SPI_RX_DMA_SetXloopDataCount(&SPI_RX_DMA_First256BytesDescriptor,     256);

        SPI_RX_DMA_SetXloopDataCount(&SPI_RX_DMA_Last256BytesDescriptor , len-256);

       

        SPI_RX_DMA_SetSrcAddress(&SPI_RX_DMA_Last256BytesDescriptor, (const void *) SPI_HW->RX_FIFO_RD);

        SPI_RX_DMA_SetDstAddress(&SPI_RX_DMA_Last256BytesDescriptor, (const void *) &buf[256]);

       

        SPI_RX_DMA_SetNextDescriptor(&SPI_RX_DMA_Last256BytesDescriptor);

    }

    SPI_TX_DMA_SetDescriptor(&SPI_TX_DMA_First256BytesDescriptor);

    SPI_RX_DMA_SetDescriptor(&SPI_RX_DMA_First256BytesDescriptor);

}

static void _SpiDma_RxDoneIsr(void) {

    /* Clear rx DMA interrupt. */

    Cy_DMA_Channel_ClearInterrupt(SPI_RX_DMA_HW, SPI_RX_DMA_DW_CHANNEL);

   

    /* Check interrupt cause to capture errors. */

    if (CY_DMA_INTR_CAUSE_COMPLETION    != Cy_DMA_Channel_GetStatus(SPI_RX_DMA_HW, SPI_RX_DMA_DW_CHANNEL)&&

       (CY_DMA_INTR_CAUSE_CURR_PTR_NULL != Cy_DMA_Channel_GetStatus(SPI_RX_DMA_HW, SPI_RX_DMA_DW_CHANNEL)))

    {

        /* DMA error occured during RX operations */

        _rxDmaStatus = -1;

    } else {

        _rxDmaStatus = 1;

    }

    _rxDmaDone = 1;

}

static void _Spi_SpiDoneIsr(void) {

     uint32_t masterStatus;

   

    /* Check the status of master */

    masterStatus  = Cy_SCB_SPI_GetSlaveMasterStatus(SPI_HW);

   

    if(masterStatus == CY_SCB_SPI_MASTER_DONE) {

        _spiDone = 1;

    } else {

        _spiDone = -1;

    }

    Cy_SCB_SPI_ClearSlaveMasterStatus(SPI_HW, masterStatus);

}

0 Likes

I changed my SpiDma_Read function so that the write is handled by the SPI module itself (By using SPI_WriteArray(txBuf, 16);), and now I get into the RX_DMA_isr.

This means that my DMA_TX setup is wrong. I'll investigate it.

0 Likes

My error was that I forgot the ampersand before SPI_HW->TX_FIFO_WR.

SPI_TX_DMA_Start((const void *) &_spiReadToken, (const void *) SPI_HW->TX_FIFO_WR);

instead of

SPI_TX_DMA_Start((const void *) &_spiReadToken, (const void *) &SPI_HW->TX_FIFO_WR);