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 |
|
Servo, dimming, basic control |
Complementary half-bridge PWM |
Half bridge, inverter leg |
||
Aligned or phase-shifted outputs |
Multi-phase motor, paralleled converters |
||
Measure pulse width or period |
HC-SR04, tachometer, RC input |
||
Hardware peripheral linking |
|
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
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_mcpwmcomponent. To declare that your component depends onesp_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
-
uint32_t count_value
-
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
-
uint32_t compare_ticks
-
struct mcpwm_capture_event_data_t
MCPWM capture event data.
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
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_mcpwmcomponent. To declare that your component depends onesp_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
-
enumerator MCPWM_TIMER_DIRECTION_UP
-
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
-
enumerator MCPWM_TIMER_EVENT_EMPTY
-
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
-
enumerator MCPWM_TIMER_COUNT_MODE_PAUSE
-
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
-
enumerator MCPWM_TIMER_STOP_EMPTY
-
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
-
enumerator MCPWM_GEN_ACTION_KEEP
-
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
-
enumerator MCPWM_OPER_BRAKE_MODE_CBC
-
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
-
enumerator MCPWM_CAP_EDGE_POS