This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

SDK15.3.0: Counting Rising Edges only for 1 ms using the GPIOTE example

I am using the gpiote example in the peripheral folder as a base project which works as is. I can see that the example is counting rising edges properly matching the frequency on my scope in a continuous way; however, I am struggling to change it to satisfy the following requirements:

- Fire the timer_handler_read event on demand from a button press.

- Get count of the rising edges that occurred for 1ms

- Average out 5 sampling counts for accuracy

Thanks

Parents
  • Hi

    Can you provide the full path to the example you are using?

    The gpiote example uses an output channel only, not an input channel, so I assume you have made quite a bit of changes to it?

    Maybe you can share the code you are using, and I can try to provide input on how best to implement what you need. 

    Best regards
    Torbjørn

  • Hi,

    This is the path: \nRF52_SDK\15.3.0\examples\peripheral\gpiote

    I am using it as is, the only thing I changed was the GPIO pin assignment from 27 to 25, the rest is all the same. I hope we are talking about the same example.

    Thanks, 

  • Hi 

    You talk about the example counting rising edges, which sounds like you are using a GPIOTE in channel to generate an event/interrupt based on some signal applied to the pin, but maybe I misunderstood?

    The GPIOTE example only uses an out channel, no in channel is configured. 

    Also, you refer to a timer_handler_read event, but I can't find a reference to this either in the example. 

    Best regards
    Torbjørn

  • ,

    You are right, I mixed up the projects, I found a sample code from Sigurd here: NRF52 Calculation Method Between Rising Edges and done my modifications (file attached) to fit my needs. By adding a button to trigger the read timer on button push, I managed to make it work but 2 things I still need to figure it out.

    #1. When pushing the button I get multiple inaccurate readings and I think it has to do with the push button when changing states. I would like the button to trigger the read timer once.

    #2: While 2 timers/ppi channels work, is it possible to use only one timer and one ppi channel to accomplish this? If yes, any guidance would be appreciated?

    /**
     * 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.
     * 
     */
    
    #include <stdbool.h>
    #include "nrf.h"
    #include "nrf_drv_gpiote.h"
    #include "nrf_drv_ppi.h"
    #include "nrf_drv_timer.h"
    #include "nrf_delay.h"
    #include "app_error.h"
    #include "boards.h"
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    #include "app_uart.h"
    #include "nrf_pwr_mgmt.h"
    #include "bsp.h"
    #include "math.h"
    
    #define PIN_IN  25
    #define COUNT_READ_INTERVAL 1 // Interval in ms to read the counting timer (DEFAULT 10 SEC)
    #define INT1 BUTTON_1
    
    static const nrf_drv_timer_t m_timer_count = NRF_DRV_TIMER_INSTANCE(1);
    static const nrf_drv_timer_t m_timer_read = NRF_DRV_TIMER_INSTANCE(2);
    volatile uint8_t sampleCount = 0;
    volatile uint32_t accuValues = 0;
    
    
    nrf_ppi_channel_t ppi_channel_1, ppi_channel_2;
    
    
    void in_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
    {
    }
    
    void timer_handler_count(nrf_timer_event_t event_type, void * p_context)
    {
    }
    
    void timer_handler_read(nrf_timer_event_t event_type, void * p_context)
    {
      uint32_t risingEdgecount = nrf_drv_timer_capture_get(&m_timer_count, NRF_TIMER_CC_CHANNEL0);
      
      switch (event_type)
        {
            case NRF_TIMER_EVENT_COMPARE0:
             if(risingEdgecount > 0)
             {
                  //uint32_t risingEdgecount = nrf_drv_timer_capture_get(&m_timer_count, NRF_TIMER_CC_CHANNEL0);
                accuValues += risingEdgecount;
                sampleCount += 1;
    
    //            if (sampleCount == 5 && accuValues == 0)
    //            {
    //              sampleCount = 0;
    //              break;
    //            }
                
                if (sampleCount == 5)
                {
                  NRF_LOG_INFO("timer_handler_read %d", accuValues/sampleCount);
                  //NRF_TIMER1->TASKS_STOP = 1;
                  //NRF_TIMER2->TASKS_STOP = 1;
                  nrf_drv_timer_disable(&m_timer_read);
                  nrf_drv_timer_disable(&m_timer_count);
                  sampleCount = 0;
                  accuValues = 0;
                }
             }
             break;
            default:
                //Do nothing.
                break;
        }
    }
    
    /**
     * @brief Function for configuring: PIN_IN pin for input sensing
     */
    static void gpiote_init(void)
    {
        ret_code_t err_code;
    
        err_code = nrf_drv_gpiote_init();
        APP_ERROR_CHECK(err_code);
    
        nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_LOTOHI(true);
        in_config.pull = NRF_GPIO_PIN_NOPULL;
    
        err_code = nrf_drv_gpiote_in_init(PIN_IN, &in_config, in_pin_handler);
        APP_ERROR_CHECK(err_code);
        
        nrf_drv_gpiote_in_event_enable(PIN_IN, false);
    }
    
    void timer_init()
    {
        ret_code_t err_code;
    
        // Configure TIMER1 for counting of low to high events on GPIO
        nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
        timer_cfg.bit_width = NRF_TIMER_BIT_WIDTH_32;
        timer_cfg.mode = NRF_TIMER_MODE_LOW_POWER_COUNTER;
        err_code = nrf_drv_timer_init(&m_timer_count, &timer_cfg, timer_handler_count);
        APP_ERROR_CHECK(err_code);
    
        // Configure TIMER2 for reading the counter timer at a given interval COUNT_READ_INTERVAL
        timer_cfg.mode = NRF_TIMER_MODE_TIMER;
        err_code = nrf_drv_timer_init(&m_timer_read, &timer_cfg, timer_handler_read);
        APP_ERROR_CHECK(err_code);
        
        uint32_t ticks = nrf_drv_timer_ms_to_ticks(&m_timer_read, COUNT_READ_INTERVAL);
        nrf_drv_timer_extended_compare(&m_timer_read,
                                       NRF_TIMER_CC_CHANNEL0,
                                       ticks,
                                       NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,
                                       true);
        nrf_drv_timer_enable(&m_timer_read);
    }
    
    void ppi_init()
    {
        ret_code_t err_code;
    
        err_code = nrf_drv_ppi_init();
        APP_ERROR_CHECK(err_code);
        
        err_code = nrf_drv_ppi_channel_alloc(&ppi_channel_1);
        APP_ERROR_CHECK(err_code);
        
        err_code = nrf_drv_ppi_channel_alloc(&ppi_channel_2);
        APP_ERROR_CHECK(err_code);
        
        uint32_t gpiote_evt_addr_1              = nrf_drv_gpiote_in_event_addr_get(PIN_IN);
        
        uint32_t timer_count_count_task_addr    = nrf_drv_timer_task_address_get(&m_timer_count, NRF_TIMER_TASK_COUNT);
        uint32_t timer_count_capture_task_addr  = nrf_drv_timer_task_address_get(&m_timer_count, NRF_TIMER_TASK_CAPTURE0);
        uint32_t timer_count_clear_task_addr    = nrf_drv_timer_task_address_get(&m_timer_count, NRF_TIMER_TASK_CLEAR);
        
        uint32_t timer_read_compare_event_addr  = nrf_drv_timer_event_address_get(&m_timer_read, NRF_TIMER_EVENT_COMPARE0);
        
        
        err_code = nrf_drv_ppi_channel_assign(ppi_channel_1,
                                              gpiote_evt_addr_1,
                                              timer_count_count_task_addr); // Trigger timer count task when GPIOTE pin go from low to high.
    
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_drv_ppi_channel_assign(ppi_channel_2,
                                              timer_read_compare_event_addr,
                                              timer_count_capture_task_addr); // Capture counter timer using PPI to make sure this is not delayed by BLE interrupt
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_drv_ppi_channel_fork_assign(ppi_channel_2,
                                                   timer_count_clear_task_addr); // Clear counter timer using PPI to make sure this is not delayed by BLE interrupt
        APP_ERROR_CHECK(err_code);
        
    
        err_code = nrf_drv_ppi_channel_enable(ppi_channel_1);
        APP_ERROR_CHECK(err_code);
        err_code = nrf_drv_ppi_channel_enable(ppi_channel_2);
        APP_ERROR_CHECK(err_code);
    }
    
    void button_handler(nrf_drv_gpiote_pin_t pin_no, nrf_gpiote_polarity_t button_action)
    {
        //uint32_t err_code = app_timer_start(m_timer, APP_TIMER_TICKS(500), NULL);
        //nrf_gpio_pin_set(LED_1);
        //nrfx_timer_enable(&m_timer_led);
        //APP_ERROR_CHECK(err_code);
        //nrf_drv_timer_enable(&m_timer_read);
    
        switch (button_action)
        {
            case NRF_GPIOTE_POLARITY_HITOLO:
    //              NRF_TIMER1->TASKS_START = 1;
    //              NRF_TIMER2->TASKS_START = 1;
                  if (!nrf_drv_timer_is_enabled(&m_timer_read))
                    nrf_drv_timer_enable(&m_timer_read);
    
                  if (!nrf_drv_timer_is_enabled(&m_timer_count))
                    nrf_drv_timer_enable(&m_timer_count);
                  break;
            default:
                //Do nothing.
                break;
        }
    }
    
    static void init_button(void)
    {
    	//uint32_t err_code;
    	
    	//err_code = nrf_drv_gpiote_init();
    	//APP_ERROR_CHECK(err_code);
    	
            //nrf_gpio_cfg_output(LED_1);
    
    	nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
    	in_config.pull = NRF_GPIO_PIN_PULLUP;
    	
    	uint32_t err_code = nrf_drv_gpiote_in_init(INT1, &in_config, button_handler);
    	APP_ERROR_CHECK(err_code);
    	nrf_drv_gpiote_in_event_enable(INT1, true);
    }
    
    /**
     * @brief Function for application main entry.
     */
    int main(void)
    {
        //nrf_power_dcdcen_set(1);
        uint32_t err_code = NRF_LOG_INIT(NULL);
        APP_ERROR_CHECK(err_code);
    
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    
        //lfclk_config();
        //power_management_init();
        gpiote_init();
        timer_init();
        ppi_init();
        init_button();
    
        NRF_LOG_INFO("Example start\r\n");
        
        while (true)
       {
            // Wait for an event.
            __WFE();
            while(NRF_LOG_PROCESS() == true);
        }
    }
    
    
    /** @} */
    

    Thanks

  • Hi

    Thanks for the clarification, that makes more sense Slight smile

    1) Are you making sure not to start the timer if it is already running?

    When pressing a button there is always a risk that you will get multiple activations from a single button press, so you might want to make sure you can only accept a new button press once the previous action is completed. 

    2) I don't think you can remove any PPI channels without risking dropped flanks, but you should have 16 PPI channels in total, so it is rare to run low on these?

    In terms of timers you could change one of the timers for an RTC timer, if you have more of these available. Normally you have at least one RTC timer available, after the BLE stack takes one and the app_timer module takes another. 

    If you are using the app_timer module you could also use this to schedule the start/stop of the counter (instead of a dedicated RTC/TIMER), but then you could be affected by other interrupts, leading to inaccurate readings. 

    Best regards
    Torbjørn

  • Hi,

    Thanks for the feedback. I've replaced the button with a 3rd timer that triggers the count which works just fine. However, the current consumption increased from ~10 uA up to 400uA.

    Is this because the 3rd timer requires HF clock? How can I reduce the current consumption while using Timer3?

    Thanks

  • Hi

    A TIMER module in counter mode should draw less current than one in timer mode, but is this the only TIMER module that you are using?

    A TIMER in timer mode will use considerably more current than the RTC timers, because they use the HF clock as you say. 

    Best regards
    Torbjørn

Reply Children
No Data
Related