Hardware PWM doesn't operate with 100% duty cycle

Hello

I'm currently working SDK upgrade from ncs v2.6.4 to v3.2.1

I found that, in fade in/out scenario, the PWM works well. however, if it need to turn on in 100%duty cycle, it doesn't turned on.

When I compare both driver implementation in sdk, I found a suspicious part.

Path: **zephyr/drivers/pwm/pwm_nrfx.c**

line 202:207

	} else if (pulse_cycles >= period_cycles) {
		/* Constantly active (duty 100%). */
		/* This value is always greater than or equal to COUNTERTOP. */
		compare_value = PWM_NRFX_CH_COMPARE_MASK;
		needs_pwm = IS_ENABLED(NRF_PWM_HAS_IDLEOUT) &&
			    IS_ENABLED(CONFIG_PWM_NRFX_NO_GLITCH_DUTY_100);
	} else {

Could you tell me there is similar issue or solution to solve this problem?

Best regards,
Woo-chan Kim

Parents
  • Hi,

     

    As per the code, if you check further down in the same function that you copied code from, it will disable the pwm module, and use nrf_gpio to set it to a high or low level, based on if you input 100% or 0 % duty cycle.

    Here is a quick test, based on zephyr/samples/basic/blink_pwm:

    /*
     * Copyright (c) 2016 Intel Corporation
     * Copyright (c) 2020 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    /**
     * @file Sample app to demonstrate PWM.
     */
    
    #include <zephyr/kernel.h>
    #include <zephyr/sys/printk.h>
    #include <zephyr/device.h>
    #include <zephyr/drivers/pwm.h>
    
    static const struct pwm_dt_spec pwm_led0 = PWM_DT_SPEC_GET(DT_ALIAS(pwm_led0));
    
    #define MIN_PERIOD PWM_SEC(1U) / 128U
    #define MAX_PERIOD PWM_SEC(1U)
    
    int main(void)
    {
    	uint32_t max_period;
    	uint32_t period;
    	uint8_t dir = 0U;
    	int ret;
    
    	printk("PWM-based blinky\n");
    
    	if (!pwm_is_ready_dt(&pwm_led0)) {
    		printk("Error: PWM device %s is not ready\n",
    		       pwm_led0.dev->name);
    		return 0;
    	}
    
    	/*
    	 * In case the default MAX_PERIOD value cannot be set for
    	 * some PWM hardware, decrease its value until it can.
    	 *
    	 * Keep its value at least MIN_PERIOD * 4 to make sure
    	 * the sample changes frequency at least once.
    	 */
    	printk("Calibrating for channel %d...\n", pwm_led0.channel);
    	max_period = MAX_PERIOD;
    	while (pwm_set_dt(&pwm_led0, max_period, max_period / 2U)) {
    		max_period /= 2U;
    		if (max_period < (4U * MIN_PERIOD)) {
    			printk("Error: PWM device "
    			       "does not support a period at least %lu\n",
    			       4U * MIN_PERIOD);
    			return 0;
    		}
    	}
    
    	printk("Done calibrating; maximum/minimum periods %u/%lu nsec\n",
    	       max_period, MIN_PERIOD);
    
    	period = max_period;
    	while (1) {
    		/* Set 100% for 1 sec */
    		ret = pwm_set_dt(&pwm_led0, period, period);
    		if (ret) {
    			printk("ret: %d\n", ret);
    		}
    		k_sleep(K_SECONDS(1U));
    		/* 50 % for 2 sec */
    		ret = pwm_set_dt(&pwm_led0, period, period / 2U);
    		if (ret) {
    			printk("ret: %d\n", ret);
    		}
    		k_sleep(K_SECONDS(2U));
    		/* 0 % for 4 sec */
    		ret = pwm_set_dt(&pwm_led0, period, 0);
    		if (ret) {
    			printk("ret: %d\n", ret);
    		}
    		k_sleep(K_SECONDS(4U));
    	}
    	return 0;
    }
    

    Which behaves like this: 

     

    Kind regards,

    Håkon

  • Thanks for your reply.

    When I fixed using the `sw_pwm` periperal that you recommend by refer the sample,

    it makes error like this.

    /opt/nordic/ncs/v3.2.1/zephyr/drivers/pwm/pwm_nrf_sw.c:420:9: error: invalid operands to binary & (have 'nrfx_gpiote_t *' and 'nrfx_gpiote_t')
      420 |         &GPIOTE_NRFX_INST_BY_NODE(NRF_DT_GPIOTE_NODE_BY_IDX(_node_id, _prop, _idx))
          |         ^
    /Users/woochankim/Project/Firmware-Sense1-Guardian/build_development/Firmware-Sense1-Guardian/zephyr/include/generated/zephyr/devicetree_generated.h:8654:9: note: in expansion of macro 'GPIOTE_AND_COMMA'
     8654 |         fn(DT_N_S_sw_pwm, channel_gpios, 1) \
          |         ^~
    /opt/nordic/ncs/v3.2.1/zephyr/include/zephyr/devicetree.h:5499:33: note: in expansion of macro 'DT_N_S_sw_pwm_P_channel_gpios_FOREACH_PROP_ELEM'
     5499 | #define DT_CAT4(a1, a2, a3, a4) a1 ## a2 ## a3 ## a4
          |                                 ^~
    /opt/nordic/ncs/v3.2.1/zephyr/include/zephyr/devicetree.h:5271:9: note: in expansion of macro 'DT_FOREACH_PROP_ELEM'
     5271 |         DT_FOREACH_PROP_ELEM(DT_DRV_INST(inst), prop, fn)
          |         ^~~~~~~~~~~~~~~~~~~~
    /opt/nordic/ncs/v3.2.1/zephyr/drivers/pwm/pwm_nrf_sw.c:425:17: note: in expansion of macro 'DT_INST_FOREACH_PROP_ELEM'
      425 |                 DT_INST_FOREACH_PROP_ELEM(0, channel_gpios, GPIOTE_AND_COMMA)
          |                 ^~~~~~~~~~~~~~~~~~~~~~~~~
    ninja: build stopped: subcommand failed.

    I need to control 3 channel pwm to implement full color led.

    this is the overlay that i used to.

    &pwm0 {
        status = "disabled";
    };
    
    &sw_pwm {
        status = "okay";
        channel-gpios = <&gpio1 4 PWM_POLARITY_INVERTED>,
                        <&gpio1 5 PWM_POLARITY_INVERTED>,
                        <&gpio1 6 PWM_POLARITY_INVERTED>;
    };
    
    pwmleds {
        compatible = "pwm-leds";
        pwm_green: pwm_led_0 {
            pwms = <&sw_pwm 0 PWM_MSEC(20) PWM_POLARITY_INVERTED>;
        };
        pwm_blue: pwm_led_1 {
            pwms = <&sw_pwm 1 PWM_MSEC(20) PWM_POLARITY_INVERTED>;
        };
        pwm_red: pwm_led_2 {
            pwms = <&sw_pwm 2 PWM_MSEC(20) PWM_POLARITY_INVERTED>;
        };
    };

    could you help me to solve this problem.

    If you can, please tell me why I need to use the `sw_pwm`?

    Because, In previous SDK version, the pwm works well.

  • Hi,

     

    Sorry, forgot to mention that you should rename/delete that file for the purpose of this testing.

    Woo-chan Kim said:

    When I fixed using the `sw_pwm` periperal that you recommend by refer the sample,

    blinky_pwm defines that specifically for the nrf5340dk_nrf5340_cpuapp.overlay board. You can safely use "normal" pwm with it, by deleting that file.

    Check this by searching for "CONFIG_PWM_NRFX" in build/blinky_pwm/zephyr/.config file, and if this is set to 'y'; then "NRF_PWM" hardware peripheral is used.

     

    Woo-chan Kim said:

    I need to control 3 channel pwm to implement full color led.

    sw_pwm is made specifically for 1 channel. Having several will cause issues, as explained here:

     RE: How to use two sw_pwm instances with nrf5340 with Zephyr? 

     

    Kind regards,

    Håkon

  • Hi Hakon,

    I'm having a similar issue on an nRF54LM20 although the issue seems more driver related than hardware related. It seems to be an interaction with CONFIG_PM_DEVICE_RUNTIME. When enabled, as soon as the pwm api call is finished, the pm subsystem puts the pinctrl node to sleep. This only seems to happen when i set the duty cycle to 100% and it happens whether or not  zephyr,pm-device-runtime-auto is on the pwm node.

    node:

    &pwm20 {
        status = "okay";
        pinctrl-0 = <&pwm20_default>;
        pinctrl-1 = <&pwm20_sleep>;
        pinctrl-names = "default", "sleep";
    };
    
        pwm20_default: pwm20_default {
            group1 {
                psels = <NRF_PSEL(PWM_OUT0, 1, 17)>,
                    <NRF_PSEL(PWM_OUT1, 1, 18)>;
                nordic,invert;
            };
        };
    
        pwm20_sleep: pwm20_sleep {
            group1 {
                psels = <NRF_PSEL(PWM_OUT0, 1, 17)>,
                    <NRF_PSEL(PWM_OUT1, 1, 18)>;
                low-power-enable;
            };
        };

    console:

    uart:~$ pwm usec pwm@d2000 0 30 15 # duty to 50%
    uart:~$ pwm usec pwm@d2000 0 30 30 # duty to 100%

    logic analyzer:

Reply
  • Hi Hakon,

    I'm having a similar issue on an nRF54LM20 although the issue seems more driver related than hardware related. It seems to be an interaction with CONFIG_PM_DEVICE_RUNTIME. When enabled, as soon as the pwm api call is finished, the pm subsystem puts the pinctrl node to sleep. This only seems to happen when i set the duty cycle to 100% and it happens whether or not  zephyr,pm-device-runtime-auto is on the pwm node.

    node:

    &pwm20 {
        status = "okay";
        pinctrl-0 = <&pwm20_default>;
        pinctrl-1 = <&pwm20_sleep>;
        pinctrl-names = "default", "sleep";
    };
    
        pwm20_default: pwm20_default {
            group1 {
                psels = <NRF_PSEL(PWM_OUT0, 1, 17)>,
                    <NRF_PSEL(PWM_OUT1, 1, 18)>;
                nordic,invert;
            };
        };
    
        pwm20_sleep: pwm20_sleep {
            group1 {
                psels = <NRF_PSEL(PWM_OUT0, 1, 17)>,
                    <NRF_PSEL(PWM_OUT1, 1, 18)>;
                low-power-enable;
            };
        };

    console:

    uart:~$ pwm usec pwm@d2000 0 30 15 # duty to 50%
    uart:~$ pwm usec pwm@d2000 0 30 30 # duty to 100%

    logic analyzer:

Children
Related