PWM Spike on starting/stopping on all channels

Hello there,
we're using multiple pwm-channels of one module of the nRF54L15 for different purposes (multiple motors, h-bridge control; depending on the project).
When the first channel is being turned on or the last channel is being turned off we're seeing spikes on all configured channels - which seems to be unexpected behaviour to us.
We are currently able to work around this (in the h-bridge case just by the correct disable-sequence, in the other case by using different pwm-modules), but still it would be great to have a clean solution here.
Btw - the off spike looks like it changes the drive-mode of the pin, since it's not a clean edge to GND but looks like a discharge curve.
Here is a screenshot of this (I can measure the exact spike width, if needed):
C1 is the inactive pwm-channel, c2 the active one. C3 and C4 are the statically driven gpio's of the h-bridge.
We're on sdk-nrf 3.0.2 and have the following setup:
dts:
/ {
    motor_hbridge_static{
    	compatible = "gpio-leds";
    	motor_common_lowside: motor_common_lowside {
    		gpios = <&gpio1 3 GPIO_ACTIVE_HIGH>;
    		label = "motor common lowside";
    	};
    	motor1_minus_lowside: motor1_minus_lowside {
    		gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>;
    		label = "motor1 minus lowside";
    	};
    };
    
    motor_hbridge_pwm{
    	compatible = "pwm-leds";
    	motor_common_highside: motor_common_highside{
    		pwms = <&pwm20 0 PWM_USEC(40) PWM_POLARITY_NORMAL>;
    	};
    	motor1_minus_highside: motor1_minus_highside{
    		pwms = <&pwm20 1 PWM_USEC(40) PWM_POLARITY_NORMAL>;
    	};
    };
};

&pinctrl {
	pwm20_default: pwm20_default {
		group1 {
			psels = <NRF_PSEL(PWM_OUT0, 1, 0)>,
					<NRF_PSEL(PWM_OUT1, 1, 1)>;
			nordic,drive-mode = <NRF_DRIVE_H0H1>;
		};
	};
	pwm20_sleep: pwm20_sleep {
		group1 {
			psels = <NRF_PSEL(PWM_OUT0, 1, 0)>,
					<NRF_PSEL(PWM_OUT1, 1, 1)>;
			low-power-enable;
			nordic,drive-mode = <NRF_DRIVE_H0H1>;
		};
	};
};

&pwm20 {
	status = "okay";
	pinctrl-0 = <&pwm20_default>;
	pinctrl-1 = <&pwm20_sleep>;
	pinctrl-names = "default", "sleep";
	zephyr,pm-device-runtime-auto;
};

Test-code:
void test_fn(void)
{
    motor_start(MOTOR_DRIVE_LEFT, MOTOR_PWM_FREQUENCY_LOW, 5000, 8000);
    k_sleep(K_MSEC(50));
    motor_stop();
    k_sleep(K_SECONDS(2));
}

int motor_start(enum motor_drive drive, enum motor_pwm_frequency freq, uint16_t target_voltage_mv, uint16_t voltage_mv)
{
    motor_stop();
    m_motor.drive                                       = drive;
    motor_set_target_voltage(target_voltage_mv);
    motor_set_pwm_frequency(freq);
    motor_update(voltage_mv, 0);
    return 0;
}

int motor_update(uint16_t voltage_mv, uint16_t current_ma)
{
    uint32_t period = PWM_MSEC(10); // 100 Hz
    if (m_motor.frequency == MOTOR_PWM_FREQUENCY_HIGH) {
        period = PWM_USEC(40); // 25 kHz
    }

    uint32_t pulse = (uint32_t)((uint64_t)((uint64_t)period * (uint64_t)m_motor.target_voltage_mv) / voltage_mv); //) / m_motor.target_voltage_mv;

    enum motor_drive drv = m_motor.drive;
    if (drv != MOTOR_DRIVE_RIGHT && drv != MOTOR_DRIVE_LEFT) {
        LOG_ERR("Wrong drive mode. Not driving...");
        return -ENOTSUP;
    }

    pwm_set_dt(m_bridge_pairs[drv].inactive_pwm, period, 0);
    k_sleep(K_USEC(10));
    pwm_set_dt(m_bridge_pairs[drv].active_pwm, period, pulse);
    k_sleep(K_USEC(10));
    gpio_pin_set_dt(m_bridge_pairs[drv].active_gpio, 1);
    gpio_pin_set_dt(m_bridge_pairs[drv].inactive_gpio, 0);

    LOG_DBG("%s: pulse/period: %d / %d", __FUNCTION__, pulse, period);
    return 0;
}

void motor_stop(void)
{

    gpio_pin_set_dt(m_bridge_pairs[drv].active_gpio, 0);
    gpio_pin_set_dt(m_bridge_pairs[drv].inactive_gpio, 0);  
    k_sleep(K_USEC(10));
    pwm_set_dt(m_bridge_pairs[drv].inactive_pwm, period, 0);
    pwm_set_dt(m_bridge_pairs[drv].active_pwm, period, 0);
}

test_fn is called inside a loop in main.
If you need any further information feel free to ask!
I'll also try to make a minimal test-project to test this with different mcu-targets to see if it's a nRF54L problem or also present on the nRF52840 which we use in our older projects.
Thanks and best regards
Marco
  • Forgot this in the example-code:

    static const struct gpio_dt_spec m_motor_common_lowside = GPIO_DT_SPEC_GET(DT_NODELABEL(motor_common_lowside), gpios);
    static const struct gpio_dt_spec m_motor1_minus_lowside = GPIO_DT_SPEC_GET(DT_NODELABEL(motor1_minus_lowside), gpios);
    
    static const struct pwm_dt_spec m_motor_common_highside = PWM_DT_SPEC_GET(DT_NODELABEL(motor_common_highside));
    static const struct pwm_dt_spec m_motor1_minus_highside = PWM_DT_SPEC_GET(DT_NODELABEL(motor1_minus_highside));
    
    struct hbridge_pair {
        const struct pwm_dt_spec  *active_pwm;
        const struct pwm_dt_spec  *inactive_pwm;
        const struct gpio_dt_spec *active_gpio;
        const struct gpio_dt_spec *inactive_gpio;
    };
    
    static const struct hbridge_pair m_bridge_pairs[NUM_MOTOR_DRIVE] = {
        [MOTOR_DRIVE_LEFT]  = { .active_pwm = &m_motor1_minus_highside, .active_gpio = &m_motor_common_lowside, .inactive_pwm = &m_motor_common_highside, .inactive_gpio = &m_motor1_minus_lowside },
        [MOTOR_DRIVE_RIGHT] = { .active_pwm = &m_motor_common_highside, .active_gpio = &m_motor1_minus_lowside, .inactive_pwm = &m_motor1_minus_highside, .inactive_gpio = &m_motor_common_lowside },
    };
    
    

  • Hi there,
    another Update from my side - I have a minimal test-project to test this attached to this post.

    I also tested this on an nRF52840 and I can still see a small spike, although it does only get up to about 0.3V.

    Here a screenshot of the nRF52840 result from the testproject:


    And from the nRF54L15:

    Note that Channel 2 is set to 500mV/div to better see the spike especially on the nRF52840.

    I also just realized, that the dts-overlay on the nRF52840 probably doesn't deactivate polarity-inversion - so this might be the reason for different default pin-level on Channel 1.

    pwm_spike_test.zip

    The code was tested on:

    • nRF52840-DK (1.0.0)
    • nRF54L15-DK (0.9.2)

    Hope this helps to reproduce this!

    Best regards
    Marco

  • Hi Marco,

    Could you try with NCS v3.1.0, and see if the issue is solved there?

    I'm seeing some strange spikes with NCS 3.0.2, but not with NCS 3.1.0

    NCS 3.0.2

    NCS 3.1.0

    BR,

    Sigurd

  • Hi Sigurd,

    thank you very much for the feedback - I just tested it myself and it looks great now!

    Do you have any idea what change fixed this? Just out of curiosity!

    Best regards
    Marco

  • marcoster said:
    thank you very much for the feedback - I just tested it myself and it looks great now!

    Great!

    marcoster said:
    Do you have any idea what change fixed this? Just out of curiosity!

    I believe it was fixed with this commit here: https://github.com/nrfconnect/sdk-zephyr/commit/c1d7194e05c3798e2acc6a461247eb20e5d27a0a

Related