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

dim led to minimize current consumption

Hello, I am using nrf52832 custom board, with SDK 11.0.0 and s132_nrf52_2.0.0 and trying to dim led. It seems the only suggested solution is to use the pwm driver. I was wondering if there are any other simpler solutions?

My use case is to set led brightness to a min value to save current consumption.

Looking at a similar post I was able to follow the pwm_driver examples as well as suggestion in this link: devzone.nordicsemi.com/.../

  • However, it requires hardcoding the duration or setting the playback in continuous loop, also I do not need to gradually step up or down the brightness via a seq_values array and playback mechanism. Appreciate any suggestions.

  • The other issue I have seen with the example is that during resource intensive operations like flash write the driver doesn't seem to function, as the flash write fails also the playback stops much sooner than the set duration, possibly due to high cpu usage. Please confirm if this is expected.

Thanks in advanced.

#define LED_DIM_VALUE               900                                 /**< Brightness scale: 0->max, 1000->min. >**/
static nrf_drv_pwm_t                m_pwm0 = NRF_DRV_PWM_INSTANCE(0);   /**< Create an instance of PWM driver. >**/
static volatile bool                m_used = false;                     /**< PWM driver initilization status. >**/
                
void led_pwm(int pb_cnt)
{
    uint32_t err_code;
    nrf_drv_pwm_config_t const config0 =
    {
        .output_pins =
        {
            LED_PIN | NRF_DRV_PWM_PIN_INVERTED, // channel 0
            NRF_DRV_PWM_PIN_NOT_USED,             // channel 1
            NRF_DRV_PWM_PIN_NOT_USED,             // channel 2
            NRF_DRV_PWM_PIN_NOT_USED,             // channel 3
        },
        .irq_priority = APP_IRQ_PRIORITY_LOW,
        .base_clock   = NRF_PWM_CLK_125kHz,
        .count_mode   = NRF_PWM_MODE_UP,
        .top_value    = 1000,
        .load_mode    = NRF_PWM_LOAD_COMMON,
        .step_mode    = NRF_PWM_STEP_AUTO
    };
    if(m_used)
    {
       nrf_drv_pwm_uninit(&m_pwm0);
       m_used = false;
    }
    err_code = nrf_drv_pwm_init(&m_pwm0, &config0, NULL);
    m_used = true;
    APP_ERROR_CHECK(err_code);

    // 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 nrf_pwm_values_common_t seq_values[] =
    {
       LED_DIM_VALUE
    };
    nrf_pwm_sequence_t const seq =
    {
       .values.p_common = seq_values,
       .length          = NRF_PWM_VALUES_LENGTH(seq_values),
       .repeats         = 100,
       .end_delay       = 0
    };
    nrf_drv_pwm_simple_playback(&m_pwm0, &seq, pb_cnt, NRF_DRV_PWM_FLAG_STOP);
    nrf_gpio_pin_clear(LED_PIN);
}

The duty cycle above would be 50% and the max playback duration I use is ~10 sec.

  • There's a simple solution if you are making your own board and want two brightnesses only and have two GPIOs you can use. Hook the LED up to two different GPIOs with two different resistors for two different brighnesses. If both are LOW, the LED is off, if one is HIGH and the other is set to high impedence, you'll get the LED powered via the one which is high. I guess if you power them both HIGH you'll get a third really bright brightness.

    No PWM required.

  • No, there is no real "simpler" solution then to alter GPIO high and low on the output line towards LED in time faster then your eye can notice so the effective duty cycle (time when line is HIGH divided by total time of one HIGH/LOW period) will determine how much "dim" it will look like. Now PWM is pretty straight forward solution as it should be able to run autonomously (thanks to things specific for nRF52 such as EasyDMA and PPI Tasks&Events) and thus no other activity should impact it (unless it blocks MCU for longer time then you can "program" in PWM registers). Quote from PWM driver documentation on Infocenter:

    Key features include:

    • Multi-instance support
    • Two kinds of playback:
      • Simple: a single sequence of duty cycle values
      • Complex: two concatenated sequences
    • Sequences are played back a specified number of times
    • Optionally, the whole playback can be repeated in a loop
    • A user-defined event handler can be used to perform additional actions when a sequence or the whole playback is finished
    • If no event handler is used, playbacks can be carried out without involving the CPU
  • Hi RK, Thank you for your comment. Unfortunately I can't make design changes to the board at this time. I will check the datasheet to see if it is already implemented. Would you please let me know if there is a software solution or workarounds for the issues I am seeing with pwm?

  • This is why you're supposed to develop first and then make the board later :)

    There are no issues with PWM if you use Nordic's driver, so you must just have a bug.

  • I learned my lesson the very hard way unfortunately.
    The issue was due to reinitialization of the driver, that would return 8, then the error check would reset as it is other than success. I have updated my code, would you please let me know if there are better ways of avoiding initilization on every call to the driver. I call the routine few times. How abut the repeats and playback cycles, I am using the lowest possible clock to reduce duty cycle. Please advise here too.

    Also please help with instructions to setup debugging on linux environment using segger RTT. I have added the app_error.h and app_error.c and added the DEBUG symbol on my makefile but I don't see debug traces when checking the error (after rebuilt it doesn't reset anymore but also no print of the error value, line number, and file name where the error was triggered). Thanks.

Related