NRF5340DK High frequency SPIM communication failure / spi_transceive() delay problem

Hello,

I am trying to exchange data with an external slave sensor via SPIM communication using the nRF5340DK.

I am currently facing two main issues:

1.The spi_transceive() function takes too long to execute.

This is significantly reducing the effective data transfer rate.

I have also reviewed a Q&A from someone who pointed out a similar delay with spi_transceive().

Time gap between each spi_transceive )

However, the solution suggested in that Q&A, which involved modifying spi configuration in the main.c, doesn't seem to be applicable to the nRF5340 as it resulted in an error

To clearly explain my situation, I have attached my main.c, overlay, and configuration files. 

main.c

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/spi.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/sys/printk.h>
#include <zephyr/drivers/gpio.h>
#include <stdio.h>

/* Get SPI device from devicetree */
#define SPI_DEVICE_NODE DT_ALIAS(spi4_basic)
static const struct device *spi_dev = DEVICE_DT_GET(SPI_DEVICE_NODE);

static const struct device *uart_for_data_tx = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));

#define SW0_NODE     DT_ALIAS(sw0)
static const struct gpio_dt_spec cs = GPIO_DT_SPEC_GET(SW0_NODE, gpios);


static struct spi_config spi_cfg = {
    .frequency = 1000000,           // SPI frequency.
    .operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8) | SPI_TRANSFER_MSB, // SPI Mode 0
    .slave = 0,                     // Master mode
    .cs = NULL,
    //.cs = &spi_cs,                // Manage CS pin with cs-gpios in Devicetree
};


/* RHD2000 command definitions (16-bit) */
#define RHD_CMD_CONVERT(channel) (uint16_t)((((uint16_t)(channel) & 0x3F) << 8)) 
// Input: 00RR RRRR 0000 0000 
// Output: DDDD DDDD DDDD DDDD
#define RHD_CMD_WRITE(reg, data) (uint16_t)(0x8000 | (((uint16_t)(reg) & 0x3F) << 8) | ((uint16_t)(data) & 0xFF)) 
// Input: 10RR RRRR DDDD DDDD
// Output: 1111 1111 DDDD DDDD , D: same with input's D (for confirm)
#define RHD_CMD_READ(reg)        (uint16_t)(0xC000 | (((uint16_t)(reg) & 0x3F) << 8))
// Input: 11RR RRRR 0000 0000
#define RHD_CMD_CALIBRATE        (uint16_t)(0x5500) // 0101 0101 0000 0000 
#define RHD_CMD_DUMMY_READ       RHD_CMD_READ(63) //


static int rhd_spi_transfer(uint16_t tx_command, uint8_t *rx_buffer) {
    uint8_t tx_buffer[2];

    // Convert 16-bit command to a Big-Endian byte array
    tx_buffer[0] = (uint8_t)(tx_command >> 8);  // MSB
    tx_buffer[1] = (uint8_t)(tx_command & 0xFF); // LSB

    const struct spi_buf tx_spi_buf = {
        .buf = tx_buffer,
        .len = sizeof(tx_buffer)
    };
    const struct spi_buf_set tx_spi_bufs = {
        .buffers = &tx_spi_buf,
        .count = 1
    };

    // Directly store received data in the rx_buffer passed from main
    struct spi_buf rx_spi_buf = {
        .buf = rx_buffer,
        .len = 2
    };
    const struct spi_buf_set rx_spi_bufs = {
        .buffers = &rx_spi_buf,
        .count = 1
    };

    while (1) {

        gpio_pin_set_dt(&cs, 1); // CS -> 0

        int err = spi_transceive(spi_dev, &spi_cfg,
            &tx_spi_bufs, &rx_spi_bufs); 

        gpio_pin_set_dt(&cs, 0);

        return 0;
    }
}

int main(void)
{
    int ret;
    int err;
    ret = gpio_pin_configure_dt(&cs, GPIO_OUTPUT_INACTIVE);
    gpio_pin_set_dt(&cs, 0); // I still don't understand why the output is 1 when 0 is passed to the set function.

    if (!device_is_ready(spi_dev)) {
        printk("SPI device %s is not ready!\n", spi_dev->name);
        return 0;
    }

    if (!device_is_ready(uart_for_data_tx)) {
        printk("UART device for data TX (%s) is not ready!\n", uart_for_data_tx->name);
        return 0;
    }

    printk("nRF5340 SPIM example started.\n");
    printk("Reading MISO pin (connected to DC voltage) and sending to PC via UART every second.\n");


    uint8_t rx_main_buffer[2];

    err = rhd_spi_transfer(RHD_CMD_WRITE(0, 0xDE), rx_main_buffer); // ADC configuration, disable fast settle
    if (err) return err;


    while (1) {
        /* SPI communication (transceive) - send dummy bytes via MOSI to read MISO value */
        rhd_spi_transfer(RHD_CMD_READ(0) , rx_main_buffer);
    }
    return 0;
};

app.overlay

/ {

    chosen {
        zephyr,console = &uart0;    
        zephyr,shell-uart = &uart0; 
    };

    aliases {
        spi4-basic = &spi4;
    };

};




&uart0 {
    status = "okay";             
    current-speed = <1000000>;    
};


&pinctrl {
    spi4_custom_pins: spi4_custom_pins { 
        group1 {
            psels = <NRF_PSEL(SPIM_SCK,  1, 15)>,   
                    <NRF_PSEL(SPIM_MOSI, 1, 14)>,  
                    <NRF_PSEL(SPIM_MISO, 1, 13)>;  
        };
    };


    spi4_custom_pins_sleep: spi4_custom_pins_sleep {
         group1 {
            psels = <NRF_PSEL(SPIM_SCK,  1, 15)>,
                    <NRF_PSEL(SPIM_MOSI, 1, 14)>,
                    <NRF_PSEL(SPIM_MISO, 1, 13)>;
            low-power-enable; 
        };
    };
};


&spi4 {
    compatible = "nordic,nrf-spim";
    status = "okay"; 
    pinctrl-0 = <&spi4_custom_pins>; 
    pinctrl-1 = <&spi4_custom_pins_sleep>; 
    pinctrl-names = "default", "sleep";   
    cs-gpios = <&gpio1 12 GPIO_ACTIVE_LOW>;
    //max-frequency = <560000>;
    //easydma-maxcnt-bits = <32>;
};

prj.conf

# enable console
CONFIG_CONSOLE=y
CONFIG_UART_CONSOLE=y

CONFIG_SERIAL=y
CONFIG_PRINTK=y


CONFIG_SPI=y

CONFIG_GPIO=y 

In main.c, when I configure the SPI structure, I set the frequency to 1MHz.

I have confirmed with an oscilloscope that the actual clock frequency is indeed 1MHz.

Also, I'm controlling the CS pin directly via GPIO

2. Although the nRF5340 specifications state that it supports SPI frequencies up to 8MHz (and even up to 32MHz for spi4),

the quality of the Clock (CLK) signal degrades significantly at frequencies higher than 4MHz.

Thank you.

Best regards, 

gwan0624

Parents
  • Here is a possible strategy for reducing the delay using (G)PPI. 

    (Note: I sometimes use PPI and GPPI interchangeably. GPPI is a wrapper library that unifies PPI, DPPI, and the newer PPI variants introduced for multicore systems.)

    Take a look at
    ncs\modules\hal\nordic\nrfx\samples\src\nrfx_gppi\one-to-one

    This sample sets up a timer together with GPIOTE to generate a kind of PWM signal.

    As you know, the (G)PPI (Generic Programmable Peripheral Interconnect) can be used to connect events and tasks between different peripherals.

    For example, when generating PWM you can use a timer event to toggle a GPIO.

    In your case, you probably want to connect an SPI event (e.g. “transfer done”) directly to an SPI task (e.g. “start transfer”).

    If the SPI transfer happens too quickly, you could instead trigger the SPI transfer from a timer event.

    The point here is to avoid waiting for the CPU (running an OS) to notice that an SPI transfer has finished, then prepare the next one, and finally send it. By wiring events and tasks directly in hardware through GPPI, you get immediate peripheral-to-peripheral triggering without CPU involvement.

    Looking again at
    ncs\modules\hal\nordic\nrfx\samples\src\nrfx_gppi\one_to_one\main.c
    At the end of the file you’ll see:

        nrfx_gppi_channel_endpoints_setup(gppi_channel,
            nrfx_timer_compare_event_address_get(&timer_inst, NRF_TIMER_CC_CHANNEL0),
            nrfx_gpiote_out_task_address_get(&gpiote_inst, OUTPUT_PIN));
     
        nrfx_gppi_channels_enable(BIT(gppi_channel));

    What we want to do is replace the GPIOTE task with an SPI start task.

    If you open
    ncs\modules\hal\nordic\nrfx\drivers\include\nrfx_spim.h
    and search for "address_get(", you’ll see several helper functions. These are designed to fetch the hardware addresses of peripheral tasks and events, so they can be used with (G)PPI.

    In particular:

    • nrfx_spim_start_task_address_get() is very useful here.

    • nrfx_spim_end_event_address_get() can also be handy if you later want to chain “transfer done” → “start transfer.”

    For a first step, I would suggest taking the one-to-one example and replacing the GPIOTE task with nrfx_spim_start_task_address_get(). This way, you can make SPI transactions start directly on a timer event. Then you can freely control how often/how fast by adjusting the timer.

  • Thank you very much for taking the time to help me.

    My plan was as follows:

    1.Before using DPPI as you advised, I first wanted to try using the nrfx_spim_xfer() function directly, which appears to be for direct SPI driver access, rather than the spi_transceive() function that seems to be used with the Zephyr RTOS.

    2.After that, I intended to move on to the code using GPPI.

    ---------------

    1. Results from using nrfx_spim_xfer()


    #include <zephyr/kernel.h>
    #include <stdio.h>
    
    #include <zephyr/sys/printk.h>
    #include <zephyr/device.h>
    
    #include <zephyr/drivers/uart.h>
    #include <zephyr/drivers/gpio.h>
    #include <zephyr/drivers/spi.h>
    
    #include <nrfx_spim.h>
    
    
    
    #define RHD_CMD_CONVERT(channel) (uint16_t)((((uint16_t)(channel) & 0x3F) << 8)) 
    // Input: 00RR RRRR 0000 0000 
    // Output: DDDD DDDD DDDD DDDD
    #define RHD_CMD_WRITE(reg, data) (uint16_t)(0x8000 | (((uint16_t)(reg) & 0x3F) << 8) | ((uint16_t)(data) & 0xFF)) 
    // Input: 10RR RRRR DDDD DDDD
    // Output: 1111 1111 DDDD DDDD , D: same with input's D (for confirm)
    #define RHD_CMD_READ(reg)        (uint16_t)(0xC000 | (((uint16_t)(reg) & 0x3F) << 8))
    // Input: 11RR RRRR 0000 0000
    #define RHD_CMD_CALIBRATE        (uint16_t)(0x5500) // 0101 0101 0000 0000 
    #define RHD_CMD_DUMMY_READ       RHD_CMD_READ(63) //
    
    
    
    
    /* =================================================================== */
    
    static const nrfx_spim_t spim_inst = NRFX_SPIM_INSTANCE(4);
    
    K_SEM_DEFINE(sem_data_ready, 0, 1);
    
    
    
    static int rhd_nrfx_spi_transfer_blocking(uint16_t tx_command, uint8_t *rx_buffer)
    {
        uint8_t tx_b[2];
        tx_b[0] = (tx_command >> 8) & 0xFF; // MSB
        tx_b[1] = tx_command & 0xFF;        // LSB
        nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_XFER_TRX(tx_b, sizeof(tx_b), rx_buffer, 2); 
        nrfx_err_t result = nrfx_spim_xfer(&spim_inst, &xfer, 0); 
        return (result == NRFX_SUCCESS) ? 0 : -EIO;
    }
    
    /* =================================================================== */
    
    
    int main(void)
    {
        int err;
        printk("nRF5340 SPIM high-speed example started.\n");
    
        nrfx_spim_config_t spim_config = {
            .sck_pin      = 47, // P1.15
            .mosi_pin     = 46, // P1.14
            .miso_pin     = 45, // P1.13
            .ss_pin       = 44, // P1.12
            .ss_active_high = false,
            .irq_priority = NRFX_SPIM_DEFAULT_CONFIG_IRQ_PRIORITY,
            .orc          = 0xFF,
            .frequency    = 16000000UL,
            .mode         = NRF_SPIM_MODE_0,
            .bit_order    = NRF_SPIM_BIT_ORDER_MSB_FIRST,
            .use_hw_ss    = true, 
            .ss_duration  = 2,    
       
        };
    
    
    
        err = nrfx_spim_init(&spim_inst, &spim_config, NULL, NULL);
    	if (err != NRFX_SUCCESS) {
            printk("NRFx SPIM blocking init failed: %d\n", err);
            return 0;
        }
        printk("Configuring RHD2000 chip...\n");
    
        uint8_t rx_main_buffer[2];
    
        while (1) {
    
             err = rhd_nrfx_spi_transfer_blocking(RHD_CMD_WRITE(0, 0xDE), rx_main_buffer);
    
        }
        return 0;
    }
    
    


    Surprisingly, using the nrfx_spim_xfer() function almost completely eliminated the long delay between the CS and CLK signals. While this is a very good result, the downside is that the time during which the CS signal is deasserted (inactive) is too long.

    To address this, I attempted to control the SPI communication timing with a timer using GPPI-based code, as you suggested (refer to GPIOTE task code)

    ---------------------

    2. Results from using GPPI

    As you advised, I tried to adapt all the necessary parts from the GPPI code for GPIOTE to control the SPIM, but I am not getting any signal output.

    (CS pin seems always disable, and CLK & MOSI pin output signal was always 0)

    I also attach my main.c, configure, and overlay file below:

    #include <zephyr/kernel.h>
    #include <stdio.h>
    
    #include <zephyr/sys/printk.h>
    #include <zephyr/device.h>
    
    #include <zephyr/drivers/uart.h>
    #include <zephyr/drivers/gpio.h>
    #include <zephyr/drivers/spi.h>
    
    #include <nrfx_spim.h>
    #include <nrfx_timer.h>
    
    #include <helpers/nrfx_gppi.h>
    
    
    
    #define RHD_CMD_CONVERT(channel) (uint16_t)((((uint16_t)(channel) & 0x3F) << 8))
    #define RHD_CMD_WRITE(reg, data) (uint16_t)(0x8000 | (((uint16_t)(reg) & 0x3F) << 8) | ((uint16_t)(data) & 0xFF))
    #define RHD_CMD_READ(reg)        (uint16_t)(0xC000 | (((uint16_t)(reg) & 0x3F) << 8))
    #define RHD_CMD_DUMMY_READ       RHD_CMD_READ(63)
    
    static const nrfx_spim_t spim_inst = NRFX_SPIM_INSTANCE(4);
    // 
    static const nrfx_timer_t timer_inst = NRFX_TIMER_INSTANCE(0);
    
    
    /** @brief Symbol specifying timer instance to be used. */
    #define TIMER_INST_IDX 0
    
    /** @brief Symbol specifying GPIOTE instance to be used. */
    #define SPIM_INST_IDX 4
    
    // static const nrfx_dppi_t dppi_inst = NRFX_DPPI_INSTANCE(0);
    static uint8_t m_gppi_channel; 
    
    
    
    
    int main(void)
    {
        nrfx_err_t err; 
        (void)err; 
        printk("nRF5340 SPIM high-speed example started.\n");
    
    
        
        // 1. SPIM
        nrfx_spim_config_t spim_config = {
            .sck_pin      = 47, // P1.15
            .mosi_pin     = 46, // P1.14
            .miso_pin     = 45, // P1.13
            .ss_pin       = 44, // P1.12
            .ss_active_high = false,
            .irq_priority = NRFX_SPIM_DEFAULT_CONFIG_IRQ_PRIORITY,
            .orc          = 0xFF,
            .frequency    = NRF_SPIM_FREQ_16M, // 16MHz
            .mode         = NRF_SPIM_MODE_0,
            .bit_order    = NRF_SPIM_BIT_ORDER_MSB_FIRST,
            .use_hw_ss    = true,
            .ss_duration  = 2,
        };
    
        err = nrfx_spim_init(&spim_inst, &spim_config, NULL, NULL);
    
        NRFX_ASSERT(err == NRFX_SUCCESS);
    
        // 2. TIMER 
        nrfx_timer_config_t timer_config = {
            .frequency          = NRF_TIMER_FREQ_16MHz, // 16MHz
            .mode               = NRF_TIMER_MODE_TIMER,
            .bit_width          = NRF_TIMER_BIT_WIDTH_16,
            .interrupt_priority = NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY,
            .p_context          = NULL
        };
    
        err = nrfx_timer_init(&timer_inst, &timer_config, NULL);
        NRFX_ASSERT(err == NRFX_SUCCESS);
    
        // 2us
        uint32_t ticks = nrfx_timer_us_to_ticks(&timer_inst, 2);
        nrfx_timer_extended_compare(&timer_inst, NRF_TIMER_CC_CHANNEL0, ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, false);
    
    
        // 3. GPPI 
        //GPPI channel allocation
        err = nrfx_gppi_channel_alloc(&m_gppi_channel); 
        NRFX_ASSERT(err == NRFX_SUCCESS);
    
        nrfx_gppi_channel_endpoints_setup(m_gppi_channel,
            
        nrfx_timer_compare_event_address_get(&timer_inst, NRF_TIMER_CC_CHANNEL0),
        nrfx_spim_start_task_address_get(&spim_inst)
    
        );
    
        nrfx_gppi_channels_enable(BIT(m_gppi_channel));
    
        //4. buffer
        uint16_t tx_cmd = RHD_CMD_CONVERT(0);
        uint8_t rx_b[2]; 
        uint8_t tx_b[2];
        
        tx_b[0] = (tx_cmd >> 8) & 0xFF; // MSB
        tx_b[1] = tx_cmd & 0xFF;        // LSB
    
        nrfx_spim_xfer_desc_t xfer_desc = NRFX_SPIM_XFER_TRX(&tx_cmd, sizeof(tx_cmd), &rx_b, sizeof(rx_b));
    
    
        uint32_t xfer_flags = NRFX_SPIM_FLAG_REPEATED_XFER | NRFX_SPIM_FLAG_HOLD_XFER; //nrfm_spim.h 
        
        
        err = nrfx_spim_xfer(&spim_inst, &xfer_desc, xfer_flags);
        NRFX_ASSERT(err == NRFX_SUCCESS);
    
        // 5. TIMER start
        nrfx_timer_enable(&timer_inst);
        printk("TIMER started. SPI transfers are now running automatically.\n");
    
    
    
    
        while (1) {
            k_cpu_idle();
        }
        return 0;
    }

    # enable console
    CONFIG_CONSOLE=y
    
    CONFIG_SERIAL=y
    CONFIG_PRINTK=y
    
    CONFIG_SENSOR=n
    
    CONFIG_SPI=y
    CONFIG_SPI_NRFX=y
    CONFIG_NRFX_SPIM4=y 
    
    
    CONFIG_NRFX_TIMER0=y
    CONFIG_NRFX_DPPI0=y
    CONFIG_NRFX_GPPI=y

    / {
    
        chosen {
            zephyr,console = &uart0;    
            zephyr,shell-uart = &uart0; 
        };
    
        aliases {
            spi4-basic = &spi4;
        };
    
    };
    
    
    
    &uart0 {
        status = "okay";             
        current-speed = <1000000>;    
    };
    
    
    &pinctrl {
        spi4_custom_pins: spi4_custom_pins { 
            group1 {
                psels = <NRF_PSEL(SPIM_SCK,  1, 15)>,   
                        <NRF_PSEL(SPIM_MOSI, 1, 14)>,  
                        <NRF_PSEL(SPIM_MISO, 1, 13)>;
    
                nordic,drive-mode = <NRF_DRIVE_H0H1>;
                         
    
            };
        };
    
    
        spi4_custom_pins_sleep: spi4_custom_pins_sleep {
             group1 {
                psels = <NRF_PSEL(SPIM_SCK,  1, 15)>,
                        <NRF_PSEL(SPIM_MOSI, 1, 14)>,
                        <NRF_PSEL(SPIM_MISO, 1, 13)>;
                low-power-enable; 
            };
        };
    };
    
    
    &timer0 {
        status = "okay";
    };
    
    
     &spi4 {
        compatible = "nordic,nrf-spim";
        status = "okay"; 
        pinctrl-0 = <&spi4_custom_pins>; 
        pinctrl-1 = <&spi4_custom_pins_sleep>; 
        pinctrl-names = "default", "sleep";   
        cs-gpios = <&gpio1 12 GPIO_ACTIVE_LOW>;
        max-frequency = <560000>;
        easydma-maxcnt-bits = <32>;
        rx-delay-supported;
        rx-delay = <2>;
    
        bme280:bme280@0 {
        compatible = "bosch,bme280";
        reg = <0>;
        spi-max-frequency = <560000>;
        };
    
        
        //nRF7002: nRF7002@1 {
        //compatible = "nordic,nrf7002-spi";
        //reg = <1>;
        //spi-max-frequency = <1000000>;
        //};
        
    
    }; 
    
    
    
    

    I will investigate what the problem is and get back to you. (especially, using nrfx_spim_end_event_address_get() function).

    ------------

    Additional Questions

    These are slightly unrelated to the current thread, but I would appreciate it if you could let me know if the following are feasible.

    After I improve the SPI speed, I plan to perform two tasks:

    1.I will control an RHD2132 device with the nRF5340.

    To operate the RHD2132, I need to send an initial configuration of about 20 lines of data, and then continuously send a single-line command in an infinite loop.

    However, the code I have created so far can only perform a repetitive task (i.e., continuously sending the same command). Is it possible to implement the code to first send the ~20 lines of configuration data and then enter a loop to send the single-line command repeatedly?

    2.I will use the nRF5340's SPIM4 peripheral to control both an RHD2132 and an nRF7002.

    I would like to create a loop where I communicate with the RHD2132, then communicate with the nRF7002, and then repeat the cycle.

    This doesn't seem impossible, but I wanted to ask just to be sure.

    Best regards, 

    gwan0624

     

Reply
  • Thank you very much for taking the time to help me.

    My plan was as follows:

    1.Before using DPPI as you advised, I first wanted to try using the nrfx_spim_xfer() function directly, which appears to be for direct SPI driver access, rather than the spi_transceive() function that seems to be used with the Zephyr RTOS.

    2.After that, I intended to move on to the code using GPPI.

    ---------------

    1. Results from using nrfx_spim_xfer()


    #include <zephyr/kernel.h>
    #include <stdio.h>
    
    #include <zephyr/sys/printk.h>
    #include <zephyr/device.h>
    
    #include <zephyr/drivers/uart.h>
    #include <zephyr/drivers/gpio.h>
    #include <zephyr/drivers/spi.h>
    
    #include <nrfx_spim.h>
    
    
    
    #define RHD_CMD_CONVERT(channel) (uint16_t)((((uint16_t)(channel) & 0x3F) << 8)) 
    // Input: 00RR RRRR 0000 0000 
    // Output: DDDD DDDD DDDD DDDD
    #define RHD_CMD_WRITE(reg, data) (uint16_t)(0x8000 | (((uint16_t)(reg) & 0x3F) << 8) | ((uint16_t)(data) & 0xFF)) 
    // Input: 10RR RRRR DDDD DDDD
    // Output: 1111 1111 DDDD DDDD , D: same with input's D (for confirm)
    #define RHD_CMD_READ(reg)        (uint16_t)(0xC000 | (((uint16_t)(reg) & 0x3F) << 8))
    // Input: 11RR RRRR 0000 0000
    #define RHD_CMD_CALIBRATE        (uint16_t)(0x5500) // 0101 0101 0000 0000 
    #define RHD_CMD_DUMMY_READ       RHD_CMD_READ(63) //
    
    
    
    
    /* =================================================================== */
    
    static const nrfx_spim_t spim_inst = NRFX_SPIM_INSTANCE(4);
    
    K_SEM_DEFINE(sem_data_ready, 0, 1);
    
    
    
    static int rhd_nrfx_spi_transfer_blocking(uint16_t tx_command, uint8_t *rx_buffer)
    {
        uint8_t tx_b[2];
        tx_b[0] = (tx_command >> 8) & 0xFF; // MSB
        tx_b[1] = tx_command & 0xFF;        // LSB
        nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_XFER_TRX(tx_b, sizeof(tx_b), rx_buffer, 2); 
        nrfx_err_t result = nrfx_spim_xfer(&spim_inst, &xfer, 0); 
        return (result == NRFX_SUCCESS) ? 0 : -EIO;
    }
    
    /* =================================================================== */
    
    
    int main(void)
    {
        int err;
        printk("nRF5340 SPIM high-speed example started.\n");
    
        nrfx_spim_config_t spim_config = {
            .sck_pin      = 47, // P1.15
            .mosi_pin     = 46, // P1.14
            .miso_pin     = 45, // P1.13
            .ss_pin       = 44, // P1.12
            .ss_active_high = false,
            .irq_priority = NRFX_SPIM_DEFAULT_CONFIG_IRQ_PRIORITY,
            .orc          = 0xFF,
            .frequency    = 16000000UL,
            .mode         = NRF_SPIM_MODE_0,
            .bit_order    = NRF_SPIM_BIT_ORDER_MSB_FIRST,
            .use_hw_ss    = true, 
            .ss_duration  = 2,    
       
        };
    
    
    
        err = nrfx_spim_init(&spim_inst, &spim_config, NULL, NULL);
    	if (err != NRFX_SUCCESS) {
            printk("NRFx SPIM blocking init failed: %d\n", err);
            return 0;
        }
        printk("Configuring RHD2000 chip...\n");
    
        uint8_t rx_main_buffer[2];
    
        while (1) {
    
             err = rhd_nrfx_spi_transfer_blocking(RHD_CMD_WRITE(0, 0xDE), rx_main_buffer);
    
        }
        return 0;
    }
    
    


    Surprisingly, using the nrfx_spim_xfer() function almost completely eliminated the long delay between the CS and CLK signals. While this is a very good result, the downside is that the time during which the CS signal is deasserted (inactive) is too long.

    To address this, I attempted to control the SPI communication timing with a timer using GPPI-based code, as you suggested (refer to GPIOTE task code)

    ---------------------

    2. Results from using GPPI

    As you advised, I tried to adapt all the necessary parts from the GPPI code for GPIOTE to control the SPIM, but I am not getting any signal output.

    (CS pin seems always disable, and CLK & MOSI pin output signal was always 0)

    I also attach my main.c, configure, and overlay file below:

    #include <zephyr/kernel.h>
    #include <stdio.h>
    
    #include <zephyr/sys/printk.h>
    #include <zephyr/device.h>
    
    #include <zephyr/drivers/uart.h>
    #include <zephyr/drivers/gpio.h>
    #include <zephyr/drivers/spi.h>
    
    #include <nrfx_spim.h>
    #include <nrfx_timer.h>
    
    #include <helpers/nrfx_gppi.h>
    
    
    
    #define RHD_CMD_CONVERT(channel) (uint16_t)((((uint16_t)(channel) & 0x3F) << 8))
    #define RHD_CMD_WRITE(reg, data) (uint16_t)(0x8000 | (((uint16_t)(reg) & 0x3F) << 8) | ((uint16_t)(data) & 0xFF))
    #define RHD_CMD_READ(reg)        (uint16_t)(0xC000 | (((uint16_t)(reg) & 0x3F) << 8))
    #define RHD_CMD_DUMMY_READ       RHD_CMD_READ(63)
    
    static const nrfx_spim_t spim_inst = NRFX_SPIM_INSTANCE(4);
    // 
    static const nrfx_timer_t timer_inst = NRFX_TIMER_INSTANCE(0);
    
    
    /** @brief Symbol specifying timer instance to be used. */
    #define TIMER_INST_IDX 0
    
    /** @brief Symbol specifying GPIOTE instance to be used. */
    #define SPIM_INST_IDX 4
    
    // static const nrfx_dppi_t dppi_inst = NRFX_DPPI_INSTANCE(0);
    static uint8_t m_gppi_channel; 
    
    
    
    
    int main(void)
    {
        nrfx_err_t err; 
        (void)err; 
        printk("nRF5340 SPIM high-speed example started.\n");
    
    
        
        // 1. SPIM
        nrfx_spim_config_t spim_config = {
            .sck_pin      = 47, // P1.15
            .mosi_pin     = 46, // P1.14
            .miso_pin     = 45, // P1.13
            .ss_pin       = 44, // P1.12
            .ss_active_high = false,
            .irq_priority = NRFX_SPIM_DEFAULT_CONFIG_IRQ_PRIORITY,
            .orc          = 0xFF,
            .frequency    = NRF_SPIM_FREQ_16M, // 16MHz
            .mode         = NRF_SPIM_MODE_0,
            .bit_order    = NRF_SPIM_BIT_ORDER_MSB_FIRST,
            .use_hw_ss    = true,
            .ss_duration  = 2,
        };
    
        err = nrfx_spim_init(&spim_inst, &spim_config, NULL, NULL);
    
        NRFX_ASSERT(err == NRFX_SUCCESS);
    
        // 2. TIMER 
        nrfx_timer_config_t timer_config = {
            .frequency          = NRF_TIMER_FREQ_16MHz, // 16MHz
            .mode               = NRF_TIMER_MODE_TIMER,
            .bit_width          = NRF_TIMER_BIT_WIDTH_16,
            .interrupt_priority = NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY,
            .p_context          = NULL
        };
    
        err = nrfx_timer_init(&timer_inst, &timer_config, NULL);
        NRFX_ASSERT(err == NRFX_SUCCESS);
    
        // 2us
        uint32_t ticks = nrfx_timer_us_to_ticks(&timer_inst, 2);
        nrfx_timer_extended_compare(&timer_inst, NRF_TIMER_CC_CHANNEL0, ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, false);
    
    
        // 3. GPPI 
        //GPPI channel allocation
        err = nrfx_gppi_channel_alloc(&m_gppi_channel); 
        NRFX_ASSERT(err == NRFX_SUCCESS);
    
        nrfx_gppi_channel_endpoints_setup(m_gppi_channel,
            
        nrfx_timer_compare_event_address_get(&timer_inst, NRF_TIMER_CC_CHANNEL0),
        nrfx_spim_start_task_address_get(&spim_inst)
    
        );
    
        nrfx_gppi_channels_enable(BIT(m_gppi_channel));
    
        //4. buffer
        uint16_t tx_cmd = RHD_CMD_CONVERT(0);
        uint8_t rx_b[2]; 
        uint8_t tx_b[2];
        
        tx_b[0] = (tx_cmd >> 8) & 0xFF; // MSB
        tx_b[1] = tx_cmd & 0xFF;        // LSB
    
        nrfx_spim_xfer_desc_t xfer_desc = NRFX_SPIM_XFER_TRX(&tx_cmd, sizeof(tx_cmd), &rx_b, sizeof(rx_b));
    
    
        uint32_t xfer_flags = NRFX_SPIM_FLAG_REPEATED_XFER | NRFX_SPIM_FLAG_HOLD_XFER; //nrfm_spim.h 
        
        
        err = nrfx_spim_xfer(&spim_inst, &xfer_desc, xfer_flags);
        NRFX_ASSERT(err == NRFX_SUCCESS);
    
        // 5. TIMER start
        nrfx_timer_enable(&timer_inst);
        printk("TIMER started. SPI transfers are now running automatically.\n");
    
    
    
    
        while (1) {
            k_cpu_idle();
        }
        return 0;
    }

    # enable console
    CONFIG_CONSOLE=y
    
    CONFIG_SERIAL=y
    CONFIG_PRINTK=y
    
    CONFIG_SENSOR=n
    
    CONFIG_SPI=y
    CONFIG_SPI_NRFX=y
    CONFIG_NRFX_SPIM4=y 
    
    
    CONFIG_NRFX_TIMER0=y
    CONFIG_NRFX_DPPI0=y
    CONFIG_NRFX_GPPI=y

    / {
    
        chosen {
            zephyr,console = &uart0;    
            zephyr,shell-uart = &uart0; 
        };
    
        aliases {
            spi4-basic = &spi4;
        };
    
    };
    
    
    
    &uart0 {
        status = "okay";             
        current-speed = <1000000>;    
    };
    
    
    &pinctrl {
        spi4_custom_pins: spi4_custom_pins { 
            group1 {
                psels = <NRF_PSEL(SPIM_SCK,  1, 15)>,   
                        <NRF_PSEL(SPIM_MOSI, 1, 14)>,  
                        <NRF_PSEL(SPIM_MISO, 1, 13)>;
    
                nordic,drive-mode = <NRF_DRIVE_H0H1>;
                         
    
            };
        };
    
    
        spi4_custom_pins_sleep: spi4_custom_pins_sleep {
             group1 {
                psels = <NRF_PSEL(SPIM_SCK,  1, 15)>,
                        <NRF_PSEL(SPIM_MOSI, 1, 14)>,
                        <NRF_PSEL(SPIM_MISO, 1, 13)>;
                low-power-enable; 
            };
        };
    };
    
    
    &timer0 {
        status = "okay";
    };
    
    
     &spi4 {
        compatible = "nordic,nrf-spim";
        status = "okay"; 
        pinctrl-0 = <&spi4_custom_pins>; 
        pinctrl-1 = <&spi4_custom_pins_sleep>; 
        pinctrl-names = "default", "sleep";   
        cs-gpios = <&gpio1 12 GPIO_ACTIVE_LOW>;
        max-frequency = <560000>;
        easydma-maxcnt-bits = <32>;
        rx-delay-supported;
        rx-delay = <2>;
    
        bme280:bme280@0 {
        compatible = "bosch,bme280";
        reg = <0>;
        spi-max-frequency = <560000>;
        };
    
        
        //nRF7002: nRF7002@1 {
        //compatible = "nordic,nrf7002-spi";
        //reg = <1>;
        //spi-max-frequency = <1000000>;
        //};
        
    
    }; 
    
    
    
    

    I will investigate what the problem is and get back to you. (especially, using nrfx_spim_end_event_address_get() function).

    ------------

    Additional Questions

    These are slightly unrelated to the current thread, but I would appreciate it if you could let me know if the following are feasible.

    After I improve the SPI speed, I plan to perform two tasks:

    1.I will control an RHD2132 device with the nRF5340.

    To operate the RHD2132, I need to send an initial configuration of about 20 lines of data, and then continuously send a single-line command in an infinite loop.

    However, the code I have created so far can only perform a repetitive task (i.e., continuously sending the same command). Is it possible to implement the code to first send the ~20 lines of configuration data and then enter a loop to send the single-line command repeatedly?

    2.I will use the nRF5340's SPIM4 peripheral to control both an RHD2132 and an nRF7002.

    I would like to create a loop where I communicate with the RHD2132, then communicate with the nRF7002, and then repeat the cycle.

    This doesn't seem impossible, but I wanted to ask just to be sure.

    Best regards, 

    gwan0624

     

Children
No Data
Related