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?
  • 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?

Reply
  • 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?

Children
No Data
Related