Zephyr PWM: pwm_nrfx.c - Limitation with NCS driver? "Incompatible period."

I am working with the nRF54L15 to create a driver for a peripheral which is controlled by two PWM signals.

My driver relies on the Zephyr PWM driver which goes back to your implementation of it in pwm_nrfx.c. Everything works fine except one case, and I don't understand this limitation.

If I command one of the signals to pulse at 100% duty cycle and I leave the other one off, if I then try to set the off PWM signal to a non-static setting (1-99% duty cycle) then it results in the "Incompatible period." error.  [SDK\zephyr\drivers\pwm\pwm_nrfx.c, function  pwm_period_check_and_set()]

Both of my PWM channels are set to the same frequency (PWM_USEC(100)) so I don't understand what the problem is here.  Is the driver optimizing the PWM period to something other than my setting when I command the one signal to 100% duty cycle? And so when I set the other signal, the PWM driver is actually running a different period than my setting? I could understand this for power savings, but I need to somehow deal with this limitation.

Relevant code:

struct tps923653_config
{
    const struct pwm_dt_spec en_pwm;  // EN/PWM pin  (High=Always On, Low=Device Disabled, PWM=PWM Dimming)
    const struct pwm_dt_spec adim_hd; // ADIM/HD pin (High=PWM Dimming, Low=Hybrid Dimming, PWM=Analog Dimming)
};

/* Startup state: */
(void)pwm_set_pulse_dt(&config->en_pwm, 0); // Set low
(void)pwm_set_pulse_dt(&config->adim_hd, config->adim_hd.period); // Set high (100% duty cycle)

/* Produces the error: */
err = pwm_set_pulse_dt(&config->en_pwm, config->en_pwm.period/2);
if (err)
{
    LOG_ERR("Failed to set EN/PWM: %d", err);
    return err;
}

Relevant devicetree overlay:

&pinctrl {
	pwm21_default: pwm21_default {
		group1 {
			psels = <NRF_PSEL(PWM_OUT0, 1, 11)>,
			        <NRF_PSEL(PWM_OUT1, 1, 12)>;
		};
	};
};

&pwm21 {
	status = "okay";
	pinctrl-0 = <&pwm21_default>; /* Only default, no low power config */
	pinctrl-names = "default";
};

/ {
	ir_driver: tps923653 {
		compatible = "indesign,tps923653";
		status = "okay";
		
		/* EN/PWM and ADIM/HD pwm configuration */
		pwms = <&pwm21 0 PWM_USEC(100) PWM_POLARITY_NORMAL>,
			   <&pwm21 1 PWM_USEC(100) PWM_POLARITY_NORMAL>;
		// pwm-names = "en-pwm", "adim-hd";
	};
};

Let me know if I am just outside of the scope of expected use of the pwm_nrfx.c driver and need to make something custom for my application, but I feel like this is may be a bug.

Parents
  • Hi!

    So you are hitting this condition in pwm_nrfx.c:

    /* If any other channel is driven by the PWM peripheral, the period
    * that is currently set cannot be changed, as this would influence
    * the output for that channel.
    */
    if ((data->pwm_needed & ~BIT(channel)) != 0) {
    LOG_ERR("Incompatible period.");
    return false;
    }

    Since adim_hd (channel 1) still has its bit set (because 100% duty keeps PWM active on nRF54L15), the driver blocks your attempt to change en_pwm.

    A workaround is to set set adim_hd to 0% duty cycle, and then do your changes. Does that work for you?
Reply
  • Hi!

    So you are hitting this condition in pwm_nrfx.c:

    /* If any other channel is driven by the PWM peripheral, the period
    * that is currently set cannot be changed, as this would influence
    * the output for that channel.
    */
    if ((data->pwm_needed & ~BIT(channel)) != 0) {
    LOG_ERR("Incompatible period.");
    return false;
    }

    Since adim_hd (channel 1) still has its bit set (because 100% duty keeps PWM active on nRF54L15), the driver blocks your attempt to change en_pwm.

    A workaround is to set set adim_hd to 0% duty cycle, and then do your changes. Does that work for you?
Children
  • This would not be ideal, as my peripheral is perceptive of the adim_hd line changing which causes it to "glitch" into a different mode.

    I understand now that my code is hitting this condition in the pwm_nrfx.c file, but it doesn't make sense why this condition exists exactly. 

    Since the same prescaler and countertop support both 100% duty cycle, and 50% duty cycle, why should this function give up on the request to change the en_pwm channel to 50% duty cycle, when it wouldn't actually affect the adim_hd channel?

    	/* Try to find a prescaler that will allow setting the requested period
    	 * after prescaling as the countertop value for the PWM peripheral.
    	 */
    	prescaler = 0;
    	countertop = period_cycles;
    	do {
    		if (countertop <= PWM_COUNTERTOP_COUNTERTOP_Msk) {
    			data->period_cycles = period_cycles;
    			data->prescaler     = prescaler;
    
    			nrf_pwm_configure(config->pwm.p_reg,
    					  data->prescaler,
    					  config->initial_config.count_mode,
    					  (uint16_t)countertop);
    			return true;
    		}
    
    		countertop >>= 1;
    		++prescaler;
    	} while (prescaler <= PWM_PRESCALER_PRESCALER_Msk);

    In other words, since all of the channels within this PWM peripheral are using the same period (prescaler and countertop), why is pwm_nrfx.c restricting the use of the peripheral?

  • For those who are still waiting on some movement from Nordic on this thread, one way to work around this is to just put your PWM signals on separate PWM peripherals.

    I thankfully have a spare PWM peripheral to be able to do this, but it is obviously very wasteful.

       Is there any movement within Nordic on improving the PWM driver to better support multiple PWM signals with the same period (prescaler and countertop)?

Related