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_DEFAULTis 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 lasts1 / resolution_hzseconds, 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 byprescale. Raise it to extend the measurable period range, at the cost of time resolution.pos_edgeandneg_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 logical1on the pin is seen as0by 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; passNULLto 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 alwaysMCPWM_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
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_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
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 channelconfig -- [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_enableNote
User can deregister a previously registered callback by calling this function and setting the callback member in the
cbsstructure 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_resolutionto 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.
-
uint32_t allow_pd
-
int group_id
-
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
-
mcpwm_sync_handle_t sync_src
-
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.
-
int gpio_num
-
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
-
mcpwm_capture_event_cb_t on_cap