case about nRF54L15 NCS V3.2.1 PWM &DMA

Hi,

1. Since I need to use PWM DMA to transfer 320 bytes of data to the PWM for sequential playback.
2. I wrote the following test example to test the nrfx pwm driver, but I haven't captured the waveform of the PWM IO port. Please assist me in troubleshooting the issue.

// /*
//  * Copyright (c) 2012-2014 Wind River Systems, Inc.
//  *
//  * SPDX-License-Identifier: Apache-2.0
//  */

// #include <stdio.h>
// #include <zephyr/sys/printk.h>
// #include <nrfx_pwm.h>
// #include "user_debug.h"
// // // 翻转 GPIOx,指示一帧发送完成
// // USER_DEBUG(USER_GPIO1, TOGGLE);
// // USER_DEBUG(USER_GPIO2, TOGGLE);
// // USER_DEBUG(USER_GPIO3, TOGGLE);
// // USER_DEBUG(USER_GPIO4, TOGGLE);

// /* 定义 PWM 实例索引(使用 PWM20) */
// #define PWM_INST_IDX 20

// /* 引脚映射辅助宏(与原始示例兼容) */
// #define USER_GPIO_PIN_MAP(port, pin)   (((pin) & 0x1F) | ((port) << 5))
// #define I2S_PLUS_PIN    USER_GPIO_PIN_MAP(1, 6)
// #define I2S_MINUS_PIN   USER_GPIO_PIN_MAP(1, 7)

// /* PWM 实例(使用索引获取) */
// static nrfx_pwm_t m_pwm = NRFX_PWM_INSTANCE(NRF_PWM_INST_GET(PWM_INST_IDX));

// /* 定义 IRQ 处理函数(nrfx 4.0 新方式) */
// NRFX_INSTANCE_IRQ_HANDLER_DEFINE(pwm, PWM_INST_IDX, &m_pwm);

// /* PWM 序列数据(individual 模式,每个通道独立值) */
// static nrf_pwm_values_individual_t m_seq_values;
// static nrf_pwm_sequence_t const m_seq = {
//     .values.p_individual = &m_seq_values,
//     .length              = 1,   // 只有一个序列条目(包含四个通道值)
//     .repeats             = 0,
//     .end_delay           = 0,
// };

// /**
//  * @brief PWM 事件处理函数(简单打印,不停止播放)
//  *
//  * @param[in] event_type PWM 事件类型
//  * @param[in] p_context  上下文指针(未使用)
//  */
// static void pwm_handler(nrfx_pwm_event_type_t event_type, void *p_context)
// {
//     (void)p_context;
//     if (event_type == NRFX_PWM_EVENT_FINISHED) {
//         printk("PWM playback loop finished (continuing due to LOOP flag)\n");
//     }
// }

// /**
//  * @brief 初始化并启动 PWM 输出
//  */
// void pwm_test(void)
// {
//     /* 配置 PWM 输出引脚、时钟、计数模式等 */
//     nrfx_pwm_config_t config = {
//         .output_pins = {
//             I2S_PLUS_PIN,
//             I2S_MINUS_PIN,
//             NRF_PWM_PIN_NOT_CONNECTED,
//             NRF_PWM_PIN_NOT_CONNECTED,
//         },
//         .pin_inverted  = {false, false, false, false},
//         .irq_priority  = NRFX_PWM_DEFAULT_CONFIG_IRQ_PRIORITY,
//         .base_clock    = NRF_PWM_CLK_1MHz,
//         .count_mode    = NRF_PWM_MODE_UP,
//         .top_value     = 1000,       // PWM 周期 = 1MHz / 1000 = 1kHz
//         .load_mode     = NRF_PWM_LOAD_INDIVIDUAL,
//         .step_mode     = NRF_PWM_STEP_AUTO,
//         .skip_gpio_cfg = false,
//         .skip_psel_cfg = false,
//     };

//     nrfx_err_t err = nrfx_pwm_init(&m_pwm, &config, pwm_handler, NULL);
//     if (err != NRFX_SUCCESS) {
//         printk("nrfx_pwm_init failed: %d\n", err);
//         return;
//     }

//     /* 设置各通道的占空比(基于 top_value = 1000) */
//     m_seq_values.channel_0 = 500;   // 50% 占空比
//     m_seq_values.channel_1 = 250;   // 25% 占空比
//     m_seq_values.channel_2 = 0;
//     m_seq_values.channel_3 = 0;

//     /* 开始播放,无限循环(NRFX_PWM_FLAG_LOOP) */
//     nrfx_pwm_simple_playback(&m_pwm, &m_seq, 1, NRFX_PWM_FLAG_LOOP);
// }

// int main(void)
// {
//     /* Zephyr 环境下需要手动连接 IRQ 处理函数 */
// #if defined(__ZEPHYR__)
//     IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_PWM_INST_GET(PWM_INST_IDX)), 
//                 IRQ_PRIO_LOWEST,
//                 nrfx_pwm_irq_handler, &m_pwm, 0);
// #endif

//     printk("Starting PWM I2S simulation example\n");

//     pwm_test();

//     printk("Hello World! %s\n", CONFIG_BOARD_TARGET);

//     while (1) {
//         /* 主循环空闲,PWM 硬件自动循环播放 */
//         __WFE();
//     }

//     return 0;
// }



/*
 * Copyright (c) 2022 - 2025, Nordic Semiconductor ASA
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <nrfx_pwm.h>

/* ========== 硬件配置(nRF54L15 专用) ========== */
/* PWM 实例:必须使用 20 */
#define PWM_INST_IDX        20
/* LED 引脚(nRF54L15 DK 上 LED1~LED4 对应的 GPIO,请根据实际开发板修改) */
#define USER_GPIO_PIN_MAP(port, pin)   (((pin) & 0x1F) | ((port) << 5))
#define NRFX_PWM_PIN_NOT_USED   0xFFFFFFFF

#define PWM_LED1_PIN        USER_GPIO_PIN_MAP(1, 6)   /* P0.13 -> LED1 */
#define PWM_LED2_PIN        USER_GPIO_PIN_MAP(1, 7)   /* P0.14 -> LED2 */
#define PWM_LED3_PIN        NRFX_PWM_PIN_NOT_USED
#define PWM_LED4_PIN        NRFX_PWM_PIN_NOT_USED

/* ========== 呼吸灯参数 ========== */
#define VALUE_REPEATS       150UL   /* 每个占空比重复次数,控制呼吸速度 */
#define NUM_OF_LOOPS        3UL     /* 完整呼吸循环次数 */
#define PLAYBACK_COUNT      1UL     /* 每次播放的序列重复次数(这里设为1) */

/* PWM 实例对象(使用 PWM20) */
static nrfx_pwm_t pwm_instance = NRFX_PWM_INSTANCE(NRF_PWM_INST_GET(PWM_INST_IDX));

/* 定义 IRQ 处理函数(nrfx 4.0 方式,自动生成对应 PWM20 的中断处理) */
NRFX_INSTANCE_IRQ_HANDLER_DEFINE(pwm, PWM_INST_IDX, &pwm_instance);

/* 占空比序列(common mode,所有通道共用该值) */
static nrf_pwm_values_common_t pwm_val[] = {
    0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000,
    900, 800, 700, 600, 500, 400, 300, 200, 100, 0
};

/**
 * @brief PWM 事件回调
 */
static void pwm_handler(nrfx_pwm_event_type_t event_type, void *p_context)
{
    static uint32_t curr_loop = 1;

    if (event_type == NRFX_PWM_EVENT_FINISHED) {
        printk("Loop %u / %lu completed\n", curr_loop, NUM_OF_LOOPS);
        if (curr_loop == NUM_OF_LOOPS) {
            printk("PWM finished, uninitializing driver\n");
            nrfx_pwm_uninit(&pwm_instance);
        }
        curr_loop++;
    }
}

int main(void)
{
    printk("Starting nrfx_pwm common mode example on nRF54L15 using PWM20\n");

    IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_PWM_INST_GET(PWM_INST_IDX)), IRQ_PRIO_LOWEST,
                nrfx_pwm_irq_handler, &pwm_instance, 0);

    /* 配置 PWM(使用默认配置宏,传入四个 LED 引脚) */
    nrfx_pwm_config_t config = NRFX_PWM_DEFAULT_CONFIG(PWM_LED1_PIN, PWM_LED2_PIN,
                                                       PWM_LED3_PIN, PWM_LED4_PIN);
    /* 可选:调整 PWM 频率(默认 top_value = 1000,基频 1MHz => 1kHz) */

    nrfx_err_t err = nrfx_pwm_init(&pwm_instance, &config, pwm_handler, NULL);
    if (err != NRFX_SUCCESS) {
        printk("nrfx_pwm_init failed: %d\n", err);
        return 0;
    }

    /* 构建 PWM 序列 */
    nrf_pwm_sequence_t seq = {
        .values.p_common = pwm_val,
        .length          = ARRAY_SIZE(pwm_val),
        .repeats         = VALUE_REPEATS,
        .end_delay       = 0
    };

    /* 启动播放(循环模式,播放 PLAYBACK_COUNT 次完整序列) */
    nrfx_pwm_simple_playback(&pwm_instance, &seq, PLAYBACK_COUNT, NRFX_PWM_FLAG_LOOP);

    while (1) {
        k_sleep(K_SECONDS(1));   /* 主循环空闲,呼吸灯由 PWM 硬件自动完成 */
    }
    return 0;
}


&pwm20 {
    status = "disabled";
};


// /* 配置 PWM20 输出 P1.06 (正相) 和 P1.07 (反相) */
// &pwm20 {
//     status = "okay";
//     pinctrl-0 = <&pwm20_audio_default>;
//     pinctrl-1 = <&pwm20_audio_sleep>;
//     pinctrl-names = "default", "sleep";
// };

// &pinctrl {
//     pwm20_audio_default: pwm20_audio_default {
//         group1 {
//             psels = <NRF_PSEL(PWM_OUT0, 1, 6)>,   /* P1.06 */
//                     <NRF_PSEL(PWM_OUT1, 1, 7)>;   /* P1.07 */
//         };
//     };
//     pwm20_audio_sleep: pwm20_audio_sleep {
//         group1 {
//             psels = <NRF_PSEL(PWM_OUT0, 1, 6)>,
//                     <NRF_PSEL(PWM_OUT1, 1, 7)>;
//             low-power-enable;
//         };
//     };
// };

// /* 定义 PWM LED 节点,用于 pwm_dt_spec 绑定 */
// / {
//     pwm_audio_devices {
//         compatible = "pwm-leds";
//         pwm_audio_plus: pwm_audio_plus {
//             // 48kHz频率,正常极性
// 			pwms = <&pwm20 0 PWM_MSEC(1000/48) PWM_POLARITY_NORMAL>;
//         };
//         pwm_audio_minus: pwm_audio_minus {
//             // 48kHz频率,相反极性
// 			pwms = <&pwm20 1 PWM_MSEC(1000/48) PWM_POLARITY_INVERTED>;
//         };
//     };
//     aliases {
//         pwm-audio-plus = &pwm_audio_plus;
//         pwm-audio-minus = &pwm_audio_minus;
//     };
// };


//添加用户自己的LED
/ {
	user_gpio1: user_gpio_1 {
		compatible = "nordic,gpio-pins";
		gpios = <&gpio2 7 (GPIO_ACTIVE_HIGH)>;
	};

    user_gpio2: user_gpio_2 {
		compatible = "nordic,gpio-pins";
		gpios = <&gpio2 8 (GPIO_ACTIVE_HIGH)>;
	};

    user_gpio3: user_gpio_3 {
		compatible = "nordic,gpio-pins";
		gpios = <&gpio2 9 (GPIO_ACTIVE_HIGH)>;
	};

    user_gpio4: user_gpio_4 {
		compatible = "nordic,gpio-pins";
		gpios = <&gpio2 10 (GPIO_ACTIVE_HIGH)>;
	};

};

# # nothing here
# CONFIG_USER_DEBUG=y
# CONFIG_NRFX_PWM=y


# 启用 PWM 驱动及 nrfx 底层
CONFIG_PWM=y
CONFIG_NRFX_PWM=y
# 必须启用 PWM20 实例
# CONFIG_NRFX_PWM20=y
# 使用 printk 输出(无需完整日志系统)
CONFIG_PRINTK=y

3. A testable and simpler example is as follows:

7268.1_hello_world_pwm.zip

Best regards,

Peter.Min

  • Hi,

    I built and ran your sample with SDK v3.2.4 and it seems to work fine here:

    The only change I made was to update the error check here to check for 0 instead NRFX_SUCCESS as the nrfx has been updated to use the posix error codes in this SDK release.

    Also, if you are testing on the nrf54l DK, please note that P1.07 and P1.06 are both routed to interface MCU by default. You can change this through the board configurator app:

    Best regards,

    Vidar

Related