5 Replies Latest reply on Nov 15, 2019 12:46 PM by user_1669321

    Unable to properly setup DMA channels for SPI operation

    user_1669321

      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

        • 1. Re: Unable to properly setup DMA channels for SPI operation
          RodolfoG_11

          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.

          1 of 1 people found this helpful
          • 2. Re: Unable to properly setup DMA channels for SPI operation
            user_1669321

            Thanks, Rodolfo.

             

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

            • 3. Re: Unable to properly setup DMA channels for SPI operation
              user_1669321

              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);

              }

              • 4. Re: Unable to properly setup DMA channels for SPI operation
                user_1669321

                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.

                • 5. Re: Unable to properly setup DMA channels for SPI operation
                  user_1669321

                  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);