Max. measurable GPIO input frequency

Hi all, 

I have to measure the frequency of a PWM signal routed to one of the nRF52840 GPIO pins. 

The signal frequency is ~3MHz. 

The measurement is done with the help of two timers, the code is taken from this thread

// ------------------------------------------------------------ dependencies ---
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "nrf.h"
#include "boards.h"
#include "app_error.h"
#include "nrf_delay.h"
#include "app_util_platform.h"
#include "nrf_pwr_mgmt.h"
#include "nrf_gpio.h"
#include "nrf_drv_gpiote.h"



#define FREQ_MEASURE_PIN 24
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_PPI->CHEN |= 1 << 0;
  *(&(NRF_PPI->CH0_EEP)) = (uint32_t)&NRF_TIMER1->EVENTS_COMPARE[0];
  *(&(NRF_PPI->CH0_TEP)) = (uint32_t)&NRF_TIMER2->TASKS_STOP;
  NRF_PPI->CHENSET |= 1 << 0;
}

static void ppi_gpiote_counter_init()
{
  NRF_PPI->CHEN |= 1 << 1;
  *(&(NRF_PPI->CH1_EEP)) = (uint32_t)&NRF_GPIOTE->EVENTS_IN[0];
  *(&(NRF_PPI->CH1_TEP)) = (uint32_t)&NRF_TIMER2->TASKS_COUNT;
  NRF_PPI->CHENSET |= 1 << 1;
}

int main()
{
    uint32_t err_code = NRF_LOG_INIT(NULL);

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

    NRF_LOG_DEFAULT_BACKENDS_INIT();
    NRF_LOG_INFO("Timer Test");
    NVIC_EnableIRQ(TIMER1_IRQn);
    NVIC_SetPriority(TIMER1_IRQn, APP_IRQ_PRIORITY_LOW);	

    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;
    
    while(1)
    {
      NRF_LOG_FLUSH();
    }
}

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;
                  
      NRF_LOG_INFO("%lu Hz", NRF_TIMER2->CC[0]*time);

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

As long as the PWM frequency on the GPIO pin is below ~2.66 MHz, the measured value is correct, here some results for different frequencies (generated with a signal generator): 

Input Frequency 1.5MHz: 

Input Frequency 2.5MHz: 

If i increase the frequency to 3 MHz, the measured value is incorrect: 

According to this thread the nRF52 is not able to measure frequencies above 2.66 MHz. Is this true?? 


I have - additional to this problem - another problem with the code: 

I have a button on my PCB, which I am currently using to toggle a LED, here is the code: 

Initialization of the GPIO button input:

nrf_drv_gpiote_in_config_t in_config_toggle_button = NRFX_GPIOTE_CONFIG_IN_SENSE_TOGGLE(true); // Configure a pin to use a GPIO IN or PORT EVENT to detect any change on the pin.
in_config_toggle_button.pull = NRF_GPIO_PIN_PULLUP;
APP_ERROR_CHECK(nrf_drv_gpiote_in_init(BUTTON_1,  &in_config_toggle_button, button1_handler));  // Call handler when switch pin toggles
nrf_drv_gpiote_in_event_enable(BUTTON_1, true);

Button callback function:

static void button1_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action) //called when switch toggles
{
    UNUSED_VARIABLE(pin);
    UNUSED_VARIABLE(action);
    button1_state = 1; 
#ifdef LED_WHITE_ON
    nrf_drv_gpiote_out_toggle(LED_WHITE);
#endif
}

When I initialize and use the button like this WITH the timer code shown above, it does not output the frequency to the console. Instead, only the button's callback function is constantly called (the white LED then flashes constantly). It is as if the counter interrupt from my high frequency signal triggers the button's interrupt. Do I have to delete an interrupt flag in the timer interrupt? 

Thanks for your support.

Best Regards

hypn0

Parents
  • Hello hypn0,

    Sorry for the late reply. Without having checked or done the math, I believe this frequency is about the maximum frequency possible to measure using PPI. 

    The PPI itself is running at 16MHz. There i up to 1 tick delay on the PPI, so that gives you a 8MHz. The you have some delay in the GPIO itself. The pad capacitance is 5pF. Depending on the input signal you are measuring it will take a certain amount of time from you set the output high until the input on the nRF is actually pulled high. If this takes a few µs, then that would explain the delay you are seeing.

    If you try to use a logic analyzer to measure your input signal, and then use the PPI to toggle a pin on every GPIO event (so that you basically duplicate the input signal), what sort of delay are you seeing between the (PWM) input and the (GPIO) output using the PWM generator that you are using?

    Best regards,

    Edvin

  • Hi, I would like to use this method to measure continuously changing frequency onto a gpio pin on my nrf5340dk running nCS V2.6.1 , however, above exact code splits out error, I am assuming it is regarding the include files which in my case always starts with ''zephyr/...'' , I did couple of these changes but error persist. Also, I need some idea on which configuration lines on prj.config this code is using? another question is , will it be possible to have  6 instances of these measurements? 

  • Yes, that is a different SDK, which is not Zephyr. 

    try ignoring the includes on the top of the snippet above. As long as the code builds before you add those snippets, you shouldn't need anything else. These are bare metal registers, so they are not part of any library.

    Try running it in a sample that doesn't do much else at first, such as the ncs\zephyr\samples\hello_world. And try to not use TIMER0 or RTC0.

    frogrammer said:
    will it be possible to have  6 instances of these measurements

    I don't know the logics in this sample (it was the other user's code, so I didn't test it). The limiting factor would be the number of CC registers in the timer instance that you are using. They seem to be 8 (0-7) on all timer instances on the nRF5340.

    https://docs.nordicsemi.com/bundle/ps_nrf5340/page/timer.html#ariaid-title28

    Best regards,

    Edvin

  • I have tried compiling this code but these are the errors :

    C:/ncs/test/freq_calc/src/main.c: In function 'ppi_timer_stop_counter_init':
    C:/ncs/test/freq_calc/src/main.c:49:3: error: 'NRF_PPI' undeclared (first use in this function); did you mean 'NRF_SPU'?
       49 |   NRF_PPI->CHEN |= 1 << 0;
          |   ^~~~~~~
          |   NRF_SPU
    C:/ncs/test/freq_calc/src/main.c:49:3: note: each undeclared identifier is reported only once for each function it appears in
    C:/ncs/test/freq_calc/src/main.c: In function 'ppi_gpiote_counter_init':
    C:/ncs/test/freq_calc/src/main.c:57:3: error: 'NRF_PPI' undeclared (first use in this function); did you mean 'NRF_SPU'?
       57 |   NRF_PPI->CHEN |= 1 << 1;
          |   ^~~~~~~
          |   NRF_SPU
    C:/ncs/test/freq_calc/src/main.c: In function 'main':
    C:/ncs/test/freq_calc/src/main.c:65:25: warning: implicit declaration of function 'NRF_LOG_INIT'; did you mean 'Z_LOG_INST'? [-Wimplicit-function-declaration]
       65 |     uint32_t err_code = NRF_LOG_INIT(NULL);
          |                         ^~~~~~~~~~~~
          |                         Z_LOG_INST
    C:/ncs/test/freq_calc/src/main.c:72:5: warning: implicit declaration of function 'NRF_LOG_DEFAULT_BACKENDS_INIT' [-Wimplicit-function-declaration]
       72 |     NRF_LOG_DEFAULT_BACKENDS_INIT();
          |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    C:/ncs/test/freq_calc/src/main.c:73:5: warning: implicit declaration of function 'NRF_LOG_INFO' [-Wimplicit-function-declaration]
       73 |     NRF_LOG_INFO("Timer Test");
          |     ^~~~~~~~~~~~
    C:/ncs/test/freq_calc/src/main.c:75:35: error: 'APP_IRQ_PRIORITY_LOW' undeclared (first use in this function); did you mean 'NRFY_IRQ_PRIORITY_SET'?
       75 |     NVIC_SetPriority(TIMER1_IRQn, APP_IRQ_PRIORITY_LOW);
          |                                   ^~~~~~~~~~~~~~~~~~~~
          |                                   NRFY_IRQ_PRIORITY_SET
    C:/ncs/test/freq_calc/src/main.c:90:7: warning: implicit declaration of function 'NRF_LOG_FLUSH' [-Wimplicit-function-declaration]
       90 |       NRF_LOG_FLUSH();
          |       ^~~~~~~~~~~~~
    C:/ncs/test/freq_calc/src/main.c:65:14: warning: unused variable 'err_code' [-Wunused-variable]
       65 |     uint32_t err_code = NRF_LOG_INIT(NULL);
          |              ^~~~~~~~
    [52/149] Building C object zephyr/CMakeFiles/zephyr.dir/subsys/logging/log_core.c.obj
    ninja: build stopped: subcommand failed.
    FATAL ERROR: command exited with status 1: 'C:\ncs\toolchains\cf2149caf2\opt\bin\cmake.EXE' --build 'c:\ncs\test\freq_calc\build_1'
    
     *  The terminal process terminated with exit code: 1. 
     *  Terminal will be reused by tasks, press any key to close it. 

    Initially, i had errors with 

    NRF_GPIOTE , 
    NRF_TIMER2 then i have added the header file ''
    #include <nrfx_timer.h>
    #include <nrfx_gpiote.h>'' and it went away however, the NRF_PPI seems to be needed a library reference, which is i am not sure how to adjust. Aparently, NRF_LOG() / NRF_LOG_FLUSH() macro isnt supported anymore? found it through a discussion on this devzone, can i easily replace it by ''
    #include <zephyr/logging/log.h>'' , would you mind checking both issues?
Reply
  • I have tried compiling this code but these are the errors :

    C:/ncs/test/freq_calc/src/main.c: In function 'ppi_timer_stop_counter_init':
    C:/ncs/test/freq_calc/src/main.c:49:3: error: 'NRF_PPI' undeclared (first use in this function); did you mean 'NRF_SPU'?
       49 |   NRF_PPI->CHEN |= 1 << 0;
          |   ^~~~~~~
          |   NRF_SPU
    C:/ncs/test/freq_calc/src/main.c:49:3: note: each undeclared identifier is reported only once for each function it appears in
    C:/ncs/test/freq_calc/src/main.c: In function 'ppi_gpiote_counter_init':
    C:/ncs/test/freq_calc/src/main.c:57:3: error: 'NRF_PPI' undeclared (first use in this function); did you mean 'NRF_SPU'?
       57 |   NRF_PPI->CHEN |= 1 << 1;
          |   ^~~~~~~
          |   NRF_SPU
    C:/ncs/test/freq_calc/src/main.c: In function 'main':
    C:/ncs/test/freq_calc/src/main.c:65:25: warning: implicit declaration of function 'NRF_LOG_INIT'; did you mean 'Z_LOG_INST'? [-Wimplicit-function-declaration]
       65 |     uint32_t err_code = NRF_LOG_INIT(NULL);
          |                         ^~~~~~~~~~~~
          |                         Z_LOG_INST
    C:/ncs/test/freq_calc/src/main.c:72:5: warning: implicit declaration of function 'NRF_LOG_DEFAULT_BACKENDS_INIT' [-Wimplicit-function-declaration]
       72 |     NRF_LOG_DEFAULT_BACKENDS_INIT();
          |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    C:/ncs/test/freq_calc/src/main.c:73:5: warning: implicit declaration of function 'NRF_LOG_INFO' [-Wimplicit-function-declaration]
       73 |     NRF_LOG_INFO("Timer Test");
          |     ^~~~~~~~~~~~
    C:/ncs/test/freq_calc/src/main.c:75:35: error: 'APP_IRQ_PRIORITY_LOW' undeclared (first use in this function); did you mean 'NRFY_IRQ_PRIORITY_SET'?
       75 |     NVIC_SetPriority(TIMER1_IRQn, APP_IRQ_PRIORITY_LOW);
          |                                   ^~~~~~~~~~~~~~~~~~~~
          |                                   NRFY_IRQ_PRIORITY_SET
    C:/ncs/test/freq_calc/src/main.c:90:7: warning: implicit declaration of function 'NRF_LOG_FLUSH' [-Wimplicit-function-declaration]
       90 |       NRF_LOG_FLUSH();
          |       ^~~~~~~~~~~~~
    C:/ncs/test/freq_calc/src/main.c:65:14: warning: unused variable 'err_code' [-Wunused-variable]
       65 |     uint32_t err_code = NRF_LOG_INIT(NULL);
          |              ^~~~~~~~
    [52/149] Building C object zephyr/CMakeFiles/zephyr.dir/subsys/logging/log_core.c.obj
    ninja: build stopped: subcommand failed.
    FATAL ERROR: command exited with status 1: 'C:\ncs\toolchains\cf2149caf2\opt\bin\cmake.EXE' --build 'c:\ncs\test\freq_calc\build_1'
    
     *  The terminal process terminated with exit code: 1. 
     *  Terminal will be reused by tasks, press any key to close it. 

    Initially, i had errors with 

    NRF_GPIOTE , 
    NRF_TIMER2 then i have added the header file ''
    #include <nrfx_timer.h>
    #include <nrfx_gpiote.h>'' and it went away however, the NRF_PPI seems to be needed a library reference, which is i am not sure how to adjust. Aparently, NRF_LOG() / NRF_LOG_FLUSH() macro isnt supported anymore? found it through a discussion on this devzone, can i easily replace it by ''
    #include <zephyr/logging/log.h>'' , would you mind checking both issues?
Children
Related