Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

NRFX PWM driver timing

Hello,

I'm working on some LED sequences using the NRFX PWM driver and I can't get the durations I want right.

Let's say that I want a semi-blinking LED (100% - 50% - 100%...) with a period of 500 ms on each state and I initialize the PWM driver as follows:

nrfx_pwm_config_t const pwm_config =
{
	.output_pins =
	{
		led.red | led.polarity,     // Channel 0
		led.green | led.polarity,   // Channel 1
		led.blue | led.polarity,    // Channel 2
		NRFX_PWM_PIN_NOT_USED       // Channel 3
	},
	.irq_priority = APP_IRQ_PRIORITY_LOW,
	.base_clock   = NRF_PWM_CLK_16MHz,						// NRF_PWM_CLK_125kHz,
	.count_mode   = NRF_PWM_MODE_UP_AND_DOWN,				// For symmetric color mixing
	.top_value    = 255,
	.load_mode    = NRF_PWM_LOAD_INDIVIDUAL,
	.step_mode    = NRF_PWM_STEP_AUTO
};
err_code = nrfx_pwm_init(&m_pwm0, &pwm_config, pwm_event_handler);
APP_ERROR_CHECK(err_code);

As far as I understand I could do this to get my timing right:

void setup_single_color_sequence(nrf_pwm_sequence_t * sequence, color_t color, uint32_t duration_in_us) {
	uint32_t top = 255;
	uint32_t pwm_period = top / 16; 						// In µs (top / clock_frequency in MHz)
	uint16_t pwm_pulses_for_duration = duration_in_us / (pwm_period * NRF_PWM_VALUES_LENGTH(pwm_values_single_color));

	for (uint8_t i = 0; i < 2; i++) {
		pwm_values_single_color[i].channel_0 = color.r;
		pwm_values_single_color[i].channel_1 = color.g;
		pwm_values_single_color[i].channel_2 = color.b;
		pwm_values_single_color[i].channel_3 = 0;
	}

	sequence->values.p_individual		= pwm_values_single_color;
	sequence->length					= NRF_PWM_VALUES_LENGTH(pwm_values_single_color);
	sequence->repeats					= pwm_pulses_for_duration;
	sequence->end_delay					= 0;
}

That is using the .repeats field to make each duty cycle repeat in order to get my desired timing. That however doesn't really work, I get shorter periods.

My logic is: The PWM counts up to 255. If I'm running the PWM at 16 MHz, then each duty cycle has a duration of 255/16 MHz = ~16 us. If I want the PWM to stay at that level for 500 ms, then I divide that duration (in us) by the duration of the duty cycle and I get the number of times that I need to repeat each duty cycle in order to get my target duration.

Is that logic wrong?

Parents Reply Children
  • Hi,

    Maybe you can try something like this:

    nrfx_pwm_t m_pwm0 = NRFX_PWM_INSTANCE(0);
    
    #define PWM_TOP_VALUE 100
    
    static void pwm_init(void)
    {
        nrfx_pwm_config_t const pwm_config =
        {
    	    .output_pins =
    	    {
    		    LED_1,     // Channel 0
    		    NRFX_PWM_PIN_NOT_USED,   // Channel 1
    		    NRFX_PWM_PIN_NOT_USED,    // Channel 2
    		    NRFX_PWM_PIN_NOT_USED       // Channel 3
    	    },
    	    .irq_priority = APP_IRQ_PRIORITY_LOW,
    	    .base_clock   = NRF_PWM_CLK_16MHz,						// NRF_PWM_CLK_125kHz,
    	    .count_mode   = NRF_PWM_MODE_UP_AND_DOWN,				// For symmetric color mixing
    	    .top_value    = PWM_TOP_VALUE,
    	    .load_mode    = NRF_PWM_LOAD_INDIVIDUAL,
    	    .step_mode    = NRF_PWM_STEP_AUTO
        };
        uint32_t err_code = nrfx_pwm_init(&m_pwm0, &pwm_config, NULL);
        APP_ERROR_CHECK(err_code);
    }
    
    
    static nrf_pwm_values_individual_t pwm_values[2]; 
    
    nrf_pwm_sequence_t static pwm_sequence =
    {
        .values.p_individual = pwm_values,
        .length          = NRF_PWM_VALUES_LENGTH(pwm_values),
        .repeats         = 1,
        .end_delay       = 0
    };
    
    
    void make_sequence(uint32_t duration_us)
    {
        uint32_t pwm_period = 2 * PWM_TOP_VALUE / 16; // In µs (top / clock_frequency in MHz). Multiply by 2 if using NRF_PWM_MODE_UP_AND_DOWN
        uint32_t repeats = duration_us / pwm_period;
        
        pwm_sequence.repeats = repeats;
        
        pwm_values[0].channel_0 = 100;
        pwm_values[1].channel_0 = 5;
    }
    
    
    int main(void)
    {    
        NRF_CLOCK->TASKS_HFCLKSTART = 1;
        
        LEDS_CONFIGURE(LEDS_MASK);
        LEDS_OFF(LEDS_MASK);
    
        pwm_init();
        
        make_sequence(500000);
        nrfx_pwm_simple_playback(&m_pwm0, &pwm_sequence, 1, NRFX_PWM_FLAG_LOOP);
    
        for (;;)
        {
    
        }
    }

    This code will blink LED_1 with a frequency of 1 Hz.

     

  • Hi Martin, just to let you know that I still haven't been able to make it work, but it looks like there's something else wrong with my implementation. I'll update here when I figure it out

Related