Help nRF5340: GPIOTE, DPPI, and Timer Configuration for Encoder Counting

Hi everyone,

I'm working on a project using the Nordic nRF5340 microcontroller and I need to count both rising and falling edges of a square wave signal on pin P0.14, coming from an incremental encoder.

My current approach involves combining GPIOTE, DPPI, and a TIMER peripheral configured as a counter. I've tried to use Zephyr APIs as much as possible.

Here's the relevant code snippet I'm using:

#define ENCODER_01_NODE              DT_NODELABEL(encoder_01)
static const struct gpio_dt_spec encoder_01              = GPIO_DT_SPEC_GET(ENCODER_01_NODE, gpios);

#define ENCODER_COUNTER_NODE DT_NODELABEL(timer0)

// Input pin initialization function
void InputInit()
{
    if (!device_is_ready(encoder_01.port)) {
        BOARD_DEBUG("Error: %s is not ready", encoder_01.port->name);
        return 1;
    }

    ret = gpio_pin_configure_dt(&encoder_01, GPIO_INPUT | GPIO_PULL_DOWN);
    if (ret < 0) {
        BOARD_DEBUG("Error %d: failed to configure Encoder 01", ret);
        return 1;
    }

    ret = gpio_pin_interrupt_configure_dt(&encoder_01, GPIO_INT_EDGE_BOTH);
    if (ret < 0) {
        BOARD_DEBUG("Error %d: failed to configure Encoder 01 edge", ret);
        return 1;
    }
}

/**
 * @brief Initialize the encoder counter using TIMER0 in counter mode
 * @return 0 on success, non-zero on failure
 */
int EncoderCounterInit(void)
{
    // Get timer0 device for counter functionality
    encoder_counter_dev = DEVICE_DT_GET(ENCODER_COUNTER_NODE);
    
    if (!device_is_ready(encoder_counter_dev)) {
        BOARD_DEBUG("Error: Encoder counter device not ready");
        return 1;
    }

    // Start the counter
    int ret = counter_start(encoder_counter_dev);
    if (ret < 0) {
        BOARD_DEBUG("Error %d: failed to start encoder counter", ret);
        return 1;
    }

    BOARD_DEBUG("Encoder counter initialized successfully");
    return 0;
}

/**
 * @brief Read the current encoder counter value
 * @return Current counter value
 */
uint32_t EncoderCounterRead(void)
{
    uint32_t value = 0;
    
    if (encoder_counter_dev == NULL) {
        BOARD_DEBUG("Error: Encoder counter not initialized");
        return 0;
    }

    int ret = counter_get_value(encoder_counter_dev, &value);
    if (ret < 0) {
        BOARD_DEBUG("Error %d: failed to read encoder counter", ret);
        return 0;
    }

    return value;
}

void DPPI_init()
{
    nrfx_err_t err;

    nrfx_dppi_t dppi = NRFX_DPPI_INSTANCE(0);

    uint8_t ppi_channel = 0;

    err = nrfx_dppi_channel_alloc(&dppi, &ppi_channel);
    if (err != NRFX_SUCCESS) {
        BOARD_DEBUG("Error %d: failed to allocate DPPI channel", err);
        return EXIT_FAILURE;
    }

    #define GPIOTE_INST	NRF_DT_GPIOTE_INST(DT_NODELABEL(encoder_01), gpios)

    const nrfx_gpiote_t gpiote = NRFX_GPIOTE_INSTANCE(GPIOTE_INST);



    //const nrfx_timer_t timer0 = NRFX_TIMER_INSTANCE(0);
    // Definizione alternativa per timer instance (nRF Connect SDK 2.9.0)
    static const nrfx_timer_t timer0 = {
        .p_reg = NRF_TIMER0,
        .instance_id = 0,
        .cc_channel_count = 6  // nRF5340 Timer0 ha 6 canali CC
    };


    uint32_t gpiote_event_addr = nrfx_gpiote_in_event_address_get(&gpiote, ((32 + 0) + 24));
    uint32_t timer_count_task_addr = nrfx_timer_task_address_get(&timer0, NRF_TIMER_TASK_COUNT);


    nrfx_gppi_channel_endpoints_setup(ppi_channel, gpiote_event_addr, timer_count_task_addr);

    // Enable the channel.
	nrfx_gppi_channels_enable(BIT(ppi_channel));
    
    BOARD_DEBUG("DPPI channel %d configured for encoder counter", ppi_channel);
    BOARD_DEBUG("Encoder events will now increment timer counter");
}

The code compiles successfully, but when I read the counter register using EncoderCounterRead(), I get incorrect values that are too high, as if the counter is counting an internal frequency.

Then, I tried to disconnect the event and task by not enabling the DPPI channel, but I noticed that the counter still continues to count. Shouldn't it remain stopped since I haven't enabled the DPPI?

I also tried changing the timer and using timer2 but the result doesn't change.

Can anyone help me troubleshoot this issue? I'm finding very little documentation on the internet and various forums for this specific scenario.

I'm using the nRF5340 microcontroller with Zephyr and nRF Connect SDK v2.9.0.

Thank you.

Related