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

nRF52832 PWM loop forever between duty cycles defined in a sequence

Hi,

This is my first post here. I hope I will follow the structure of a good question.

I want to have a sequence of defined duty cycles (let's say 4 values). The aim is to loop forever between the 4 values in this sequence in order to "smoothly" dim a LED (use of a single output pin). I would like to run PWM by itself to do that without any ISR support by CPU.

I'm working with the NRF52832. I can't use the SDK because we are building our own code.

I have looked at the Nordic datasheet (rev 1.1) and at this post but I still haven't understood how to solve my problem.

Here is what I tried as code:

#include "pwm.h"

#include "nrf52.h"
#include "nrf52_bitfields.h"
#include "nrf_gpio.h"

//#include "app.h"

static uint32_t pwm_max_value = 100;


static uint16_t pwm_seq[4][4]={{10, 10, 10, 10}, {25, 25, 25, 25}, { 50, 50, 50, 50}, {75,75,75,75}}; // Pay attention that it is INVERSE LOGICAL in the sequence (ex: 1= 99%, 90 = 10%)
                                      
/** Is PWM Initialized */
static bool m_initialized = false;

void pwm_init()
{
    nrf_gpio_cfg_output(BOARD_PWM_OUTPUT_GPIO);
    nrf_gpio_pin_clear(BOARD_PWM_OUTPUT_GPIO);

    // Select output pin
    NRF_PWM0->PSEL.OUT[0] = (BOARD_PWM_OUTPUT_GPIO << PWM_PSEL_OUT_PIN_Pos) |
                            (PWM_PSEL_OUT_CONNECT_Connected <<
                                                     PWM_PSEL_OUT_CONNECT_Pos);
    // Enable PWM module
    NRF_PWM0->ENABLE      = (PWM_ENABLE_ENABLE_Enabled << PWM_ENABLE_ENABLE_Pos);

    // Set Mode
    NRF_PWM0->MODE        = (PWM_MODE_UPDOWN_Up << PWM_MODE_UPDOWN_Pos);

    // Set prescaler to set frequency clock of 1MHz (= f_default(16MHz)/PWM_PRESCALER_PRESCALER_DIV_16)
    NRF_PWM0->PRESCALER   = (PWM_PRESCALER_PRESCALER_DIV_16 <<
                                                     PWM_PRESCALER_PRESCALER_Pos);
    // Set counter top to have a PWM frequency of 10 KHz (= PWM_PRESCALER_PRESCALER_DIV_16/pwm_max_value)
    NRF_PWM0->COUNTERTOP  = (pwm_max_value << PWM_COUNTERTOP_COUNTERTOP_Pos);

    NRF_PWM0->LOOP        = (PWM_LOOP_CNT_Disabled<< PWM_LOOP_CNT_Pos);

    NRF_PWM0->DECODER   = (PWM_DECODER_LOAD_WaveForm << PWM_DECODER_LOAD_Pos) |
                          (PWM_DECODER_MODE_RefreshCount << PWM_DECODER_MODE_Pos);

    NRF_PWM0->SEQ[0].PTR  = ((uint32_t)(pwm_seq) << PWM_SEQ_PTR_PTR_Pos);
    NRF_PWM0->SEQ[0].CNT  = ((sizeof(pwm_seq) / sizeof(uint16_t)) << PWM_SEQ_CNT_CNT_Pos);
    NRF_PWM0->SEQ[0].REFRESH  = 1;
    NRF_PWM0->SEQ[0].ENDDELAY = 0;
    NRF_PWM0->TASKS_SEQSTART[0] = 1;

    m_initialized = true;
}

The file pwm.h simply includes the header of pwm_init().

Any comments or suggestions about how I have to do ?

Thanks in advance for your answer,

Best regards,

Florent

  • Hi,

    What is the issue? Is it looping forever, and you want it to not loop? is that correct? or do you want it to loop ?

  • Hi Sigurd,

    Thank you for your comment. I want the duty cycles values I put in pwm_seq loop until I decide to stop it.

    Best regards

  • How about something like this:

    /** Is PWM Initialized */
    static bool m_initialized = false;
    
    void pwm_init()
    {
        nrf_gpio_cfg_output(BOARD_PWM_OUTPUT_GPIO);
        nrf_gpio_pin_clear(BOARD_PWM_OUTPUT_GPIO);
    
    
    
        enum { // [local constants]
            TOP        = 10000,
            STEP_COUNT = 100
        };
    
    
    
        // This array cannot be allocated on stack (hence "static") and it must
        // be in RAM.
        static nrf_pwm_values_common_t seq0_values[STEP_COUNT];
        uint16_t value = TOP;
        uint16_t step  = TOP / STEP_COUNT;
        uint8_t  i;
        uint8_t dim_percentage = 10;
    
        for (i = 0; i < STEP_COUNT; ++i)
        {
            if(i>dim_percentage)
            {
             value         -= step;
            }
            seq0_values[i] = value;
        }
    
    
    
    
        // Select output pin
        NRF_PWM0->PSEL.OUT[0] = (BOARD_PWM_OUTPUT_GPIO << PWM_PSEL_OUT_PIN_Pos) |
                                (PWM_PSEL_OUT_CONNECT_Connected <<
                                                         PWM_PSEL_OUT_CONNECT_Pos);
     
    
        // Set Mode
        NRF_PWM0->MODE        = (PWM_MODE_UPDOWN_Up << PWM_MODE_UPDOWN_Pos);
    
        // Set prescaler to set frequency clock of 1MHz (= f_default(16MHz)/PWM_PRESCALER_PRESCALER_DIV_16)
        NRF_PWM0->PRESCALER   = (PWM_PRESCALER_PRESCALER_DIV_32 <<
                                                         PWM_PRESCALER_PRESCALER_Pos);
        // Set counter top to have a PWM frequency of 10 KHz (= PWM_PRESCALER_PRESCALER_DIV_16/pwm_max_value)
        NRF_PWM0->COUNTERTOP  = (TOP << PWM_COUNTERTOP_COUNTERTOP_Pos);
    
        NRF_PWM0->LOOP        = (PWM_LOOP_CNT_Disabled<< PWM_LOOP_CNT_Pos);
    
        NRF_PWM0->DECODER   = (PWM_DECODER_LOAD_Common                       << PWM_DECODER_LOAD_Pos) |
                              (PWM_DECODER_MODE_RefreshCount << PWM_DECODER_MODE_Pos);
    
        NRF_PWM0->SEQ[0].PTR  = ((uint32_t)(seq0_values) << PWM_SEQ_PTR_PTR_Pos);
        NRF_PWM0->SEQ[0].CNT  = ((sizeof(seq0_values) / sizeof(uint16_t)) << PWM_SEQ_CNT_CNT_Pos);
        NRF_PWM0->SEQ[0].REFRESH  = 1;
        NRF_PWM0->SEQ[0].ENDDELAY = 0;
    
           // Enable PWM module
        NRF_PWM0->ENABLE      = (PWM_ENABLE_ENABLE_Enabled << PWM_ENABLE_ENABLE_Pos);
        NRF_PWM0->TASKS_SEQSTART[0] = 1;
    
        m_initialized = true;
    }
    
Related