# Motor Control Pulse Width Modulator (MCPWM)

[中文]

## Start Here

MCPWM turns a counter into accurately timed output edges. It is a good fit when an LEDC-style PWM is no longer enough: motor bridges need complementary outputs and dead time, inverters need synchronized phases, and sensors need pulse-width measurement.

The smallest useful MCPWM design has four objects: a timer supplies time, an operator owns the waveform resources, a comparator chooses an edge position, and a generator drives a GPIO. The other modules extend that design without changing its foundation.

## Build a PWM output

For a first PWM output, create objects from left to right in the following flow. Each stage of the main path is color-coded by role: time base (blue), operator core (purple), waveform setup (cyan), start and output (green). Amber nodes are optional additions; the red node is the safety brake. Start the timer only after all output actions are configured.

            flowchart LR
        T1["1. Create timer<br/>mcpwm_new_timer"]:::time
        O1["2. Create operator<br/>mcpwm_new_operator"]:::core
        LINK["3. Connect time base<br/>mcpwm_operator_connect_timer"]:::core
        C1["4. Create comparator<br/>mcpwm_new_comparator"]:::wave
        G1["5. Create generator<br/>mcpwm_new_generator"]:::wave
        A1["6. Describe edges<br/>mcpwm_generator_set_action_on_*_event"]:::wave
        RUN["7. Enable and start<br/>mcpwm_timer_enable<br/>mcpwm_timer_start_stop"]:::run
        PIN["PWM on GPIO"]:::output

        T1 --> O1 --> LINK --> C1 --> G1 --> A1 --> RUN --> PIN

        DT["Dead time<br/>mcpwm_generator_set_dead_time"]:::optional
        BR["Fault and brake<br/>mcpwm_new_*_fault<br/>mcpwm_operator_set_brake_on_fault"]:::safety
        SY["Phase synchronization<br/>mcpwm_new_*_sync_src<br/>mcpwm_timer_set_phase_on_sync"]:::optional
        CA["Carrier modulation<br/>mcpwm_operator_apply_carrier"]:::optional

        A1 -. extend .-> DT
        O1 -. protect .-> BR
        T1 -. align .-> SY
        O1 -. modulate .-> CA

        classDef time fill:#dbeafe,stroke:#2563eb,color:#172554
        classDef core fill:#ede9fe,stroke:#7c3aed,color:#2e1065
        classDef wave fill:#cffafe,stroke:#0891b2,color:#164e63
        classDef run fill:#dcfce7,stroke:#16a34a,color:#14532d
        classDef output fill:#bbf7d0,stroke:#15803d,color:#14532d
        classDef optional fill:#fef3c7,stroke:#d97706,color:#78350f
        classDef safety fill:#fee2e2,stroke:#dc2626,color:#7f1d1d

The following code creates one 20 kHz PWM output with a 30% duty cycle. It is meant to be read before the individual pages so a first-time user can see the whole object chain in one place. It also shows the most common runtime adjustment: changing the comparator rather than rebuilding the waveform.

    mcpwm_timer_handle_t timer = NULL;
    mcpwm_oper_handle_t oper = NULL;
    mcpwm_cmpr_handle_t comparator = NULL;
    mcpwm_gen_handle_t generator = NULL;

    // 1 MHz → 1 tick = 1 µs
    // 50 ticks → 50 µs period → 20 kHz
    ESP_ERROR_CHECK(mcpwm_new_timer(
        &(mcpwm_timer_config_t) {
            .group_id = 0,
            .clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT,
            .resolution_hz = 1000000,
            .period_ticks = 50,
            .count_mode = MCPWM_TIMER_COUNT_MODE_UP,
        },
        &timer));

    ESP_ERROR_CHECK(mcpwm_new_operator(
        &(mcpwm_operator_config_t) {
            .group_id = 0,
        },
        &oper));
    ESP_ERROR_CHECK(mcpwm_operator_connect_timer(oper, timer));

    ESP_ERROR_CHECK(mcpwm_new_comparator(
        oper,
        &(mcpwm_comparator_config_t) {
            .flags.update_cmp_on_tez = true,
        },
        &comparator));
    // 15 / 50 = 30% duty cycle
    ESP_ERROR_CHECK(mcpwm_comparator_set_compare_value(comparator, 15));

    ESP_ERROR_CHECK(mcpwm_new_generator(
        oper,
        &(mcpwm_generator_config_t) {
            .gen_gpio_num = 18,
        },
        &generator));

    // timer empty → output HIGH; compare match → output LOW
    ESP_ERROR_CHECK(mcpwm_generator_set_action_on_timer_event(
        generator,
        MCPWM_GEN_TIMER_EVENT_ACTION(
            MCPWM_TIMER_DIRECTION_UP,
            MCPWM_TIMER_EVENT_EMPTY,
            MCPWM_GEN_ACTION_HIGH)));
    ESP_ERROR_CHECK(mcpwm_generator_set_action_on_compare_event(
        generator,
        MCPWM_GEN_COMPARE_EVENT_ACTION(
            MCPWM_TIMER_DIRECTION_UP,
            comparator,
            MCPWM_GEN_ACTION_LOW)));

    ESP_ERROR_CHECK(mcpwm_timer_enable(timer));
    ESP_ERROR_CHECK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_START_NO_STOP));

    // Change duty at run time by moving the edge.
    // 25 / 50 = 50% duty
    ESP_ERROR_CHECK(mcpwm_comparator_set_compare_value(comparator, 25));

The timer's `resolution_hz` and `period_ticks` set the timing scale. The comparator's `compare_value` chooses an edge in that scale, and the generator action APIs decide the output level at the timer boundary or comparator crossing. This division is useful when tuning: change the timer for frequency, the comparator for duty or edge position, and generator actions for polarity or waveform shape.

After the waveform is configured, call `mcpwm_timer_enable()` and `mcpwm_timer_start_stop()`. At run time, update the comparator with `mcpwm_comparator_set_compare_value()` rather than rebuilding the generator actions. Use the relevant optional branch only when the application needs it: dead time for a half bridge, fault and brake for a safety path, sync for phase alignment, and carrier for isolated drive.

## Feature map

Goal | Read first | Key APIs | Typical use
--- | --- | --- | ---
One PWM output | timer -> operator -> comparator generator | `mcpwm_new_timer()` `mcpwm_new_comparator()` `mcpwm_generator_set_action_on_*_event` | Servo, dimming, basic control
Complementary half-bridge PWM | dead-time section in generator + fault | `mcpwm_generator_set_dead_time()` `mcpwm_operator_set_brake_on_fault()` | Half bridge, inverter leg
Aligned or phase-shifted outputs | sync | `mcpwm_timer_set_phase_on_sync()` `mcpwm_new_timer_sync_src()` | Multi-phase motor, paralleled converters
Measure pulse width or period | capture | `mcpwm_new_capture_timer()` `mcpwm_capture_channel_register_event_callbacks()` | HC-SR04, tachometer, RC input
Hardware peripheral linking | ETM | `mcpwm_timer_new_etm_event()` `mcpwm_new_event_comparator()` | ADC trigger, timing chains

Each page in this guide covers one MCPWM module:

- MCPWM Timer: Set the Frequency
- MCPWM Operator: Assemble an Output Stage
- MCPWM Comparator: Turn a Ratio into an Edge
- MCPWM Generator: Create the PWM Waveform
- MCPWM Fault: Bring a Protection Signal into MCPWM
- MCPWM Synchronization: Align PWM Phases
- MCPWM Capture: Measure an Input Pulse
- MCPWM ETM: Hardware Linking Between Peripherals
- MCPWM Advanced Topics

## Resource and lifetime rules

All objects belong to an MCPWM group. A timer and the operator connected to it must be in the same group; GPIO fault and GPIO sync sources can likewise be consumed only inside their group. Hardware resources are limited, so creation can return `ESP_ERR_NOT_FOUND`.

Every object is created by a `mcpwm_new_*()` factory that returns an opaque handle, and released with the matching `mcpwm_del_*()` function — for example `mcpwm_new_timer()` and `mcpwm_del_timer()`. Create parent objects before their children and destroy them in reverse order: generators/comparators first, then their operator, then the timer. A timer must be disabled before it can be deleted. Capture channels must be deleted before their capture timer.

The group clock divider is shared by timers and, on some targets, capture timers. Allocate objects in monotonic requested-resolution order (high-to-low or low-to-high) to avoid a divider conflict. See advanced topics for the exact resolution rules.

## Glossary

- TEZ: Timer equals zero, when the timer count reaches zero.

- TEP: Timer equals peak, when the timer count reaches its peak.

- Timer: The time base that defines PWM frequency and tick granularity.

- Operator: The container between the timer and the outputs; it manages comparators, generators, brake, dead time, and carrier.

- Comparator: Emits an event when the count reaches a threshold; often used to place an edge or define duty.

- Generator: Drives the GPIO level in response to timer, comparator, fault, or sync events.

- Dead time: A non-overlap interval between half-bridge transitions to avoid shoot-through.

- Fault: An abnormal condition source, from GPIO or software.

- Brake: The output safety policy applied after a fault.

- CBC: Cycle-by-cycle braking that recovers automatically at a cycle boundary after the fault clears.

- OST: One-shot braking that stays latched until software recovers it.

- Sync: Loading a timer to a chosen count and direction on a sync edge.

- Capture: Timestamping external input edges to measure pulse width, period, or speed.

## Application examples

- peripherals/mcpwm/mcpwm_servo_control — one PWM output for an RC servo.

- peripherals/mcpwm/mcpwm_bdc_speed_control — brushed DC motor and speed feedback.

- peripherals/mcpwm/mcpwm_bldc_hall_control — BLDC commutation using Hall-sensor feedback.

- peripherals/mcpwm/mcpwm_capture_hc_sr04 — pulse width measurement with an HC-SR04.

- peripherals/mcpwm/mcpwm_sync — GPIO, timer, and software synchronization.

- peripherals/mcpwm/mcpwm_foc_svpwm_open_loop — three complementary PWM pairs for open-loop FOC.

## API Reference

### Common types

#### Header File

- components/esp_driver_mcpwm/include/driver/mcpwm_types.h

- This header file can be included with:

  >     #include "driver/mcpwm_types.h"

- This header file is a part of the API provided by the `esp_driver_mcpwm` component. To declare that your component depends on `esp_driver_mcpwm`, add the following to your CMakeLists.txt:

  >     REQUIRES esp_driver_mcpwm

  > or

  >     PRIV_REQUIRES esp_driver_mcpwm

#### Structures

struct mcpwm_timer_event_data_t

  MCPWM timer event data.

  Public Members

  uint32_t count_value

    MCPWM timer count value

  mcpwm_timer_direction_t direction

    MCPWM timer count direction

struct mcpwm_brake_event_data_t

  MCPWM brake event data.

struct mcpwm_fault_event_data_t

  MCPWM fault event data.

struct mcpwm_compare_event_data_t

  MCPWM compare event data.

  Public Members

  uint32_t compare_ticks

    Compare value

  mcpwm_timer_direction_t direction

    Count direction

struct mcpwm_capture_event_data_t

  MCPWM capture event data.

  Public Members

  uint32_t cap_value

    Captured value

  mcpwm_capture_edge_t cap_edge

    Capture edge

#### Type Definitions

typedef struct mcpwm_timer_t *mcpwm_timer_handle_t

  Type of MCPWM timer handle.

typedef struct mcpwm_oper_t *mcpwm_oper_handle_t

  Type of MCPWM operator handle.

typedef struct mcpwm_cmpr_t *mcpwm_cmpr_handle_t

  Type of MCPWM comparator handle.

typedef struct mcpwm_gen_t *mcpwm_gen_handle_t

  Type of MCPWM generator handle.

typedef struct mcpwm_fault_t *mcpwm_fault_handle_t

  Type of MCPWM fault handle.

typedef struct mcpwm_sync_t *mcpwm_sync_handle_t

  Type of MCPWM sync handle.

typedef struct mcpwm_cap_timer_t *mcpwm_cap_timer_handle_t

  Type of MCPWM capture timer handle.

typedef struct mcpwm_cap_channel_t *mcpwm_cap_channel_handle_t

  Type of MCPWM capture channel handle.

typedef bool (*mcpwm_timer_event_cb_t)(mcpwm_timer_handle_t timer, const mcpwm_timer_event_data_t *edata, void *user_ctx)

  MCPWM timer event callback function.

  Param timer:

    [in] MCPWM timer handle

  Param edata:

    [in] MCPWM timer event data, fed by driver

  Param user_ctx:

    [in] User data, set in `mcpwm_timer_register_event_callbacks()`

  Return:

    Whether a high priority task has been waken up by this function

typedef bool (*mcpwm_brake_event_cb_t)(mcpwm_oper_handle_t oper, const mcpwm_brake_event_data_t *edata, void *user_ctx)

  MCPWM operator brake event callback function.

  Param oper:

    [in] MCPWM operator handle

  Param edata:

    [in] MCPWM brake event data, fed by driver

  Param user_ctx:

    [in] User data, set in `mcpwm_operator_register_event_callbacks()`

  Return:

    Whether a high priority task has been waken up by this function

typedef bool (*mcpwm_fault_event_cb_t)(mcpwm_fault_handle_t fault, const mcpwm_fault_event_data_t *edata, void *user_ctx)

  MCPWM fault event callback function.

  Param fault:

    MCPWM fault handle

  Param edata:

    MCPWM fault event data, fed by driver

  Param user_ctx:

    User data, set in `mcpwm_fault_register_event_callbacks()`

  Return:

    whether a task switch is needed after the callback returns

typedef bool (*mcpwm_compare_event_cb_t)(mcpwm_cmpr_handle_t comparator, const mcpwm_compare_event_data_t *edata, void *user_ctx)

  MCPWM comparator event callback function.

  Param comparator:

    MCPWM comparator handle

  Param edata:

    MCPWM comparator event data, fed by driver

  Param user_ctx:

    User data, set in `mcpwm_comparator_register_event_callbacks()`

  Return:

    Whether a high priority task has been waken up by this function

typedef bool (*mcpwm_capture_event_cb_t)(mcpwm_cap_channel_handle_t cap_channel, const mcpwm_capture_event_data_t *edata, void *user_ctx)

  MCPWM capture event callback function.

  Param cap_channel:

    MCPWM capture channel handle

  Param edata:

    MCPWM capture event data, fed by driver

  Param user_ctx:

    User data, set in `mcpwm_capture_channel_register_event_callbacks()`

  Return:

    Whether a high priority task has been waken up by this function

#### Header File

- components/esp_hal_mcpwm/include/hal/mcpwm_types.h

- This header file can be included with:

  >     #include "hal/mcpwm_types.h"

- This header file is a part of the API provided by the `esp_hal_mcpwm` component. To declare that your component depends on `esp_hal_mcpwm`, add the following to your CMakeLists.txt:

  >     REQUIRES esp_hal_mcpwm

  > or

  >     PRIV_REQUIRES esp_hal_mcpwm

#### Type Definitions

typedef soc_periph_mcpwm_timer_clk_src_t mcpwm_timer_clock_source_t

  MCPWM timer clock source.

typedef soc_periph_mcpwm_capture_clk_src_t mcpwm_capture_clock_source_t

  MCPWM capture clock source.

typedef soc_periph_mcpwm_carrier_clk_src_t mcpwm_carrier_clock_source_t

  MCPWM carrier clock source.

#### Enumerations

enum mcpwm_timer_direction_t

  MCPWM timer count direction.

  Values:

  enumerator MCPWM_TIMER_DIRECTION_UP

    Counting direction: Increase

  enumerator MCPWM_TIMER_DIRECTION_DOWN

    Counting direction: Decrease

enum mcpwm_timer_event_t

  MCPWM timer events.

  Values:

  enumerator MCPWM_TIMER_EVENT_EMPTY

    MCPWM timer counts to zero (i.e. counter is empty)

  enumerator MCPWM_TIMER_EVENT_FULL

    MCPWM timer counts to peak (i.e. counter is full)

  enumerator MCPWM_TIMER_EVENT_INVALID

    MCPWM timer invalid event

enum mcpwm_timer_count_mode_t

  MCPWM timer count modes.

  Values:

  enumerator MCPWM_TIMER_COUNT_MODE_PAUSE

    MCPWM timer paused

  enumerator MCPWM_TIMER_COUNT_MODE_UP

    MCPWM timer counting up

  enumerator MCPWM_TIMER_COUNT_MODE_DOWN

    MCPWM timer counting down

  enumerator MCPWM_TIMER_COUNT_MODE_UP_DOWN

    MCPWM timer counting up and down

enum mcpwm_timer_start_stop_cmd_t

  MCPWM timer commands, specify the way to start or stop the timer.

  Values:

  enumerator MCPWM_TIMER_STOP_EMPTY

    MCPWM timer stops when next count reaches zero

  enumerator MCPWM_TIMER_STOP_FULL

    MCPWM timer stops when next count reaches peak

  enumerator MCPWM_TIMER_START_NO_STOP

    MCPWM timer starts counting, and don't stop until received stop command

  enumerator MCPWM_TIMER_START_STOP_EMPTY

    MCPWM timer starts counting and stops when next count reaches zero

  enumerator MCPWM_TIMER_START_STOP_FULL

    MCPWM timer starts counting and stops when next count reaches peak

enum mcpwm_generator_action_t

  MCPWM generator actions.

  Values:

  enumerator MCPWM_GEN_ACTION_KEEP

    Generator action: Keep the same level

  enumerator MCPWM_GEN_ACTION_LOW

    Generator action: Force to low level

  enumerator MCPWM_GEN_ACTION_HIGH

    Generator action: Force to high level

  enumerator MCPWM_GEN_ACTION_TOGGLE

    Generator action: Toggle level

enum mcpwm_operator_brake_mode_t

  MCPWM operator brake mode.

  Values:

  enumerator MCPWM_OPER_BRAKE_MODE_CBC

    Brake mode: CBC (cycle by cycle)

  enumerator MCPWM_OPER_BRAKE_MODE_OST

    Brake mode: OST (one shot)

  enumerator MCPWM_OPER_BRAKE_MODE_INVALID

    MCPWM operator invalid brake mode

enum mcpwm_capture_edge_t

  MCPWM capture edge.

  Values:

  enumerator MCPWM_CAP_EDGE_POS

    Capture on the positive edge

  enumerator MCPWM_CAP_EDGE_NEG

    Capture on the negative edge

enum mcpwm_timer_etm_event_type_t

  MCPWM timer specific events that supported by the ETM module.

  Values:

  enumerator MCPWM_TIMER_ETM_EVENT_TEZ

    The timer reaches zero

  enumerator MCPWM_TIMER_ETM_EVENT_TEP

    The timer reaches peak

  enumerator MCPWM_TIMER_ETM_EVENT_MAX

    Maximum number of timer events

enum mcpwm_comparator_etm_event_type_t

  MCPWM comparator specific events that supported by the ETM module.

  Values:

  enumerator MCPWM_CMPR_ETM_EVENT_EQUAL

    The count value equals the value of comparator

  enumerator MCPWM_CMPR_ETM_EVENT_MAX

    Maximum number of comparator events

Was this page helpful?

- Thank you! We received your feedback.
- If you have any comments, fill in Espressif Documentation Feedback Form.

- We value your feedback.
- Let us know how we can improve this page by filling in Espressif Documentation Feedback Form.