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.

  • 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

  • Hi 

    I mean that you configure the GPIOTE by writing to the CONFIG registers directly, using hard coded indexes. 

    Something like this:

    NRF_GPIOTE->CONFIG[0] = GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos |
    						GPIOTE_CONFIG_POLARITY_LoToHi << GPIOTE_CONFIG_POLARITY_Pos |
    						IR_RECEIVER_PIN << GPIOTE_CONFIG_PSEL_Pos;
    NRF_GPIOTE->CONFIG[1] = GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos |
    						GPIOTE_CONFIG_POLARITY_HiToLo << GPIOTE_CONFIG_POLARITY_Pos |
    						IR_RECEIVER_PIN << GPIOTE_CONFIG_PSEL_Pos;

    Best regards
    Torbjørn

  • Just to give some closure to ths thread, I did manage to implement this with a help of a colleague, but not following this method, as I couldn't figure out what was wrong. 

    What we did was use the timers with a resolution of 10us, to count each time there was an interrupt on the GPIOTE peripheral activated by a toggle of the pin, this proved to be enough as I didn't exactly need to know the time of pulse in the "high" state, but only the duration between the pin toggles. With a state machine I then managed to parse the durations in accordance to the protocol type I was expecting (NEC IR protocol).

    Best regards, and thanks for the support.

Related