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

Count slow pulses, most power friendly way

nRF52832 custom board plus some devices. Using SDK 15

I want to count pulses from a previously configured accelerometer. Pulses are slow, around 500 per minute.

I have a working implementation using ppi, pin SENSE and a timer in counter mode. 

  ret_code_t err_code;
    if(!nrfx_gpiote_is_init()){
	err_code = nrfx_gpiote_init();
	APP_ERROR_CHECK(err_code);
    }

    nrfx_timer_config_t timer_cfg = NRFX_TIMER_DEFAULT_CONFIG;
    timer_cfg.mode = NRF_TIMER_MODE_COUNTER;

    nrfx_timer_init(&m_timer, &timer_cfg, timer_dummy_handler);

    nrfx_gpiote_in_config_t i_config = NRFX_GPIOTE_CONFIG_IN_SENSE_HITOLO(false);
    i_config.pull = NRF_GPIO_PIN_PULLUP;

    nrfx_gpiote_in_init(INT1, &i_config, NULL);
    nrf_gpio_cfg_sense_input(INT1, NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_HIGH);
    nrfx_gpiote_in_event_enable(INT1, true);

    err_code = nrf_drv_ppi_init(); 
    if(err_code != NRF_ERROR_MODULE_ALREADY_INITIALIZED){
	APP_ERROR_CHECK(err_code);
    }

    nrf_ppi_channel_t ppi_channel;
    nrfx_ppi_channel_alloc(&ppi_channel);
    nrfx_ppi_channel_assign(ppi_channel, nrfx_gpiote_in_event_addr_get(INT1), nrfx_timer_task_address_get(&m_timer, NRF_TIMER_TASK_COUNT));

    nrfx_ppi_channel_enable(ppi_channel);
    nrfx_timer_enable(&m_timer);

However, I think my approach is wrong and using a timer as a counter is not the most power efficient way. 

Comparing used power with same board running eddystone beacon example from SDK (green) as reference, this program running (red trace) yields absurd power use.

My program draws more than 1mA average vs 54uA average. :( !!! 

My questions:

-Using TIMER burns so much power? Is HF clock always ON when using TIMERS? Even as a counter with ppi as clock?

-Whats the most efficient way to do this? Time interval does not need to be very precise, this is not working as a frequencymeter

-I read about using the DETECT signal which is shared between GPIOS and is the lowest power burning interrupt source from GPIO. Is that true? 

Thanks a lot!!

Parents
  • Hi,

     

    When a TIMER is in COUNT mode, it will be low power.

    Here's an example, based on yours, that consumes very little in sleep mode (just toggles a LED every "AMOUNT_OF_PULSES" detected).

    /**
     * Copyright (c) 2014 - 2018, 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 gpiote_example_main main.c
    * @{
    * @ingroup nrf_gpiote_example
    * @brief GPIOTE Example Application main file.
    *
    * This file contains the source code for a sample application using GPIOTE.
    */
    
    #include <stdbool.h>
    #include <stdint.h>
    #include "app_error.h"
    #include "boards.h"
    #include "nrf.h"
    #include "nrf_drv_gpiote.h"
    #include "nrf_drv_ppi.h"
    #include "nrf_drv_timer.h"
    #include "nrf_gpio.h"
    #include "nrfx_gpiote.h"
    
    #define INT1 BUTTON_1
    
    #define AMOUNT_OF_PULSES 1
    
    static nrf_drv_timer_t m_timer = NRF_DRV_TIMER_INSTANCE(0);
    
    void timer_dummy_handler(nrf_timer_event_t event_type, void *p_context) 
    {
    }
    
    static void led_blinking_setup()
    {
        ret_code_t err_code;
        if (!nrfx_gpiote_is_init())
        {
            err_code = nrfx_gpiote_init();
            APP_ERROR_CHECK(err_code);
        }
    
        nrfx_timer_config_t timer_cfg = NRFX_TIMER_DEFAULT_CONFIG;
        timer_cfg.mode                = NRF_TIMER_MODE_COUNTER;
    
        nrfx_timer_init(&m_timer, &timer_cfg, NULL);
        
        nrfx_timer_compare(&m_timer, NRF_TIMER_CC_CHANNEL0, AMOUNT_OF_PULSES, false);    
        
        nrfx_gpiote_in_config_t i_config = NRFX_GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
        i_config.pull                    = NRF_GPIO_PIN_PULLUP;
    
        nrfx_gpiote_in_init(INT1, &i_config, NULL);
        nrfx_gpiote_in_event_enable(INT1, true);
        
        #warning "Hack to enable SHORTS"
        m_timer.p_reg->SHORTS = TIMER_SHORTS_COMPARE0_CLEAR_Enabled << TIMER_SHORTS_COMPARE0_CLEAR_Pos;
    
        nrfx_gpiote_out_config_t out_config = 
        {
            .action = NRF_GPIOTE_POLARITY_TOGGLE,
            .init_state = NRF_GPIOTE_INITIAL_VALUE_HIGH,
            .task_pin = true,
        };
        
        nrfx_gpiote_out_init(LED_1, &out_config);
        nrfx_gpiote_out_task_enable(LED_1);
        
        uint32_t out_task = nrfx_gpiote_out_task_addr_get(LED_1);
        
        err_code = nrf_drv_ppi_init();
        if (err_code != NRF_ERROR_MODULE_ALREADY_INITIALIZED)
        {
            APP_ERROR_CHECK(err_code);
        }
    
        nrf_ppi_channel_t ppi_channel_input, ppi_channel_output;
        // Setup the GPIOTE input to trigger TASKS_COUNT
        nrfx_ppi_channel_alloc(&ppi_channel_input);
        nrfx_ppi_channel_assign(ppi_channel_input, nrfx_gpiote_in_event_addr_get(INT1), nrfx_timer_task_address_get(&m_timer, NRF_TIMER_TASK_COUNT));
        nrfx_ppi_channel_enable(ppi_channel_input);
    
        // Setup EVENTS_COMPARE0 to trigger GPIOTE OUT
        nrfx_ppi_channel_alloc(&ppi_channel_output);
        nrfx_ppi_channel_assign(ppi_channel_output, nrfx_timer_event_address_get(&m_timer, NRF_TIMER_EVENT_COMPARE0), out_task);
        nrfx_ppi_channel_enable(ppi_channel_output);    
        
        
        nrfx_timer_enable(&m_timer);
    }
    
    /**
     * @brief Function for application main entry.
     */
    int main(void)
    {
        ret_code_t err_code;
    
        err_code = nrf_drv_ppi_init();
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_drv_gpiote_init();
        APP_ERROR_CHECK(err_code);
    
        // Setup PPI channel with event from TIMER compare and task GPIOTE pin toggle.
        led_blinking_setup();
    
        while (true)
        {
            // Do Nothing - GPIO can be toggled without software intervention.
            __WFE();
        }
    }
    
    /** @} */
    

    Your initial example uses the PORT event, and will then trigger if you have any other buttons configured with the PORT event as well, so I changed it to use GPIOTE->IN instead.

    If you want to count the amount of pulses from an external source within a given time-frame, you could just comment out the call to "nrfx_timer_compare" + the second PPI channel and run a TASKS_CAPTURE[0] in a app_timer instance.

     

    Kind regards,

    Håkon

  • Thanks for your kind reply Håkon.

    After enabling timer nrfx_timer_enable(&m_timer) power consumption jumps from 40uA to 500uA.

    Also I understand from documentation that NRFX_GPIOTE_CONFIG_IN_SENSE_HITOLO(true) with TRUE value enables IN_EVENT so the high frequency clock is requested (and more power drawn).

     

  • Enabling IN_EVENT will give a bit of higher consumption (~20 uA), per this errata:

    High current consumption in System ON Idle mode

    But it should not use 0.5 mA.

    Did you run the example I posted standalone (based on $SDK/examples/peripherals/gpiote)? I get approx. 20 uA of sleep current with it on my nRF52-DK. If you keep the input pin active, you'll see an added ~250 uA (from the pull resistor).

    Kind regards,

    Håkon

  • You are right Hakon. Your help is very appreciated.

    Problem was Anomaly 89. After adding the workaround code inside my TWI uninit function all is working as expected. As you said, 20uA extra drawn by the IN_EVENT and I got around 20uA average in sleep  ON with your code too. What a nightmare!

    Thank you man, you rock!

Reply Children
No Data
Related