PWM register-based LED dimming not updating duty cycle dynamically on nRF5340

Hi,
I’m trying to generate a PWM signal directly using the nRF5340’s PWM peripheral registers (not using Zephyr’s pwm_dt_spec or driver API).

The LED turns ON correctly, so the PWM peripheral is initialized and running, but the duty cycle doesn’t change dynamically — it stays at the first value even though I update the sequence buffer in code.

Here’s the simplified code I’m using:

#include <zephyr/kernel.h>
#include <hal/nrf_pwm.h>
#include <hal/nrf_gpio.h>

static NRF_PWM_Type *pwm = NRF_PWM0;
static uint16_t seq_values[2];
static uint16_t duty = 0;
static int dir = 200;

void pwm_init(void)
{
    pwm->ENABLE = 0;
    pwm->PSEL.OUT[0] = 25; // P0.25
    pwm->PSEL.OUT[1] = 26; // P0.26
    pwm->MODE = PWM_MODE_UPDOWN_Up;
    pwm->PRESCALER = PWM_PRESCALER_PRESCALER_DIV_1;
    pwm->COUNTERTOP = 16000;
    pwm->LOOP = 0;
    pwm->DECODER = (PWM_DECODER_LOAD_Individual << PWM_DECODER_LOAD_Pos) |
                   (PWM_DECODER_MODE_RefreshCount << PWM_DECODER_MODE_Pos);
    pwm->SEQ[0].PTR = (uint32_t)&seq_values[0];
    pwm->SEQ[0].CNT = 2;
    pwm->SEQ[0].REFRESH = 0;
    pwm->SEQ[0].ENDDELAY = 0;
    pwm->ENABLE = 1;
}

void pwm_update(uint16_t duty1, uint16_t duty2)
{
    seq_values[0] = duty1;
    seq_values[1] = duty2;
    pwm->TASKS_SEQSTART[0] = 1; // restart sequence
}

int main(void)
{
    pwm_init();

    while (1)
    {
        duty += dir;
        if (duty >= 16000) dir = -200;
        if (duty <= 0) dir = 200;
        pwm_update(duty, 16000 - duty);
        k_msleep(20);
    }
}

The LED stays ON continuously — it doesn’t fade in/out as expected.

I am using nRf5340 Audio DK and SDK version 2.9

Am I missing any register configuration (like SEQ[0].REFRESH or LOOP) for dynamic updates? 

Parents
  • Hello,

    It is not closed. It is open. But it did have the status "Verified Answer" at some point:

    So if you press "Verify answer" on one of the replies in the ticket, it will get this state. But if you then later reply in it, it will go back to open.

    However, the reason you want to use the registers directly is that you want to have them running synchronously, correct?

    While it is technically possible to run the PWM registers directly, as it is (almost) done in the nRF5 SDK's SDK\examples\peripheral\pwm_driver for the nRF52 series, it is a bit more complicated here, since there is a different method of settings GPIOs as output pins in NCS, requiring you to involve devicetree (an .overlay file). 

    Attached is a modified version of:

    NCS\modules\hal\nordic\nrfx\samples\src\nrfx_pwm\common_mode

    hello_world_nrfx_pwm.zip

    It is a quite simple app controlling the 4 LEDs as PWM pins, but it doesn't need to be the LEDs, just replace the pin numbers.

    However, when the nrfx pwm driver is finished handling the PWM sequence, it will release the pins, leaving them as disconnected input.

    If you rather want to control the pins at a fixed PWM signal "forever", but also with the possibility to turn them always on/always off, I suggest you look into the blinky_pwm implementation from NCS\zephyr\samples\basic\blinky_pwm.

    Attached here is another simplified version, using the pins that you wanted, P0.25 and P0.26. 

    hello_world_pwm.zip

    Note that if you use this approach (the overlay files and zephyr pwm library), the pins will be set as a static output 0 or 1 if you set the duty cycle to 100% or 0% of the PWM period.

    Also note that the pins are selected in the nrf5340dk_nrf5340_cpuapp.overlay file. It should be quite easy to change them (pin number, polarity, etc).

    Best regards,

    Edvin

  • My goal is to play PCM audio (48 kHz, int16) coming from a PC,  Down sampled to 8k uint8_t and play through a GPIO pin using PWM, and also mix it with other generated waveforms (like sine or custom tones).

    Here’s the concept:

    • I will use the nrfx_pwm driver in loop mode.

    • I maintain a ring buffer that continuously stores new PCM data.

    • The PWM sequence (nrf_pwm_sequence_t) will point to this buffer, and I’ll use nrfx_pwm_sequence_update() periodically to refresh it with the latest data.

    • This should allow asynchronous audio playback, avoiding glitches caused by blocking or synchronous updates.

    I believe this approach will give smooth asynchronous playback and help eliminate current glitches.
    Does this seem like a good way to handle real-time PCM playback using PWM on nRF5340?
    Any suggestions or potential pitfalls I should be aware

Reply
  • My goal is to play PCM audio (48 kHz, int16) coming from a PC,  Down sampled to 8k uint8_t and play through a GPIO pin using PWM, and also mix it with other generated waveforms (like sine or custom tones).

    Here’s the concept:

    • I will use the nrfx_pwm driver in loop mode.

    • I maintain a ring buffer that continuously stores new PCM data.

    • The PWM sequence (nrf_pwm_sequence_t) will point to this buffer, and I’ll use nrfx_pwm_sequence_update() periodically to refresh it with the latest data.

    • This should allow asynchronous audio playback, avoiding glitches caused by blocking or synchronous updates.

    I believe this approach will give smooth asynchronous playback and help eliminate current glitches.
    Does this seem like a good way to handle real-time PCM playback using PWM on nRF5340?
    Any suggestions or potential pitfalls I should be aware

Children
No Data
Related