Issue about PWM of NCS

Dear Nordic Engineers

I developed a PWM function to drive the RGB LED. Then I found a problem, which is when I used a PWM signal of one channel to control the RED light of the RGB lamp, causing it to go from bright to off and then back to bright, the other two channels would have a level reversal when the lamp was turned off. This is the data I captured using a logic analyzer.

But in fact, my program doesn't control these two channels. Here is my program and the overlay. This issue causes one of the RGB lights to go out, and the other two channels will flicker for a moment when that happens. Could you please tell me how to turn off the level inversion of the other two channels?

/*
 * Copyright (c) 2016 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @file Sample app to demonstrate PWM-based RGB LED control
 */

#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <errno.h>
#include <zephyr/drivers/led.h>
#include <zephyr/sys/util.h>
#include <zephyr/kernel.h>

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(main, CONFIG_LOG_DEFAULT_LEVEL);

#define LED_PWM_NODE_ID	 DT_COMPAT_GET_ANY_STATUS_OKAY(pwm_leds)

const int num_leds = 3;

#define MAX_BRIGHTNESS	100

#define FADE_DELAY_MS	10
#define FADE_DELAY	K_MSEC(FADE_DELAY_MS)

/**
 * @brief Run tests on a single LED using the LED API syscalls.
 *
 * @param led_pwm LED PWM device.
 * @param led Number of the LED to test.
 */
static void run_led_test(const struct device *led_pwm, uint8_t led)
{
	int err;
	uint16_t level;

	/* Increase LED brightness gradually up to the maximum level. */
	LOG_INF("  Increasing brightness gradually");
	for (level = 0; level <= MAX_BRIGHTNESS; level++) {
		err = led_set_brightness(led_pwm, led, level);
		if (err < 0) {
			LOG_ERR("err=%d brightness=%d\n", err, level);
			return;
		}
		k_sleep(FADE_DELAY);
	}
	k_sleep(K_MSEC(1000));

	err = led_set_brightness(led_pwm, led, 100);
		if (err < 0) {
			LOG_ERR("err=%d brightness=%d\n", err, level);
			return;
		}
	k_sleep(K_MSEC(1000));

	for (level = MAX_BRIGHTNESS; level > 0; level--) {
		err = led_set_brightness(led_pwm, led, level);
		if (err < 0) {
			LOG_ERR("err=%d brightness=%d\n", err, level);
			return;
		}
		k_sleep(FADE_DELAY);
	}
	k_sleep(K_MSEC(1000));

	err = led_set_brightness(led_pwm, led, 0);
	if (err < 0) {
		LOG_ERR("err=%d brightness=%d\n", err, level);
		return;
	}
	k_sleep(K_MSEC(1000));

}

static void run_led_test_red_and_green(const struct device *led_pwm, uint8_t led)
{
	int err;
	uint16_t level;

	err = led_set_brightness(led_pwm, led, 100);
		if (err < 0) {
			LOG_ERR("err=%d brightness=%d\n", err, level);
			return;
		}
		k_sleep(FADE_DELAY);

	for (level = MAX_BRIGHTNESS; level > led; level--) {
		err = led_set_brightness(led_pwm, led, level);
		if (err < 0) {
			LOG_ERR("err=%d brightness=%d\n", err, level);
			return;
		}
		k_sleep(FADE_DELAY);
	}
	k_sleep(K_MSEC(1000));

	err = led_set_brightness(led_pwm, led, 0);
	if (err < 0) {
		LOG_ERR("err=%d brightness=%d\n", err, level);
		return;
	}
	k_sleep(FADE_DELAY);
}

int main(void)
{
	const struct device *led_pwm;
	uint8_t led;

	led_pwm = DEVICE_DT_GET(LED_PWM_NODE_ID);
	if (!device_is_ready(led_pwm)) {
		LOG_ERR("Device %s is not ready", led_pwm->name);
		return 0;
	}

	if (!num_leds) {
		LOG_ERR("No LEDs found for %s", led_pwm->name);
		return 0;
	}

	// for (led = 1; led <= 2; led++) {
	// 		run_led_test_red_and_green(led_pwm,led);
	// 	}

	while (1)
	{
		// for (led = 1; led <= num_leds; led++) {
			run_led_test(led_pwm, 3);
		// }
	}
}

6087.nrf54l15dk_nrf54l15_cpuapp.overlay

Best regards,

Hannibal

Parents
  • Hi Hannibal,

    I reproduced the behavior and trying to debug it now. Will come back to you once I have some progress and new info to share with you.

  • Hi Hannibal, 

    I tried to debug this and use a lot of different ways to initialize the PWM and also use pwm_set_pulse_dt individually to set PWM but I can still see the other channels being impacted whenever we start or stop the PWM (brightness level 0->1 or 1->0). I need to spend more time to debug this. 

  • Hi Hannibal, 

    It is possible that there is a bug in PWM driver in this scenario. The thing is that this is only reproduced on nRF54 and I am unable to see such issues on nRF52. I need to spend more time on it and probably create an internal ticket to debug it to the hardware level. This might take time. But until then can you please try to see if you can change your firmware and devicetree to use three different pwm instances for three LEDs? 

  • Hi Susheel

    Thank you for your best supports. Do you mean using three PWMs? Then, do you think this way of writing the device tree is correct?

    / {
        aliases {
            led0 = &led0;
    		led1 = &led1;
    		led2 = &led2;
            red-pwm-led   = &led_r;
            green-pwm-led = &led_g;
            blue-pwm-led  = &led_b;
        };
    
        leds {
    		compatible = "gpio-leds";
            led0: led_0 {
    			gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>;
    			label = "Red LED 0";
    		};
    		led1: led_1 {
    			gpios = <&gpio1 5 GPIO_ACTIVE_HIGH>;
    			label = "Green LED 1";
    		};
    		led2: led_2 {
    			gpios = <&gpio1 6 GPIO_ACTIVE_HIGH>;
    			label = "Blue LED 2";
    		};
    	};
    
        pwmleds {
            compatible = "pwm-leds";
            
            led_r: led_0 {
                pwms = <&pwm20 0 PWM_MSEC(20) PWM_POLARITY_NORMAL>;
                label = "Red LED";
            };
        
            led_g: led_1 {
                pwms = <&pwm21 0 PWM_MSEC(20) PWM_POLARITY_NORMAL>;
                label = "Green LED";
            };
        
            led_b: led_2 {
                pwms = <&pwm22 0 PWM_MSEC(20) PWM_POLARITY_NORMAL>;
                label = "Blue LED";
            };
        };
    };
    
    &pwm20 {
        status = "okay";
        pinctrl-0 = <&pwm20_default>;
        pinctrl-1 = <&pwm20_sleep>;
        pinctrl-names = "default", "sleep";
    };
    
    &pwm21 {
        status = "okay";
        pinctrl-0 = <&pwm21_default>;
        pinctrl-1 = <&pwm21_sleep>;
        pinctrl-names = "default", "sleep";
    };
    
    &pwm22 {
        status = "okay";
        pinctrl-0 = <&pwm22_default>;
        pinctrl-1 = <&pwm22_sleep>;
        pinctrl-names = "default", "sleep";
    };
    
    
    &pinctrl {
        pwm20_default: pwm20_default {
            group1 {
                psels = <NRF_PSEL(PWM_OUT0, 1, 4)>;   /* P1.04 -> LED_R */
            };
        };
        pwm20_sleep: pwm20_sleep {
            group1 {
                psels = <NRF_PSEL(PWM_OUT0, 1, 4)>;   /* P1.04 -> LED_R */
                        low-power-enable;
            };
        };
    
        pwm21_default: pwm21_default {
            group1 {
                psels = <NRF_PSEL(PWM_OUT0, 1, 5)>;   /* P1.05 -> LED_G */
            };
        };
        pwm21_sleep: pwm21_sleep {
            group1 {
                psels = <NRF_PSEL(PWM_OUT0, 1, 5)>;   /* P1.05 -> LED_G */
                        low-power-enable;
            };
        };
    
        pwm22_default: pwm22_default {
            group1 {
                psels = <NRF_PSEL(PWM_OUT0, 1, 6)>;   /* P1.06 -> LED_B */
            };
        };
        pwm22_sleep: pwm22_sleep {
            group1 {
                psels = <NRF_PSEL(PWM_OUT0, 1, 6)>;   /* P1.06 -> LED_B */
                        low-power-enable;
            };
        };
    };
    
    
    
    
    &uart20 {
        status = "disabled";
    };
    
    &uart30 {
        status = "okay";
    };
    /{
        chosen {
            zephyr,console = &uart30;
            zephyr,shell-uart = &uart30;
            zephyr,uart-mcumgr = &uart30;
            zephyr,bt-mon-uart = &uart30;
            zephyr,bt-c2h-uart = &uart30;
        };
    };
    

    Best regards,

    Hannibal

  • Yes, Hannibal, I meant to use three PWMs for testing. And it seemed  like your device tree looks right.

Reply Children
No Data
Related