MCPWM Timer: Set the Frequency

The timer is the time base for every PWM output attached to its operator. It counts ticks at resolution_hz and wraps around at period_ticks. Choose the resolution first — it determines the finest step of edge placement — then choose the period for the target frequency.

For a servo, speed loop, or inverter, the timer answers the two most basic questions: how fine is one tick, and how long is one PWM cycle. Comparators and generators only place edges on top of that time base.

Build a 20 kHz time base

For an up-counting timer, period_ticks = resolution_hz / frequency_hz. The following timer has a 1 MHz tick (one microsecond per tick) and a 50-tick period, giving 20 kHz. The diagram below shows the counter climbing from 0 to 50, then resetting — the TEZ (timer event zero) and TEP (timer event peak) markers are the two boundaries that generators use.

Up-counting timer: the counter forms a sawtooth, rising from 0 to 50, firing TEZ at zero and TEP at peak.

Up-counting timer: the counter forms a sawtooth, rising from 0 to 50, firing TEZ at zero and TEP at peak.

mcpwm_timer_handle_t timer = NULL;
mcpwm_timer_config_t timer_config = {
    .group_id = 0,
    .clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT,
    .resolution_hz = 1000000,
    .period_ticks = 50,
    .count_mode = MCPWM_TIMER_COUNT_MODE_UP,
};
ESP_ERROR_CHECK(mcpwm_new_timer(&timer_config, &timer));

The timer configuration is worth reading field by field, because a few important knobs are not shown in the example:

  • group_id — the MCPWM group the timer is allocated from. Chips may expose more than one groups; each group bundles timers, operators, comparators, and generators that share clock dividers. 0 selects the first group, which is enough for most designs.

  • clk_src — the clock that feeds the timer. MCPWM_TIMER_CLK_SRC_DEFAULT selects a PLL clock and is right for almost every application. On targets with extra sources, you can pick one explicitly — for example to keep the timer counting when the PLL is switched off, such as during light sleep.

  • resolution_hz — the tick rate of the counter. One tick lasts 1 / resolution_hz seconds, so 1 MHz means one microsecond per tick. This sets the finest edge step available to the comparator.

  • period_ticks — the length of one full PWM cycle in ticks. The frequency is resolution_hz / period_ticks.

  • count_mode — whether the counter counts up only (edge-aligned PWM) or up and down (center-aligned PWM). See Counting modes and waveforms for the two shapes; the hardware also supports counting down.

  • intr_priority — the interrupt priority used by the timer callbacks. Not setting it (0) lets the driver choose a low priority; raise it when a callback must preempt other ISRs, for example in tightly timed motor control.

The example does not touch flags, so all of them are off — which is the safe default. Two of them are worth knowing:

  • update_period_on_empty and update_period_on_sync — off by default, so mcpwm_timer_set_period() takes effect immediately. Turn them on to defer frequency changes to a safe boundary; see Safe frequency updates.

  • allow_pd — lets the MCPWM power domain switch off during sleep. The driver then backs up and restores the timer registers around the sleep transition, saving power at the cost of extra RAM.

Do not start the timer yet. First create and connect the operator, comparator, and generator (see the following pages), then enable and start:

ESP_ERROR_CHECK(mcpwm_timer_enable(timer));
ESP_ERROR_CHECK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_START_NO_STOP));

mcpwm_timer_enable() activates the services the timer needs to run: it enables the timer interrupt and, with power management on, holds the group's power-management lock so clock scaling cannot disturb PWM timing. mcpwm_timer_start_stop() then starts and later stops the counter. Call mcpwm_timer_disable() to reverse the enable before freeing the timer with mcpwm_del_timer().

The third argument of mcpwm_timer_start_stop() selects the stop behavior:

  • MCPWM_TIMER_START_NO_STOP — runs continuously until you explicitly stop it.

  • MCPWM_TIMER_START_STOP_EMPTY — stops automatically when the next count reaches zero (TEZ). Use this for a single-shot or synchronized start where the cycle should complete before stopping.

  • MCPWM_TIMER_START_STOP_FULL — stops automatically when the next count reaches the peak (TEP). Use this for a single cycle that ends at the period boundary.

Counting modes and waveforms

In up mode, the counter counts from 0 to period_ticks and resets. The waveform is a sawtooth and the PWM edges align to one side of the period — this is called edge-aligned PWM.

In up-down mode, the counter counts up to period_ticks / 2 and then down to 0. The waveform is a triangle and the PWM edges are centered around the middle of the period — center-aligned PWM. Center-aligned PWM is preferred for motor control because it produces less harmonic distortion.

Up-down counting: the counter forms a triangle, rising to 25 (half of 50), then falling back to 0.

Up-down counting: the counter forms a triangle, rising to 25 (half of 50), then falling back to 0.

The frequency is still resolution_hz / period_ticks in both modes. Choose a resolution high enough for the duty precision you need, then choose a period for the desired frequency.

Important

period_ticks is the total number of ticks in one full PWM cycle. It is not always the same thing as the timer peak value.

  • In MCPWM_TIMER_COUNT_MODE_UP, the counter runs 0 -> period_ticks.

  • In MCPWM_TIMER_COUNT_MODE_UP_DOWN, the hardware peak is period_ticks / 2, and the full cycle is 0 -> peak -> 0.

For example, with resolution_hz = 1 MHz and period_ticks = 50: up mode counts 0 -> 50, while up-down mode counts 0 -> 25 -> 0. Both still take 50 microseconds for a full cycle, so both are 20 kHz. What changes is the edge placement, not the period length.

Safe frequency updates

mcpwm_timer_set_period() takes effect immediately by default. That can truncate the current cycle and produce a runt pulse. Set update_period_on_empty to defer the new period until the counter reaches zero, or update_period_on_sync to defer it until a sync event. When changing the period, also scale the comparator threshold if the duty ratio must remain unchanged:

// Keep 40 % duty while changing a 50-tick period to 100 ticks.
ESP_ERROR_CHECK(mcpwm_comparator_set_compare_value(comparator, 40));
ESP_ERROR_CHECK(mcpwm_timer_set_period(timer, 100));

In most runtime tuning paths, change the comparator to change duty and touch the timer only when the PWM frequency itself must change. Motor-control and power-conversion applications should usually combine this with update_period_on_empty or a sync-triggered update to avoid mid-cycle changes.

Timer event callbacks

The timer can notify your application at peak (on_full), zero (on_empty), or when it stops (on_stop). Register callbacks before enabling the timer. They run in ISR context: do not block, allocate memory, or call normal FreeRTOS APIs; use ...FromISR variants when needed.

Note

The timer and capture timer may share a divider with other objects in the same group. When one group needs several resolutions, create objects in monotonic requested-resolution order to avoid divider conflicts. See advanced topics for the full rule.

static bool IRAM_ATTR on_timer_empty(mcpwm_timer_handle_t timer,
                                     const mcpwm_timer_event_data_t *edata,
                                     void *user_ctx)
{
    BaseType_t high_task_woken = pdFALSE;
    vTaskNotifyGiveFromISR((TaskHandle_t)user_ctx, &high_task_woken);
    return high_task_woken == pdTRUE;
}

mcpwm_timer_event_callbacks_t cbs = { .on_empty = on_timer_empty };
ESP_ERROR_CHECK(mcpwm_timer_register_event_callbacks(timer, &cbs,
                                                      xTaskGetCurrentTaskHandle()));

The synchronization page shows how a timer can reset to a chosen phase on a sync edge.

API Reference

MCPWM Timer Driver Functions

Header File

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

  • This header file can be included with:

    #include "driver/mcpwm_timer.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_timer(const mcpwm_timer_config_t *config, mcpwm_timer_handle_t *ret_timer)

Create MCPWM timer.

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

  • ret_timer -- [out] Returned MCPWM timer handle

Returns:

  • ESP_OK: Create MCPWM timer successfully

  • ESP_ERR_INVALID_ARG: Create MCPWM timer failed because of invalid argument

  • ESP_ERR_NO_MEM: Create MCPWM timer failed because out of memory

  • ESP_ERR_NOT_FOUND: Create MCPWM timer failed because all hardware timers are used up and no more free one

  • ESP_FAIL: Create MCPWM timer failed because of other error

esp_err_t mcpwm_del_timer(mcpwm_timer_handle_t timer)

Delete MCPWM timer.

Parameters:

timer -- [in] MCPWM timer handle, allocated by mcpwm_new_timer()

Returns:

  • ESP_OK: Delete MCPWM timer successfully

  • ESP_ERR_INVALID_ARG: Delete MCPWM timer failed because of invalid argument

  • ESP_ERR_INVALID_STATE: Delete MCPWM timer failed because timer is not in init state

  • ESP_FAIL: Delete MCPWM timer failed because of other error

esp_err_t mcpwm_timer_set_period(mcpwm_timer_handle_t timer, uint32_t period_ticks)

Set a new period for MCPWM timer.

Note

If mcpwm_timer_config_t::update_period_on_empty and mcpwm_timer_config_t::update_period_on_sync are not set, the new period will take effect immediately. Otherwise, the new period will take effect when timer counts to zero or on sync event.

Note

You may need to use mcpwm_comparator_set_compare_value to set a new compare value for MCPWM comparator in order to keep the same PWM duty cycle.

Parameters:
  • timer -- [in] MCPWM timer handle, allocated by mcpwm_new_timer

  • period_ticks -- [in] New period in count ticks

Returns:

  • ESP_OK: Set new period for MCPWM timer successfully

  • ESP_ERR_INVALID_ARG: Set new period for MCPWM timer failed because of invalid argument

  • ESP_FAIL: Set new period for MCPWM timer failed because of other error

esp_err_t mcpwm_timer_enable(mcpwm_timer_handle_t timer)

Enable MCPWM timer.

Parameters:

timer -- [in] MCPWM timer handle, allocated by mcpwm_new_timer()

Returns:

  • ESP_OK: Enable MCPWM timer successfully

  • ESP_ERR_INVALID_ARG: Enable MCPWM timer failed because of invalid argument

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

  • ESP_FAIL: Enable MCPWM timer failed because of other error

esp_err_t mcpwm_timer_disable(mcpwm_timer_handle_t timer)

Disable MCPWM timer.

Parameters:

timer -- [in] MCPWM timer handle, allocated by mcpwm_new_timer()

Returns:

  • ESP_OK: Disable MCPWM timer successfully

  • ESP_ERR_INVALID_ARG: Disable MCPWM timer failed because of invalid argument

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

  • ESP_FAIL: Disable MCPWM timer failed because of other error

esp_err_t mcpwm_timer_start_stop(mcpwm_timer_handle_t timer, mcpwm_timer_start_stop_cmd_t command)

Send specific start/stop commands to MCPWM timer.

Parameters:
  • timer -- [in] MCPWM timer handle, allocated by mcpwm_new_timer()

  • command -- [in] Supported command list for MCPWM timer

Returns:

  • ESP_OK: Start or stop MCPWM timer successfully

  • ESP_ERR_INVALID_ARG: Start or stop MCPWM timer failed because of invalid argument

  • ESP_ERR_INVALID_STATE: Start or stop MCPWM timer failed because timer is not enabled

  • ESP_FAIL: Start or stop MCPWM timer failed because of other error

esp_err_t mcpwm_timer_register_event_callbacks(mcpwm_timer_handle_t timer, const mcpwm_timer_event_callbacks_t *cbs, void *user_data)

Set event callbacks for MCPWM timer.

Note

The first call to this function needs to be before the call to mcpwm_timer_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:
  • timer -- [in] MCPWM timer handle, allocated by mcpwm_new_timer()

  • 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 timer is not in init state

  • ESP_FAIL: Set event callbacks failed because of other error

esp_err_t mcpwm_timer_set_phase_on_sync(mcpwm_timer_handle_t timer, const mcpwm_timer_sync_phase_config_t *config)

Set sync phase for MCPWM timer.

Parameters:
  • timer -- [in] MCPWM timer handle, allocated by mcpwm_new_timer()

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

Returns:

  • ESP_OK: Set sync phase for MCPWM timer successfully

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

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

Structures

struct mcpwm_timer_event_callbacks_t

Group of supported MCPWM timer event callbacks.

Note

The callbacks are all running under ISR environment

Public Members

mcpwm_timer_event_cb_t on_full

callback function when MCPWM timer counts to peak value

mcpwm_timer_event_cb_t on_empty

callback function when MCPWM timer counts to zero

mcpwm_timer_event_cb_t on_stop

callback function when MCPWM timer stops

struct mcpwm_timer_config_t

MCPWM timer configuration.

Public Members

int group_id

Specify from which group to allocate the MCPWM timer

mcpwm_timer_clock_source_t clk_src

MCPWM timer clock source

uint32_t resolution_hz

Counter resolution in Hz The step size of each count tick equals to (1 / resolution_hz) seconds

mcpwm_timer_count_mode_t count_mode

Count mode

uint32_t period_ticks

Number of count ticks within a period. For up-down mode, the timer peak value is half of the period_ticks

int intr_priority

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

struct mcpwm_timer_config_t::extra_mcpwm_timer_flags flags

Extra configuration flags for timer

struct extra_mcpwm_timer_flags

Extra configuration flags for MCPWM timer.

Public Members

uint32_t update_period_on_empty

Whether to update period when timer counts to zero

uint32_t update_period_on_sync

Whether to update period on sync event

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_timer_sync_phase_config_t

MCPWM Timer sync phase configuration.

Public Members

mcpwm_sync_handle_t sync_src

The sync event source. Set to NULL will disable the timer being synced by others

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


Was this page helpful?