How to configure 2 PWM pins with same frequency and duty cycle but opposite polarity

I'm developing a product with the nRF52840, using Zephyr (Toolchain & SDK version 2.7.0).  Is there a way to set up two PWM outputs having the same frequency and duty cycle, but with opposite polarity (such that when one pin goes high, the other goes low, and vice-versa)?

I'll also need to start and stop the outputs together (or at least close to it).

Thanks in advance for any help,

--mkj

Parents
  • Try this code for inverted PWM after you configured two PWM in the device tree

    const struct pwm_dt_spec pwm_1 = PWM_DT_SPEC_GET(DT_ALIAS(pwm0));
    const struct pwm_dt_spec pwm_2 = PWM_DT_SPEC_GET(DT_ALIAS(pwm1));
    
    void configure_pwm_opposite_polarity(uint32_t period_usec, uint32_t duty_cycle_usec)
    {
        if (!device_is_ready(pwm_1.dev) || !device_is_ready(pwm_2.dev)) {
            printk("Error: PWM device is not ready.\n");
            return;
        }
    
        // Set PWM for the first channel (normal polarity)
        pwm_set_dt(&pwm_1, period_usec, duty_cycle_usec);
    
        // Set PWM for the second channel (inverted polarity)
        pwm_set_dt(&pwm_2, period_usec, period_usec - duty_cycle_usec);
    }

Reply
  • Try this code for inverted PWM after you configured two PWM in the device tree

    const struct pwm_dt_spec pwm_1 = PWM_DT_SPEC_GET(DT_ALIAS(pwm0));
    const struct pwm_dt_spec pwm_2 = PWM_DT_SPEC_GET(DT_ALIAS(pwm1));
    
    void configure_pwm_opposite_polarity(uint32_t period_usec, uint32_t duty_cycle_usec)
    {
        if (!device_is_ready(pwm_1.dev) || !device_is_ready(pwm_2.dev)) {
            printk("Error: PWM device is not ready.\n");
            return;
        }
    
        // Set PWM for the first channel (normal polarity)
        pwm_set_dt(&pwm_1, period_usec, duty_cycle_usec);
    
        // Set PWM for the second channel (inverted polarity)
        pwm_set_dt(&pwm_2, period_usec, period_usec - duty_cycle_usec);
    }

Children
  • Since my duty cycle is 50%, the pulse length for pwm_2 is exactly the same as pmw_1; I don't understand how this gets the opposite polarity.

    Maybe there's something I need to do in the overlay file where I define the PWMs?

  • Here's what I've defined in my overlay file:

    pwm0: &pwm0 {
        status = "okay";
        pinctrl-0 = <&pwm0_default>;
        pinctrl-names = "default";
    };

    &pinctrl {
        pwm0_default: pwm0_default {
            group1 {
                psels = <NRF_PSEL(PWM_OUT0, 0, 22)>,    // Channel 0: Audio PWM
                        <NRF_PSEL(PWM_OUT0, 0, 23)>;    // Channel 1: Boost PWM
                nordic,drive-mode = <NRF_DRIVE_H0H1>;
            };
        };
    };

    Here's what's at the top my source file:

    const struct pwm_dt_spec pwm = PWM_DT_SPEC_GET(DT_NODELABEL(pwm0));

    #define AUDIO_CHANNEL   0
    #define BOOST_CHANNEL   1

    It simply will not compile; it is unable to find pwm0 (I think; it is actually impossible to tell what the build system is unhappy about).  Here is the compiler output:

    C:/ncs/v2.7.0/zephyr/include/zephyr/device.h:91:41: error: '__device_dts_ord_DT_N_S_soc_S_pwm_4001c000_P_pwms_IDX_0_PH_ORD' undeclared here (not in a function)
       91 | #define DEVICE_NAME_GET(dev_id) _CONCAT(__device_, dev_id)
          |                                         ^~~~~~~~~
    C:/ncs/v2.7.0/zephyr/include/zephyr/toolchain/common.h:137:26: note: in definition of macro '_DO_CONCAT'
      137 | #define _DO_CONCAT(x, y) x ## y
          |                          ^
    C:/ncs/v2.7.0/zephyr/include/zephyr/device.h:91:33: note: in expansion of macro '_CONCAT'
       91 | #define DEVICE_NAME_GET(dev_id) _CONCAT(__device_, dev_id)
          |                                 ^~~~~~~
    C:/ncs/v2.7.0/zephyr/include/zephyr/device.h:228:37: note: in expansion of macro 'DEVICE_NAME_GET'
      228 | #define DEVICE_DT_NAME_GET(node_id) DEVICE_NAME_GET(Z_DEVICE_DT_DEV_ID(node_id))
          |                                     ^~~~~~~~~~~~~~~
    C:/ncs/v2.7.0/zephyr/include/zephyr/device.h:245:34: note: in expansion of macro 'DEVICE_DT_NAME_GET'
      245 | #define DEVICE_DT_GET(node_id) (&DEVICE_DT_NAME_GET(node_id))
          |                                  ^~~~~~~~~~~~~~~~~~
    C:/ncs/v2.7.0/zephyr/include/zephyr/drivers/pwm.h:260:24: note: in expansion of macro 'DEVICE_DT_GET'
      260 |                 .dev = DEVICE_DT_GET(DT_PWMS_CTLR_BY_IDX(node_id, idx)),       \
          |                        ^~~~~~~~~~~~~
    C:/ncs/v2.7.0/zephyr/include/zephyr/drivers/pwm.h:328:34: note: in expansion of macro 'PWM_DT_SPEC_GET_BY_IDX'
      328 | #define PWM_DT_SPEC_GET(node_id) PWM_DT_SPEC_GET_BY_IDX(node_id, 0)
          |                                  ^~~~~~~~~~~~~~~~~~~~~~
    C:/Clients/Venture/Phone/src/Keyboard/peripheral_hids_keyboard/src/speaker.c:16:32: note: in expansion of macro 'PWM_DT_SPEC_GET'
       16 | const struct pwm_dt_spec pwm = PWM_DT_SPEC_GET(DT_NODELABEL(pwm0));
          |                                ^~~~~~~~~~~~~~~
    In file included from C:/ncs/v2.7.0/zephyr/include/zephyr/arch/arm/arch.h:20,
                     from C:/ncs/v2.7.0/zephyr/include/zephyr/arch/cpu.h:19,
                     from C:/ncs/v2.7.0/zephyr/include/zephyr/kernel_includes.h:36:
    C:/Clients/Venture/Phone/src/Keyboard/peripheral_hids_keyboard/build/zephyr/include/generated/devicetree_generated.h:5965:36: error: 'DT_N_S_soc_S_pwm_4001c000_P_pwms_IDX_0_VAL_channel' undeclared here (not in a function); did you mean 'DT_N_S_soc_S_pwm_4001c000_REG_IDX_0_VAL_SIZE'?
     5965 | #define DT_N_NODELABEL_pwm0        DT_N_S_soc_S_pwm_4001c000
          |                                    ^~~~~~~~~~~~~~~~~~~~~~~~~
    C:/ncs/v2.7.0/zephyr/include/zephyr/devicetree.h:4543:9: note: in definition of macro 'DT_CAT7'
     4543 |         a1 ## a2 ## a3 ## a4 ## a5 ## a6 ## a7
          |         ^~
    C:/ncs/v2.7.0/zephyr/include/zephyr/devicetree/pwms.h:136:9: note: in expansion of macro 'DT_PHA_BY_IDX'
      136 |         DT_PHA_BY_IDX(node_id, pwms, idx, cell)
          |         ^~~~~~~~~~~~~
    C:/ncs/v2.7.0/zephyr/include/zephyr/devicetree/pwms.h:208:9: note: in expansion of macro 'DT_PWMS_CELL_BY_IDX'
      208 |         DT_PWMS_CELL_BY_IDX(node_id, idx, channel)
          |         ^~~~~~~~~~~~~~~~~~~
    C:/ncs/v2.7.0/zephyr/include/zephyr/drivers/pwm.h:261:28: note: in expansion of macro 'DT_PWMS_CHANNEL_BY_IDX'
      261 |                 .channel = DT_PWMS_CHANNEL_BY_IDX(node_id, idx),               \
          |                            ^~~~~~~~~~~~~~~~~~~~~~
    C:/ncs/v2.7.0/zephyr/include/zephyr/drivers/pwm.h:328:34: note: in expansion of macro 'PWM_DT_SPEC_GET_BY_IDX'
      328 | #define PWM_DT_SPEC_GET(node_id) PWM_DT_SPEC_GET_BY_IDX(node_id, 0)
          |                                  ^~~~~~~~~~~~~~~~~~~~~~
    C:/Clients/Venture/Phone/src/Keyboard/peripheral_hids_keyboard/src/speaker.c:16:32: note: in expansion of macro 'PWM_DT_SPEC_GET'
       16 | const struct pwm_dt_spec pwm = PWM_DT_SPEC_GET(DT_NODELABEL(pwm0));
          |                                ^~~~~~~~~~~~~~~
    C:/ncs/v2.7.0/zephyr/include/zephyr/devicetree.h:4532:24: note: in expansion of macro 'DT_N_NODELABEL_pwm0'
     4532 | #define DT_CAT(a1, a2) a1 ## a2
          |                        ^~
    C:/ncs/v2.7.0/zephyr/include/zephyr/devicetree.h:200:29: note: in expansion of macro 'DT_CAT'
      200 | #define DT_NODELABEL(label) DT_CAT(DT_N_NODELABEL_, label)
          |                             ^~~~~~
    C:/Clients/Venture/Phone/src/Keyboard/peripheral_hids_keyboard/src/speaker.c:16:48: note: in expansion of macro 'DT_NODELABEL'
       16 | const struct pwm_dt_spec pwm = PWM_DT_SPEC_GET(DT_NODELABEL(pwm0));
          |                                                ^~~~~~~~~~~~
    C:/Clients/Venture/Phone/src/Keyboard/peripheral_hids_keyboard/build/zephyr/include/generated/devicetree_generated.h:5965:36: error: 'DT_N_S_soc_S_pwm_4001c000_P_pwms_IDX_0_VAL_period' undeclared here (not in a function); did you mean 'DT_N_S_soc_S_pwm_4001c000_IRQ_IDX_0_VAL_irq'?
     5965 | #define DT_N_NODELABEL_pwm0        DT_N_S_soc_S_pwm_4001c000
          |                                    ^~~~~~~~~~~~~~~~~~~~~~~~~
    C:/ncs/v2.7.0/zephyr/include/zephyr/devicetree.h:4543:9: note: in definition of macro 'DT_CAT7'
     4543 |         a1 ## a2 ## a3 ## a4 ## a5 ## a6 ## a7
          |         ^~
    C:/ncs/v2.7.0/zephyr/include/zephyr/devicetree/pwms.h:136:9: note: in expansion of macro 'DT_PHA_BY_IDX'
      136 |         DT_PHA_BY_IDX(node_id, pwms, idx, cell)
          |         ^~~~~~~~~~~~~
    C:/ncs/v2.7.0/zephyr/include/zephyr/devicetree/pwms.h:249:9: note: in expansion of macro 'DT_PWMS_CELL_BY_IDX'
      249 |         DT_PWMS_CELL_BY_IDX(node_id, idx, period)
          |         ^~~~~~~~~~~~~~~~~~~
    C:/ncs/v2.7.0/zephyr/include/zephyr/drivers/pwm.h:262:27: note: in expansion of macro 'DT_PWMS_PERIOD_BY_IDX'
      262 |                 .period = DT_PWMS_PERIOD_BY_IDX(node_id, idx),                 \
          |                           ^~~~~~~~~~~~~~~~~~~~~
    C:/ncs/v2.7.0/zephyr/include/zephyr/drivers/pwm.h:328:34: note: in expansion of macro 'PWM_DT_SPEC_GET_BY_IDX'
      328 | #define PWM_DT_SPEC_GET(node_id) PWM_DT_SPEC_GET_BY_IDX(node_id, 0)
          |                                  ^~~~~~~~~~~~~~~~~~~~~~
    C:/Clients/Venture/Phone/src/Keyboard/peripheral_hids_keyboard/src/speaker.c:16:32: note: in expansion of macro 'PWM_DT_SPEC_GET'
       16 | const struct pwm_dt_spec pwm = PWM_DT_SPEC_GET(DT_NODELABEL(pwm0));
          |                                ^~~~~~~~~~~~~~~
    C:/ncs/v2.7.0/zephyr/include/zephyr/devicetree.h:4532:24: note: in expansion of macro 'DT_N_NODELABEL_pwm0'
     4532 | #define DT_CAT(a1, a2) a1 ## a2
          |                        ^~
    C:/ncs/v2.7.0/zephyr/include/zephyr/devicetree.h:200:29: note: in expansion of macro 'DT_CAT'
      200 | #define DT_NODELABEL(label) DT_CAT(DT_N_NODELABEL_, label)
          |                             ^~~~~~
    C:/Clients/Venture/Phone/src/Keyboard/peripheral_hids_keyboard/src/speaker.c:16:48: note: in expansion of macro 'DT_NODELABEL'
       16 | const struct pwm_dt_spec pwm = PWM_DT_SPEC_GET(DT_NODELABEL(pwm0));
          |                                                ^~~~~~~~~~~~

Related