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

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

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

Jump to solution
769 Views
jeremygordon
Contributor II

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

  }

}

Labels (1)
0 Kudos
1 Solution
507 Views
Hui_Ma
NXP TechSupport
NXP TechSupport

Hi,

If customer set PCS to SCK Delay (tCSC) 3.11us, which will be exists during each SPI frame transfer (8 bits).

So, customer see "512 groups of eight bit pulses separated by 3.11us gaps" is reasonable.

Please check below picture from K20_72MHz reference manual for the detailed info:

1.png

2.png

For you are using the interrupt mode to handle the SPI TX/RX, the ISR routine will take 12 system clock cycles to enter into ISR routine.

The ISR routine code (assembly instruction) length will decide how many cycles take in interrupt service routine.

It will take us level to do the SPI interrupt service.

When change to use DMA module, which will reduce the core work load and enhance the SPI TX/RX efficiency.

I think you also check the KSDK SPI example code with <dspi_edma_blocking> demo with 18MHz SPI baud rate. Right?

Wish it helps.


Have a great day,
Ma Hui

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

View solution in original post

0 Kudos
2 Replies
508 Views
Hui_Ma
NXP TechSupport
NXP TechSupport

Hi,

If customer set PCS to SCK Delay (tCSC) 3.11us, which will be exists during each SPI frame transfer (8 bits).

So, customer see "512 groups of eight bit pulses separated by 3.11us gaps" is reasonable.

Please check below picture from K20_72MHz reference manual for the detailed info:

1.png

2.png

For you are using the interrupt mode to handle the SPI TX/RX, the ISR routine will take 12 system clock cycles to enter into ISR routine.

The ISR routine code (assembly instruction) length will decide how many cycles take in interrupt service routine.

It will take us level to do the SPI interrupt service.

When change to use DMA module, which will reduce the core work load and enhance the SPI TX/RX efficiency.

I think you also check the KSDK SPI example code with <dspi_edma_blocking> demo with 18MHz SPI baud rate. Right?

Wish it helps.


Have a great day,
Ma Hui

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

0 Kudos
507 Views
jeremygordon
Contributor II

Thank you, this is helpful.

I have successfully implemented "bare metal" SPI with DMA following the code in the Kinetis SDK as an example. My code uses three DMA channels, one for receive, and two for transmit. The receive channel is pretty basic, but as per the code in the Kinetis SDK I am using channel linking to transfer my 8-bit data to a temporary location where the data and SPI command flags are combined. The result is then DMA'd to the PUSHR register. I am now able to set the PCSSCK dividers to their fastest possible setting and am getting seeing much better performance.

Thank you!

0 Kudos