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:

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

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?