Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs

Using timer to count pulse width on interrupt pin

Hi! I'm trying to use a counter to count the pulse widths of an IR signal packet everytime it tirggers an event on a GPIO, however I've been going in loops without sucess. 

Here is my current code:

void timer_handler(nrf_timer_event_t event_type, void * p_context)
{
    switch (event_type)
    {
      case NRF_TIMER_EVENT_COMPARE0:
        if (timer_started){
          timer_started = false;
          NRF_TIMER0->TASKS_CAPTURE[0] = 1;
	      pulse_duration = NRF_TIMER0->CC[0];
          //pulse_duration = nrf_timer_cc_read(m_timer.p_reg, NRF_TIMER_CC_CHANNEL0);
          NRF_LOG_INFO("PULSE DURATION: %u", pulse_duration);
        }
      break;

      default:
          //Do nothing.
      break;
    }
}

void gpiote_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{
  nrf_drv_gpiote_out_toggle(LED_1);
  //NRF_LOG_INFO("GPIOTE HANDLER: %d | %d", pin, action);
  if(nrf_gpio_pin_read(pin) == 1) {
    pulse_count++;
    NRF_LOG_INFO("PULSE Count: %u", pulse_count);
    nrf_drv_timer_enable(&m_timer);
    nrf_drv_timer_clear(&m_timer);
  }
  else {
    NRF_LOG_INFO("PIN IS 0");
    nrf_drv_timer_disable(&m_timer);
    
  }
}


/**
 * @brief Function for application main entry.
 */
int main(void)
{
    ret_code_t err_code;
    
    // Initialize.
    log_init();
    
    timers_init();
    err_code = nrf_drv_gpiote_init();
    APP_ERROR_CHECK(err_code);

    nrf_drv_gpiote_out_config_t out_config = GPIOTE_CONFIG_OUT_SIMPLE(false);

    err_code = nrf_drv_gpiote_out_init(LED_1, &out_config);
    APP_ERROR_CHECK(err_code);

    nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_TOGGLE(true);
    in_config.pull = NRF_GPIO_PIN_NOPULL;

    err_code = nrf_drv_gpiote_in_init(IR_RECEIVER_PIN, &in_config, gpiote_handler);
    APP_ERROR_CHECK(err_code);
    nrf_drv_gpiote_in_event_enable(IR_RECEIVER_PIN, true);

    nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
    err_code = nrf_drv_timer_init(&m_timer, &timer_cfg, timer_handler);
    APP_ERROR_CHECK(err_code);
    nrf_drv_timer_extended_compare(&m_timer, NRF_TIMER_CC_CHANNEL0, 0xFFFFFF, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true);
    nrf_drv_timer_enable(&m_timer);

    timer_started = false;

    // Enter main loop.
    for (;;) {

      idle_state_handle();

    }
}

I've used both methods to count pulse_duration incluing the one currently commented, in the current case I always get 0, in the commented one I get a value "16777215" which seems to be an overflowing 24 bit timer, however mine is 16bit not 24. What am I doing wrong here, can someone help?

Thanks in advance.

Parents Reply Children
  • Hi Jared, thanks for the example, I have a question though. 

    How do I assign the different ppi channels to the different gpiote configs? I have these two configs:

    nrf_drv_gpiote_in_config_t in_config_1 = GPIOTE_CONFIG_IN_SENSE_LOTOHI(true);
    nrf_drv_gpiote_in_config_t in_config_2 = GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
    nrfx_gpiote_in_init(IR_RECEIVER_PIN, &in_config_1, gpiote_handler);
    nrfx_gpiote_in_init(IR_RECEIVER_PIN, &in_config_2, gpiote_handler);

    But when I assign the ppi channels I'm doing the following: 

        nrfx_ppi_channel_alloc(&ppi_channel_a);
        nrfx_ppi_channel_assign(ppi_channel_a, nrfx_gpiote_in_event_addr_get(IR_RECEIVER_PIN), nrfx_timer_task_address_get(&m_timer, NRF_TIMER_TASK_START));
        nrfx_ppi_channel_fork_assign(ppi_channel_a, nrfx_timer_task_address_get(&m_timer, NRF_TIMER_TASK_CLEAR));
        nrfx_ppi_channel_enable(ppi_channel_a);
    
        nrfx_ppi_channel_alloc(&ppi_channel_b);
        nrfx_ppi_channel_assign(ppi_channel_b, nrfx_gpiote_in_event_addr_get(IR_RECEIVER_PIN), nrfx_timer_task_address_get(&m_timer, NRF_TIMER_TASK_STOP));
        nrfx_ppi_channel_fork_assign(ppi_channel_b, nrfx_timer_task_address_get(&m_timer, NRF_TIMER_TASK_CAPTURE0));
        nrfx_ppi_channel_enable(ppi_channel_b);

    However this doesn't seem to be assigning the PPI to LOTOHI and HITOLO events but rather any gpiote event. Am I missing something? Can I configure GPIOTE channels in a different way? 

    Thank you for your help.

  • Hi Gabriel

    Jared is currently unavailable, and I will help you out in the mean time. 

    The nrfx_gpiote driver is a bit limited if you want to assign multiple GPIOTE channels to the same pin, since the pin is used to index the different channels rather than the channel index. 

    In this case I think you would have to use the nrfx_gpiote driver only for allocating the channels, using the nrfx_gpiote_channel_alloc(uint8_t * p_channel) function, and then configure the channels directly using the NRF_GPIOTE->CONFIG[ch] registers. Then you need to use the channel index provided by the alloc function as an index in the CONFIG register. 

    Small example:

    #define IR_PIN 9
    uint8_t gpiote_ch_a, gpiote_ch_b;
    nrfx_gpiote_channel_alloc(&gpiote_ch_a);
    nrfx_gpiote_channel_alloc(&gpiote_ch_b);
    NRF_GPIOTE->CONFIG[gpiote_ch_a] = GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos |
    						GPIOTE_CONFIG_POLARITY_LoToHi << GPIOTE_CONFIG_POLARITY_Pos |
    						IR_PIN << GPIOTE_CONFIG_PSEL_Pos;
    NRF_GPIOTE->CONFIG[gpiote_ch_b] = GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos |
    						GPIOTE_CONFIG_POLARITY_HiToLo << GPIOTE_CONFIG_POLARITY_Pos |
    						IR_PIN << GPIOTE_CONFIG_PSEL_Pos;

    Best regards
    Torbjørn

  • Hello Torbjorn. 

    I just checked the nrfx_gpiote drivers and the nrfx_gpiote_channel_alloc() function doesn't seem to exist, that's why I tried to translate the code Jared provided to my case.

    According to those drivers the allocation is done automatically through the in_init function:

    nrfx_err_t nrfx_gpiote_in_init(nrfx_gpiote_pin_t pin, nrfx_gpiote_in_config_t const * p_config, nrfx_gpiote_evt_handler_t evt_handler)

    Of course this function assumes I have previously declared a config before as in: 

    nrf_drv_gpiote_in_config_t in_config_1 = GPIOTE_CONFIG_IN_SENSE_LOTOHI(true);

    But through this method I'm not associating a channel to LoToHi and HiToLo events and this way I don't know how to assign the different PPI channels to each event.

    Is there any other way to do this?

    Thank you for your support.

    Best regards.

  • Hi 

    My bad, I didn't realize you were using the nRF5 SDK...

    You can just hard code the channels. The GPIOTE peripheral has 8 channels in total, which means channel indexes in the range 0 to 7 are available. 

    It is just critical to ensure that if you have other parts of your application using the nrf_gpiote driver you don't get a conflict where you are trying to use the same GPIOTE channels multiple places. 

    Best regards
    Torbjørn

  • Hi Torbjorn.

    By hard coding, do you mean just assigning the PPI channel to the GPIOTE channel this way?

        nrfx_ppi_channel_alloc(&ppi_channel_a);
        nrfx_ppi_channel_assign(ppi_channel_a,(uint32_t)&NRF_GPIOTE->EVENTS_IN[0], nrfx_timer_task_address_get(&m_timer, NRF_TIMER_TASK_START));
        nrfx_ppi_channel_fork_assign(ppi_channel_a, nrfx_timer_task_address_get(&m_timer, NRF_TIMER_TASK_CLEAR));
        nrfx_ppi_channel_enable(ppi_channel_a);
    
        nrfx_ppi_channel_alloc(&ppi_channel_b);
        nrfx_ppi_channel_assign(ppi_channel_b,(uint32_t)&NRF_GPIOTE->EVENTS_IN[1], nrfx_timer_task_address_get(&m_timer, NRF_TIMER_TASK_STOP));
        nrfx_ppi_channel_fork_assign(ppi_channel_b, nrfx_timer_task_address_get(&m_timer, NRF_TIMER_TASK_CAPTURE0));
        nrfx_ppi_channel_enable(ppi_channel_b);

    I'm assuming it starts by assigning channel 0, 1 and so on. However just by doing this I'm not getting any results inn my main loop.

    Here's the full code on my main function:

        nrf_drv_gpiote_in_config_t in_config_1 = GPIOTE_CONFIG_IN_SENSE_LOTOHI(true);
        nrf_drv_gpiote_in_config_t in_config_2 = GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
        nrfx_gpiote_in_init(IR_RECEIVER_PIN, &in_config_1, gpiote_handler);
        nrfx_gpiote_in_init(IR_RECEIVER_PIN, &in_config_2, gpiote_handler);
        nrf_drv_gpiote_in_event_enable(IR_RECEIVER_PIN, true);
    
        nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
        err_code = nrf_drv_timer_init(&m_timer, &timer_cfg, NULL);
        APP_ERROR_CHECK(err_code);
        nrf_drv_timer_enable(&m_timer);
    
        nrfx_ppi_channel_alloc(&ppi_channel_a);
        nrfx_ppi_channel_assign(ppi_channel_a,(uint32_t)&NRF_GPIOTE->EVENTS_IN[0], nrfx_timer_task_address_get(&m_timer, NRF_TIMER_TASK_START));
        nrfx_ppi_channel_fork_assign(ppi_channel_a, nrfx_timer_task_address_get(&m_timer, NRF_TIMER_TASK_CLEAR));
        nrfx_ppi_channel_enable(ppi_channel_a);
    
        nrfx_ppi_channel_alloc(&ppi_channel_b);
        nrfx_ppi_channel_assign(ppi_channel_b,(uint32_t)&NRF_GPIOTE->EVENTS_IN[1], nrfx_timer_task_address_get(&m_timer, NRF_TIMER_TASK_STOP));
        nrfx_ppi_channel_fork_assign(ppi_channel_b, nrfx_timer_task_address_get(&m_timer, NRF_TIMER_TASK_CAPTURE0));
        nrfx_ppi_channel_enable(ppi_channel_b);
    
        // Enter main loop.
        for (;;) {
          idle_state_handle();
          pulse_length = nrfx_timer_capture_get(&m_timer, NRF_TIMER_CC_CHANNEL0);
          NRF_LOG_INFO("PULSE LENGTH: %u",pulse_length);
          nrf_delay_ms(1000);
        }

    To clarify the NRF_LOG_INFO just prints 0 on the pulse length.

    Am I missing something? 

    Thank you and best regards

Related