improving SPI timings

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

improving SPI timings

Jump to solution
2,628 Views
gschelotto
Contributor V

Hi,

I'm trying to transmit some bytes through SPI as fast as possible. To do it I use DMA transfers with this setting:

  • MKL17Z running at 48MHz (Bus clock: 24MHz)
  • SDK v2.3.0
  • SPI: baud rate 1MHz and Slave Select as GPIO

With this configuration I'm able to see this timing.

Screenshot from 2018-05-02 12-03-51.png

After setting the Slave Select to Low state I immediately initiate a SPI_MasterTransferDMA call. Then I wait for the sending completion.

How can I do to improve -reduce- the time (18us) from the Slave Select to the first pulse of SPI clock?

regards,

gaston

1 Solution
2,248 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, Gaston,

As you know the KSDK code is ineffcient, if you want to remove the delay, you can not use SPI and DMA driver, but you can develop code based KSDK architecture, remove spi/dma driver, write the spi/dma register directly. For simplicity, you can use SPI interrupt mechanism to develop the code without DMA, pls have a try.

unfortunately, I have not source code for the SPI plus DMA without KSDK.

Hope it can help you

BR

Xiangjun Rong

View solution in original post

0 Kudos
10 Replies
1,763 Views
MVWare
Contributor II

Hi, Im facing the same slow timing performance.
My case is a LPC55S69 running at 150MHz.  SDK:  2.12.0
I need 3byte SPI out put to a DAC-device with an upfdate rate of 100ksmps. im timing the SPI progress with an IO toggle. This shows an execution time of 2us for a SPI_WriteData() instruction.  incredible long for a request to write a byte to the SPI TX register !
Using 20MHz SPI devices has no sense with this slow performance of the code...
I tried:  polling, transfere and interrupt mode.  All slow..
Now I can try DMA, hoping this does not block the main program for so much time  (3x 2us   in a 10us loop is a lot !)
DMA should leave the core running its application, I expect?   Am I right?
Or are there other improvements tips?  Like smart inline direct register examples to get that byte transmitted.

0 Kudos
1,759 Views
MVWare
Contributor II
What I did now: Modify the SDK overhead in this function. Just make it direct to FC3 which I use.
1,485 Views
Oussama1
Contributor I

Hi, I have the same issue can you elaborate a bit more on how to implent this solution?

0 Kudos
2,249 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, Gaston,

As you know the KSDK code is ineffcient, if you want to remove the delay, you can not use SPI and DMA driver, but you can develop code based KSDK architecture, remove spi/dma driver, write the spi/dma register directly. For simplicity, you can use SPI interrupt mechanism to develop the code without DMA, pls have a try.

unfortunately, I have not source code for the SPI plus DMA without KSDK.

Hope it can help you

BR

Xiangjun Rong

0 Kudos
2,248 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, Gaston,

I have downloaded the SDK_2.3.1_FRDM-KL27Z.zip and unzip it, use the example:

C:\SDK_2.3.1_FRDM_KL27Z\boards\frdmkl27z\driver_examples\spi\dma_b2b_transfer\master

In order to check the spi timing continuously, I change the spi_dma_b2b_transfer_master.c as following, but I do not observe the timing as your figure above.

For your figure, one /CS cycle covers three Bytes in other words 24 spi clcok cycles, but running the example code, I observe that one /CS cycle covers only ONE byte, do you modify the example code?

Secondly, I do not observe the long delay after the /CS falling edge.

I attach the KSDK package, pls run the example code.

BR

Xiangjun Rong

/*
 * The Clear BSD License
 * Copyright (c) 2015, Freescale Semiconductor, Inc.
 * Copyright 2016-2017 NXP
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted (subject to the limitations in the disclaimer below) provided
 * that the following conditions are met:
 *
 * o Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * o Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * o Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE.
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "fsl_spi_dma.h"
#include "fsl_dmamux.h"
#include "board.h"
#include "fsl_debug_console.h"

#include "pin_mux.h"
#include "clock_config.h"
/*******************************************************************************
 * Definitions
 ******************************************************************************/
#define EXAMPLE_SPI_MASTER SPI0
#define EXAMPLE_DMA DMA0
#define EXAMPLE_DMAMUX DMAMUX0
#define EXAMPLE_SPI_TX_CHANNEL 0U
#define EXAMPLE_SPI_RX_CHANNEL 1U
#define EXAMPLE_SPI_TX_SOURCE kDmaRequestMux0SPI0Tx
#define EXAMPLE_SPI_RX_SOURCE kDmaRequestMux0SPI0Rx
#define EXAMPLE_SPI_MASTER_SOURCE_CLOCK kCLOCK_BusClk
#define EXAMPLE_SPI_MASTER_CLK_FREQ CLOCK_GetFreq(kCLOCK_BusClk)

/*******************************************************************************
 * Prototypes
 ******************************************************************************/

/*******************************************************************************
 * Variables
 ******************************************************************************/
#define BUFFER_SIZE (64)
static uint8_t srcBuff[BUFFER_SIZE];
static uint8_t destBuff[BUFFER_SIZE];
static spi_dma_handle_t s_handle;
static dma_handle_t txHandle;
static dma_handle_t rxHandle;
static volatile bool masterFinished = false;

/*******************************************************************************
 * Code
 ******************************************************************************/
static void masterCallback(SPI_Type *base, spi_dma_handle_t *handle, status_t status, void *userData)
{
    masterFinished = true;
}

int main(void)
{
    spi_transfer_t xfer = {0};
    spi_master_config_t userConfig;
    uint8_t i = 0;
    uint32_t err = 0;
    uint32_t srcFreq = 0;

    BOARD_InitPins();
    BOARD_BootClockRUN();
    BOARD_InitDebugConsole();
    PRINTF("\n\rMaster Start...\n\r");

    /* Init DMAMUX */
    DMAMUX_Init(EXAMPLE_DMAMUX);
    DMAMUX_SetSource(EXAMPLE_DMAMUX, EXAMPLE_SPI_TX_CHANNEL, EXAMPLE_SPI_TX_SOURCE);
    DMAMUX_SetSource(EXAMPLE_DMAMUX, EXAMPLE_SPI_RX_CHANNEL, EXAMPLE_SPI_RX_SOURCE);
    DMAMUX_EnableChannel(EXAMPLE_DMAMUX, EXAMPLE_SPI_TX_CHANNEL);
    DMAMUX_EnableChannel(EXAMPLE_DMAMUX, EXAMPLE_SPI_RX_CHANNEL);

    /* Init the DMA module */
    DMA_Init(EXAMPLE_DMA);
    DMA_CreateHandle(&txHandle, EXAMPLE_DMA, EXAMPLE_SPI_TX_CHANNEL);
    DMA_CreateHandle(&rxHandle, EXAMPLE_DMA, EXAMPLE_SPI_RX_CHANNEL);

    /* Init SPI */
    /*
     * userConfig->enableStopInWaitMode = false;
     * userConfig->polarity = kSPI_ClockPolarityActiveHigh;
     * userConfig->phase = kSPI_ClockPhaseFirstEdge;
     * userConfig->direction = kSPI_MsbFirst;
     * userConfig->dataMode = kSPI_8BitMode;
     * userConfig->txWatermark = kSPI_TxFifoOneHalfEmpty;
     * userConfig->rxWatermark = kSPI_RxFifoOneHalfFull;
     * userConfig->pinMode = kSPI_PinModeNormal;
     * userConfig->outputMode = kSPI_SlaveSelectAutomaticOutput;
     * userConfig->baudRate_Bps = 500000U;
     */
    SPI_MasterGetDefaultConfig(&userConfig);
    srcFreq = EXAMPLE_SPI_MASTER_CLK_FREQ;
    SPI_MasterInit(EXAMPLE_SPI_MASTER, &userConfig, srcFreq);

    /* Init Buffer */
    for (i = 0; i < BUFFER_SIZE; i++)
    {
        srcBuff[i] = i;
    }

    /* Send to slave */
    xfer.txData = srcBuff;
    xfer.rxData = destBuff;
    xfer.dataSize = BUFFER_SIZE;
    SPI_MasterTransferCreateHandleDMA(EXAMPLE_SPI_MASTER, &s_handle, masterCallback, NULL, &txHandle, &rxHandle);
    SPI_MasterTransferDMA(EXAMPLE_SPI_MASTER, &s_handle, &xfer);

    while (masterFinished != true)
    {
    }

    /* Check if the data is right */
    for (i = 0; i < BUFFER_SIZE; i++)
    {
        if (srcBuff[i] != destBuff[i])
        {
            err++;
            PRINTF("The %d is wrong! data is %d\n\r", i, destBuff[i]);
        }
    }
    if (err == 0)
    {
        PRINTF("Succeed!\n\r");
    }

    while (1)
    {
        SPI_MasterTransferDMA(EXAMPLE_SPI_MASTER, &s_handle, &xfer);
        while (masterFinished != true)
        {
        }
       // SPI_MasterTransferDMA(EXAMPLE_SPI_MASTER, &s_handle, &xfer);

    }
}

0 Kudos
2,248 Views
gschelotto
Contributor V

Hi Xiangjun,

Yes indeed, in your code after the /CS falling edge the clock starts to run immediately. This is so in the case of a Slave Select Automatic Output configuration (default) where you can see one \CS cycle every sent byte. However, in my application I have to activate \CS and keep it in low state after sending the complete frame (3 bytes) as I've mentioned in the first post. To do it I've had to modify the code as follows:

Before the SPI initialization (SPI_MasterInit)

masterConfig.outputMode = kSPI_SlaveSelectAsGpio;

After SPI initialization I set PTC4 (/CS) as GPIO

gpio_pin_config_t ss;
ss.outputLogic = 1;
ss.pinDirection = kGPIO_DigitalOutput;
PORT_SetPinMux(PORTC, 4U, kPORT_MuxAsGpio);
GPIO_PinInit(GPIOC, 4U, &ss);‍‍

Then I start the sending

xfer.txData = srcBuff;xfer.rxData = destBuff;
//xfer.dataSize = BUFFER_SIZE;xfer.dataSize = 3;
SPI_MasterTransferCreateHandleDMA(EXAMPLE_SPI_MASTER, &s_handle, masterCallback, NULL, &txHandle, &rxHandle);
GPIO_PortClear(GPIOC, 1u << 4U); // CS: 0
SPI_MasterTransferDMA(EXAMPLE_SPI_MASTER, &s_handle, &xfer);
while (masterFinished != true);
GPIO_PortSet(GPIOC, 1u << 4U); // CS: 1

Here's the captured frame using this settings:

pic_325_1_b.gif

Could you test this in your environment? I wonder  what is the reason for this time (~24us)?

regards,

gaston

0 Kudos
2,248 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, Gaston,

I think firmware executing time leads to the delay. If you set the SlaveSelectAutomaticOutput, the SPI master drives the /CS pin automatically by hardware, of course, there is no delay.

if you use GPIO mode, you clear the GPIO yourself, then call the SPI_MasterTransferDMA(), the delay time is the time the KL17 execute the api function SPI_MasterTransferDMA(), you can set another GPIO pin before the SPI_MasterTransferDMA() and clear the GPIO after SPI_MasterTransferDMA(), then compare the timing.

Hope it can help you

BR

Xiangjun rong

0 Kudos
2,248 Views
gschelotto
Contributor V

Hi Xiangjun,

I've been testing the driver more deeply. The delay does not related to /CS and no matter if it configured as GPIO or SS Automatic Output (default). You will always have a delay of ~24us from the API call to the first clock pulse. From my point of view this delay time is excessive, considering I'm running at a maximum speed of 48MHz (Bus clock 24MHz). Is there any way to reduce this time? I'd like to know your opinion.

regards,
gaston

0 Kudos
2,248 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, Gaston,

I still think the SPI_MasterTransferDMA() takes a long time, you can test with the code:


xfer.txData = srcBuff;xfer.rxData = destBuff;
//xfer.dataSize = BUFFER_SIZE;xfer.dataSize = 3;
SPI_MasterTransferCreateHandleDMA(EXAMPLE_SPI_MASTER, &s_handle, masterCallback, NULL, &txHandle, &rxHandle);
GPIO_PortClear(GPIOC, 1u << 4U); // CS: 0

//Rong Wrote: Set another GPIO for example GPIOB0 pin
SPI_MasterTransferDMA(EXAMPLE_SPI_MASTER, &s_handle, &xfer);
//Rong Wrote: clear the GPIOB0
while (masterFinished != true);
GPIO_PortSet(GPIOC, 1u << 4U); // CS: 1

Then you can compare the timing for the GPIOB0 and  GPIOC4 to know the time of the


If the SPI_MasterTransferDMA()is the root cause of the long delay, I suggest you do not use SDK code, 
write the SPI, DMA module register directly. in the DMA ISR, you can clear interrupt flag, write BCR value, 
set the /CS GPIO, delay some time, clear the /CS GPIO, write one 
Byte to SPI data register, enable DMA..., i think you can eliminate the delay.
Hope it can help you
BR
Xiangjun Rong


0 Kudos
2,248 Views
gschelotto
Contributor V

Hi Xiangjun,

I've set PTA13 (blue LED in FRDM) instead PTB0. Indeed, the SPI_MasterTransferDMA()is the cause of the 23.5us delay.

Selection_003.png

Could you give me some example of writing SPI directly from ISR? I don't understand your point.

regards,

gaston

0 Kudos