AnsweredAssumed Answered

K20(72mhz) can I do a 18mhz SPI transfer?

Question asked by Jeremy Gordon on Sep 1, 2015
Latest reply on Sep 7, 2015 by Jeremy Gordon

Hi All,

 

I'm writing some "from scratch" SPI based SD card code (well, from scratch = FatFS + InitSPI via PE). My code (below) is driven off of a TX/RX ISR with a lw_event to signal my control logic when a transfer is complete (i.e. when the receive fifo is empty following a transfer).

 

At a 400khz rate in my CTAR0 with the fastest possible PCSSCK dividers, everything works great and I'm able to reset the card into SPI mode, and get all the way through the sequence of commands to get to 512 block reads of my 4GB SD card.

 

If I switch to an 18mhz rate in CTAR1, everything still works fine as long as my PCSSCK delay is at least 3.11us. For a 512 byte block, with continuous CS, and a reasonably tight ISR, I would have expected to see a 18mhz-ish stream of 512*8 clocks on my SPI CLK line with a 3.11us delay at the start of the transfer but otherwise a continuous and steady stream of clocks .

 

Instead, I see 512 groups of eight bit pulses separated by 3.11us gaps; nowhere near an 18mhz transfer rate for my 512 byte block.

 

If I try to set the the PCSSCK low like it is for my 400khz rate, I immediately start getting SPI_SR_RFOF errors in my TX/RX ISR.

 

Does it sounds reasonable that the 3.11us delay the time is what my ISR requires to be able to get around to sampling the RX fifo after filling up the TX fifo? It doesn't look like the example code in the Kinetis SDK has this kind of timing problem.

 

Also, it feels like with continuous CS in SPI mode "0, 0", the 3.11us delay should only be at the start of my transfer, not in front of each frame (i.e. one delay per PUSHR).

 

Any thoughts from those with high speed SPI experience? Is this where I absolutely need DMA?

 

Thanks!

 

Jeremy

 

void SPI0_isr(void* unused) {

  uint32_t StatReg = SPI_PDD_GetInterruptFlags(SPI0_BASE_PTR); /* Read status register */

  uint16_t Data;                       /* Temporary variable for data */

 

  if ((StatReg & SPI_PDD_RX_FIFO_OVERFLOW_INT) != 0U) {

    SEGGER_RTT_printf(0, "overflow\n");

    SPI_PDD_DisableDmasInterrupts(SPI0_BASE_PTR, SPI_PDD_RX_FIFO_DRAIN_INT_DMA); /* Disable RX interrupt */

    SPI_PDD_DisableDmasInterrupts(SPI0_BASE_PTR, SPI_PDD_TX_FIFO_FILL_INT_DMA); /* Disable TX interrupt */

    SPI_PDD_ClearInterruptFlags(SPI0_BASE_PTR, SPI_PDD_RX_FIFO_DRAIN_INT_DMA); /* Clear Rx FIFO drain flags */

    SPI_PDD_ClearInterruptFlags(SPI0_BASE_PTR, SPI_PDD_TX_FIFO_FILL_INT_DMA); /* Clear Tx FIFO fill flags */

    SPI_PDD_ClearInterruptFlags(SPI0_BASE_PTR, SPI_PDD_RX_FIFO_OVERFLOW_INT); /* Clear error flags */

    _err = 1;

    _inDataLen = _outDataLen = 0;

    _lwevent_set(&spiTransferDone, 1);

    return;

  }

  while ((StatReg & SPI_PDD_RX_FIFO_DRAIN_INT_DMA) != 0U) { /* Is any char in HW buffer? */

    Data = (uint16_t)SPI_PDD_ReadPopRxFIFOReg(SPI0_BASE_PTR); /* Read character from receiver */

    SPI_PDD_ClearInterruptFlags(SPI0_BASE_PTR, SPI_PDD_RX_FIFO_DRAIN_INT_DMA); /* Clear Rx FIFO drain flags */

    if( _inDataLen != 0 ) {

      _inDataLen--;

      *(_inDataPtr++) = (uint8_t)Data;

      if( _inDataLen == 0 ) {

        SPI_PDD_DisableDmasInterrupts(SPI0_BASE_PTR, SPI_PDD_RX_FIFO_DRAIN_INT_DMA); /* Disable RX interrupt */

        _lwevent_set(&spiTransferDone, 1);

      }

    }

    if( _inDataLen == 0 ) {

      break;

    }

    StatReg = SPI_PDD_GetInterruptFlags(SPI0_BASE_PTR); /* Read status register */

  }

  while ((StatReg & SPI_PDD_TX_FIFO_FILL_INT_DMA) != 0U) { /* Is HW buffer empty? */

    if ( _outDataLen != 0 ) { /* Is number of sent characters less than the number of requested incoming characters? */

      _outDataLen--;

      uint32_t txCommand =

        (_highSpeed ? 0x10000000 : 0x0) |

        ((_continuous && (_outDataLen != 0)) ? 0x80000000 : 0x0 ) |

        (_assertPCS ? 0x10000 : 0x0) |

        (_outDataLen == 0 ? 0x08000000 : 0x0);

      SPI_PDD_WriteMasterPushTxFIFOReg(SPI0_BASE_PTR, (uint32_t)(*(_outDataPtr++) | txCommand));

      SPI_PDD_ClearInterruptFlags(SPI0_BASE_PTR, SPI_PDD_TX_FIFO_FILL_INT_DMA); /* Clear Tx FIFO fill flags */

      if( _outDataLen == 0 ) {

        SPI_PDD_DisableDmasInterrupts(SPI0_BASE_PTR, SPI_PDD_TX_FIFO_FILL_INT_DMA); /* Disable TX interrupt */

      }

    }

    if( _outDataLen == 0 ) {

      break;

    }

    StatReg = SPI_PDD_GetInterruptFlags(SPI0_BASE_PTR); /* Read status register */

  }

}

Outcomes