using pwm input to measure frequency

I have an NRF9160 system running and I see mention of "The nRF9160 PWM peripheral can capture the period and pulse width of an external PWM signal" but  was a little confused as to how to implement it. I have used and I am using PWM as an output so that I understand. I have used a generic GPIO as input and used interrupts to start and stop a timer to measure time between edges. This uses two items a generic GPIO and a timer. as I understand the capture capability it just used a single PWM to do the measurement.

can you point me to any examples how to implement this if I am correct in assuming all I need is a single PWM pin.

Parents
  • Which Timer are you using? RTC? if so which instance? make sure that the instance you are using is not used by anything else in your system. Preferably use RTC1 or 2 if available.

  • I have used a generic GPIO and timer on other examples in the past. I am not using it now for this application. I wanted to use the capture capability of the PWM. I see there is a ...\v3.02\zephyr\tests\drivers\pwm\pwm_loopback example. this looks like it uses a PWM output connected to a PWM input that does use capture capabilities. I tired to understand and build this but was unsuccessful. there is not an overlay for nRF9160 so I could not get it to compile. Is this what I need to understand to do PWM measure frequency and period. The example is not very clear is there some description of the PWM loopback example?

  • I set up the timers as described in https://docs.nordicsemi.com/bundle/ncs-latest/page/zephyr/kernel/services/timing/timers.html. but I could not get an accurate timer at settings lower than 500 usec. my example code is shown below. At the timer expiry I toggle an output pin so I can see the timer frequency with a scope on the output pin. 1 msec and 500 usec seem to work fine but when I set it to 100 usec I see ~150 usec. below that 50 usec is ~ 170 usec, 10 usec is ~300 usec. below 500 usec the counts are very inaccurate. they do not represent the time between interrupts.

    what am I doing wrong. how do I get the timer to count at 1 usec?

       k_timer_init(&gpio_timer, gpio_timer_handler, NULL);
        k_timer_start(&gpio_timer, K_USEC(100), K_USEC(100));
    void gpio_handler(struct k_work *work)
    {
        gpioCounter++;
        gpio_pin_toggle(sw2ctrl_dev, SW2CTRL_DEV_PIN);
    }
  • Susheel

    can you please  try to answer these questions.

    1. is PWM capture possible for my device?

    2. how can I get a timer to time at 1 usec?

  • Sorry for delayed response Timothy, 

    Timothy said:
    1. is PWM capture possible for my device?

    The PWM peripheral in itself is output so it cannot do a input capture. But you can use a TIMER-GPIOTE in event mode + PPI to make a capture like configuration, 

    Timothy said:
    2. how can I get a timer to time at 1 usec?

    In nrf connect SDK, you cannot get RTOS timer to have a better resolution than 1ms using RTOS timer API. So you would have to use nrfx_timer HAL API directly for measuring time with 1us resolution, you can do something like this in your code (use as template)

    // top of src/jfet_init.c
    #include <zephyr.h>
    #include <nrfx_timer.h>
    
    
    #define TIMER_ID 1
    static const nrfx_timer_t timer = NRFX_TIMER_INSTANCE(TIMER_ID);
    
    
    void jfet_init(void)
    {
        /* Configure the timer for 1 MHz (1 µs ticks) */
        nrfx_timer_config_t cfg = NRFX_TIMER_DEFAULT_CONFIG;
        cfg.frequency = NRF_TIMER_FREQ_1MHz;
        cfg.mode      = NRF_TIMER_MODE_TIMER;
        cfg.bit_width = NRF_TIMER_BIT_WIDTH_32;
    
        /* Init & start */
        nrfx_timer_init(&timer, &cfg, NULL);
        nrfx_timer_enable(&timer);
    
        /* …rest of your JFET setup… */
    }
    
    
    uint32_t read_now_us(void)
    {
        nrfx_timer_capture(&timer, NRF_TIMER_CC_CHANNEL0);
        return nrfx_timer_capture_get(&timer, NRF_TIMER_CC_CHANNEL0);
    }
    

  • Susheel
    when I try to add just the first few lines it does not build. I am using SDK toolchain 3.0.2.
    what am I missing in order to get this to build. I tried to build the lines shown below.
    *************************************
    #include <nrfx_timer.h>
    
    #define TIMER_ID 1
    static const nrfx_timer_t timer = NRFX_TIMER_INSTANCE(TIMER_ID);
    
    ******************** errors  *******************
                   from C:/Nordic4/test_n9160/agr_ind_new_9160/src/jfet_files/jfet_init.c:1982:
    C:/Nordic4/v2.9.0/modules/hal/nordic/nrfx/hal/nrf_timer.h: In function 'jfet_init':
    C:/Nordic4/v2.9.0/modules/hal/nordic/nrfx/hal/nrf_timer.h:472:24: error: invalid storage class for function 'nrf_timer_prescaler_set'
      472 | NRF_STATIC_INLINE void nrf_timer_prescaler_set(NRF_TIMER_Type * p_reg, uint32_t prescaler_factor);
          |                        ^~~~~~~~~~~~~~~~~~~~~~~
    C:/Nordic4/v2.9.0/modules/hal/nordic/nrfx/hal/nrf_timer.h:481:28: error: invalid storage class for function 'nrf_timer_prescaler_get'
      481 | NRF_STATIC_INLINE uint32_t nrf_timer_prescaler_get(NRF_TIMER_Type const * p_reg);
          |                            ^~~~~~~~~~~~~~~~~~~~~~~
    C:/Nordic4/v2.9.0/modules/hal/nordic/nrfx/hal/nrf_timer.h:489:24: error: invalid storage class for function 'nrf_timer_task_trigger'
      489 | NRF_STATIC_INLINE void nrf_timer_task_trigger(NRF_TIMER_Type * p_reg,
          |                        ^~~~~~~~~~~~~~~~~~~~~~
    C:/Nordic4/v2.9.0/modules/hal/nordic/nrfx/hal/nrf_timer.h:500:28: error: invalid storage class for function 'nrf_timer_task_address_get'
      500 | NRF_STATIC_INLINE uint32_t nrf_timer_task_address_get(NRF_TIMER_Type const * p_reg,
          |                            ^~~~~~~~~~~~~~~~~~~~~~~~~~
    C:/Nordic4/v2.9.0/modules/hal/nordic/nrfx/hal/nrf_timer.h:509:24: error: invalid storage class for function 'nrf_timer_event_clear'
      509 | NRF_STATIC_INLINE void nrf_timer_event_clear(NRF_TIMER_Type *  p_reg,
          |                        ^~~~~~~~~~~~~~~~~~~~~
    C:/Nordic4/v2.9.0/modules/hal/nordic/nrfx/hal/nrf_timer.h:521:24: error: invalid storage class for function 'nrf_timer_event_check'
      521 | NRF_STATIC_INLINE bool nrf_timer_event_check(NRF_TIMER_Type const * p_reg,
          |                        ^~~~~~~~~~~~~~~~~~~~~
    C:/Nordic4/v2.9.0/modules/hal/nordic/nrfx/hal/nrf_timer.h:532:28: error: invalid storage class for function 'nrf_timer_event_address_get'
      532 | NRF_STATIC_INLINE uint32_t nrf_timer_event_address_get(NRF_TIMER_Type const * p_reg,
          |                            ^~~~~~~~~~~~~~~~~~~~~~~~~~~
    C:/Nordic4/v2.9.0/modules/hal/nordic/nrfx/hal/nrf_timer.h:541:24: error: invalid storage class for function 'nrf_timer_shorts_enable'
      541 | NRF_STATIC_INLINE void nrf_timer_shorts_enable(NRF_TIMER_Type * p_reg,
          |                        ^~~~~~~~~~~~~~~~~~~~~~~
    C:/Nordic4/v2.9.0/modules/hal/nordic/nrfx/hal/nrf_timer.h:550:24: error: invalid storage class for function 'nrf_timer_shorts_disable'
      550 | NRF_STATIC_INLINE void nrf_timer_shorts_disable(NRF_TIMER_Type * p_reg,
          |                        ^~~~~~~~~~~~~~~~~~~~~~~~
    C:/Nordic4/v2.9.0/modules/hal/nordic/nrfx/hal/nrf_timer.h:559:24: error: invalid storage class for function 'nrf_timer_shorts_set'
      559 | NRF_STATIC_INLINE void nrf_timer_shorts_set(NRF_TIMER_Type * p_reg,
          |                        ^~~~~~~~~~~~~~~~~~~~
    C:/Nordic4/v2.9.0/modules/hal/nordic/nrfx/hal/nrf_timer.h:569:42: error: invalid storage class for function 'nrf_timer_short_compare_clear_get'
      569 | NRF_STATIC_INLINE nrf_timer_short_mask_t nrf_timer_short_compare_clear_get(uint8_t channel);
          |                                          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    C:/Nordic4/v2.9.0/modules/hal/nordic/nrfx/hal/nrf_timer.h:578:42: error: invalid storage class for function 'nrf_timer_short_compare_stop_get'
      578 | NRF_STATIC_INLINE nrf_timer_short_mask_t nrf_timer_short_compare_stop_get(uint8_t channel);
       
Reply Children
  • Thanks for lettings us know Timothy, if possible please post the solution here so that it could help others aswell. The code I posted compiled fine at my end, but I am not sure what your complete setup is.

  • #include <nrfx_timer.h>
    
    #if 1
    
    #define TIMER_ID 1
    static const nrfx_timer_t timer = NRFX_TIMER_INSTANCE(TIMER_ID);
    
    static void on_timer_event(nrf_timer_event_t evt, void *ctx) {}
    
    void init_us_timer(void)
    {
    
        nrfx_timer_config_t cfg = NRFX_TIMER_DEFAULT_CONFIG(1000000);
    //    nrfx_timer_config_t cfg = NRFX_TIMER_DEFAULT_CONFIG(16000000);
    //    cfg.frequency = NRF_TIMER_FREQ_1MHz;
        cfg.mode = NRF_TIMER_MODE_TIMER;
        cfg.bit_width = NRF_TIMER_BIT_WIDTH_32;
    
    uint32_t status = nrfx_timer_init(&timer, &cfg, on_timer_event);
    __ASSERT_NO_MSG(status == NRFX_SUCCESS);
        nrfx_timer_enable(&timer);
    }
    
    uint32_t get_us_count(void)
    {
        return nrfx_timer_capture_get(&timer, NRF_TIMER_CC_CHANNEL0);
    }
    
    uint32_t capture_us(void)
    {
        nrfx_timer_capture(&timer, NRF_TIMER_CC_CHANNEL0);
        return get_us_count();
    }
    
    void clear_us(void)
    {
        nrfx_timer_clear(&timer);
        return;
    }
    #endif
    

    I used the attached file. my interrupt handler for GPIO 09 is as follows. this is as minimum as I could get it.

     
    uint32_t nrfxCount = NRFX_COUNTER_ARRAY_SIZE;
    uint32_t nrfxCountArray[NRFX_COUNTER_ARRAY_SIZE];
     
    void gpio09Callback(struct device *dev, struct gpio_callback *cb, uint32_t pins)
    {

        if (nrfxCount < NRFX_COUNTER_ARRAY_SIZE)
        {
            nrfxCountArray[nrfxCount++] = capture_us();
        }
       }
    the simple test program is as follows
       struct gpio_callback gpio_09_cb;

        init_us_timer();
        k_sleep(K_SECONDS(1));

        gpio_init_callback(&gpio_09_cb, (gpio_callback_handler_t)gpio09Callback, BIT(GPIO_09_DEV_PIN));
        gpio_add_callback(gpio_09_dev, &gpio_09_cb);
        gpio_pin_interrupt_configure(gpio_09_dev, GPIO_09_DEV_PIN, GPIO_INT_ENABLE | GPIO_INT_EDGE | GPIO_INT_EDGE_RISING);

        printf("Start GPIO_09 Interrupt test\r\n");
        nrfxCount = 0;
        while (1)
        {
            if (nrfxCount >= NRFX_COUNTER_ARRAY_SIZE)
            {
                for (int j = 1; j < NRFX_COUNTER_ARRAY_SIZE; j++)
                {
                     printk("GPIO_09 %d\tcounts:%d\tsec: %3.5e\tfreq: %3.3e\r\n", j, (nrfxCountArray[j] - nrfxCountArray[j - 1]),
                           (nrfxCountArray[j] - nrfxCountArray[j - 1]) / NRFX_COUNTER_FREQ,
                           1 / ((nrfxCountArray[j] - nrfxCountArray[j - 1]) / NRFX_COUNTER_FREQ));
                    k_sleep(K_MSEC(200));
                }
                printk("\r\n");
                k_sleep(K_SECONDS(1));
                nrfxCount = 0;
            }
            k_sleep(K_SECONDS(1));
        }
    NOTES: !!!
    this measures the frequency well up to about 30 kHz. I am setting the timer frequency to 16 MHz to get the most counts (accuracy). It does not work at all past that. anything past 60 kHz reads as ~65 kHz. I need to be able to measure a frequency of up to 150 kHz. this solution cannot do that. The interrupt handler looks like it takes about 20 usec so I miss counts when the period is < 20 usec. the interrupt handler needs to be on the order of 5 usec in order to count 150 kHz (6.67 usec).
    How can I get the timer to measue frequencies up to 150 kHz?
  • How can I assure that the GPIO 09 pin has the highest priority and is handle in the fastest way.

  • You cannot set the interrupt priority of the pin handling itself but you can set the priority of interrupt handler for GPIO driver. The priority is handled with NRFX_DEFAULT_IRQ_PRIORITY in modules\hal\nordic\nrfx\templates\nrfx_config_nrf91.h

Related