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

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

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

Jump to solution
1,221 Views
alessandromorni
Contributor III

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

Labels (1)
0 Kudos
1 Solution
498 Views
alessandromorni
Contributor III

Maybe I've found the problem: it would be nice if anyone could confirm it.

The problem seems to be the clearing of the RX FIFO: this is done writing 1 to bit CLR_RXF of the SPI MCR register.


This is the description of the register MCR from the datasheet:

Contains bits to configure various attributes associated with the module operations. The

HALT and MDIS bits can be changed at any time, but the effect takes place only on the

next frame boundary. Only the HALT and MDIS bits in the MCR can be changed, while

the module is in the Running state.

The module starts or transitions to Running when all of the following conditions are true:

• SR[EOQF] bit is clear

• MCU is not in the Debug mode or the MCR[FRZ] bit is clear

• MCR[HALT] bit is clear

I changed from

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

to

/* Enter HALT mode in order to clear RX fifo. */

SPI_PDD_EnableHaltMode (SPI0_BASE_PTR, PDD_ENABLE);

/* Wait for RX/TX to finish (eventually). */

while (SPI_PDD_GetTxRxActiveFlag (SPI0_BASE_PTR))

{

    ;

}

/* Clear RX fifo and interrupt flag. */

SPI_PDD_ClearRxFIFO         (SPI0_BASE_PTR);

SPI_PDD_ClearInterruptFlags (SPI0_BASE_PTR, SPI_PDD_RX_FIFO_DRAIN_INT_DMA);

/* Exit from HALT mode. */

SPI_PDD_EnableHaltMode (SPI0_BASE_PTR, PDD_DISABLE);

and everything is working ok now.

View solution in original post

0 Kudos
2 Replies
499 Views
alessandromorni
Contributor III

Maybe I've found the problem: it would be nice if anyone could confirm it.

The problem seems to be the clearing of the RX FIFO: this is done writing 1 to bit CLR_RXF of the SPI MCR register.


This is the description of the register MCR from the datasheet:

Contains bits to configure various attributes associated with the module operations. The

HALT and MDIS bits can be changed at any time, but the effect takes place only on the

next frame boundary. Only the HALT and MDIS bits in the MCR can be changed, while

the module is in the Running state.

The module starts or transitions to Running when all of the following conditions are true:

• SR[EOQF] bit is clear

• MCU is not in the Debug mode or the MCR[FRZ] bit is clear

• MCR[HALT] bit is clear

I changed from

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

to

/* Enter HALT mode in order to clear RX fifo. */

SPI_PDD_EnableHaltMode (SPI0_BASE_PTR, PDD_ENABLE);

/* Wait for RX/TX to finish (eventually). */

while (SPI_PDD_GetTxRxActiveFlag (SPI0_BASE_PTR))

{

    ;

}

/* Clear RX fifo and interrupt flag. */

SPI_PDD_ClearRxFIFO         (SPI0_BASE_PTR);

SPI_PDD_ClearInterruptFlags (SPI0_BASE_PTR, SPI_PDD_RX_FIFO_DRAIN_INT_DMA);

/* Exit from HALT mode. */

SPI_PDD_EnableHaltMode (SPI0_BASE_PTR, PDD_DISABLE);

and everything is working ok now.

0 Kudos
498 Views
EarlOrlando
Senior Contributor II

Hi Alessandro,

Your implementation is OK because if you do not disable the transactions (halt register) when the RX FIFO is not full the DMA does a transaction to the RX FIFO, this causes that the RX FIFO never is empty and the SPI is sending data until the RX FIFO is empty so you would have been always sending data.

Regards,

Earl.