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.

Parents
  • Hi Sergi, 

    Are you sure that your timer is in counter mode and not the timer mode?

    Your code int ret = counter_start(encoder_counter_dev); does not necessarily start the timer in counter mode. Do you have these configs enabled in your prj.conf? 

    CONFIG_COUNTER=y
    CONFIG_COUNTER_TIMER0=y
    

    Timer in counter mode is discussed before in this forum for example here my colleague gave a working example on how to use it using a similar configuration as yours. 

    Like in other thread, I recommend using the nrfx API directly to have full control on the mode of timer and other peripherals. I have not tried using Zephyr API to try this configuration to automate counting edges.

Reply
  • Hi Sergi, 

    Are you sure that your timer is in counter mode and not the timer mode?

    Your code int ret = counter_start(encoder_counter_dev); does not necessarily start the timer in counter mode. Do you have these configs enabled in your prj.conf? 

    CONFIG_COUNTER=y
    CONFIG_COUNTER_TIMER0=y
    

    Timer in counter mode is discussed before in this forum for example here my colleague gave a working example on how to use it using a similar configuration as yours. 

    Like in other thread, I recommend using the nrfx API directly to have full control on the mode of timer and other peripherals. I have not tried using Zephyr API to try this configuration to automate counting edges.

Children
  • Hi Susheel, thanks for your time.

    I have already included the "CONFIG_COUNTER=y" configuration. However, the "CONFIG_COUNTER_TIMER0=y" configuration gives me the following error:

    /Users/stefanosergi/Documents/GitHub/AdamsHand_AH03/prj.conf:69: warning: attempt to assign the value 'y' to the undefined symbol COUNTER_TIMER0

    I will try to follow the example you linked and let you know.

    Thanks

    UPDATE:
    I’m following the example you gave me, and I’m running into some problems.
    For example, I’m using the gpiote_init function (which I modified because in my case I only use one pin).


    void gpiote_init(uint32_t pin_tilt)
    {        
        uint32_t ret; 
        
        // Already initialized by GPIO subsystem
        (void)nrfx_gpiote_init(NRFX_GPIOTE_DEFAULT_CONFIG_IRQ_PRIORITY);
    
        ret = nrfx_gpiote_channel_alloc(&tilt_channel);
        if (ret != NRFX_SUCCESS) {
            printk("%d %x\n",__LINE__, ret);
        }  
    
        nrfx_gpiote_input_config_t config = NRFX_GPIOTE_DEFAULT_INPUT_CONFIG;    
        config.pull = NRF_GPIO_PIN_PULLUP;    
    
    	const nrfx_gpiote_trigger_config_t trigger_tilt_config = {
    		.trigger = NRFX_GPIOTE_TRIGGER_LOTOHI,
    		.p_in_channel = &tilt_channel,
    	}; 
    
        ret = nrfx_gpiote_input_configure(pin_tilt, &config, &trigger_tilt_config,NULL);
        if (ret != NRFX_SUCCESS) {
            printk("%d %x\n",__LINE__, ret);
        }
        
        nrfx_gpiote_trigger_enable(pin_tilt, 0);
    }
    

    But I’m getting the following errors:

    too few arguments to function 'nrfx_err_t nrfx_gpiote_init(const nrfx_gpiote_t*, uint8_t)'
    
    error: cannot convert 'uint8_t*' {aka 'unsigned char*'} to 'const nrfx_gpiote_t*'
     1021 |     ret = nrfx_gpiote_channel_alloc(&tilt_channel);
          |                                     ^~~~~~~~~~~~~
          |                                     |
          |                                     uint8_t* {aka unsigned char*}
    
    error: 'NRFX_GPIOTE_DEFAULT_INPUT_CONFIG' was not declared in this scope; did you mean 'NRFX_GPIOTE_DEFAULT_OUTPUT_CONFIG'?
     1026 |     nrfx_gpiote_input_config_t config = NRFX_GPIOTE_DEFAULT_INPUT_CONFIG;
          |                                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          |                                         NRFX_GPIOTE_DEFAULT_OUTPUT_CONFIG
    
    error: invalid conversion from 'uint32_t' {aka 'unsigned int'} to 'const nrfx_gpiote_t*' [-fpermissive]
     1034 |     ret = nrfx_gpiote_input_configure(pin_tilt, &config, &trigger_tilt_config,NULL);
          |                                       ^~~~~~~~
          |                                       |
          |                                       uint32_t {aka unsigned int}
    
    error: invalid conversion from 'nrfx_gpiote_input_config_t*' to 'nrfx_gpiote_pin_t' {aka 'unsigned int'} [-fpermissive]
     1034 |     ret = nrfx_gpiote_input_configure(pin_tilt, &config, &trigger_tilt_config,NULL);
          |                                                 ^~~~~~~
          |                                                 |
          |                                                 nrfx_gpiote_input_config_t*
    
    error: cannot convert 'const nrfx_gpiote_trigger_config_t*' to 'const nrfx_gpiote_input_pin_config_t*'
     1034 |     ret = nrfx_gpiote_input_configure(pin_tilt, &config, &trigger_tilt_config,NULL);
          |                                                          ^~~~~~~~~~~~~~~~~~~~
          |                                                          |
          |                                                          const nrfx_gpiote_trigger_config_t*
    

    It seems that the example uses deprecated functions. Right?

    Continuing with the example, I notice that the code refers to the PPI module, but I'm using the microcontroller nRF5340 that should have the DPPI module (not PPI).
    Is there an updated example using the nRF5340 microcontroller?

Related