How to Measure Period of input signal at TWO GPIO's

Hi All, I'm trying to capture the TWO pulses at TWO GPIO's by measuring the period of the signal. Im using the following code. This code is perfectly working for 1 Signal at 1 GPIO. After making it for TWO signals at 2 GPIO's - Its working for 1 and not showing for second or showing some values. I checked both by commenting one by one and it seems both programs are working fine but when I try to work for boths its showing one and not responding for other.

The code is attached below,

/**
 * Copyright (c) 2014 - 2019, Nordic Semiconductor ASA
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form, except as embedded into a Nordic
 *    Semiconductor ASA integrated circuit in a product or a software update for
 *    such product, must reproduce the above copyright notice, this list of
 *    conditions and the following disclaimer in the documentation and/or other
 *    materials provided with the distribution.
 *
 * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * 4. This software, with or without modification, must only be used with a
 *    Nordic Semiconductor ASA integrated circuit.
 *
 * 5. Any software provided in binary form under this license must not be reverse
 *    engineered, decompiled, modified and/or disassembled.
 *
 * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
/** @file
* @defgroup temperature_example_main main.c
* @{
* @ingroup temperature_example
* @brief Temperature Example Application main file.
* @details
* This file contains the source code for a sample application using the temperature sensor.
* This contains workaround for PAN_028 rev2.0A anomalies 28, 29,30 and 31. PAN 43 is not covered.
*  - PAN_028 rev2.0A anomaly 28 - TEMP: Negative measured values are not represented correctly
*  - PAN_028 rev2.0A anomaly 29 - TEMP: Stop task clears the TEMP register.
*  - PAN_028 rev2.0A anomaly 30 - TEMP: Temp module analog front end does not power down when DATARDY event occurs.
*  - PAN_028 rev2.0A anomaly 31 - TEMP: Temperature offset value has to be manually loaded to the TEMP module
*  - PAN_028 rev2.0A anomaly 43 - TEMP: Using PPI between DATARDY event and START task is not functional.
*
*/
#include <stdint.h>

#include "nrf_delay.h"
#include "app_error.h"

#include "nrf_drv_ppi.h"
#include "nrf_drv_timer.h"

#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"

#include <stdbool.h>
//#include <stdint.h>
#include "nrf.h"
#include "nrf_drv_timer.h"
#include "bsp.h"
#include "app_error.h"

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "nrf.h"
#include "nrf_delay.h"
#include "app_error.h"
#include "bsp.h"
#include "nrf_drv_ppi.h"
#include "nrf_drv_timer.h"
#include "nrf_drv_ppi.h"

#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"

const nrf_drv_timer_t capture_timer_1 = NRF_DRV_TIMER_INSTANCE(1);
const nrf_drv_timer_t capture_timer = NRF_DRV_TIMER_INSTANCE(3);

//#define NRF_LOG_BACKEND_UART_TX_PIN  NRF_GPIO_PIN_MAP(1, 7)

#define BUTTON_Reset_PIN          NRF_GPIO_PIN_MAP(1, 0)   // 30 // NRF_GPIO_PIN_MAP(1,12)

//////////For 1st Period/////
#define SAMPLE_PIN                      NRF_GPIO_PIN_MAP(1, 6)
#define TIMER_PRESCALER                 NRF_TIMER_FREQ_16MHz
#define GPIOTE_CH_CAPTURE               1
#define GPIOTE_CH_RESTART               1
#define GPIOTE_CH_SELFTEST              2

#define SELF_TEST                       1

//////////For 2nd Period/////

#define SAMPLE_PIN_1                      NRF_GPIO_PIN_MAP(1, 5)
#define TIMER_PRESCALER_1                 NRF_TIMER_FREQ_16MHz
#define GPIOTE_CH_CAPTURE_1               1
#define GPIOTE_CH_RESTART_1               1
#define GPIOTE_CH_SELFTEST_1              2

#define SELF_TEST_1                       1

//////////////////PWM/////////////////////
//#define CMFB_CLK_PIN 

int16_t buf[] = {(1 << 15) | 5}; // Inverse polarity (bit 15)

/////editing pasting from PPI Example


#define PPI_EXAMPLE_TIMERS_PHASE_SHIFT_DELAY   (1)// (10)    // 1s = 10 * 100ms (Timer 0 interrupt)
#define PPI_EXAMPLE_TIMER0_INTERVAL             (1)//(100)   // Timer interval in milliseconds
#define PPI_EXAMPLE_TIMER3_INTERVAL             (1)//(2000)  // Timer interval in milliseconds
#define PPI_EXAMPLE_TIMER4_INTERVAL             (1)//(2000)  // Timer interval in milliseconds
#define PPI_EXAMPLE_TIMER1_INTERVAL             (1)//(2000)  // Timer interval in milliseconds
#define PPI_EXAMPLE_TIMER2_INTERVAL             (1)//(2000)  // Timer interval in milliseconds




static nrf_ppi_channel_t n_ppi_channel1;
static nrf_ppi_channel_t n_ppi_channel2;


static nrf_ppi_channel_t m_ppi_channel1;
static nrf_ppi_channel_t m_ppi_channel2;


static volatile uint32_t m_counter;

static void timer0_event_handler(nrf_timer_event_t event_type, void * p_context)
{
    ++m_counter;
}

/* Timer event handler. Not used since Timer1 and Timer2 are used only for PPI. */
static void empty_timer_handler(nrf_timer_event_t event_type, void * p_context)
{
}


#if SELF_TEST
const nrf_drv_timer_t test_timer = NRF_DRV_TIMER_INSTANCE(4);

static void run_self_test_pin(uint32_t pin_no, uint32_t us_on, uint32_t us_off)
{
    uint32_t err_code;
    static nrf_ppi_channel_t ppi_ch_test_pin_run, ppi_ch_test_pin_run2;
    
    nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
    timer_cfg.frequency = NRF_TIMER_FREQ_16MHz;
    timer_cfg.bit_width = NRF_TIMER_BIT_WIDTH_32;
    err_code = nrf_drv_timer_init(&test_timer, &timer_cfg, empty_timer_handler);
    APP_ERROR_CHECK(err_code);
    
    nrf_drv_timer_extended_compare(&test_timer, 0, us_on,          0,                               false);
    nrf_drv_timer_extended_compare(&test_timer, 1, us_on + us_off, TIMER_SHORTS_COMPARE1_CLEAR_Msk, false);
    
    NRF_GPIOTE->CONFIG[GPIOTE_CH_SELFTEST] = GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos |
                                             GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos |
                                             pin_no << GPIOTE_CONFIG_PSEL_Pos;
                                             
    nrfx_ppi_channel_alloc(&ppi_ch_test_pin_run);
    nrfx_ppi_channel_assign(ppi_ch_test_pin_run,
                            nrf_drv_timer_compare_event_address_get(&test_timer, 0),
                            (uint32_t)&NRF_GPIOTE->TASKS_CLR[GPIOTE_CH_SELFTEST]);
    nrfx_ppi_channel_enable(ppi_ch_test_pin_run);
    
    nrfx_ppi_channel_alloc(&ppi_ch_test_pin_run2);
    nrfx_ppi_channel_assign(ppi_ch_test_pin_run2,
                            nrf_drv_timer_compare_event_address_get(&test_timer, 1),
                            (uint32_t)&NRF_GPIOTE->TASKS_SET[GPIOTE_CH_SELFTEST]);
    nrfx_ppi_channel_enable(ppi_ch_test_pin_run2);
    
    nrf_drv_timer_clear(&test_timer);
    nrf_drv_timer_resume(&test_timer);
}
#endif






#if SELF_TEST_1
const nrf_drv_timer_t test_timer_1 = NRF_DRV_TIMER_INSTANCE(2);

static void run_self_test_1_pin(uint32_t pin_no_1, uint32_t us_on_1, uint32_t us_off_1)
{
    uint32_t err_code;
    static nrf_ppi_channel_t ppi_ch_test_pin_run_1, ppi_ch_test_pin_run2_1;
    
    nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
    timer_cfg.frequency = NRF_TIMER_FREQ_16MHz;
    timer_cfg.bit_width = NRF_TIMER_BIT_WIDTH_32;
    err_code = nrf_drv_timer_init(&test_timer_1, &timer_cfg, empty_timer_handler);
    APP_ERROR_CHECK(err_code);
    
    nrf_drv_timer_extended_compare(&test_timer_1, 0, us_on_1,          0,                               false);
    nrf_drv_timer_extended_compare(&test_timer_1, 1, us_on_1 + us_off_1, TIMER_SHORTS_COMPARE1_CLEAR_Msk, false);
    
    NRF_GPIOTE->CONFIG[GPIOTE_CH_SELFTEST_1] = GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos |
                                             GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos |
                                             pin_no_1 << GPIOTE_CONFIG_PSEL_Pos;
                                             
    nrfx_ppi_channel_alloc(&ppi_ch_test_pin_run_1);
    nrfx_ppi_channel_assign(ppi_ch_test_pin_run_1,
                            nrf_drv_timer_compare_event_address_get(&test_timer_1, 0),
                            (uint32_t)&NRF_GPIOTE->TASKS_CLR[GPIOTE_CH_SELFTEST_1]);
    nrfx_ppi_channel_enable(ppi_ch_test_pin_run_1);
    
    nrfx_ppi_channel_alloc(&ppi_ch_test_pin_run2_1);
    nrfx_ppi_channel_assign(ppi_ch_test_pin_run2_1,
                            nrf_drv_timer_compare_event_address_get(&test_timer_1, 1),
                            (uint32_t)&NRF_GPIOTE->TASKS_SET[GPIOTE_CH_SELFTEST_1]);
    nrfx_ppi_channel_enable(ppi_ch_test_pin_run2_1);
    
    nrf_drv_timer_clear(&test_timer_1);
    nrf_drv_timer_resume(&test_timer_1);
}
#endif


static void gpiote_capture_init(void)
{
    uint32_t err_code;
    static nrf_ppi_channel_t ppi_ch_gpiote_capture;
    static nrf_ppi_channel_t ppi_ch_gpiote_restart;
    
    // Optionally, enable pullup or pulldown on the input pin
    //nrf_gpio_cfg_input(SAMPLE_PIN, NRF_GPIO_PIN_PULLUP);

    // Allocate two PPI channels
    nrfx_ppi_channel_alloc(&ppi_ch_gpiote_capture);
    nrfx_ppi_channel_alloc(&ppi_ch_gpiote_restart);
    
    // Configure the capture timer
    nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
    timer_cfg.frequency = TIMER_PRESCALER;
    timer_cfg.bit_width = NRF_TIMER_BIT_WIDTH_32;
    err_code = nrf_drv_timer_init(&capture_timer, &timer_cfg, empty_timer_handler);
    APP_ERROR_CHECK(err_code);

    // The GPIOTE driver doesn't support two GPIOTE channels on the same pin, so direct register access is necessary
    NRF_GPIOTE->CONFIG[GPIOTE_CH_CAPTURE] = GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos |
                                            GPIOTE_CONFIG_POLARITY_HiToLo << GPIOTE_CONFIG_POLARITY_Pos |
                                            SAMPLE_PIN << GPIOTE_CONFIG_PSEL_Pos;
    NRF_GPIOTE->CONFIG[GPIOTE_CH_RESTART] = GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos |
                                            GPIOTE_CONFIG_POLARITY_LoToHi << GPIOTE_CONFIG_POLARITY_Pos |
                                            SAMPLE_PIN << GPIOTE_CONFIG_PSEL_Pos;

    // Assign a PPI channel to capture the current timer state and store it in CC register 0
    nrfx_ppi_channel_assign(ppi_ch_gpiote_capture, 
                            (uint32_t)&NRF_GPIOTE->EVENTS_IN[GPIOTE_CH_CAPTURE], 
                            nrf_drv_timer_capture_task_address_get(&capture_timer, 0));
    
    // Assign a second PPI channel to restart the timer when a new pulse is detected
    nrfx_ppi_channel_assign(ppi_ch_gpiote_restart, 
                            (uint32_t)&NRF_GPIOTE->EVENTS_IN[GPIOTE_CH_RESTART], 
                            nrf_drv_timer_task_address_get(&capture_timer, NRF_TIMER_TASK_CLEAR));
    
    // Enable both PPI channels                
    nrfx_ppi_channel_enable(ppi_ch_gpiote_capture);
    nrfx_ppi_channel_enable(ppi_ch_gpiote_restart);

    // Make sure the GPIOTE capture in event is cleared. This will be  used to detect if a capture has occured. 
    NRF_GPIOTE->EVENTS_IN[GPIOTE_CH_CAPTURE] = 0;
    
    // Start the timer
    nrfx_timer_resume(&capture_timer);
}


static void gpiote_capture_1_init(void)
{
    uint32_t err_code;
    static nrf_ppi_channel_t ppi_ch_gpiote_capture_1;
    static nrf_ppi_channel_t ppi_ch_gpiote_restart_1;
    
    // Optionally, enable pullup or pulldown on the input pin
    //nrf_gpio_cfg_input(SAMPLE_PIN, NRF_GPIO_PIN_PULLUP);

    // Allocate two PPI channels
    nrfx_ppi_channel_alloc(&ppi_ch_gpiote_capture_1);
    nrfx_ppi_channel_alloc(&ppi_ch_gpiote_restart_1);
    
    // Configure the capture timer
    nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
    timer_cfg.frequency = TIMER_PRESCALER_1;
    timer_cfg.bit_width = NRF_TIMER_BIT_WIDTH_32;
    err_code = nrf_drv_timer_init(&capture_timer_1, &timer_cfg, empty_timer_handler);
    APP_ERROR_CHECK(err_code);

    // The GPIOTE driver doesn't support two GPIOTE channels on the same pin, so direct register access is necessary
    NRF_GPIOTE->CONFIG[GPIOTE_CH_CAPTURE_1] = GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos |
                                            GPIOTE_CONFIG_POLARITY_HiToLo << GPIOTE_CONFIG_POLARITY_Pos |
                                            SAMPLE_PIN_1 << GPIOTE_CONFIG_PSEL_Pos;
    NRF_GPIOTE->CONFIG[GPIOTE_CH_RESTART_1] = GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos |
                                            GPIOTE_CONFIG_POLARITY_LoToHi << GPIOTE_CONFIG_POLARITY_Pos |
                                            SAMPLE_PIN_1 << GPIOTE_CONFIG_PSEL_Pos;

    // Assign a PPI channel to capture the current timer state and store it in CC register 0
    nrfx_ppi_channel_assign(ppi_ch_gpiote_capture_1, 
                            (uint32_t)&NRF_GPIOTE->EVENTS_IN[GPIOTE_CH_CAPTURE_1], 
                            nrf_drv_timer_capture_task_address_get(&capture_timer_1, 0));
    
    // Assign a second PPI channel to restart the timer when a new pulse is detected
    nrfx_ppi_channel_assign(ppi_ch_gpiote_restart_1, 
                            (uint32_t)&NRF_GPIOTE->EVENTS_IN[GPIOTE_CH_RESTART_1], 
                            nrf_drv_timer_task_address_get(&capture_timer_1, NRF_TIMER_TASK_CLEAR));
    
    // Enable both PPI channels                
    nrfx_ppi_channel_enable(ppi_ch_gpiote_capture_1);
    nrfx_ppi_channel_enable(ppi_ch_gpiote_restart_1);

    // Make sure the GPIOTE capture in event is cleared. This will be  used to detect if a capture has occured. 
    NRF_GPIOTE->EVENTS_IN[GPIOTE_CH_CAPTURE_1] = 0;
    
    // Start the timer
    nrfx_timer_resume(&capture_timer_1);
}


static uint32_t timer_capture_value_get(void)
{
    // Make sure the capture event occured before checking the capture register
    if(NRF_GPIOTE->EVENTS_IN[GPIOTE_CH_CAPTURE] != 0)
    {
        // Clear the capture event
        NRF_GPIOTE->EVENTS_IN[GPIOTE_CH_CAPTURE] = 0;
        
        // Return the stored capture value in the timer
        return nrf_drv_timer_capture_get(&capture_timer, 0);
    }
    else
    {
        // In case no capture occured, return 0
        return 0;
    }
}

static uint32_t timer_capture_1_value_get(void)
{
    // Make sure the capture event occured before checking the capture register
    if(NRF_GPIOTE->EVENTS_IN[GPIOTE_CH_CAPTURE_1] != 0)
    {
        // Clear the capture event
        NRF_GPIOTE->EVENTS_IN[GPIOTE_CH_CAPTURE_1] = 0;
        
        // Return the stored capture value in the timer
        return nrf_drv_timer_capture_get(&capture_timer_1, 0);
    }
    else
    {
        // In case no capture occured, return 0
        return 0;
    }
}

/** @brief Function for main application entry.
 */
int main(void)
{
nrf_gpio_cfg_input(BUTTON_Reset_PIN, NRF_GPIO_PIN_PULLUP);
nrf_gpio_cfg_output(ENB_MOD_2_PIN);
nrf_gpio_cfg_output(FREQ_CNT_PIN);
nrf_gpio_cfg_output(CMFB_CLK_PIN);
nrf_gpio_cfg_output(PHI_RST_PIN);
nrf_gpio_cfg_output(RST_CMFB_PIN);
nrf_gpio_cfg_output(ENB_PWDN1);
nrf_gpio_cfg_output(ENB_PWDN2);
nrf_gpio_cfg_output(ENB_PWDN3);


    APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
    NRF_LOG_DEFAULT_BACKENDS_INIT();
    
    NRF_LOG_INFO("Pulse capture example started");

//gpiote_capture_init();
  gpiote_capture_1_init();
//gpiote_capture_1_init();
   // gpiote_capture_1_init();
  #if SELF_TEST
    run_self_test_pin(LED_1, 678, 10);
#endif  
#if SELF_TEST_1
    run_self_test_1_pin(LED_2, 678, 10);
#endif

    while (true)
    {
        uint32_t captured_pulse_length_1 = timer_capture_1_value_get();
       uint32_t captured_pulse_length = timer_capture_value_get();
        
        if(captured_pulse_length > 0)
        {
            NRF_LOG_INFO("Period 1: %i us", (captured_pulse_length * (1 << TIMER_PRESCALER) / 16));
        } 
       
        if(captured_pulse_length_1 > 0)
        {
            NRF_LOG_INFO("\tPeriod 2: %i us", (captured_pulse_length_1 * (1 << TIMER_PRESCALER_1) / 16));
        }
       // else NRF_LOG_INFO("No capture detected");
        
       // nrf_delay_ms(500);   */
        NRF_LOG_FLUSH();

Parents
  • Hi,

    Are you using a devkit?

    This code is perfectly working for 1 Signal at 1 GPIO. After making it for TWO signals at 2 GPIO's - Its working for 1 and not showing for second or showing some values.

    If you disable one, does the other work, meaning that it only fails to work when both are enabled? Or does one of them never work? 

    What about if you change the pin that is used as input pin?

    regards

    Jared 

Reply
  • Hi,

    Are you using a devkit?

    This code is perfectly working for 1 Signal at 1 GPIO. After making it for TWO signals at 2 GPIO's - Its working for 1 and not showing for second or showing some values.

    If you disable one, does the other work, meaning that it only fails to work when both are enabled? Or does one of them never work? 

    What about if you change the pin that is used as input pin?

    regards

    Jared 

Children
  • Hi Jared, Thanks for the connection.

    1 - Yes Im using DEVKIT-NRF52840

    2 - Yes, when I disable 1 or comment the following box the other is working fine and when I comment other box the 1st is working fine. Even I tested separately, it was working fine. 

  • I again tested by commenting each line of code for one GPIO it is working finefor 2nd GPIO and also by commenting each line of 2nd GPIO it is working fine for 1st GPIO. But when I want to get the result from both simultaneously or when detected period, it shows only 1 GPIO  and ignores the 2nd GPIO signal.

  • Hi,

    In the init for the GPIOTE peripheral you call 

      NRF_GPIOTE->CONFIG[GPIOTE_CH_CAPTURE] = GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos |
                                                GPIOTE_CONFIG_POLARITY_HiToLo << GPIOTE_CONFIG_POLARITY_Pos |
                                                SAMPLE_PIN << GPIOTE_CONFIG_PSEL_Pos;
        NRF_GPIOTE->CONFIG[GPIOTE_CH_RESTART] = GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos |
                                                GPIOTE_CONFIG_POLARITY_LoToHi << GPIOTE_CONFIG_POLARITY_Pos |
                                                SAMPLE_PIN << GPIOTE_CONFIG_PSEL_Pos;
    and then repeat that for the second init function only with different SAMPLE pin but same n since
    GPIOTE_CH_CAPTURE_1 and GPIOTE_CH_CAPTURE are both set to 1.
    This will just overwrite the previous configuration of the CONFIG register.

    You can double check that the GPIOTE CONFIG register is configured correctly and it's not overwritten by setting a breakpoint after the init routine and check the value of the CONFIG register.
    regards
    Jared 
  • Hi Jared.

    Thanks for pointing out the problem. I got your point and issue is resolved now. Thanks for your nice cooperation.

Related