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

nRF52840 PWM uninit power consumption

  • nRF52840
  • SDK 15.3
  • Segger Embedded Studio

I PWM a piezo buzzer (with a transistor) , using the PWM peripheral. The problem I encounter is that before I call the PWM function the power consumption is around 5uA, while after the PWM uninit() from the PWM event handler, the power consumption has increased to ~660uA. I checked that the driver is succesfully disabled, but I guess that the DMA is still on? If I go into shutdown mode with nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_STAY_IN_SYSOFF); the power consumption is reduced to 1.1uA as expected (mostly external hardware connected to the nRF52840).

So is there any way to reduce the power consumption after the PWM has been unitialized or is this not possible and should I use a different methode of PWM generation?

Code:

pwm.c

#include <stdbool.h>
#include <stdint.h>
#include "pwm.h"
#include "app_timer.h"
#include "nrf_gpio.h"
#include "boards.h"
#include "gpio-board.h"
#include "nrf_drv_pwm.h"
#include "nrf_drv_clock.h"
#include "nrf_log.h"
#include "app_util.h"

static nrf_drv_pwm_t m_pwm0 = NRF_DRV_PWM_INSTANCE(0);

void PWM_shutdown(void)
{
	if(!nrfx_pwm_is_uninit(&m_pwm0))
	{
		nrf_drv_pwm_uninit(&m_pwm0);
	}
}


static void PWM_handler(nrf_drv_pwm_evt_type_t event_type)
{
    if (event_type == NRF_DRV_PWM_EVT_FINISHED)
    {
        nrf_drv_pwm_uninit(&m_pwm0);
        NRF_LOG_INFO("PWM handler unint");
    }
    NRF_LOG_INFO("PWM handler %u/0x%02X, uninit %s", event_type, event_type, bool_to_str(nrfx_pwm_is_uninit(&m_pwm0)));
}


void PWM_event(uint8_t dutycycle, uint16_t frequency, uint16_t nRepeat)
{
    uint32_t err; 
    static uint32_t regVal, topvalue;

	// Ensure that the boost converter is enabled before attempting to sounds the buzzer.
	#if 0
	if(!GpioBoostConverterGet())
	{
		return;
	}
	#endif

    if(frequency > 10000)
	{
       frequency = 10000;
    }

    if(frequency < 1000)
	{
       frequency = 1000;
    }

    topvalue = 1250000 / frequency;

    if(dutycycle > 100)
	{
      dutycycle = 50;
    }

    // Calculate the PWM register value from the maximum top value and the given duty Cycle.
    regVal = (topvalue * dutycycle) / 1000;
    topvalue /= 10;

    nrf_drv_pwm_config_t const config0 =
    {
        .output_pins =
        {
            BUZZER_PWM ,
            NRF_DRV_PWM_PIN_NOT_USED,
            NRF_DRV_PWM_PIN_NOT_USED,
            NRF_DRV_PWM_PIN_NOT_USED,
        },
        .irq_priority = APP_IRQ_PRIORITY_LOWEST,
        .base_clock   = NRF_PWM_CLK_125kHz,
        .count_mode   = NRF_PWM_MODE_UP,
        .top_value    = topvalue,
        .load_mode    = NRF_PWM_LOAD_COMMON,
        .step_mode    = NRF_PWM_STEP_AUTO
    };

	// Check whether the pwm peripheral is already initialized.
	bool pwmInit = nrfx_pwm_is_uninit(&m_pwm0);
	NRF_LOG_INFO("PWM start, uninit=%s", bool_to_str(pwmInit));

    if(!pwmInit)
    {
        nrf_drv_pwm_uninit(&m_pwm0);
        NRF_LOG_INFO("PWM start unint");
    }

    err = nrf_drv_pwm_init(&m_pwm0, &config0, PWM_handler);
    APP_ERROR_CHECK(err);


    // This array cannot be allocated on stack (hence "static") and it must
    // be in RAM (hence no "const", though its content is not changed).
    static uint16_t /*const*/ seq_values[1];
    seq_values[0] = regVal;

    nrf_pwm_sequence_t const seq =
    {
        .values.p_common = seq_values,
        .length          = NRF_PWM_VALUES_LENGTH(seq_values),
        .repeats         = 0,
        .end_delay       = 0
    };

    (void)nrf_drv_pwm_simple_playback(&m_pwm0, &seq, nRepeat, NRF_DRV_PWM_FLAG_STOP);
}

pwm.h

/*!
 * \file      pwm.h
 *
 * \brief     PWM for the buzzer.
 *
 */
#ifndef __PWM_H__
#define __PWM_H__

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

	void PWM_event(uint8_t dutycycle, uint16_t frequency, uint16_t nRepeat);
    void PWM_shutdown(void);

#endif // __PWM_H__

Addition to nrfx_pwm.c to check if the PWM state is equal to NRFX_DRV_STATE_UNINITIALIZED.

bool nrfx_pwm_is_uninit(nrfx_pwm_t const * const p_instance)
{
    pwm_control_block_t * p_cb  = &m_cb[p_instance->drv_inst_idx];
	return (p_cb->state == NRFX_DRV_STATE_UNINITIALIZED) ? true : false;
}

Parents
  • Hi,

    The problem I encounter is that before I call the PWM function the power consumption is around 5uA, while after the PWM uninit() from the PWM event handler, the power consumption has increased to ~660uA.

    It looks strange. When the device is using PWM, the power will consume around 500-1000 uA depending on frequency and load normally.  

    Do you also turn off logging, NRF_LOG_ENABLED in sdk_config.h?

    The nrf_drv_pwm library has a couple of debug outputs using "NRF_LOG_INFO", and if your sdk_config.h has the defines "NRF_LOG_BACKEND_SERIAL_USES_UART" and "NRF_LOG_ENABLED" set to '1', it may try to start up the serial interface, which will draw power.

    If you place a breakpoint after the nrf_drv_pwm_uninit call and look at the different peripheral registers in the debug overview (especially PWMx and UARTE0), are you able to see any peripheral still active that should ideally not be?

    Also, see this post.  

    -Amanda H.

  • Hello Amanda,

    I test with a release build in which the logging is fully disabled. I can go down to 1.1uA in OFF_MODE and ~5uA in Sleep. So the logging doesn't consume power in this case as that would apply a static current consumption of a few hundred microamps. The increased power consumption only starts after I enable the PWM module in the bsp event from a button event or an apptimer.

    However, I have the ADC measuring every 10 seconds currently according to this low power example.

    https://devzone.nordicsemi.com/f/nordic-q-a/23867/nrf52-best-practise-for-ultra-low-power-saadc-4-channel-conversion#post-id-164115

    In which much like to how I use the PWM0 module, the SAADC is initialized before a sample is taken and disabled after the sample is taken. However, the saadc_uninit seems to be able to disable the DMA after completion whereas the PWM unit can't. From a measurement with the nRF power profiler I can clearly see the interval of the SAADC on which it samples. After each sample the increased power consumption caused by the PWM seems to be removed.

    It might be a little difficult to see, but the highlighted area is the sample interval of the SAADC. Ever 10 seconds it takes a sample. In the highligted area you see  the boost converter being enabled first at the first and highest current peak. After that follow four beeps of the buzzer with some off-time. The current consumption is then around  a few hundred microamps until the ADC samples after which the current consumption goes back to the expected 5uA.

    I don't see any other PWM peripherals active and the logging is disabled, so I assume it's the DMA current that causes the increased power consumption. The SAADC example I linked to has the same issue, but works around it by disabling and initing the peripheral only when it's needed. I've tried this approach for the PWM0 module, including disabling the IRQ and clearing the registers in the event handler, but the increased power consumption still persists.

    The link you provided does not specify how to disable the PWM peripheral properly. I would expect the nrf_drv_pwm_uninit function would do so, which I already use.

  • Hi, 

    I will have to check in the lab and see if there is something wrong with the PWM hardware on the chip, or if the library is doing something wrong. I will be back when I have the update.
    -Amanda H.
  • Hello Amanda,

    Have you heard anything from the lab yet?

Reply Children
Related