SPI communication using DPPI i wanna control cs pins as specific period. but cs pin is doesn't move at all.

/dts-v1/;
#include <nordic/nrf5340_cpuapp_qkaa.dtsi>
#include "bridge_wifi_nrf5340-pinctrl.dtsi"
#include "nrf5340_cpuapp_common.dtsi"

/ {
	model = "Nordic NRF7002 DK NRF5340 Application";
	compatible = "nordic,nrf7002-dk-nrf5340-cpuapp";

	chosen {
		zephyr,sram = &sram0_image;
		zephyr,flash = &flash0;
		zephyr,code-partition = &slot0_partition;
		zephyr,sram-secure-partition = &sram0_s;
		zephyr,sram-non-secure-partition = &sram0_ns;
		zephyr,wifi = &wlan0;
	};
};
#include "bridge_wifi_nrf5340-cpuapp_partitioning.dtsi"
#include "bridge_wifi_nrf5340-shared_sram.dtsi"

&qspi {
	nrf70: nrf7002@1 {
		compatible = "nordic,nrf7002-qspi";
		status = "okay";
		reg = <1>;
		qspi-frequency = <24000000>;
		qspi-quad-mode;

		#include "nrf70_common.dtsi"
		#include "nrf70_common_5g.dtsi"
	};
};

&spi3 {
	compatible = "nordic,nrf-spim";
	status = "okay";
	pinctrl-0 = <&spi3_default>;
	pinctrl-1 = <&spi3_sleep>;
	pinctrl-names = "default", "sleep";

/* ---------------- Child device: Intan RHS2116 ---------------- */
	intanrhs: intanrhs@0 {
		compatible = "vnd,spi-device";
		status = "okay"; 
		reg = <0>; /* CS#0 */
		spi-max-frequency = <8000000>; 
		duplex = <0>; /* SPI_FULL_DUPLEX[2] */
		frame-format = <0>; /* Motorola */
		label = "IntanRHS";
	};
};


&dppic {
    compatible = "nordic,nrf-dppic";
    status = "okay";
};

&timer1 {
	status = "okay";
};


&gpiote1 {
	status = "okay";
};

  • #include <zephyr/logging/log.h>
    #include <haly/nrfy_gpio.h>
    #include <zephyr/net/socket.h>
    #include <zephyr/sys/fdtable.h>
    #include <zephyr/device.h>
    #include <zephyr/devicetree.h>
    #include <zephyr/drivers/gpio.h>
    #include <zephyr/drivers/spi.h>

    #include <zephyr/kernel.h>
    #include <hal/nrf_dppi.h>
    #include <nrfx_dppi.h>
    #include <nrfx_timer.h>
    #include <nrfx_spim.h>
    #include <nrfx_gpiote.h>

    #include "Wifi_Stationing.h"
    #include <unistd.h>
    #include <errno.h>
    #include <stdio.h>
    #include <stdint.h>
    #include <stdbool.h>
    #include "UDP_Server_Ack.h"
    #include "deviceInformation.h"

    // White check mark FIX 1: Use a unique name for the log module
    LOG_MODULE_REGISTER(intan_spi, CONFIG_LOG_DEFAULT_LEVEL);

    #define SPI_RHD_STACK_SIZE 7000
    #define STM_PRIORITY 7

    static struct k_thread intanspi_rhd;
    K_THREAD_STACK_DEFINE(INTANSPI_RHD, SPI_RHD_STACK_SIZE);

    // --- Peripheral Configuration ---
    // TIMER
    #define TIMER_TOGGLE_INSTANCE 0
    #define TIMER_SPI_INSTANCE 1
    #define TIMER_FREQUENCY NRF_TIMER_FREQ_8MHz
    #define TIMER_PERIOD_MS 2
    #define CS_ACTIVE_US 10 // How long CS is held low (in microseconds)

    // Calculate timer ticks
    #define TICKS_PER_MICROSECOND 1
    #define PERIOD_TICKS (TIMER_PERIOD_MS * 10 * TICKS_PER_MICROSECOND)
    #define CS_ACTIVE_TICKS (CS_ACTIVE_US * TICKS_PER_MICROSECOND)

    #define DPPI_INSTANCE 0

    // SPIM
    #define SPI_INSTANCE 3
    #define SPI_SCK_PIN NRF_GPIO_PIN_MAP(1,13)
    #define SPI_MOSI_PIN NRF_GPIO_PIN_MAP(1,14)
    #define SPI_MISO_PIN NRF_GPIO_PIN_MAP(1,15)
    #define SPI_CS_PIN NRF_GPIO_PIN_MAP(1,12) // The pin we will control manually
    #define SPI_FREQUENCY NRFX_MHZ_TO_HZ(8)

    // Peripherals
    static const nrfx_timer_t m_timer = NRFX_TIMER_INSTANCE(TIMER_SPI_INSTANCE);
    static const nrfx_spim_t m_spim = NRFX_SPIM_INSTANCE(SPI_INSTANCE);
    static const nrfx_gpiote_t m_gpiote = NRFX_GPIOTE_INSTANCE(1);


    // DPPI Channels
    static uint8_t m_dppi_ch_timer_to_spi;
    static uint8_t m_dppi_ch_spi_to_cs_high;

    // Buffers and flags
    static uint8_t m_tx_buf[64] = {0x00, 0x01, 0x01, 0x01, 0x02, 0x01, 0x03, 0x01, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x07, 0x01,
    0x08, 0x01, 0x09, 0x01, 0x0a, 0x01, 0x0b, 0x01, 0x0c, 0x01, 0x0d, 0x01, 0x0e, 0x01, 0x0f, 0x01,
    0x10, 0x01, 0x11, 0x01, 0x12, 0x01, 0x13, 0x01, 0x14, 0x01, 0x15, 0x01, 0x16, 0x01, 0x17, 0x01,
    0x18, 0x01, 0x19, 0x01, 0x1a, 0x01, 0x1b, 0x01, 0x1c, 0x01, 0x1d, 0x01, 0x1e, 0x01, 0x1f, 0x01,};
    static uint8_t m_rx_buf[64];

    static volatile bool m_spi_xfer_done = false;

    // --- Event Handler ---
    void spim_event_handler(nrfx_spim_evt_t const *p_event, void *p_context)
    {
    if (p_event->type == NRFX_SPIM_EVENT_DONE) {
    m_spi_xfer_done = true;
    }
    }

    // --- Peripheral Initialization ---
    static void gpiote_init(void)
    {
    nrfx_err_t err;

    // nrfx_gpiote_init() is deprecated, use nrfx_gpiote_init_check and nrfx_gpiote_init
    if (!nrfx_gpiote_init_check(&m_gpiote)) {
    err = nrfx_gpiote_init(&m_gpiote, NRFX_GPIOTE_DEFAULT_CONFIG_IRQ_PRIORITY);
    NRFX_ASSERT(err == NRFX_SUCCESS);
    }
    nrfx_gpiote_output_config_t output_config = {
    .drive = NRF_GPIO_PIN_H0H1,
    .input_connect = NRF_GPIO_PIN_INPUT_DISCONNECT,
    .pull = NRF_GPIO_PIN_NOPULL,
    };
    // The task polarity is relative to the task execution, not the pin level.
    // OUT task will set the pin to the state configured in .initial_value
    // CLR task will set it to the opposite. SET task will set it to the same.
    nrfx_gpiote_task_config_t task_config = {
    .task_ch = 0, // A specific channel can be used if needed, 0 is fine for basic tasks
    .polarity = GPIOTE_CONFIG_POLARITY_Toggle, // Not critical for OUT tasks, but good to define
    .init_val = NRF_GPIOTE_INITIAL_VALUE_HIGH
    };

    err = nrfx_gpiote_output_configure(&m_gpiote, SPI_CS_PIN, &output_config, &task_config);
    NRFX_ASSERT(err == NRFX_SUCCESS);
    }

    static void timer_init(void)
    {
    nrfx_err_t err;

    nrfx_timer_config_t timer_config = NRFX_TIMER_DEFAULT_CONFIG(TIMER_FREQUENCY);
    timer_config.bit_width = NRF_TIMER_BIT_WIDTH_32;
    err = nrfx_timer_init(&m_timer, &timer_config, NULL); // No timer handler needed
    NRFX_ASSERT(err == NRFX_SUCCESS);

    nrfx_timer_extended_compare(&m_timer, NRF_TIMER_CC_CHANNEL0, PERIOD_TICKS, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, false);
    nrfx_timer_compare(&m_timer, NRF_TIMER_CC_CHANNEL1, CS_ACTIVE_TICKS, false);

    LOG_INF("TIMER initialized. Period: %d ms, CS Active: %d us", TIMER_PERIOD_MS, CS_ACTIVE_US);
    }


    static void spim_init(void)
    {
    nrfx_spim_config_t spim_config = NRFX_SPIM_DEFAULT_CONFIG(
    SPI_SCK_PIN,
    SPI_MOSI_PIN,
    SPI_MISO_PIN,
    NRF_SPIM_PIN_NOT_CONNECTED
    );
    spim_config.frequency = SPI_FREQUENCY;
    // White check mark FIX 4: Use the 'err' variable to make the code more robust.
    nrfx_err_t err = nrfx_spim_init(&m_spim, &spim_config, spim_event_handler, NULL);
    NRFX_ASSERT(err == NRFX_SUCCESS);
    LOG_INF("SPIM%d initialized with manual CS.", SPI_INSTANCE);
    }

    static void dppi_init_and_connect(void)
    {
    nrfx_err_t err;
    const nrfx_dppi_t m_dppi = NRFX_DPPI_INSTANCE(DPPI_INSTANCE);

    // DPPI 채널 할당
    err = nrfx_dppi_channel_alloc(&m_dppi, &m_dppi_ch_timer_to_spi);
    NRFX_ASSERT(err == NRFX_SUCCESS);
    err = nrfx_dppi_channel_alloc(&m_dppi, &m_dppi_ch_spi_to_cs_high);
    NRFX_ASSERT(err == NRFX_SUCCESS);

    uint32_t timer_compare0_evt = nrfx_timer_event_address_get(&m_timer, NRF_TIMER_EVENT_COMPARE0);
    uint32_t spim_start_task = nrfx_spim_start_task_address_get(&m_spim);
    uint32_t spim_end_evt = nrfx_spim_end_event_address_get(&m_spim);

    uint32_t timer_compare1_evt = nrfx_timer_event_address_get(&m_timer, NRF_TIMER_EVENT_COMPARE1);
    uint32_t gpiote_cs_set_task = nrfx_gpiote_set_task_address_get(&m_gpiote, SPI_CS_PIN);
    uint32_t gpiote_cs_clr_task = nrfx_gpiote_clr_task_address_get(&m_gpiote, SPI_CS_PIN);
    uint32_t gpiote_cs_out_task = nrfx_gpiote_out_task_address_get(&m_gpiote, SPI_CS_PIN);

    NRF_DPPI_ENDPOINT_SETUP(spim_start_task, m_dppi_ch_timer_to_spi);
    NRF_DPPI_ENDPOINT_SETUP(gpiote_cs_out_task, m_dppi_ch_timer_to_spi); // CS LOW
    NRF_DPPI_ENDPOINT_SETUP(timer_compare0_evt, m_dppi_ch_timer_to_spi);
    nrfx_dppi_channel_enable(&m_dppi, m_dppi_ch_timer_to_spi);

    NRF_DPPI_ENDPOINT_SETUP(spim_end_evt , m_dppi_ch_spi_to_cs_high);
    NRF_DPPI_ENDPOINT_SETUP(gpiote_cs_out_task, m_dppi_ch_spi_to_cs_high); // CS HIGH
    nrfx_dppi_channel_enable(&m_dppi, m_dppi_ch_spi_to_cs_high);

    LOG_INF("DPPI configured with corrected logic.");
    }

    // --- Main Thread ---
    int spi_rhd(void)
    {
    LOG_INF("Starting Periodic SPI with robust state management...");

    gpiote_init();
    spim_init();
    timer_init();
    dppi_init_and_connect();

    nrfx_spim_xfer_desc_t xfer_desc = NRFX_SPIM_XFER_TRX(m_tx_buf, sizeof(m_tx_buf), m_rx_buf, sizeof(m_rx_buf));
    nrfx_spim_xfer(&m_spim, &xfer_desc, NRFX_SPIM_FLAG_HOLD_XFER);

    nrfx_timer_enable(&m_timer);

    while (1) {
    while (!m_spi_xfer_done) {
    k_sleep(K_MSEC(0.1));
    }
    m_spi_xfer_done = false;

    nrfx_spim_xfer(&m_spim, &xfer_desc, NRFX_SPIM_FLAG_HOLD_XFER);
    }
    return 0;
    }

    void Intan_SPI_Init(void) {
    k_thread_create(
    &intanspi_rhd,
    INTANSPI_RHD,
    SPI_RHD_STACK_SIZE,
    (k_thread_entry_t)spi_rhd,
    NULL,
    NULL,
    NULL,
    STM_PRIORITY,
    0,
    K_NO_WAIT
    );

    k_thread_name_set(&intanspi_rhd, "rhd_spi");
    k_thread_start(&intanspi_rhd);
    }
  • Hello,

    You have not defined CS in the devicetree file. To Control CS automatically by zephyr you need to define cs-gpios in the device tree under a SPI bus.

  • Hello,

    Sorry I have not seen the second message. From the code I can see, CS (chip select) pin is controlled in the application not by the SPIM peripheral or by the Zephyr SPI driver.  

    Then it does not need to define in the device tree. If the CS pin is assigned to both the SPIM peripheral and GPIOTE, or if it is not properly configured as a GPIOTE output, the pin may not toggle as expected. Only one peripheral can control a GPIO at a time. I can not see any issue related to this in the code.

    You may try toggling the CS pin manually in your code (without DPPI or timer) to confirm that the pin and GPIOTE configuration are working. 

Related