Adding PWM Channels in Zephyr

I am developing a custom board application in Zephyr 3.2.99.  I have a single PWM Channel running now but having trouble adding addtional channels.

In the 52811.dtsi, I have a single pwm node:

                pwm0: pwm@4001c000 {
                        compatible = "nordic,nrf-pwm";
                        reg = <0x4001c000 0x1000>;
                        interrupts = <28 NRF_DEFAULT_IRQ_PRIORITY>;
                        status = "disabled";
                        #pwm-cells = <3>;
                };

then, in my pinctrl-dtsi I have:

	pwm0_default: pwm0_default {
		// Channel 0
		group1 {
			psels = <NRF_PSEL(PWM_OUT0, 0, 23)>; 
			nordic,invert;
		};

	};

	pwm0_sleep: pwm0_sleep {
		group1 {
			psels = <NRF_PSEL(PWM_OUT0, 0, 23)>;
			low-power-enable;
		};
	};

The in my .dts I have:

	pwmleds {
		compatible = "pwm-leds";
			// pwm0 Ch 0 
			pwm_led0: pwm_led_0 { 
					pwms = <&pwm0 0 PWM_NSEC(62500) PWM_POLARITY_INVERTED>;
			};
	};

and:

&pwm0 {
	status = "okay";
	//status = "disabled";
	pinctrl-0 = <&pwm0_default>;
	pinctrl-1 = <&pwm0_sleep>;
	pinctrl-names = "default", "sleep";
};

in my main driver, I have followed the Zephyr example code:

static const struct pwm_dt_spec pwm_led0 = PWM_DT_SPEC_GET(DT_ALIAS(pwm_led0));

	#define NUM_STEPS       1024U
	#define SLEEP_MSEC      25U

	uint32_t pulse_width = 0U;
	uint32_t step = pwm_led0.period / NUM_STEPS;
	//uint32_t step = pwm_led1.period / NUM_STEPS;
	uint8_t dir = 1U;
	int ret;

	printk("PWM-based LED fade\n");

	if (!device_is_ready(pwm_led0.dev)) {
			printk("Error: PWM device %s is not ready\n",
					pwm_led0.dev->name);
			return;
	}

	while (1) {
		ret = pwm_set_pulse_dt(&pwm_led0, pulse_width);
		if (ret) {
				printk("Error %d: failed to set pulse width\n", ret);
				return;
		}

		if (dir) {
				pulse_width += step;
				if (pulse_width >= pwm_led0.period) {
						pulse_width = pwm_led0.period - step;
						dir = 0U;
				}
		} else {
				if (pulse_width >= step) {
						pulse_width -= step;
				} else {
						pulse_width = step;
						dir = 1U;
				}
		}


		k_sleep(K_MSEC(SLEEP_MSEC));
	}

}

I have been stuck on this for a while and now I'm really in a bind.

Thanks,

Drew

Parents
  • Hi Drew

    I think the problem is that you haven't properly added the additional channels to the pinctrl and pwmleds nodes. 

    I was able to add a second channel to the led_pwm sample in Zephyr simply by adding the following overlay file to the example:

    &pinctrl {
        pwm0_default: pwm0_default {
    		group1 {
    			psels = <NRF_PSEL(PWM_OUT0, 0, 13)>,
                        <NRF_PSEL(PWM_OUT1, 0, 14)>;
    			nordic,invert;
    		};
    	};
    
    	pwm0_sleep: pwm0_sleep {
    		group1 {
    			psels = <NRF_PSEL(PWM_OUT0, 0, 13)>,
                        <NRF_PSEL(PWM_OUT1, 0, 14)>;
    			low-power-enable;
    		};
    	};
    };
    
    /{
    	pwmleds {
    		compatible = "pwm-leds";
    		pwm_led0: pwm_led_0 {
    			pwms = <&pwm0 0 PWM_MSEC(20) PWM_POLARITY_INVERTED>;
    		};
            pwm_led1: pwm_led_1 {
    			pwms = <&pwm0 1 PWM_MSEC(20) PWM_POLARITY_INVERTED>;
    		};
    	};
    };

    Please note I used a nRF52840DK for the test, but the code should look similar for the nRF52811. 

    Best regards
    Torbjørn

Reply
  • Hi Drew

    I think the problem is that you haven't properly added the additional channels to the pinctrl and pwmleds nodes. 

    I was able to add a second channel to the led_pwm sample in Zephyr simply by adding the following overlay file to the example:

    &pinctrl {
        pwm0_default: pwm0_default {
    		group1 {
    			psels = <NRF_PSEL(PWM_OUT0, 0, 13)>,
                        <NRF_PSEL(PWM_OUT1, 0, 14)>;
    			nordic,invert;
    		};
    	};
    
    	pwm0_sleep: pwm0_sleep {
    		group1 {
    			psels = <NRF_PSEL(PWM_OUT0, 0, 13)>,
                        <NRF_PSEL(PWM_OUT1, 0, 14)>;
    			low-power-enable;
    		};
    	};
    };
    
    /{
    	pwmleds {
    		compatible = "pwm-leds";
    		pwm_led0: pwm_led_0 {
    			pwms = <&pwm0 0 PWM_MSEC(20) PWM_POLARITY_INVERTED>;
    		};
            pwm_led1: pwm_led_1 {
    			pwms = <&pwm0 1 PWM_MSEC(20) PWM_POLARITY_INVERTED>;
    		};
    	};
    };

    Please note I used a nRF52840DK for the test, but the code should look similar for the nRF52811. 

    Best regards
    Torbjørn

Children
  • Thanks for the reply. I figured that out shortly after I posted. The problem was that I had the pwmleds configured as the default PWM_POLARITY_INVERTED. So the led's would turn on as soon as the board started.  I switched to PWM_POLARITY_NORMAL and now everything is behaving as we desire.

    	pwm0_default: pwm0_default {
    		// Channel 0
    		group1 {
    			psels = <NRF_PSEL(PWM_OUT0, 0, 23)>, // Channel 0 
    					<NRF_PSEL(PWM_OUT1, 0, 15)>, // Channel 1
    					<NRF_PSEL(PWM_OUT2, 0, 22)>; // Channel 2
    
    			//nordic,invert;
    		};
    
    	};
    
    	pwmleds {
    		compatible = "pwm-leds";
    			// pwm0 Ch 0
    			pwm_led0: pwm_led_0 { 
    					//pwms = <&pwm0 0 PWM_NSEC(62500) PWM_POLARITY_INVERTED>;
    					pwms = <&pwm0 0 PWM_NSEC(62500) PWM_POLARITY_NORMAL>;
    			};
    			// pwm0 Ch 1
    			pwm_led1: pwm_led_1 { 
    					pwms = <&pwm0 1 PWM_NSEC(62500) PWM_POLARITY_NORMAL>;
    
    			};
    			// pwm0 Ch 2
    			pwm_led2: pwm_led_2 {
    					pwms = <&pwm0 2 PWM_NSEC(62500) PWM_POLARITY_NORMAL>;
    			};
    	
    	};

    I simply commented out the "nordic,invert" property in the pinctrl-dtsi but I'm not sure if that is the proper way to handle that.

    Drew

  • Hi Drew

    Yes, you can just remove this property from the overlay. 

    In the long run you might want to create your own custom board implementation rather than using an overlay, then you can set this properly in the board files directly. 

    Best regards
    Torbjørn

  • That is very helpful. We don't use overlays. All of our Zephyr implementions use custom BSP.  You may consider this issue closed.  Thank you all for your support.

  • Glad I could help Drew. The best of luck with your project Slight smile

Related