AnsweredAssumed Answered

K22 SPI w/ DMA - strange clock/chip select behavior

Question asked by alessandro morniroli on Apr 17, 2015
Latest reply on Jun 3, 2015 by Earl Orlando Ramirez Sanchez

Hi,

 

I'm using a K22 as MCU for my project. I used Processor Expert 10.4.2 to set up the configuration. I've managed to use the SPI peripheral with the DMA support.

 

Basically, I configured the SPI component from the PE with the interrupt service/event flag disabled, and added two different DMA channel for RX/TX.

 

Briefly, the DMA channels in PE are configured as follows:

 

TX channel

- hardware request flag enabled (SPI0_Transmit_DMA_Request);

- channel select fixed (#0);

- source transaction 0 / transaction size 32bit / address adjustment 4 / address modulo disabled

- destination start address 0x4002C034 / transaction size 32bit / address adjustment 0 / address modulo disabled

- transactions count 1 / request count 1 / bandwidth control no stalls / disable peripheral request flag enabled (after transfer complete)

 

/* Channel configuration structure */
DMA_PE_TChnInit const SPI_TX_DMA_PE_ChInit = {
  /* Logical channel number */
  DMA_PE_STATIC_CHANNEL_0,             /* Phy channel: DMA_Channel0 */
  { /* TCD initial settings */
    DMA_SADDR_SADDR(0x00),             /* TCD_SADDR register initial value */
    DMA_SOFF_SOFF(0x04),               /* TCD_SOFF register initial value */
    ( DMA_ATTR_SMOD(0x00) |
      DMA_ATTR_SSIZE(0x02) |
      DMA_ATTR_DMOD(0x00) |
      DMA_ATTR_DSIZE(0x02)
    ),                                 /* TCD_ATTR register initial value */
    DMA_NBYTES_MLNO_NBYTES(0x04),      /* TCD_NBYTES_MLNO register initial value */
    DMA_SLAST_SLAST(0x00),             /* TCD_SLAST register initial value */
    DMA_DADDR_DADDR(0x4002C034),       /* TCD_DADDR register initial value */
    DMA_DOFF_DOFF(0x00),               /* TCD_DOFF register initial value */
    (DMA_CITER_ELINKYES_LINKCH(0x00) | DMA_CITER_ELINKYES_CITER(0x01)), /* TCD_CITER_ELINKYES register initial value */
    DMA_DLAST_SGA_DLASTSGA(0x00),      /* TCD_DLASTSGA register initial value */
    ( DMA_CSR_BWC(0x02) |
      DMA_CSR_MAJORLINKCH(0x00) |
      DMA_CSR_DREQ_MASK |
      DMA_CSR_INTMAJOR_MASK
    ),                                 /* TCD_CSR register initial value */
    (DMA_BITER_ELINKYES_LINKCH(0x00) | DMA_BITER_ELINKYES_BITER(0x01)) /* TCD_BITER_ELINKYES register initial value */
  },
  /* DMAMUX initial settings */
  (DMAMUX_CHCFG_ENBL_MASK | DMAMUX_CHCFG_SOURCE(0x0F)), /* DMA multiplexor configuration register initial value 'SPI0_Transmit_DMA_Request' */
  { /* Events initial setting */
    &SPI_TX_DMA_PE_OnComplete          /* OnComplete callback address */
  },
  FALSE                                /* Channel state variable initial value */
};
          

 

RX channel

- hardware request flag enabled (SPI0_Receive_DMA_Request);

- channel select fixed (#1);

- source transaction 0x4002C038 / transaction size 32bit / address adjustment 0 / address modulo disabled

- destination start address 0/ transaction size 32bit / address adjustment 4 / address modulo disabled

- transactions count 1 / request count 1 / bandwidth control no stalls / disable peripheral request flag enabled (after transfer complete)

 

/* Channel configuration structure */
DMA_PE_TChnInit const SPI_RX_DMA_PE_ChInit = {
  /* Logical channel number */
  DMA_PE_STATIC_CHANNEL_1,             /* Phy channel: DMA_Channel1 */
  { /* TCD initial settings */
    DMA_SADDR_SADDR(0x4002C038),       /* TCD_SADDR register initial value */
    DMA_SOFF_SOFF(0x00),               /* TCD_SOFF register initial value */
    ( DMA_ATTR_SMOD(0x00) |
      DMA_ATTR_SSIZE(0x02) |
      DMA_ATTR_DMOD(0x00) |
      DMA_ATTR_DSIZE(0x02)
    ),                                 /* TCD_ATTR register initial value */
    DMA_NBYTES_MLNO_NBYTES(0x04),      /* TCD_NBYTES_MLNO register initial value */
    DMA_SLAST_SLAST(0x00),             /* TCD_SLAST register initial value */
    DMA_DADDR_DADDR(0x00),             /* TCD_DADDR register initial value */
    DMA_DOFF_DOFF(0x04),               /* TCD_DOFF register initial value */
    (DMA_CITER_ELINKYES_LINKCH(0x00) | DMA_CITER_ELINKYES_CITER(0x01)), /* TCD_CITER_ELINKYES register initial value */
    DMA_DLAST_SGA_DLASTSGA(0x00),      /* TCD_DLASTSGA register initial value */
    ( DMA_CSR_BWC(0x00) |
      DMA_CSR_MAJORLINKCH(0x00) |
      DMA_CSR_DREQ_MASK |
      DMA_CSR_INTMAJOR_MASK
    ),                                 /* TCD_CSR register initial value */
    (DMA_BITER_ELINKYES_LINKCH(0x00) | DMA_BITER_ELINKYES_BITER(0x01)) /* TCD_BITER_ELINKYES register initial value */
  },
  /* DMAMUX initial settings */
  (DMAMUX_CHCFG_ENBL_MASK | DMAMUX_CHCFG_SOURCE(0x0E)), /* DMA multiplexor configuration register initial value 'SPI0_Receive_DMA_Request' */
  { /* Events initial setting */
    &SPI_RX_DMA_PE_OnComplete          /* OnComplete callback address */
  },
  FALSE                                /* Channel state variable initial value */
};
          

 

 

In my SPI driver I use a single buffer for RX/TX.

 

static int32 i32usgBuffer[1024];
   

 

These are the init - read - write functions of my SPI driver (SWIxxx functions are wrappers for FreeRTOS).

 

INIT

 

int8s spi_init (const void* vpParameters)
{
  int8s i8slRes = SPI_ERR_INIT;


  /* Call PE SPI peripheral initialization function. */
  if (SPI_PE_Init    (NULL) &&
  SPI_TX_DMA_PE_Init  (NULL)   &&
  SPI_RX_DMA_PE_Init  (NULL) &&
  !SPI_TX_DMA_PE_Enable (SPI_TX_DMA_PE_DeviceData) &&
  !SPI_RX_DMA_PE_Enable (SPI_RX_DMA_PE_DeviceData) &&
  !SPI_PE_Enable  (SPI_PE_DeviceData))
  {
  SPI_PDD_EnableDmasInterrupts (SPI0_BASE_PTR, SPI_PDD_TX_FIFO_FILL_DMA | SPI_PDD_RX_FIFO_DRAIN_DMA);

  /* No error occurred: initialize peripheral mutex then. */
  if (!tsMutex)
  {
  tsMutex = SWIMutexCreate ();
  }


  /* Initialize RX semaphore (max count 1, initial count 0). */
  if (!tsRxSemaphore)
  {
  tsRxSemaphore = SWISemaphoreCreate (1, 0);
  }


  /* Initialize TX semaphore (max count 1, initial count 1). */
  if (!tsTxSemaphore)
  {
  tsTxSemaphore = SWISemaphoreCreate (1, 1);
  }


  i8slRes = SPI_OK;
  }


  /* Driver initialization succeed. */
  return i8slRes;
}
          

 

READ

 

int16s spi_read (const void *vpParameters, void *vpBuffer, int16u i16upSize)
{
  int16u i16ulI;


  typeSPIChipSelect tlChipSelect;
  int8s  i8ulRes = SPI_OK;


  if (!tsMutex)
  {
  return SPI_ERR_INIT;
  }


  if ((vpParameters != NULL) && (vpBuffer != NULL))
  {
  if (i16upSize)
        {
  if (i16upSize > SPI_BUFFER_SIZE)
  {
  /* Trying to send more bytes than local buffer size. */
  return SPI_ERR_SIZE_OVERFLOW;
  }


  /*
  * Take the peripheral mutex. No checks are performed on return types
  * since it blocks indefinitely.
  */
  SWIMutexTake (tsMutex, SWI_BLOCK_INDEFINITELY);


  /* Wait indefinitely till the the transmitter is ready to be used. */
  SWISemaphoreGet (tsTxSemaphore, SWI_BLOCK_INDEFINITELY);


  /* Retrieve chip select to read. */
  tlChipSelect = *((typeSPIChipSelect*) vpParameters);


  /* Configure SPI transfers (32-bit write access at SPI_PUSHR). */
            for (i16ulI = 0; i16ulI < (i16upSize - 1); i16ulI++)
            {
                i32usgBuffer[i16ulI] = ((int8u*) vpBuffer)[i16ulI] | 0x80000000U | CSToMask[tlChipSelect];
            }


  /* De-assert chip select when sending the last byte */
            i32usgBuffer[i16ulI] = (((int8u*) vpBuffer)[i16ulI] | CSToMask[tlChipSelect]) & 0x7FFFFFFFU;


  /* Configure the transfer. */
            if (SPI_PE_SelectConfiguration (SPI_PE_DeviceData, tlChipSelect, i8usgConfigLookupTab[tlChipSelect]) ||
  SPI_TX_DMA_PE_SetSourceAddress (SPI_TX_DMA_PE_DeviceData, i32usgBuffer)    ||
                SPI_TX_DMA_PE_SetRequestCount   (SPI_TX_DMA_PE_DeviceData, i16upSize)    ||
                SPI_TX_DMA_PE_EnableRequest (SPI_TX_DMA_PE_DeviceData)  ||
  SPI_RX_DMA_PE_SetDestinationAddress (SPI_RX_DMA_PE_DeviceData, i32usgBuffer)    ||
  SPI_RX_DMA_PE_SetRequestCount   (SPI_RX_DMA_PE_DeviceData, i16upSize)  ||
             SPI_RX_DMA_PE_EnableRequest (SPI_RX_DMA_PE_DeviceData))
            {
                /* Set error flag (any value). */
                i8ulRes = 1;
            }
            else
            {
               /* Enable RX and TX interrupts! */
               SPI_PDD_ClearRxFIFO (SPI0_BASE_PTR);
               SPI_PDD_ClearInterruptFlags  (SPI0_BASE_PTR, SPI_PDD_RX_FIFO_DRAIN_INT_DMA);
               SPI_PDD_EnableDmasInterrupts (SPI0_BASE_PTR, SPI_PDD_RX_FIFO_DRAIN_INT_DMA);


               SPI_PDD_ClearTxFIFO (SPI0_BASE_PTR);
               SPI_PDD_ClearInterruptFlags  (SPI0_BASE_PTR, SPI_PDD_TX_FIFO_FILL_INT_DMA);
               SPI_PDD_EnableDmasInterrupts (SPI0_BASE_PTR, SPI_PDD_TX_FIFO_FILL_INT_DMA);


                /*
                 * Take RX semaphore (wait till ISR generate a 'give semaphore'
                 * event, reporting that the block has been received).
                 */
                SWISemaphoreGet (tsRxSemaphore, SWI_BLOCK_INDEFINITELY);
            }


  /* Check if SPI_PE driver failed. */
  if (i8ulRes)
  {
  /* Return 0 bytes read. */
  i16upSize = 0;
  }
  else
            {
  /* Copy from local buffer (32-bit) to destination buffer (8-bit). */
                for (i16ulI = 0; i16ulI < i16upSize; i16ulI++)
                {
                    ((int8u*) vpBuffer)[i16ulI] = (int8u) i32usgBuffer[i16ulI];
                }
            }


  /* Release peripheral mutex. */
  SWIMutexGive (tsMutex);
  }


  /* Return number of bytes read. */
  return i16upSize;
  }


  /* NULL pointer passed as parameters. */
  return SPI_ERR_BAD_PARAMETERS;
}
          

 

WRITE

 

int16s spi_write (const void *vpParameters, const void *vpBuffer, int16u i16upSize)
{
  int16u i16ulI;


  typeSPIChipSelect tlChipSelect;
  int8s  i8ulRes = SPI_OK;


  if (!tsMutex)
  {
  return SPI_ERR_INIT;
  }


  if ((vpParameters != NULL) && (vpBuffer != NULL))
  {
  if (i16upSize)
        {
  if (i16upSize > SPI_BUFFER_SIZE)
  {
  /* Trying to send more bytes than local buffer size. */
  return SPI_ERR_SIZE_OVERFLOW;
  }


  /*
  * Take the peripheral mutex. No checks are performed on return types
  * since it blocks indefinitely.
  */
  SWIMutexTake (tsMutex, SWI_BLOCK_INDEFINITELY);


  /* Wait indefinitely till the the transmitter is ready to be used. */
  SWISemaphoreGet (tsTxSemaphore, SWI_BLOCK_INDEFINITELY);


  /* Retrieve chip select. */
  tlChipSelect = *((typeSPIChipSelect*) vpParameters);


  /* Configure SPI transfers (32-bit write access at SPI_PUSHR). */
            for (i16ulI = 0; i16ulI < (i16upSize - 1); i16ulI++)
            {
                i32usgBuffer[i16ulI] = ((int8u*) vpBuffer)[i16ulI] | 0x80000000U | CSToMask[tlChipSelect];
            }


  /* De-assert chip select when sending the last byte */
            i32usgBuffer[i16ulI] = (((int8u*) vpBuffer)[i16ulI] | CSToMask[tlChipSelect]) & 0x7FFFFFFFU;


  /* Configure the transfer. */
  if (SPI_PE_SelectConfiguration   (SPI_PE_DeviceData, tlChipSelect, i8usgConfigLookupTab[tlChipSelect]) ||
  SPI_TX_DMA_PE_SetSourceAddress (SPI_TX_DMA_PE_DeviceData, i32usgBuffer) ||
  SPI_TX_DMA_PE_SetRequestCount  (SPI_TX_DMA_PE_DeviceData, i16upSize) ||
  SPI_TX_DMA_PE_EnableRequest   (SPI_TX_DMA_PE_DeviceData))
  {
  /* Set error flag (any value). */
  i8ulRes = 1;
  }
  else
  {
  /* Enable TX! */
      SPI_PDD_ClearTxFIFO (SPI0_BASE_PTR);
      SPI_PDD_ClearInterruptFlags  (SPI0_BASE_PTR, SPI_PDD_TX_FIFO_FILL_INT_DMA);
      SPI_PDD_EnableDmasInterrupts (SPI0_BASE_PTR, SPI_PDD_TX_FIFO_FILL_INT_DMA);

  }


  /* Check if SPI_PE driver failed. */
  if (i8ulRes)
  {
  /* Return 0 bytes written. */
  i16upSize = 0;
  }


  /* Release peripheral mutex. */
  SWIMutexGive (tsMutex);
  }


  /* Return number of bytes written. */
  return i16upSize;
  }


  /* NULL pointer passed as parameters. */
  return SPI_ERR_BAD_PARAMETERS;
}
          

 

These are my "on complete" callbacks

 

void SPI_TX_DMA_PE_OnComplete(void* UserDataPtr)
{
  /* Disable TX. */
  SPI_PDD_DisableDmasInterrupts (SPI0_BASE_PTR, SPI_PDD_TX_FIFO_FILL_INT_DMA);


  /* Release TX semaphore: peripheral is ready for new operations). */
  SWISemaphorePutFromISR (tsTxSemaphore);
}
void SPI_RX_DMA_PE_OnComplete(void *UserDataPtr)
{
  /* Disable RX. */
  SPI_PDD_DisableDmasInterrupts (SPI0_BASE_PTR, SPI_PDD_RX_FIFO_DRAIN_INT_DMA);


  /* Release RX semaphore (block received, task can then continue). */
  SWISemaphorePutFromISR (tsRxSemaphore);
}
          

 

The driver seems to works fine, except for the fact that in particular conditions, alternating write/read calls, SPI SCK is clocked with the chip select de-asserted as you can see in the following image.

 

chn-prj-hsrc-vs-poweron-128us-wait2.png

 

Am I missing something (wrong DMA/SPI configuration)? Using the interrupt version w/o the DMA all is working w/o problems.

 

Alessandro

Outcomes