Measuring 1hz to 1Mhz Input frequency on up to 6 GPIOs with maximum possible high accuracy.

My board is nRF5340Dk with NCS 2.6.1 and toolchain 2.6.1.

At this development stage I want to measure continuously incoming frequency ranging from 1Hz to 1MHz with high accuracy possible. I have tried looking into some previous similar question and implemented the code from that project on to my project but I could not get past the NRF_PPI section, which I believe now is NRF_DPPI for nrf5340 boards. But how do I implement  NRF_DPPI without extensively changing existing code? Because the way NRF_PPI was used is not similar to NRF_DPPI.

Is there a best way to measure up to 6 frequencies through GPIOS?

This is the current code :

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

#include <zephyr/kernel.h>

#include <zephyr/device.h>
#include <zephyr/sys/printk.h>

#include <helpers/nrfx_gppi.h>
#include <nrfx_timer.h>
#include <nrfx_gpiote.h>
#include <nrfx_dppi.h>



#define FREQ_MEASURE_PIN 7
uint32_t time = 1; // timer time in seconds

static void timer_init()
{
  uint32_t counter; 
  counter = (16000000 >> 8)*time;
  NRF_TIMER1->TASKS_STOP = 1;
  NRF_TIMER1->MODE = TIMER_MODE_MODE_Timer;
  NRF_TIMER1->PRESCALER = 8;	// f_hck / 2^8 
  NRF_TIMER1->CC[0] = counter;	
  NRF_TIMER1->BITMODE = (TIMER_BITMODE_BITMODE_32Bit << TIMER_BITMODE_BITMODE_Pos);	
  NRF_TIMER1->TASKS_CLEAR = 1;
  NRF_TIMER1->INTENSET = (TIMER_INTENSET_COMPARE0_Enabled << TIMER_INTENSET_COMPARE0_Pos);
  NRF_TIMER1->EVENTS_COMPARE[0] = 0;
}

static void counter_init()
{
  NRF_TIMER2->TASKS_STOP = 1;	
  NRF_TIMER2->MODE = TIMER_MODE_MODE_Counter;
  NRF_TIMER2->BITMODE = (TIMER_BITMODE_BITMODE_32Bit << TIMER_BITMODE_BITMODE_Pos);
  NRF_TIMER2->TASKS_CLEAR = 1;
  NRF_TIMER2->EVENTS_COMPARE[0] = 0;
}

static void gpiote_init(uint32_t pin)
{
  NRF_GPIOTE->CONFIG[0] = 0x01 << 0;             // MODE: Event
  NRF_GPIOTE->CONFIG[0] |= pin << 8;			      // Pin number
  NRF_GPIOTE->CONFIG[0] |= NRF_GPIOTE_POLARITY_LOTOHI	<< 16; // Event rising edge 	
}

static void ppi_timer_stop_counter_init()
{
  NRF_DPPI->CHEN |= 1 << 0;
  *(&(NRF_DPPI->CH[1].EEP)) = (uint32_t)&NRF_TIMER1->EVENTS_COMPARE[0];
  *(&(NRF_DPPI->CH[1].TEP)) = (uint32_t)&NRF_TIMER2->TASKS_STOP;
  NRF_DPPI->CHENSET |= 1 << 0;
}

static void ppi_gpiote_counter_init()
{
  NRF_DPPI->CHEN |= 1 << 1;
  *(&(NRF_DPPI->CH[1].EEP)) = (uint32_t)&NRF_GPIOTE->EVENTS_IN[0];
  *(&(NRF_DPPI->CH[1].TEP)) = (uint32_t)&NRF_TIMER2->TASKS_COUNT;
  NRF_DPPI->CHENSET |= 1 << 1;
}

int main()
{
    // Start clock for accurate frequencies
    NRF_CLOCK->TASKS_HFCLKSTART = 1; 
    // Wait for clock to start
    while(NRF_CLOCK->EVENTS_HFCLKSTARTED == 0);

    printk("Timer Test");
    NVIC_EnableIRQ(TIMER1_IRQn);
    NVIC_SetPriority(TIMER1_IRQn, 3);	

    nrf_gpio_cfg_input(FREQ_MEASURE_PIN, NRF_GPIO_PIN_NOPULL);

    counter_init();
    timer_init();
    gpiote_init(FREQ_MEASURE_PIN);
    ppi_gpiote_counter_init();
    ppi_timer_stop_counter_init();

    NRF_TIMER1->TASKS_START = 1;
    NRF_TIMER2->TASKS_START = 1;
}

void TIMER1_IRQHandler(void) 
{
  if (NRF_TIMER1->EVENTS_COMPARE[0] != 0)
  {
      //nrf_drv_gpiote_out_toggle(LED_GREEN);
      NRF_TIMER1->EVENTS_COMPARE[0] = 0;
      NRF_TIMER2->TASKS_CAPTURE[0] = 1;
                  
      printk("%u Hz", NRF_TIMER2->CC[0]*time);

      NRF_TIMER1->TASKS_CLEAR = 1;
      NRF_TIMER2->TASKS_CLEAR = 1;	
                                  
      NRF_TIMER2->TASKS_START = 1;			
    }
}

and prj.conf

CONFIG_GPIO=y
CONFIG_LOG=y

This code/project primarily has been taken from this question on devzone which was running on nRF52840 and a different SDK.

I have issues in 

static void ppi_timer_stop_counter_init() and in 
static void ppi_gpiote_counter_init() 

static void ppi_timer_stop_counter_init()
{
  NRF_DPPI->CHEN |= 1 << 0;
  *(&(NRF_DPPI->CH[1].EEP)) = (uint32_t)&NRF_TIMER1->EVENTS_COMPARE[0];
  *(&(NRF_DPPI->CH[1].TEP)) = (uint32_t)&NRF_TIMER2->TASKS_STOP;
  NRF_DPPI->CHENSET |= 1 << 0;
}

static void ppi_gpiote_counter_init()
{
  NRF_DPPI->CHEN |= 1 << 1;
  *(&(NRF_DPPI->CH[1].EEP)) = (uint32_t)&NRF_GPIOTE->EVENTS_IN[0];
  *(&(NRF_DPPI->CH[1].TEP)) = (uint32_t)&NRF_TIMER2->TASKS_COUNT;
  NRF_DPPI->CHENSET |= 1 << 1;
}

Although, I have changed the name, NRF_PPI to NRF_DPPI in my code to check if that works, it doesn't and I have no clue how to implement that EEP or TEP.

It would be helpful to get some directions on these requirements.

  • I have tried your program,

    It didn't run properly on NCS 2.6.1 , it showed :

    error: NRFX_GPIOTE (defined at C:/ncs/v2.6.1/zephyr/modules/hal_nordic\nrfx/Kconfig:70,
    modules\hal_nordic\nrfx/Kconfig:70) is assigned in a configuration file, but is not directly user-
    configurable (has no prompt). It gets its value indirectly from other symbols. See
    http://docs.zephyrproject.org/latest/kconfig.html#CONFIG_NRFX_GPIOTE and/or look up NRFX_GPIOTE in
    the menuconfig/guiconfig interface. The Application Development Primer, Setting Configuration
    Values, and Kconfig - Tips and Best Practices sections of the manual might be helpful too.
    Parsing C:/ncs/v2.6.1/zephyr/Kconfig
    Loaded configuration 'C:/ncs/v2.6.1/zephyr/boards/arm/nrf5340dk_nrf5340/nrf5340dk_nrf5340_cpuapp_defconfig'
    Merged configuration 'C:/ncs/test/count_pulse/prj.conf'

    Also, It couldn't find

     

    #include <drivers/gpio.h>
    #include <zephyr.h>
    #include <logging/log.h>

    So, I have changed header files to  

    #include <zephyr/kernel.h>
    #include <zephyr/device.h>
    #include <zephyr/logging/log.h>
    #include <zephyr/drivers/gpio.h>

    Would you mind checking it?

  • Hi!

    Try this:

    pulse_counter.rar

    Connect a jumper-wire between P0.05 and P0.28 to test.

  • Thank you so much for the code. I appreciate your time. However, I have tested this frequency measurement based on Counting pulse at 100ms and it varies +- 150 at only 105KHz. 

    Average freq: 105420 [Hz]
    C: 10542
    Average freq: 105440 [Hz]
    C: 10544
    Average freq: 105430 [Hz]
    C: 10543
    Average freq: 105440 [Hz]
    C: 10544
    Average freq: 105420 [Hz]
    C: 10542
    Average freq: 105410 [Hz]
    C: 10541
    Average freq: 105390 [Hz]
    C: 10539
    Average freq: 105380 [Hz]
    C: 10538
    Average freq: 105390 [Hz]
    C: 10539
    Average freq: 105370 [Hz]
    C: 10537
    Average freq: 105330 [Hz]
    C: 10533
    Average freq: 105350 [Hz]
    C: 10535
    Average freq: 105370 [Hz]
    C: 10537
    Average freq: 105330 [Hz]
    C: 10533
    Average freq: 105320 [Hz]
    C: 10532
    Average freq: 105330 [Hz]
    C: 10533

    The variations are quite rough. Is it the highest limit of an nRF5340? 

    Although, I didn't provide the pin 28 a stable known frequency but my current setup did result stable frequency measurement on an Arduino Uno mini like this :-

     1. 13:58:25.443 -> 110344.83
     2. 13:58:25.803 -> 110344.83
     3. 13:58:26.079 -> 110344.83
     4. 13:58:26.423 -> 110344.83
     5. 13:58:26.716 -> 110344.83
    

  • Hi!

    Do you have a logic analyzer to check what the frequency deviation actually is? e.g. a saleae logic analyzer. 

Related