AnsweredAssumed Answered

SPI TX omits data

Question asked by stimpy on Jul 26, 2017
Latest reply on Aug 3, 2017 by stimpy

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

Outcomes