# 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 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,
        }));

- `sync_src` — the sync source; pass `NULL` to detach synchronization.

- `count_value` — the count loaded when the sync edge arrives.

- `direction` — the counting direction after loading; the capture timer only counts up, so it is always `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?

- 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.