SPI TX omits data

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

SPI TX omits data

1,119 Views
stimpy
Contributor III

Hello,

We are on a problem that the SPI module seems not to receive all data thus we never end waiting for it. This happens on heavy load (i.e. lot of data has to be send to different participants on SPI).

For this a MK66FN2M0VLQ18 (Mask 0N65N) is configured with RTX. Many other modules are running on the MCU (CAN0, CAN1, UART, ADC0, ADC1, Ports, etc.).

The code keeps track of how much data has been written to the PUSHR and how many bytes have been collected from the POPR. When the problem occurs the data we have pushed out is one more than the data we have popped. The TCR is always the same number as the push-counter.

Checking on the bus how much data actually have been transmitted it reveals sometimes the data was not on the bus, so there is really one byte missing. However sometimes it was there and in case it was one of the very latest bytes it is missing in the RX FIFO.

Many thanks in advance for any tips.

Regards Dani

Stripped code looks like this

#include <MK66F18.h>

...

void D_SPI_IRQHandler( T_D_SPI_IF * spi, const uint32_t fifoLength )
{
  // links to common used data
  SPI_Type* spiBase = spi->base_ptr;                        // SPI base pointer
  volatile uint32_t* statRegPtr = &(spiBase->SR);           // SPI status register

  // handle errors
  if( (*statRegPtr & SPI_SR_RFOF_MASK) ) {          // receive FIFO overflow set?
    *statRegPtr = SPI_SR_RFOF_MASK;                   // clear it
    ERROR_MSG("SPI%d RX-Overflow. Is SPI FIFO length properly set?\n", spi->id);
  }

  // handle rx
  D_SPI_irqRxHandler( spi, spiBase );
  
  // end of communication
  D_SPI_irqEoCHandler( spi, spiBase );
  
  // handle tx
  D_SPI_irqTxHandler( spi, spiBase, fifoLength );
}

void D_SPI_irqRxHandler( T_D_SPI_IF * spi, SPI_Type* spiBase) {
  volatile uint32_t* statRegPtr = &(spiBase->SR);  
  uint32_t *rxBuf = &(spi->rxBuffer[spi->rxBufferLength]);    // rx Buffer
  
  while(*statRegPtr & SPI_SR_RFDF_MASK ) {                    // as long as there is data
    uint16_t data = spi->base_ptr->POPR;                        // get data from buffer
    *statRegPtr = SPI_SR_RFDF_MASK;                             // clear receive fifo drain flag
    if( spi->rxBufferLength >= ELEMENTS_OF(spi->rxBuffer) ) {   // if we have no space left in buffer
      ERROR_MSG("SPI %d, RxBuf Overflow", spi->id );              // report it
    } else {
      *(rxBuf++) = data;                                        // fill up buffer
    }
    spi->rxBufferLength++;
  }
}

void D_SPI_irqTxHandler( T_D_SPI_IF * spi, SPI_Type* spiBase, const uint32_t fifoLength) {
  volatile uint32_t* statRegPtr = &(spiBase->SR);
  
  // new comm?
  if(!spi->inCom) {
    T_U_RingBuffer_32bit_Elem data;
    spi->rxLen = 0;
    // Read first element of ringbuffer containing the transfer length
    if( E_U_RingBuffer_RS_OK == E_U_RingBuffer_32bit_takeOut( &spi->txBuf, &data ) ) {
      spi->rxLen = data;
    }
    spi->txDone = 0;
    spiBase->RSER &= ~SPI_RSER_TFFF_RE_MASK;                 // disable tx interrupt, only needed once to enter IRQ TX
  }
  
  T_U_RingBuffer_32bit_Elem txCommand;
  while( *statRegPtr & SPI_SR_TFFF_MASK  &&                         // as long as tx fifo not full
          spi->txDone < spi->rxLen       &&                         // and we still have something left to send in active communication
          spi->txDone - spi->rxBufferLength < fifoLength            // rx Fifo has enough space for on-the-way data
        ) {
    spi->inCom = 1;
    E_U_RingBuffer_32bit_takeOut( &spi->txBuf, &txCommand );  // take tx Data
    spi->base_ptr->PUSHR = txCommand;                         // and write it to bus
    *statRegPtr = SPI_SR_TFFF_MASK;                           // clear Tx FIFO fill flag
    spi->txDone++;
  }
}

void D_SPI_irqEoCHandler( T_D_SPI_IF * spi, SPI_Type* spiBase) {
  if( spi->inCom ) {
    if ( spi->rxLen == spi->rxBufferLength ) {
      T_D_SPI_Callback* func;
      void* ctx;

      E_U_RingBuffer_RS rsF = E_U_RingBuffer_32bit_takeOut( &spi->txBuf, (uint32_t*) &func );
      E_U_RingBuffer_RS rsC = E_U_RingBuffer_32bit_takeOut( &spi->txBuf, (uint32_t*) &ctx );

      if( E_U_RingBuffer_RS_OK == rsF && func != NULL && E_U_RingBuffer_RS_OK == rsC ) {
          func(ctx, spi->id, spi->rxBuffer, spi->rxBufferLength );
      }
      spi->base_ptr->SR |= SPI_SR_EOQF_MASK;                      // clear end of queue flag
      spi->rxBufferLength=0;
      spi->inCom  = 0;                                            // remember that spi is free

      int len = E_U_RingBuffer_32bit_length(&spi->txBuf);
      if( len >= 4 ) {        // new data in queue
          spiBase->MCR |= SPI_MCR_HALT_MASK;                        // make module configuration register writable
          spiBase->MCR |= SPI_MCR_CLR_TXF_MASK |                    // clear tx fifo
                          SPI_MCR_CLR_RXF_MASK;                     // clear rx fifo
          T_U_RingBuffer_32bit_Elem elem;
          E_U_RingBuffer_32bit_at( &spi->txBuf, 1, &elem );
          uint32_t pcs = count_trailing_zeros((SPI_PUSHR_PCS_MASK & elem) >> SPI_PUSHR_PCS_SHIFT); 
          spiBase->CTAR[0] = spi->devices[pcs].CTARn.reg32;         // setup timings and configuration for this channel
          spiBase->MCR &= (uint32_t)~(uint32_t)(SPI_MCR_HALT_MASK); // lock configuration again
      } else if ( len > 0 ) {
          ERROR_MSG("Invalid TX Buffer size %d",len);
      }
    }
  }
}

E_D_SPI_Err D_SPI_Init_LL(   uint8_t interface,
                          E_D_SPI_SM masterSlave, uint8_t priority)
{
  // create an alias
  T_D_SPI_IF *spi = &D_SPI_interfaces[interface];
  spi->id = interface;
  spi->rxBufferLength = 0;
  
  // enable clock to SPI module
  uint8_t nvicP;
  switch( interface )
  {
    case 0 :
      SIM->SCGC6 |= SIM_SCGC6_SPI0_MASK;
      nvicP = SPI0_IRQn;
      break;
    case 1 :
      SIM->SCGC6 |= SIM_SCGC6_SPI1_MASK;
      nvicP = SPI1_IRQn;
      break;
    case 2 :
      SIM->SCGC3 |= SIM_SCGC3_SPI2_MASK;
      nvicP = SPI2_IRQn;
      break;
    
    default :
      ERROR_MSG("Unknown SPI %d", interface);
      return E_D_SPI_Err_UkwnIf;
  }
  
  spi->base_ptr = SPI_BASE_PTRS_AR[interface];
  
  nvicP += NVIC_OFFSET;
  uint32_t nvic_idx = nvicP/32;
  uint32_t nvic_bit = nvicP%32;
  NVIC->ICPR[nvic_idx] |= (1<< nvic_bit);                          // clear any pending interrupts
  NVIC->ISER[nvic_idx] |= (1<< nvic_bit);                          // enable interrupt for GPIO-modul
  
  NVIC->IP[nvicP] = (priority & 0xFF) << 4;  // set priority (higher nibble only)

  spi->base_ptr->MCR =   SPI_MCR_MSTR_MASK              |     // enable master mode
                         SPI_MCR_PCSIS(0x00)            |     // set inactive state to PCS to 0=low/1=high
                         SPI_MCR_ROOE_MASK              |     // enable retrieving incoming data
                         SPI_MCR_SMPL_PT(0x00)          |     // controls sample point (only valid on CPHA = 0)
                         SPI_MCR_CLR_TXF_MASK           |     // clear tx fifo
                         SPI_MCR_CLR_RXF_MASK           |     // clear rx fifo
                         SPI_MCR_HALT_MASK;                   // put transfers on hold
  
  spi->base_ptr->SR =    SPI_SR_TCF_MASK                |     // clear transfer complete flag
                         SPI_SR_EOQF_MASK               |     // clear end of tx queue underflow flag
                         SPI_SR_TFUF_MASK               |     // clear tx fifo underflow flag
                         SPI_SR_TFFF_MASK               |     // clear tx fifo full flag
                         SPI_SR_RFOF_MASK               |     // clear rx fifo overflow flag
                         SPI_SR_RFDF_MASK               |     // clear rx fifo not empty flag
                         SPI_SR_TXCTR(0x00)             |     // clear tx fifo counter
                         SPI_SR_TXNXTPTR(0x00)          |     // clear tx next pointer
                         SPI_SR_RXCTR(0x00)             |     // clear rx fifo counter
                         SPI_SR_POPNXTPTR(0x00)         |     // clear rx next pointer
                         0x00200000U;
  
  spi->base_ptr->RSER =  SPI_RSER_RFDF_RE_MASK          |     // enable IRQ that reports RX fifo not empty
                         SPI_RSER_RFOF_RE_MASK;               // enable IRQ that reports RX overflow
  
  spi->base_ptr->MCR &= (uint32_t)~(uint32_t)(SPI_MCR_HALT_MASK);
  
  spi->inCom = 0;
  
  return E_D_SPI_Err_Ok;
}

E_D_SPI_Err D_SPI_transfer( uint8_t interface, E_D_SPI_PCS pcs, uint16_t * data, uint16_t length, T_D_SPI_Callback cbFunc, void * cbCtx)
{  
  if( length == 0 )
    return E_D_SPI_Err_Length;

  // create an alias
  T_D_SPI_IF *spi = &D_SPI_interfaces[interface];

  EnterCritical();

  if( E_U_RingBuffer_32bit_spaceLeft( &spi->txBuf ) < (length+3) )
  {
    ExitCritical();
    return E_D_SPI_Err_Busy;
  }
  
  uint32_t spiInUse = E_U_RingBuffer_32bit_length( &spi->txBuf );
  
  // set basic register for all transfers
  uint32_t dataFlags = 0U;                          // keep PCS signal asserted
    // find out which Chip-Selects have to be used
  uint8_t pcsNum = getPcsNumber(pcs);

  if( ! spiInUse ) {
    // config SPI periphery for CPOL and CPHA
    spi->base_ptr->MCR |= SPI_MCR_HALT_MASK;
    spi->base_ptr->MCR |= SPI_MCR_CLR_TXF_MASK |  // clear tx fifo
                          SPI_MCR_CLR_RXF_MASK;   // clear rx fifo
    spi->base_ptr->CTAR[0] = spi->devices[pcsNum].CTARn.reg32;
    spi->base_ptr->MCR &= (uint32_t)~(uint32_t)(SPI_MCR_HALT_MASK);
    
    // trigger an interrupt for pushing our data to the SPI module
    spi->base_ptr->SR   |= SPI_SR_TXRXS_MASK;
    spi->base_ptr->RSER |= SPI_RSER_TFFF_RE_MASK;
  }
 
  // fillup buffer with transfer data
  E_U_RingBuffer_32bit_insert( &spi->txBuf, (T_U_RingBuffer_32bit_Elem) length );
  for(int i=0;i<length;i++)
  {
    // push prepared register data to buffer that includes
    if( E_U_RingBuffer_RS_Full == E_U_RingBuffer_32bit_insert( &spi->txBuf, 
        (T_U_RingBuffer_32bit_Elem) (
          data[i]                                                   |   // actual bus data
          dataFlags                                                 |   // PCS configuration
          ((i==(length-1))?SPI_PUSHR_EOQ_MASK:SPI_PUSHR_CONT_MASK)  |   // end of transfer OR PCS signal continuous asserted
          ((i==0)?SPI_PUSHR_CTCNT_MASK:0)                               // clear transfer counter (for first bit only)
        ) ) 
      )
    {
      ERROR_MSG("FATAL: SPI queue overflow!");                          // should never happen due to interrupt EnterCritical
      ExitCritical();
      return E_D_SPI_Err_Length;
    }
  }
  
  E_U_RingBuffer_32bit_insert( &spi->txBuf, (T_U_RingBuffer_32bit_Elem) cbFunc); 
  E_U_RingBuffer_32bit_insert( &spi->txBuf, (T_U_RingBuffer_32bit_Elem) cbCtx); 

  ExitCritical();
  return E_D_SPI_Err_Ok;
}
Labels (1)
2 Replies

720 Views
stimpy
Contributor III

Hi Ma Hui,

Many thanks for your response. In the mean time further attempts have been done to find a solution. We figured out that disabling ROOE in MCR register (Receive FIFO Overflow Overwrite Enable) seems to solve the problem.

However this looks bizarre because this seems to have nothing to do with our TX problem. Furthermore the overflow was never set in SR and the data we receive has a checksum that would allow us to detect if some data was missing. 

Has anyone had similar problems with the ROOE bit set?

Regards 

Dani

0 Kudos

720 Views
Hui_Ma
NXP TechSupport
NXP TechSupport

Hi,

I would recommend customer to refer MCUXpresso SDK for FRDM-K66F board DSPI driver example, which located at below path:

..\SDK_2.2_FRDM-K66F\boards\frdmk66f\driver_examples\dspi\interrupt

The DSPI interrupt service routine is below for your reference:

void EXAMPLE_DSPI_MASTER_IRQHandler(void)
{
    if (masterRxCount < TRANSFER_SIZE)
    {
        while (DSPI_GetStatusFlags(EXAMPLE_DSPI_MASTER_BASEADDR) & kDSPI_RxFifoDrainRequestFlag)
        {
            masterRxData[masterRxCount] = DSPI_ReadData(EXAMPLE_DSPI_MASTER_BASEADDR);
            ++masterRxCount;

            DSPI_ClearStatusFlags(EXAMPLE_DSPI_MASTER_BASEADDR, kDSPI_RxFifoDrainRequestFlag);

            if (masterRxCount == TRANSFER_SIZE)
            {
                break;
            }
        }
    }

    if (masterTxCount < TRANSFER_SIZE)
    {
        while ((DSPI_GetStatusFlags(EXAMPLE_DSPI_MASTER_BASEADDR) & kDSPI_TxFifoFillRequestFlag) &&
               ((masterTxCount - masterRxCount) < masterFifoSize))
        {
            if (masterTxCount < TRANSFER_SIZE)
            {
                EXAMPLE_DSPI_MASTER_BASEADDR->PUSHR = masterCommand | masterTxData[masterTxCount];
                ++masterTxCount;
            }
            else
            {
                break;
            }

            /* Try to clear the TFFF; if the TX FIFO is full this will clear */
            DSPI_ClearStatusFlags(EXAMPLE_DSPI_MASTER_BASEADDR, kDSPI_TxFifoFillRequestFlag);
        }
    }

    /* Check if we're done with this transfer.*/
    if ((masterTxCount == TRANSFER_SIZE) && (masterRxCount == TRANSFER_SIZE))
    {
        /* Complete the transfer and disable the interrupts */
        DSPI_DisableInterrupts(EXAMPLE_DSPI_MASTER_BASEADDR,
                               kDSPI_RxFifoDrainRequestInterruptEnable | kDSPI_TxFifoFillRequestInterruptEnable);
    }
}


Have a great day,
Ma Hui

-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------

0 Kudos