MCPWM Capture: Measure an Input Pulse

Capture is an independent MCPWM path: a capture timer timestamps edges on capture-channel GPIOs. It does not require a PWM timer, operator, comparator, or generator. This makes it ideal for echo pulses, tachometers, Hall sensors, and RC receiver signals.

It is the MCPWM path for bringing external timing into the chip. Use it when the problem is pulse width, period, phase, or speed rather than PWM generation.

Measure a pulse width

Configure both edges, save the rising timestamp, and subtract it from the falling timestamp. With a 1 MHz capture resolution, the difference is directly in microseconds.

mcpwm_cap_timer_handle_t cap_timer = NULL;
mcpwm_cap_channel_handle_t cap_channel = NULL;
ESP_ERROR_CHECK(mcpwm_new_capture_timer(
    &(mcpwm_capture_timer_config_t) {
        .group_id = 0,
        .clk_src = MCPWM_CAPTURE_CLK_SRC_DEFAULT,
        .resolution_hz = 1000000,
    }, &cap_timer));
ESP_ERROR_CHECK(mcpwm_new_capture_channel(cap_timer,
    &(mcpwm_capture_channel_config_t) {
        .gpio_num = 6,
        .prescale = 1,
        .flags.pos_edge = true,
        .flags.neg_edge = true,
    }, &cap_channel));

Allocation alone does not start measurement. Arm the channel and run the capture timer:

ESP_ERROR_CHECK(mcpwm_capture_channel_enable(cap_channel));
ESP_ERROR_CHECK(mcpwm_capture_timer_enable(cap_timer));
ESP_ERROR_CHECK(mcpwm_capture_timer_start(cap_timer));

mcpwm_capture_channel_enable() and mcpwm_capture_timer_enable() set up the system services the capture needs; neither starts the measurement yet. mcpwm_capture_timer_start() finally makes the counter run, so edges start being timestamped.

The captured edge values reach the application through a callback, described in the next section.

Capture timestamps the rising and falling edges; subtracting them yields the high-pulse width.

Capture the rising and falling edge timestamps, then subtract to get the high-pulse width.

The two configuration structs are worth reading separately:

Capture timer configuration

  • group_id — the MCPWM group the capture timer is allocated from.

  • clk_src — the clock feeding the capture timer. MCPWM_CAPTURE_CLK_SRC_DEFAULT is right for most applications. Pick a specific source when the default one may be gated — for example, in low-power scenarios where a clock that can be switched off would stop the capture timer and corrupt your timestamps.

  • resolution_hz — the tick rate of the capture timer. One tick lasts 1 / resolution_hz seconds, so 1 MHz gives microsecond resolution. It directly sets the precision of every captured timestamp.

  • allow_pd — lets the MCPWM power domain switch off during sleep, backing up and restoring the capture registers around the sleep transition at the cost of extra RAM.

Capture channel configuration

  • gpio_num — the GPIO carrying the input signal.

  • prescale — divides the input signal before capture; the effective input frequency is the capture clock divided by prescale. Raise it to extend the measurable period range, at the cost of time resolution.

  • pos_edge and neg_edge — which edges are captured. The example captures both, which is what a pulse-width measurement needs.

  • invert_cap_signal — inverts the input signal before capture, so a logical 1 on the pin is seen as 0 by the capture peripheral and vice versa.

  • intr_priority — the interrupt priority used by the capture callbacks. Not setting it (0) lets the driver choose a low priority.

Note

The capture driver configures the GPIO as an input but does not set any pull-up or pull-down resistor. If the input signal is not actively driven to both levels, call gpio_set_pull_mode() to select the pull direction that keeps the pin at the level you expect when the line is idle.

Capture event callbacks

The event data tells you the edge and latched count. The calculation below leaves heavy work to a task in a real application.

static uint32_t rise_tick;
static bool IRAM_ATTR on_capture(mcpwm_cap_channel_handle_t channel,
                                 const mcpwm_capture_event_data_t *edata,
                                 void *user_data)
{
    if (edata->cap_edge == MCPWM_CAP_EDGE_POS) {
        rise_tick = edata->cap_value;
    } else {
        uint32_t width_ticks = edata->cap_value - rise_tick;
        // Notify a task with width_ticks; do not printf here.
    }
    return false;
}

ESP_ERROR_CHECK(mcpwm_capture_channel_register_event_callbacks(cap_channel,
    &(mcpwm_capture_event_callbacks_t) { .on_cap = on_capture }, NULL));

Obtain the actual resolution with mcpwm_capture_timer_get_resolution() before converting ticks to time. On targets where capture shares the MCPWM group clock, create capture and PWM timers in a consistent requested-resolution order.

To measure speed or period, record two timestamps of the same edge type, subtract them to get period ticks, then convert that value to frequency or RPM with the actual capture resolution.

Useful controls

mcpwm_capture_channel_trigger_soft_catch() generates a software capture event, commonly used for testing but also handy to land the timing of important software events on the same capture timebase as hardware edges; it invokes the callback as well. mcpwm_capture_get_latched_value() reads the latest timestamp without registering any callback.

mcpwm_capture_timer_stop() halts the counter, mcpwm_capture_channel_disable() gates an individual input, and stopping the timer gates the whole measurement engine. Call mcpwm_capture_timer_disable() to undo the setup done by mcpwm_capture_timer_enable() before deleting the objects.

Capture timer synchronization

The capture timer free-runs by default, so the zero point of its count is arbitrary and timestamps can only be compared with each other. Synchronization makes the running capture timer load a given count value when a sync edge arrives, anchoring the timestamps to a meaningful reference.

The most common use is aligning the capture timer with a PWM timer: use the sync emitted by the PWM timer at each period zero (TEZ) as the source and set the count value to 0, so the capture timer restarts from zero every period. A captured timestamp then directly represents the phase within the PWM period. This matters in motor control and power conversion, where feedback edges from a Hall sensor, encoder, or current sense are only meaningful at a specific phase of the PWM cycle.

Sync sources are shared with the PWM timers (GPIO, software, or timer — all in the same MCPWM group). Configure the receiving side with mcpwm_capture_timer_set_phase_on_sync():

ESP_ERROR_CHECK(mcpwm_capture_timer_set_phase_on_sync(cap_timer,
    &(mcpwm_capture_timer_sync_phase_config_t) {
        .sync_src = timer_a_sync,  // created with mcpwm_new_timer_sync_src()
        .count_value = 0,
        .direction = MCPWM_TIMER_DIRECTION_UP,
    }));

Software and GPIO sync sources can also give the capture timer a known origin or align it to an external reference. See synchronization for how to create the sync sources and other details.

API Reference

MCPWM Capture Driver Functions

Header File

  • components/esp_driver_mcpwm/include/driver/mcpwm_cap.h

  • This header file can be included with:

    #include "driver/mcpwm_cap.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
    

Functions

esp_err_t mcpwm_new_capture_timer(const mcpwm_capture_timer_config_t *config, mcpwm_cap_timer_handle_t *ret_cap_timer)

Create MCPWM capture timer.

Parameters:
  • config -- [in] MCPWM capture timer configuration

  • ret_cap_timer -- [out] Returned MCPWM capture timer handle

Returns:

  • ESP_OK: Create MCPWM capture timer successfully

  • ESP_ERR_INVALID_ARG: Create MCPWM capture timer failed because of invalid argument

  • ESP_ERR_NO_MEM: Create MCPWM capture timer failed because out of memory

  • ESP_ERR_NOT_FOUND: Create MCPWM capture timer failed because can't find free resource

  • ESP_FAIL: Create MCPWM capture timer failed because of other error

esp_err_t mcpwm_del_capture_timer(mcpwm_cap_timer_handle_t cap_timer)

Delete MCPWM capture timer.

Parameters:

cap_timer -- [in] MCPWM capture timer, allocated by mcpwm_new_capture_timer()

Returns:

  • ESP_OK: Delete MCPWM capture timer successfully

  • ESP_ERR_INVALID_ARG: Delete MCPWM capture timer failed because of invalid argument

  • ESP_FAIL: Delete MCPWM capture timer failed because of other error

esp_err_t mcpwm_capture_timer_enable(mcpwm_cap_timer_handle_t cap_timer)

Enable MCPWM capture timer.

Parameters:

cap_timer -- [in] MCPWM capture timer handle, allocated by mcpwm_new_capture_timer()

Returns:

  • ESP_OK: Enable MCPWM capture timer successfully

  • ESP_ERR_INVALID_ARG: Enable MCPWM capture timer failed because of invalid argument

  • ESP_ERR_INVALID_STATE: Enable MCPWM capture timer failed because timer is enabled already

  • ESP_FAIL: Enable MCPWM capture timer failed because of other error

esp_err_t mcpwm_capture_timer_disable(mcpwm_cap_timer_handle_t cap_timer)

Disable MCPWM capture timer.

Parameters:

cap_timer -- [in] MCPWM capture timer handle, allocated by mcpwm_new_capture_timer()

Returns:

  • ESP_OK: Disable MCPWM capture timer successfully

  • ESP_ERR_INVALID_ARG: Disable MCPWM capture timer failed because of invalid argument

  • ESP_ERR_INVALID_STATE: Disable MCPWM capture timer failed because timer is disabled already

  • ESP_FAIL: Disable MCPWM capture timer failed because of other error

esp_err_t mcpwm_capture_timer_start(mcpwm_cap_timer_handle_t cap_timer)

Start MCPWM capture timer.

Parameters:

cap_timer -- [in] MCPWM capture timer, allocated by mcpwm_new_capture_timer()

Returns:

  • ESP_OK: Start MCPWM capture timer successfully

  • ESP_ERR_INVALID_ARG: Start MCPWM capture timer failed because of invalid argument

  • ESP_FAIL: Start MCPWM capture timer failed because of other error

esp_err_t mcpwm_capture_timer_stop(mcpwm_cap_timer_handle_t cap_timer)

Stop MCPWM capture timer.

Parameters:

cap_timer -- [in] MCPWM capture timer, allocated by mcpwm_new_capture_timer()

Returns:

  • ESP_OK: Stop MCPWM capture timer successfully

  • ESP_ERR_INVALID_ARG: Stop MCPWM capture timer failed because of invalid argument

  • ESP_FAIL: Stop MCPWM capture timer failed because of other error

esp_err_t mcpwm_capture_timer_get_resolution(mcpwm_cap_timer_handle_t cap_timer, uint32_t *out_resolution)

Get MCPWM capture timer resolution, in Hz.

Parameters:
  • cap_timer -- [in] MCPWM capture timer, allocated by mcpwm_new_capture_timer()

  • out_resolution -- [out] Returned capture timer resolution, in Hz

Returns:

  • ESP_OK: Get capture timer resolution successfully

  • ESP_ERR_INVALID_ARG: Get capture timer resolution failed because of invalid argument

  • ESP_FAIL: Get capture timer resolution failed because of other error

esp_err_t mcpwm_capture_timer_set_phase_on_sync(mcpwm_cap_timer_handle_t cap_timer, const mcpwm_capture_timer_sync_phase_config_t *config)

Set sync phase for MCPWM capture timer.

Parameters:
  • cap_timer -- [in] MCPWM capture timer, allocated by mcpwm_new_capture_timer()

  • config -- [in] MCPWM capture timer sync phase configuration

Returns:

  • ESP_OK: Set sync phase for MCPWM capture timer successfully

  • ESP_ERR_INVALID_ARG: Set sync phase for MCPWM capture timer failed because of invalid argument

  • ESP_FAIL: Set sync phase for MCPWM capture timer failed because of other error

esp_err_t mcpwm_new_capture_channel(mcpwm_cap_timer_handle_t cap_timer, const mcpwm_capture_channel_config_t *config, mcpwm_cap_channel_handle_t *ret_cap_channel)

Create MCPWM capture channel.

Note

The created capture channel won't be enabled until calling mcpwm_capture_channel_enable

Parameters:
  • cap_timer -- [in] MCPWM capture timer, allocated by mcpwm_new_capture_timer(), will be connected to the new capture channel

  • config -- [in] MCPWM capture channel configuration

  • ret_cap_channel -- [out] Returned MCPWM capture channel

Returns:

  • ESP_OK: Create MCPWM capture channel successfully

  • ESP_ERR_INVALID_ARG: Create MCPWM capture channel failed because of invalid argument

  • ESP_ERR_NO_MEM: Create MCPWM capture channel failed because out of memory

  • ESP_ERR_NOT_FOUND: Create MCPWM capture channel failed because can't find free resource

  • ESP_FAIL: Create MCPWM capture channel failed because of other error

esp_err_t mcpwm_del_capture_channel(mcpwm_cap_channel_handle_t cap_channel)

Delete MCPWM capture channel.

Parameters:

cap_channel -- [in] MCPWM capture channel handle, allocated by mcpwm_new_capture_channel()

Returns:

  • ESP_OK: Delete MCPWM capture channel successfully

  • ESP_ERR_INVALID_ARG: Delete MCPWM capture channel failed because of invalid argument

  • ESP_FAIL: Delete MCPWM capture channel failed because of other error

esp_err_t mcpwm_capture_channel_enable(mcpwm_cap_channel_handle_t cap_channel)

Enable MCPWM capture channel.

Note

This function will transit the channel state from init to enable.

Note

This function will enable the interrupt service, if it's lazy installed in mcpwm_capture_channel_register_event_callbacks().

Parameters:

cap_channel -- [in] MCPWM capture channel handle, allocated by mcpwm_new_capture_channel()

Returns:

  • ESP_OK: Enable MCPWM capture channel successfully

  • ESP_ERR_INVALID_ARG: Enable MCPWM capture channel failed because of invalid argument

  • ESP_ERR_INVALID_STATE: Enable MCPWM capture channel failed because the channel is already enabled

  • ESP_FAIL: Enable MCPWM capture channel failed because of other error

esp_err_t mcpwm_capture_channel_disable(mcpwm_cap_channel_handle_t cap_channel)

Disable MCPWM capture channel.

Parameters:

cap_channel -- [in] MCPWM capture channel handle, allocated by mcpwm_new_capture_channel()

Returns:

  • ESP_OK: Disable MCPWM capture channel successfully

  • ESP_ERR_INVALID_ARG: Disable MCPWM capture channel failed because of invalid argument

  • ESP_ERR_INVALID_STATE: Disable MCPWM capture channel failed because the channel is not enabled yet

  • ESP_FAIL: Disable MCPWM capture channel failed because of other error

esp_err_t mcpwm_capture_channel_register_event_callbacks(mcpwm_cap_channel_handle_t cap_channel, const mcpwm_capture_event_callbacks_t *cbs, void *user_data)

Set event callbacks for MCPWM capture channel.

Note

The first call to this function needs to be before the call to mcpwm_capture_channel_enable

Note

User can deregister a previously registered callback by calling this function and setting the callback member in the cbs structure to NULL.

Parameters:
  • cap_channel -- [in] MCPWM capture channel handle, allocated by mcpwm_new_capture_channel()

  • cbs -- [in] Group of callback functions

  • user_data -- [in] User data, which will be passed to callback functions directly

Returns:

  • ESP_OK: Set event callbacks successfully

  • ESP_ERR_INVALID_ARG: Set event callbacks failed because of invalid argument

  • ESP_ERR_INVALID_STATE: Set event callbacks failed because the channel is not in init state

  • ESP_FAIL: Set event callbacks failed because of other error

esp_err_t mcpwm_capture_channel_trigger_soft_catch(mcpwm_cap_channel_handle_t cap_channel)

Trigger a catch by software.

Parameters:

cap_channel -- [in] MCPWM capture channel handle, allocated by mcpwm_new_capture_channel()

Returns:

  • ESP_OK: Trigger software catch successfully

  • ESP_ERR_INVALID_ARG: Trigger software catch failed because of invalid argument

  • ESP_ERR_INVALID_STATE: Trigger software catch failed because the channel is not enabled yet

  • ESP_FAIL: Trigger software catch failed because of other error

esp_err_t mcpwm_capture_get_latched_value(mcpwm_cap_channel_handle_t cap_channel, uint32_t *value)

Get the last captured value of the MCPWM capture channel.

Note

To convert the count value to a time, user can use mcpwm_capture_timer_get_resolution to get the resolution of the capture timer.

Parameters:
  • cap_channel -- [in] MCPWM capture channel handle, allocated by mcpwm_new_capture_channel()

  • value -- [out] Returned capture value

Returns:

  • ESP_OK: Get capture value successfully

  • ESP_ERR_INVALID_ARG: Get capture value failed because of invalid argument

  • ESP_FAIL: Get capture value failed because of other error

Structures

struct mcpwm_capture_timer_config_t

MCPWM capture timer configuration structure.

Public Members

int group_id

Specify from which group to allocate the capture timer

mcpwm_capture_clock_source_t clk_src

MCPWM capture timer clock source

uint32_t resolution_hz

Resolution of capture timer

struct mcpwm_capture_timer_config_t::extra_mcpwm_capture_timer_flags flags

Extra configuration flags for timer

struct extra_mcpwm_capture_timer_flags

Extra configuration flags for capture timer.

Public Members

uint32_t allow_pd

Set to allow power down. When this flag set, the driver will backup/restore the MCPWM registers before/after entering/exist sleep mode. By this approach, the system can power off MCPWM's power domain. This can save power, but at the expense of more RAM being consumed.

struct mcpwm_capture_timer_sync_phase_config_t

MCPWM Capture timer sync phase configuration.

Public Members

mcpwm_sync_handle_t sync_src

The sync event source

uint32_t count_value

The count value that should lock to upon sync event

mcpwm_timer_direction_t direction

The count direction that should lock to upon sync event

struct mcpwm_capture_channel_config_t

MCPWM capture channel configuration structure.

Public Members

int gpio_num

GPIO used capturing input signal

int intr_priority

MCPWM capture interrupt priority, if set to 0, the driver will try to allocate an interrupt with a relative low priority (1,2,3)

uint32_t prescale

Prescale of input signal, effective frequency = cap_input_clk/prescale

struct mcpwm_capture_channel_config_t::extra_capture_channel_flags flags

Extra configuration flags for capture channel

struct extra_capture_channel_flags

Extra configuration flags for capture channel.

Public Members

uint32_t pos_edge

Whether to capture on positive edge

uint32_t neg_edge

Whether to capture on negative edge

uint32_t invert_cap_signal

Invert the input capture signal

struct mcpwm_capture_event_callbacks_t

Group of supported MCPWM capture event callbacks.

Note

The callbacks are all running under ISR environment

Public Members

mcpwm_capture_event_cb_t on_cap

Callback function that would be invoked when capture event occurred


Was this page helpful?