Skip to content

Latest commit

 

History

History
1060 lines (726 loc) · 77 KB

mcpwm.rst

File metadata and controls

1060 lines (726 loc) · 77 KB

Motor Control Pulse Width Modulator (MCPWM)

:link_to_translation:`zh_CN:[中文]`

The MCPWM peripheral is a versatile PWM generator, which contains various submodules to make it a key element in power electronic applications like motor control, digital power, and so on. Typically, the MCPWM peripheral can be used in the following scenarios:

  • Digital motor control, e.g., brushed/brushless DC motor, RC servo motor
  • Switch mode-based digital power conversion
  • Power DAC, where the duty cycle is equivalent to a DAC analog value
  • Calculate external pulse width, and convert it into other analog values like speed, distance
  • Generate Space Vector PWM (SVPWM) signals for Field Oriented Control (FOC)

The main submodules are listed in the following diagram:

.. blockdiag:: /../_static/diagrams/mcpwm/mcpwm_overview.diag
    :caption: MCPWM Overview
    :align: center

  • MCPWM Timer: The time base of the final PWM signal. It also determines the event timing of other submodules.
  • MCPWM Operator: The key module that is responsible for generating the PWM waveforms. It consists of other submodules, like comparator, PWM generator, dead time, and carrier modulator.
  • MCPWM Comparator: The compare module takes the time-base count value as input, and continuously compares it to the threshold value configured. When the timer is equal to any of the threshold values, a compare event will be generated and the MCPWM generator can update its level accordingly.
  • MCPWM Generator: One MCPWM generator can generate a pair of PWM waves, complementarily or independently, based on various events triggered by other submodules like MCPWM Timer and MCPWM Comparator.
  • MCPWM Fault: The fault module is used to detect the fault condition from outside, mainly via the GPIO matrix. Once the fault signal is active, MCPWM Operator will force all the generators into a predefined state to protect the system from damage.
  • MCPWM Sync: The sync module is used to synchronize the MCPWM timers, so that the final PWM signals generated by different MCPWM generators can have a fixed phase difference. The sync signal can be routed from the GPIO matrix or from an MCPWM Timer event.
  • Dead Time: This submodule is used to insert extra delay to the existing PWM edges generated in the previous steps.
  • Carrier Modulation: The carrier submodule can modulate a high-frequency carrier signal into PWM waveforms by the generator and dead time submodules. This capability is mandatory for controlling the power-switching elements.
  • Brake: MCPWM operator can set how to brake the generators when a particular fault is detected. You can shut down the PWM output immediately or regulate the PWM output cycle by cycle, depending on how critical the fault is.
  • MCPWM Capture: This is a standalone submodule that can work even without the above MCPWM operators. The capture consists one dedicated timer and several independent channels, with each channel connected to the GPIO. A pulse on the GPIO triggers the capture timer to store the time-base count value and then notify you by an interrupt. Using this feature, you can measure a pulse width precisely. What is more, the capture timer can also be synchronized by the MCPWM Sync submodule.

Functional Overview

Description of the MCPWM functionality is divided into the following sections:

.. list::

    - :ref:`mcpwm-resource-allocation-and-initialization` - covers how to allocate various MCPWM objects, like timers, operators, comparators, generators and so on. These objects are the basis of the following IO setting and control functions.
    - :ref:`mcpwm-timer-operations-and-events` - describes control functions and event callbacks supported by the MCPWM timer.
    - :ref:`mcpwm-comparator-operations-and-events` - describes control functions and event callbacks supported by the MCPWM comparator.
    - :ref:`mcpwm-generator-actions-on-events` - describes how to set actions for MCPWM generators on particular events that are generated by the MCPWM timer and comparators.
    - :ref:`mcpwm-classical-pwm-waveforms-and-generator-configurations` - demonstrates some classical PWM waveforms that can be achieved by configuring generator actions.
    - :ref:`mcpwm-dead-time` - describes how to set dead time for MCPWM generators.
    - :ref:`mcpwm-classical-pwm-waveforms-and-dead-time-configurations` - demonstrates some classical PWM waveforms that can be achieved by configuring dead time.
    - :ref:`mcpwm-carrier-modulation` - describes how to set and modulate a high frequency onto the final PWM waveforms.
    - :ref:`mcpwm-faults-and-brake-actions` - describes how to set brake actions for MCPWM operators on particular fault events.
    - :ref:`mcpwm-generator-force-actions` - describes how to control the generator output level asynchronously in a forceful way.
    - :ref:`mcpwm-synchronization` - describes how to synchronize the MCPWM timers and get a fixed phase difference between the generated PWM signals.
    - :ref:`mcpwm-capture` - describes how to use the MCPWM capture module to measure the pulse width of a signal.
    :SOC_MCPWM_SUPPORT_ETM: - :ref:`mcpwm-etm-event-and-task` - describes what the events and tasks can be connected to the ETM channel.
    - :ref:`mcpwm-power-management` - describes how different source clocks affects power consumption.
    - :ref:`mcpwm-iram-safe` - describes tips on how to make the RMT interrupt work better along with a disabled cache.
    - :ref:`mcpwm-thread-safety` - lists which APIs are guaranteed to be thread-safe by the driver.
    - :ref:`mcpwm-kconfig-options` - lists the supported Kconfig options that can bring different effects to the driver.

Resource Allocation and Initialization

As displayed in the diagram above, the MCPWM peripheral consists of several submodules. Each submodule has its own resource allocation, which is described in the following sections.

MCPWM Timers

You can allocate a MCPWM timer object by calling :cpp:func:`mcpwm_new_timer` function, with a configuration structure :cpp:type:`mcpwm_timer_config_t` as the parameter. The configuration structure is defined as:

The :cpp:func:`mcpwm_new_timer` will return a pointer to the allocated timer object if the allocation succeeds. Otherwise, it will return an error code. Specifically, when there are no more free timers in the MCPWM group, this function will return the :c:macro:`ESP_ERR_NOT_FOUND` error. [1]

On the contrary, calling the :cpp:func:`mcpwm_del_timer` function will free the allocated timer object.

MCPWM Operators

You can allocate a MCPWM operator object by calling :cpp:func:`mcpwm_new_operator` function, with a configuration structure :cpp:type:`mcpwm_operator_config_t` as the parameter. The configuration structure is defined as:

The :cpp:func:`mcpwm_new_operator` will return a pointer to the allocated operator object if the allocation succeeds. Otherwise, it will return an error code. Specifically, when there are no more free operators in the MCPWM group, this function will return the :c:macro:`ESP_ERR_NOT_FOUND` error. [1]

On the contrary, calling :cpp:func:`mcpwm_del_operator` function will free the allocated operator object.

MCPWM Comparators

You can allocate a MCPWM comparator object by calling the :cpp:func:`mcpwm_new_comparator` function, with a MCPWM operator handle and configuration structure :cpp:type:`mcpwm_comparator_config_t` as the parameter. The operator handle is created by :cpp:func:`mcpwm_new_operator`. The configuration structure is defined as:

The :cpp:func:`mcpwm_new_comparator` will return a pointer to the allocated comparator object if the allocation succeeds. Otherwise, it will return an error code. Specifically, when there are no more free comparators in the MCPWM operator, this function will return the :c:macro:`ESP_ERR_NOT_FOUND` error. [1]

On the contrary, calling the :cpp:func:`mcpwm_del_comparator` function will free the allocated comparator object.

.. only:: SOC_MCPWM_SUPPORT_EVENT_COMPARATOR

    There's another kind of comparator called "Event Comparator", which **can not** control the final PWM directly but only generates the ETM events at a configurable time stamp. You can allocate an event comparator by calling the :cpp:func:`mcpwm_new_event_comparator` function. This function will return the same handle type as :cpp:func:`mcpwm_new_comparator`, but with a different configuration structure :cpp:type:`mcpwm_event_comparator_config_t`. For more information, please refer to :ref:`mcpwm-etm-event-and-task`.

MCPWM Generators

You can allocate a MCPWM generator object by calling the :cpp:func:`mcpwm_new_generator` function, with a MCPWM operator handle and configuration structure :cpp:type:`mcpwm_generator_config_t` as the parameter. The operator handle is created by :cpp:func:`mcpwm_new_operator`. The configuration structure is defined as:

The :cpp:func:`mcpwm_new_generator` will return a pointer to the allocated generator object if the allocation succeeds. Otherwise, it will return an error code. Specifically, when there are no more free generators in the MCPWM operator, this function will return the :c:macro:`ESP_ERR_NOT_FOUND` error. [1]

On the contrary, calling the :cpp:func:`mcpwm_del_generator` function will free the allocated generator object.

MCPWM Faults

There are two types of faults: A fault signal reflected from the GPIO and a fault generated by software.

To allocate a GPIO fault object, you can call the :cpp:func:`mcpwm_new_gpio_fault` function, with the configuration structure :cpp:type:`mcpwm_gpio_fault_config_t` as the parameter. The configuration structure is defined as:

The :cpp:func:`mcpwm_new_gpio_fault` will return a pointer to the allocated fault object if the allocation succeeds. Otherwise, it will return an error code. Specifically, when there are no more free GPIO faults in the MCPWM group, this function will return the :c:macro:`ESP_ERR_NOT_FOUND` error. [1]

Software fault object can be used to trigger a fault by calling the function :cpp:func:`mcpwm_soft_fault_activate` instead of waiting for a real fault signal on the GPIO. A software fault object can be allocated by calling the :cpp:func:`mcpwm_new_soft_fault` function, with configuration structure :cpp:type:`mcpwm_soft_fault_config_t` as the parameter. Currently, this configuration structure is left for future purposes.

The :cpp:func:`mcpwm_new_soft_fault` function will return a pointer to the allocated fault object if the allocation succeeds. Otherwise, it will return an error code. Specifically, when there is no memory left for the fault object, this function will return the :c:macro:`ESP_ERR_NO_MEM` error. Although the software fault and GPIO fault are of different types, the returned fault handle is of the same type.

On the contrary, calling the :cpp:func:`mcpwm_del_fault` function will free the allocated fault object, this function works for both software and GPIO fault.

MCPWM Sync Sources

The sync source is what can be used to synchronize the MCPWM timer and MCPWM capture timer. There are three types of sync sources: a sync source reflected from the GPIO, a sync source generated by software, and a sync source generated by an MCPWM timer event.

To allocate a GPIO sync source, you can call the :cpp:func:`mcpwm_new_gpio_sync_src` function, with configuration structure :cpp:type:`mcpwm_gpio_sync_src_config_t` as the parameter. The configuration structure is defined as:

The :cpp:func:`mcpwm_new_gpio_sync_src` will return a pointer to the allocated sync source object if the allocation succeeds. Otherwise, it will return an error code. Specifically, when there are no more free GPIO sync sources in the MCPWM group, this function will return the :c:macro:`ESP_ERR_NOT_FOUND` error. [1]

To allocate a timer event sync source, you can call the :cpp:func:`mcpwm_new_timer_sync_src` function, with configuration structure :cpp:type:`mcpwm_timer_sync_src_config_t` as the parameter. The configuration structure is defined as:

The :cpp:func:`mcpwm_new_timer_sync_src` will return a pointer to the allocated sync source object if the allocation succeeds. Otherwise, it will return an error code. Specifically, if a sync source has been allocated from the same timer before, this function will return the :c:macro:`ESP_ERR_INVALID_STATE` error.

Last but not least, to allocate a software sync source, you can call the :cpp:func:`mcpwm_new_soft_sync_src` function, with configuration structure :cpp:type:`mcpwm_soft_sync_config_t` as the parameter. Currently, this configuration structure is left for future purposes.

:cpp:func:`mcpwm_new_soft_sync_src` will return a pointer to the allocated sync source object if the allocation succeeds. Otherwise, it will return an error code. Specifically, when there is no memory left for the sync source object, this function will return the :c:macro:`ESP_ERR_NO_MEM` error. Please note, to make a software sync source take effect, do not forget to call :cpp:func:`mcpwm_soft_sync_activate`.

On the contrary, calling the :cpp:func:`mcpwm_del_sync_src` function will free the allocated sync source object. This function works for all types of sync sources.

MCPWM Capture Timer and Channels

The MCPWM group has a dedicated timer which is used to capture the timestamp when a specific event occurred. The capture timer is connected to several independent channels, each channel is assigned a GPIO.

To allocate a capture timer, you can call the :cpp:func:`mcpwm_new_capture_timer` function, with configuration structure :cpp:type:`mcpwm_capture_timer_config_t` as the parameter. The configuration structure is defined as:

.. only:: not SOC_MCPWM_CAPTURE_CLK_FROM_GROUP

    .. note::

        In {IDF_TARGET_NAME}, :cpp:member:`mcpwm_capture_timer_config_t::resolution_hz` parameter is invalid, the capture timer resolution is always equal to the :cpp:enumerator:`MCPWM_CAPTURE_CLK_SRC_APB`.

The :cpp:func:`mcpwm_new_capture_timer` will return a pointer to the allocated capture timer object if the allocation succeeds. Otherwise, it will return an error code. Specifically, when there is no free capture timer left in the MCPWM group, this function will return the :c:macro:`ESP_ERR_NOT_FOUND` error. [1]

Next, to allocate a capture channel, you can call the :cpp:func:`mcpwm_new_capture_channel` function, with a capture timer handle and configuration structure :cpp:type:`mcpwm_capture_channel_config_t` as the parameter. The configuration structure is defined as:

The :cpp:func:`mcpwm_new_capture_channel` will return a pointer to the allocated capture channel object if the allocation succeeds. Otherwise, it will return an error code. Specifically, when there is no free capture channel left in the capture timer, this function will return the :c:macro:`ESP_ERR_NOT_FOUND` error.

On the contrary, calling :cpp:func:`mcpwm_del_capture_channel` and :cpp:func:`mcpwm_del_capture_timer` will free the allocated capture channel and timer object accordingly.

MCPWM Interrupt Priority

MCPWM allows configuring interrupts separately for timer, operator, comparator, fault, and capture events. The interrupt priority is determined by the respective config_t::intr_priority. Additionally, events within the same MCPWM group share a common interrupt source. When registering multiple interrupt events, the interrupt priorities need to remain consistent.

Note

When registering multiple interrupt events within an MCPWM group, the driver will use the interrupt priority of the first registered event as the MCPWM group's interrupt priority.

Timer Operations and Events

Update Period

The timer period is initialized by the :cpp:member:`mcpwm_timer_config_t::period_ticks` parameter in :cpp:type:`mcpwm_timer_config_t`. You can update the period at runtime by calling :cpp:func:`mcpwm_timer_set_period` function. The new period will take effect based on how you set the :cpp:member:`mcpwm_timer_config_t::update_period_on_empty` and :cpp:member:`mcpwm_timer_config_t::update_period_on_sync` parameters in :cpp:type:`mcpwm_timer_config_t`. If none of them are set, the timer period will take effect immediately.

Register Timer Event Callbacks

The MCPWM timer can generate different events at runtime. If you have some function that should be called when a particular event happens, you should hook your function to the interrupt service routine by calling :cpp:func:`mcpwm_timer_register_event_callbacks`. The callback function prototype is declared in :cpp:type:`mcpwm_timer_event_cb_t`. All supported event callbacks are listed in the :cpp:type:`mcpwm_timer_event_callbacks_t`:

The callback functions above are called within the ISR context, so they should not attempt to block. For example, you may make sure that only FreeRTOS APIs with the ISR suffix are called within the function.

The parameter user_data of the :cpp:func:`mcpwm_timer_register_event_callbacks` function is used to save your own context. It is passed to each callback function directly.

This function will lazy the install interrupt service for the MCPWM timer without enabling it. It is only allowed to be called before :cpp:func:`mcpwm_timer_enable`, otherwise the :c:macro:`ESP_ERR_INVALID_STATE` error will be returned. See also Enable and Disable timer for more information.

Enable and Disable Timer

Before doing IO control to the timer, you need to enable the timer first, by calling :cpp:func:`mcpwm_timer_enable`. This function:

On the contrary, calling :cpp:func:`mcpwm_timer_disable` will put the timer driver back to the init state, disable the interrupt service and release the power management lock.

Start and Stop Timer

The basic IO operation of a timer is to start and stop. Calling :cpp:func:`mcpwm_timer_start_stop` with different :cpp:type:`mcpwm_timer_start_stop_cmd_t` commands can start the timer immediately or stop the timer at a specific event. What is more, you can even start the timer for only one round, which means, the timer will count to peak value or zero, and then stop itself.

Connect Timer with Operator

The allocated MCPWM timer should be connected with an MCPWM operator by calling :cpp:func:`mcpwm_operator_connect_timer`, so that the operator can take that timer as its time base, and generate the required PWM waves. Please make sure the MCPWM timer and operator are in the same group. Otherwise, this function will return the :c:macro:`ESP_ERR_INVALID_ARG` error.

Comparator Operations and Events

Register Comparator Event Callbacks

The MCPWM comparator can inform you when the timer counter equals the compare value. If you have some function that should be called when this event happens, you should hook your function to the interrupt service routine by calling :cpp:func:`mcpwm_comparator_register_event_callbacks`. The callback function prototype is declared in :cpp:type:`mcpwm_compare_event_cb_t`. All supported event callbacks are listed in the :cpp:type:`mcpwm_comparator_event_callbacks_t`:

The callback function provides event-specific data of type :cpp:type:`mcpwm_compare_event_data_t` to you. The callback function is called within the ISR context, so it should not attempt to block. For example, you may make sure that only FreeRTOS APIs with the ISR suffix are called within the function.

The parameter user_data of :cpp:func:`mcpwm_comparator_register_event_callbacks` function is used to save your own context. It is passed to the callback function directly.

This function will lazy the installation of interrupt service for the MCPWM comparator, whereas the service can only be removed in :cpp:type:`mcpwm_del_comparator`.

.. only:: SOC_MCPWM_SUPPORT_EVENT_COMPARATOR

    .. note::

        It is not supported to register event callbacks for an **Event Comparator** because it can not generate any interrupt.

Set Compare Value

You can set the compare value for the MCPWM comparator at runtime by calling :cpp:func:`mcpwm_comparator_set_compare_value`. There are a few points to note:

Generator Actions on Events

Set Generator Action on Timer Event

One generator can set multiple actions on different timer events, by calling :cpp:func:`mcpwm_generator_set_actions_on_timer_event` with a variable number of action configurations. The action configuration is defined in :cpp:type:`mcpwm_gen_timer_event_action_t`:

There is a helper macro :c:macro:`MCPWM_GEN_TIMER_EVENT_ACTION` to simplify the construction of a timer event action entry.

Please note, the argument list of :cpp:func:`mcpwm_generator_set_actions_on_timer_event` must be terminated by :c:macro:`MCPWM_GEN_TIMER_EVENT_ACTION_END`.

You can also set the timer action one by one by calling :cpp:func:`mcpwm_generator_set_action_on_timer_event` without varargs.

Set Generator Action on Compare Event

One generator can set multiple actions on different compare events, by calling :cpp:func:`mcpwm_generator_set_actions_on_compare_event` with a variable number of action configurations. The action configuration is defined in :cpp:type:`mcpwm_gen_compare_event_action_t`:

There is a helper macro :c:macro:`MCPWM_GEN_COMPARE_EVENT_ACTION` to simplify the construction of a compare event action entry.

Please note, the argument list of :cpp:func:`mcpwm_generator_set_actions_on_compare_event` must be terminated by :c:macro:`MCPWM_GEN_COMPARE_EVENT_ACTION_END`.

You can also set the compare action one by one by calling :cpp:func:`mcpwm_generator_set_action_on_compare_event` without varargs.

Set Generator Action on Fault Event

One generator can set action on fault based trigger events, by calling :cpp:func:`mcpwm_generator_set_action_on_fault_event` with an action configurations. The action configuration is defined in :cpp:type:`mcpwm_gen_fault_event_action_t`:

When no free trigger slot is left in the operator to which the generator belongs, this function will return the :c:macro:`ESP_ERR_NOT_FOUND` error. [1]

The trigger only support GPIO fault. when the input is not a GPIO fault, this function will return the :c:macro:`ESP_ERR_NOT_SUPPORTED` error.

There is a helper macro :c:macro:`MCPWM_GEN_FAULT_EVENT_ACTION` to simplify the construction of a trigger event action entry.

Please note, fault event does not have variadic function like :cpp:func:`mcpwm_generator_set_actions_on_fault_event`.

Set Generator Action on Sync Event

One generator can set action on sync based trigger events, by calling :cpp:func:`mcpwm_generator_set_action_on_sync_event` with an action configurations. The action configuration is defined in :cpp:type:`mcpwm_gen_sync_event_action_t`:

When no free trigger slot is left in the operator to which the generator belongs, this function will return the :c:macro:`ESP_ERR_NOT_FOUND` error. [1]

The trigger only support one sync action, regardless of the kinds. When set sync actions more than once, this function will return the :c:macro:`ESP_ERR_INVALID_STATE` error.

There is a helper macro :c:macro:`MCPWM_GEN_SYNC_EVENT_ACTION` to simplify the construction of a trigger event action entry.

Please note, sync event does not have variadic function like :cpp:func:`mcpwm_generator_set_actions_on_sync_event`.

Generator Configurations for Classical PWM Waveforms

This section will demonstrate the classical PWM waveforms that can be generated by the pair of generators. The code snippet that is used to generate the waveforms is also provided below the diagram. Some general summary:

  • The Symmetric or Asymmetric of the waveforms is determined by the count mode of the MCPWM timer.
  • The active level of the waveform pair is determined by the level of the PWM with a smaller duty cycle.
  • The period of the PWM waveform is determined by the timer's period and count mode.
  • The duty cycle of the PWM waveform is determined by the generator's various action combinations.

Single Edge Asymmetric Waveform - Active High

.. wavedrom:: /../_static/diagrams/mcpwm/single_edge_asym_active_high.json

static void gen_action_config(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb, mcpwm_cmpr_handle_t cmpa, mcpwm_cmpr_handle_t cmpb)
{
    ESP_ERROR_CHECK(mcpwm_generator_set_action_on_timer_event(gena,
                    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(gena,
                    MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpa, MCPWM_GEN_ACTION_LOW)));
    ESP_ERROR_CHECK(mcpwm_generator_set_action_on_timer_event(genb,
                    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(genb,
                    MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpb, MCPWM_GEN_ACTION_LOW)));
}

Single Edge Asymmetric Waveform - Active Low

.. wavedrom:: /../_static/diagrams/mcpwm/single_edge_asym_active_low.json

static void gen_action_config(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb, mcpwm_cmpr_handle_t cmpa, mcpwm_cmpr_handle_t cmpb)
{
    ESP_ERROR_CHECK(mcpwm_generator_set_action_on_timer_event(gena,
                    MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_FULL, MCPWM_GEN_ACTION_LOW)));
    ESP_ERROR_CHECK(mcpwm_generator_set_action_on_compare_event(gena,
                    MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpa, MCPWM_GEN_ACTION_HIGH)));
    ESP_ERROR_CHECK(mcpwm_generator_set_action_on_timer_event(genb,
                    MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_FULL, MCPWM_GEN_ACTION_LOW)));
    ESP_ERROR_CHECK(mcpwm_generator_set_action_on_compare_event(genb,
                    MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpb, MCPWM_GEN_ACTION_HIGH)));
}

Pulse Placement Asymmetric Waveform

.. wavedrom:: /../_static/diagrams/mcpwm/pulse_placement_asym.json

static void gen_action_config(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb, mcpwm_cmpr_handle_t cmpa, mcpwm_cmpr_handle_t cmpb)
{
    ESP_ERROR_CHECK(mcpwm_generator_set_actions_on_compare_event(gena,
                    MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpa, MCPWM_GEN_ACTION_HIGH),
                    MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpb, MCPWM_GEN_ACTION_LOW),
                    MCPWM_GEN_COMPARE_EVENT_ACTION_END()));
    ESP_ERROR_CHECK(mcpwm_generator_set_actions_on_timer_event(genb,
                    MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_TOGGLE),
                    MCPWM_GEN_TIMER_EVENT_ACTION_END()));
}

Dual Edge Asymmetric Waveform - Active Low

.. wavedrom:: /../_static/diagrams/mcpwm/dual_edge_asym_active_low.json

static void gen_action_config(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb, mcpwm_cmpr_handle_t cmpa, mcpwm_cmpr_handle_t cmpb)
{
    ESP_ERROR_CHECK(mcpwm_generator_set_actions_on_compare_event(gena,
                    MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpa, MCPWM_GEN_ACTION_HIGH),
                    MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_DOWN, cmpb, MCPWM_GEN_ACTION_LOW),
                    MCPWM_GEN_COMPARE_EVENT_ACTION_END()));
    ESP_ERROR_CHECK(mcpwm_generator_set_actions_on_timer_event(genb,
                    MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_LOW),
                    MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_DOWN, MCPWM_TIMER_EVENT_FULL, MCPWM_GEN_ACTION_HIGH),
                    MCPWM_GEN_TIMER_EVENT_ACTION_END()));
}

Dual Edge Symmetric Waveform - Active Low

.. wavedrom:: /../_static/diagrams/mcpwm/dual_edge_sym_active_low.json

static void gen_action_config(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb, mcpwm_cmpr_handle_t cmpa, mcpwm_cmpr_handle_t cmpb)
{
    ESP_ERROR_CHECK(mcpwm_generator_set_actions_on_compare_event(gena,
                    MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpa, MCPWM_GEN_ACTION_HIGH),
                    MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_DOWN, cmpa, MCPWM_GEN_ACTION_LOW),
                    MCPWM_GEN_COMPARE_EVENT_ACTION_END()));
    ESP_ERROR_CHECK(mcpwm_generator_set_actions_on_compare_event(genb,
                    MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpb, MCPWM_GEN_ACTION_HIGH),
                    MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_DOWN, cmpb, MCPWM_GEN_ACTION_LOW),
                    MCPWM_GEN_COMPARE_EVENT_ACTION_END()));
}

Dual Edge Symmetric Waveform - Complementary

.. wavedrom:: /../_static/diagrams/mcpwm/dual_edge_sym_complementary.json

static void gen_action_config(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb, mcpwm_cmpr_handle_t cmpa, mcpwm_cmpr_handle_t cmpb)
{
    ESP_ERROR_CHECK(mcpwm_generator_set_actions_on_compare_event(gena,
                    MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpa, MCPWM_GEN_ACTION_HIGH),
                    MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_DOWN, cmpa, MCPWM_GEN_ACTION_LOW),
                    MCPWM_GEN_COMPARE_EVENT_ACTION_END()));
    ESP_ERROR_CHECK(mcpwm_generator_set_actions_on_compare_event(genb,
                    MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpb, MCPWM_GEN_ACTION_LOW),
                    MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_DOWN, cmpb, MCPWM_GEN_ACTION_HIGH),
                    MCPWM_GEN_COMPARE_EVENT_ACTION_END()));
}

Dead Time

In power electronics, the rectifier and inverter are commonly used. This requires the use of a rectifier bridge and an inverter bridge. Each bridge arm has two power electronic devices, such as MOSFET, IGBT, etc. The two MOSFETs on the same arm can not conduct at the same time, otherwise there will be a short circuit. The fact is that, although the PWM wave shows it is turning off the switch, the MOSFET still needs a small time window to make that happen. This requires an extra delay to be added to the existing PWM wave generated by setting Generator Actions on Events.

The dead time driver works like a decorator. This is also reflected in the function parameters of :cpp:func:`mcpwm_generator_set_dead_time`, where it takes the primary generator handle (in_generator), and returns a new generator (out_generator) after applying the dead time. Please note, if the out_generator and in_generator are the same, it means you are adding the time delay to the PWM waveform in an "in-place" fashion. In turn, if the out_generator and in_generator are different, it means you are deriving a new PWM waveform from the existing in_generator.

Dead time specific configuration is listed in the :cpp:type:`mcpwm_dead_time_config_t` structure:

Warning

Due to the hardware limitation, one delay module (either posedge delay or negedge delay) can not be applied to multiple MCPWM generators at the same time. e.g., the following configuration is invalid:

mcpwm_dead_time_config_t dt_config = {
    .posedge_delay_ticks = 10,
};
// Set posedge delay to generator A
mcpwm_generator_set_dead_time(mcpwm_gen_a, mcpwm_gen_a, &dt_config);
// NOTE: This is invalid, you can not apply the posedge delay to another generator
mcpwm_generator_set_dead_time(mcpwm_gen_b, mcpwm_gen_b, &dt_config);

However, you can apply posedge delay to generator A and negedge delay to generator B. You can also set both posedge delay and negedge delay for generator A, while letting generator B bypass the dead time module.

Note

It is also possible to generate the required dead time by setting Generator Actions on Events, especially by controlling edge placement using different comparators. However, if the more classical edge delay-based dead time with polarity control is required, then the dead time submodule should be used.

Dead Time Configurations for Classical PWM Waveforms

This section demonstrates the classical PWM waveforms that can be generated by the dead time submodule. The code snippet that is used to generate the waveforms is also provided below the diagram.

Active High Complementary

.. wavedrom:: /../_static/diagrams/mcpwm/deadtime_active_high_complementary.json

static void gen_action_config(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb, mcpwm_cmpr_handle_t cmpa, mcpwm_cmpr_handle_t cmpb)
{
    ESP_ERROR_CHECK(mcpwm_generator_set_action_on_timer_event(gena,
                    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(gena,
                    MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpa, MCPWM_GEN_ACTION_LOW)));
}

static void dead_time_config(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb)
{
    mcpwm_dead_time_config_t dead_time_config = {
        .posedge_delay_ticks = 50,
        .negedge_delay_ticks = 0
    };
    ESP_ERROR_CHECK(mcpwm_generator_set_dead_time(gena, gena, &dead_time_config));
    dead_time_config.posedge_delay_ticks = 0;
    dead_time_config.negedge_delay_ticks = 100;
    dead_time_config.flags.invert_output = true;
    ESP_ERROR_CHECK(mcpwm_generator_set_dead_time(gena, genb, &dead_time_config));
}

Active Low Complementary

.. wavedrom:: /../_static/diagrams/mcpwm/deadtime_active_low_complementary.json

static void gen_action_config(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb, mcpwm_cmpr_handle_t cmpa, mcpwm_cmpr_handle_t cmpb)
{
    ESP_ERROR_CHECK(mcpwm_generator_set_action_on_timer_event(gena,
                    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(gena,
                    MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpa, MCPWM_GEN_ACTION_LOW)));
}

static void dead_time_config(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb)
{
    mcpwm_dead_time_config_t dead_time_config = {
        .posedge_delay_ticks = 50,
        .negedge_delay_ticks = 0,
        .flags.invert_output = true
    };
    ESP_ERROR_CHECK(mcpwm_generator_set_dead_time(gena, gena, &dead_time_config));
    dead_time_config.posedge_delay_ticks = 0;
    dead_time_config.negedge_delay_ticks = 100;
    dead_time_config.flags.invert_output = false;
    ESP_ERROR_CHECK(mcpwm_generator_set_dead_time(gena, genb, &dead_time_config));
}

Active High

.. wavedrom:: /../_static/diagrams/mcpwm/deadtime_active_high.json

static void gen_action_config(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb, mcpwm_cmpr_handle_t cmpa, mcpwm_cmpr_handle_t cmpb)
{
    ESP_ERROR_CHECK(mcpwm_generator_set_action_on_timer_event(gena,
                    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(gena,
                    MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpa, MCPWM_GEN_ACTION_LOW)));
}

static void dead_time_config(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb)
{
    mcpwm_dead_time_config_t dead_time_config = {
        .posedge_delay_ticks = 50,
        .negedge_delay_ticks = 0,
    };
    ESP_ERROR_CHECK(mcpwm_generator_set_dead_time(gena, gena, &dead_time_config));
    dead_time_config.posedge_delay_ticks = 0;
    dead_time_config.negedge_delay_ticks = 100;
    ESP_ERROR_CHECK(mcpwm_generator_set_dead_time(gena, genb, &dead_time_config));
}

Active Low

.. wavedrom:: /../_static/diagrams/mcpwm/deadtime_active_low.json

static void gen_action_config(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb, mcpwm_cmpr_handle_t cmpa, mcpwm_cmpr_handle_t cmpb)
{
    ESP_ERROR_CHECK(mcpwm_generator_set_action_on_timer_event(gena,
                    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(gena,
                    MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpa, MCPWM_GEN_ACTION_LOW)));
}

static void dead_time_config(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb)
{
    mcpwm_dead_time_config_t dead_time_config = {
        .posedge_delay_ticks = 50,
        .negedge_delay_ticks = 0,
        .flags.invert_output = true
    };
    ESP_ERROR_CHECK(mcpwm_generator_set_dead_time(gena, gena, &dead_time_config));
    dead_time_config.posedge_delay_ticks = 0;
    dead_time_config.negedge_delay_ticks = 100;
    ESP_ERROR_CHECK(mcpwm_generator_set_dead_time(gena, genb, &dead_time_config));
}

Rising Delay on PWMA and Bypass Dead Time for PWMB

.. wavedrom:: /../_static/diagrams/mcpwm/deadtime_reda_bypassb.json

static void gen_action_config(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb, mcpwm_cmpr_handle_t cmpa, mcpwm_cmpr_handle_t cmpb)
{
    ESP_ERROR_CHECK(mcpwm_generator_set_action_on_timer_event(gena,
                    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(gena,
                    MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpa, MCPWM_GEN_ACTION_LOW)));
    ESP_ERROR_CHECK(mcpwm_generator_set_action_on_timer_event(genb,
                    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(genb,
                    MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpb, MCPWM_GEN_ACTION_LOW)));
}

static void dead_time_config(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb)
{
    mcpwm_dead_time_config_t dead_time_config = {
        .posedge_delay_ticks = 50,
        .negedge_delay_ticks = 0,
    };
    // apply deadtime to generator_a
    ESP_ERROR_CHECK(mcpwm_generator_set_dead_time(gena, gena, &dead_time_config));
    // bypass deadtime module for generator_b
    dead_time_config.posedge_delay_ticks = 0;
    ESP_ERROR_CHECK(mcpwm_generator_set_dead_time(genb, genb, &dead_time_config));
}

Falling Delay on PWMB and Bypass Dead Time for PWMA

.. wavedrom:: /../_static/diagrams/mcpwm/deadtime_fedb_bypassa.json

static void gen_action_config(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb, mcpwm_cmpr_handle_t cmpa, mcpwm_cmpr_handle_t cmpb)
{
    ESP_ERROR_CHECK(mcpwm_generator_set_action_on_timer_event(gena,
                    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(gena,
                    MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpa, MCPWM_GEN_ACTION_LOW)));
    ESP_ERROR_CHECK(mcpwm_generator_set_action_on_timer_event(genb,
                    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(genb,
                    MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpb, MCPWM_GEN_ACTION_LOW)));
}

static void dead_time_config(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb)
{
    mcpwm_dead_time_config_t dead_time_config = {
        .posedge_delay_ticks = 0,
        .negedge_delay_ticks = 0,
    };
    // generator_a bypass the deadtime module (no delay)
    ESP_ERROR_CHECK(mcpwm_generator_set_dead_time(gena, gena, &dead_time_config));
    // apply dead time to generator_b
    dead_time_config.negedge_delay_ticks = 50;
    ESP_ERROR_CHECK(mcpwm_generator_set_dead_time(genb, genb, &dead_time_config));

}

Rising and Falling Delay on PWMB and Bypass Dead Time for PWMA

.. wavedrom:: /../_static/diagrams/mcpwm/deadtime_redb_fedb_bypassa.json

static void gen_action_config(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb, mcpwm_cmpr_handle_t cmpa, mcpwm_cmpr_handle_t cmpb)
{
    ESP_ERROR_CHECK(mcpwm_generator_set_action_on_timer_event(gena,
                    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(gena,
                    MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpa, MCPWM_GEN_ACTION_LOW)));
    ESP_ERROR_CHECK(mcpwm_generator_set_action_on_timer_event(genb,
                    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(genb,
                    MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpb, MCPWM_GEN_ACTION_LOW)));
}

static void dead_time_config(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb)
{
    mcpwm_dead_time_config_t dead_time_config = {
        .posedge_delay_ticks = 0,
        .negedge_delay_ticks = 0,
    };
    // generator_a bypass the deadtime module (no delay)
    ESP_ERROR_CHECK(mcpwm_generator_set_dead_time(gena, gena, &dead_time_config));
    // apply dead time on both edge for generator_b
    dead_time_config.negedge_delay_ticks = 50;
    dead_time_config.posedge_delay_ticks = 50;
    ESP_ERROR_CHECK(mcpwm_generator_set_dead_time(genb, genb, &dead_time_config));
}

Carrier Modulation

The MCPWM operator has a carrier submodule that can be used if galvanic isolation from the motor driver is required (e.g., isolated digital power application) by passing the PWM output signals through transformers. Any of the PWM output signals may be at 100% duty and not changing whenever a motor is required to run steadily at the full load. Coupling with non-alternating signals with a transformer is problematic, so the signals are modulated by the carrier submodule to create an AC waveform, to make the coupling possible.

To configure the carrier submodule, you can call :cpp:func:`mcpwm_operator_apply_carrier`, and provide configuration structure :cpp:type:`mcpwm_carrier_config_t`:

Specifically, the carrier submodule can be disabled by calling :cpp:func:`mcpwm_operator_apply_carrier` with a NULL configuration.

Faults and Brake Actions

The MCPWM operator is able to sense external signals with information about the failure of the motor, the power driver or any other device connected. These failure signals are encapsulated into MCPWM fault objects.

You should determine possible failure modes of the motor and what action should be performed on detection of a particular fault, e.g., drive all outputs low for a brushed motor, lock current state for a stepper motor, etc. Because of this action, the motor should be put into a safe state to reduce the likelihood of damage caused by the fault.

Set Operator Brake Mode on Fault

The way that MCPWM operator reacts to the fault is called Brake. The MCPWM operator can be configured to perform different brake modes for each fault object by calling :cpp:func:`mcpwm_operator_set_brake_on_fault`. Specific brake configuration is passed as a structure :cpp:type:`mcpwm_brake_config_t`:

Set Generator Action on Brake Event

One generator can set multiple actions on different brake events, by calling :cpp:func:`mcpwm_generator_set_actions_on_brake_event` with a variable number of action configurations. The action configuration is defined in :cpp:type:`mcpwm_gen_brake_event_action_t`:

There is a helper macro :c:macro:`MCPWM_GEN_BRAKE_EVENT_ACTION` to simplify the construction of a brake event action entry.

Please note, the argument list of :cpp:func:`mcpwm_generator_set_actions_on_brake_event` must be terminated by :c:macro:`MCPWM_GEN_BRAKE_EVENT_ACTION_END`.

You can also set the brake action one by one by calling :cpp:func:`mcpwm_generator_set_action_on_brake_event` without varargs.

Register Fault Event Callbacks

The MCPWM fault detector can inform you when it detects a valid fault or a fault signal disappears. If you have some function that should be called when such an event happens, you should hook your function to the interrupt service routine by calling :cpp:func:`mcpwm_fault_register_event_callbacks`. The callback function prototype is declared in :cpp:type:`mcpwm_fault_event_cb_t`. All supported event callbacks are listed in the :cpp:type:`mcpwm_fault_event_callbacks_t`:

The callback function is called within the ISR context, so it should not attempt to block. For example, you may make sure that only FreeRTOS APIs with the ISR suffix are called within the function.

The parameter user_data of :cpp:func:`mcpwm_fault_register_event_callbacks` function is used to save your own context. It is passed to the callback function directly.

This function will lazy the install interrupt service for the MCPWM fault, whereas the service can only be removed in :cpp:type:`mcpwm_del_fault`.

Register Brake Event Callbacks

The MCPWM operator can inform you when it is going to take a brake action. If you have some function that should be called when this event happens, you should hook your function to the interrupt service routine by calling :cpp:func:`mcpwm_operator_register_event_callbacks`. The callback function prototype is declared in :cpp:type:`mcpwm_brake_event_cb_t`. All supported event callbacks are listed in the :cpp:type:`mcpwm_operator_event_callbacks_t`:

The callback function is called within the ISR context, so it should not attempt to block. For example, you may make sure that only FreeRTOS APIs with the ISR suffix are called within the function.

The parameter user_data of the :cpp:func:`mcpwm_operator_register_event_callbacks` function is used to save your own context. It will be passed to the callback function directly.

This function will lazy the install interrupt service for the MCPWM operator, whereas the service can only be removed in :cpp:type:`mcpwm_del_operator`.

Generator Force Actions

Software can override generator output level at runtime, by calling :cpp:func:`mcpwm_generator_set_force_level`. The software force level always has a higher priority than other event actions set in e.g., :cpp:func:`mcpwm_generator_set_actions_on_timer_event`.

  • Set the level to -1 means to disable the force action, and the generator's output level will be controlled by the event actions again.
  • Set the hold_on to true, and the force output level will keep alive until it is removed by assigning level to -1.
  • Set the hole_on to false, the force output level will only be active for a short time, and any upcoming event can override it.

Synchronization

When a sync signal is taken by the MCPWM timer, the timer will be forced into a predefined phase, where the phase is determined by count value and count direction. You can set the sync phase by calling :cpp:func:`mcpwm_timer_set_phase_on_sync`. The sync phase configuration is defined in :cpp:type:`mcpwm_timer_sync_phase_config_t` structure:

Note

When the MCPWM timer is working in :cpp:enumerator:`MCPWM_TIMER_COUNT_MODE_UP_DOWN` mode, special attention needs to be taken. In this mode, counter range [0 -> peak-1] belongs to the increment phase, and counter range [peak -> 1] belongs to the decrement phase. Thus if you set the :cpp:member:`mcpwm_timer_sync_phase_config_t::count_value` to zero, you may also want to set the :cpp:member:`mcpwm_timer_sync_phase_config_t::direction` to :cpp:enumerator:`MCPWM_TIMER_DIRECTION_UP`. Otherwise, the timer will be continue with the decrement phase, and the count value underflows to peak.

Likewise, the MCPWM Capture Timer can be synced as well. You can set the sync phase for the capture timer by calling :cpp:func:`mcpwm_capture_timer_set_phase_on_sync`. The sync phase configuration is defined in :cpp:type:`mcpwm_capture_timer_sync_phase_config_t` structure:

Sync Timers by GPIO

.. blockdiag::
    :caption: GPIO Sync All MCPWM Timers
    :align: center

    blockdiag {
        GPIO -> Timer0, Timer1, Timer2;
    }

static void example_setup_sync_strategy(mcpwm_timer_handle_t timers[])
{
    mcpwm_sync_handle_t gpio_sync_source = NULL;
    mcpwm_gpio_sync_src_config_t gpio_sync_config = {
        .group_id = 0,              // GPIO fault should be in the same group of the above timers
        .gpio_num = EXAMPLE_SYNC_GPIO,
        .flags.pull_down = true,
        .flags.active_neg = false,  // By default, a posedge pulse can trigger a sync event
    };
    ESP_ERROR_CHECK(mcpwm_new_gpio_sync_src(&gpio_sync_config, &gpio_sync_source));

    mcpwm_timer_sync_phase_config_t sync_phase_config = {
        .count_value = 0,                      // sync phase: target count value
        .direction = MCPWM_TIMER_DIRECTION_UP, // sync phase: count direction
        .sync_src = gpio_sync_source,          // sync source
    };
    for (int i = 0; i < 3; i++) {
        ESP_ERROR_CHECK(mcpwm_timer_set_phase_on_sync(timers[i], &sync_phase_config));
    }
}

Capture

The basic functionality of MCPWM capture is to record the time when any pulse edge of the capture signal turns active. Then you can get the pulse width and convert it into other physical quantities like distance or speed in the capture callback function. For example, in the BLDC (Brushless DC, see figure below) scenario, you can use the capture submodule to sense the rotor position from the Hall sensor.

MCPWM BLDC with Hall Sensor

MCPWM BLDC with Hall Sensor

The capture timer is usually connected to several capture channels. Please refer to MCPWM Capture Timer and Channels for more information about resource allocation.

Register Capture Event Callbacks

The MCPWM capture channel can inform you when there is a valid edge detected on the signal. You have to register a callback function to get the timer count value of the captured moment, by calling :cpp:func:`mcpwm_capture_channel_register_event_callbacks`. The callback function prototype is declared in :cpp:type:`mcpwm_capture_event_cb_t`. All supported capture callbacks are listed in the :cpp:type:`mcpwm_capture_event_callbacks_t`:

The callback function provides event-specific data of type :cpp:type:`mcpwm_capture_event_data_t`, so that you can get the edge of the capture signal in :cpp:member:`mcpwm_capture_event_data_t::cap_edge` and the count value of that moment in :cpp:member:`mcpwm_capture_event_data_t::cap_value`. To convert the capture count into a timestamp, you need to know the resolution of the capture timer by calling :cpp:func:`mcpwm_capture_timer_get_resolution`.

The callback function is called within the ISR context, so it should not attempt to block. For example, you may make sure that only FreeRTOS APIs with the ISR suffix are called within the function.

The parameter user_data of :cpp:func:`mcpwm_capture_channel_register_event_callbacks` function is used to save your context. It is passed to the callback function directly.

This function will lazy install interrupt service for the MCPWM capture channel, whereas the service can only be removed in :cpp:type:`mcpwm_del_capture_channel`.

Enable and Disable Capture Channel

The capture channel is not enabled after allocation by :cpp:func:`mcpwm_new_capture_channel`. You should call :cpp:func:`mcpwm_capture_channel_enable` and :cpp:func:`mcpwm_capture_channel_disable` accordingly to enable or disable the channel. If the interrupt service is lazy installed during registering event callbacks for the channel in :cpp:func:`mcpwm_capture_channel_register_event_callbacks`, :cpp:func:`mcpwm_capture_channel_enable` will enable the interrupt service as well.

Enable and Disable Capture Timer

Before doing IO control to the capture timer, you need to enable the timer first, by calling :cpp:func:`mcpwm_capture_timer_enable`. Internally, this function:

  • switches the capture timer state from init to enable.
  • acquires a proper power management lock if a specific clock source (e.g., APB clock) is selected. See also Power management for more information.

On the contrary, calling :cpp:func:`mcpwm_capture_timer_disable` will put the timer driver back to init state, and release the power management lock.

Start and Stop Capture Timer

The basic IO operation of a capture timer is to start and stop. Calling :cpp:func:`mcpwm_capture_timer_start` can start the timer and calling :cpp:func:`mcpwm_capture_timer_stop` can stop the timer immediately.

Trigger a Software Capture Event

Sometimes, the software also wants to trigger a "fake" capture event. The :cpp:func:`mcpwm_capture_channel_trigger_soft_catch` is provided for that purpose. Please note that, even though it is a "fake" capture event, it can still cause an interrupt, thus your capture event callback function gets invoked as well.

.. only:: SOC_MCPWM_SUPPORT_ETM

    .. _mcpwm-etm-event-and-task:

    ETM Event and Task
    ^^^^^^^^^^^^^^^^^^

    MCPWM comparator is able to generate events that can interact with the :doc:`ETM </api-reference/peripherals/etm>` module. The supported events are listed in the :cpp:type:`mcpwm_comparator_etm_event_type_t`. You can call :cpp:func:`mcpwm_comparator_new_etm_event` to get the corresponding ETM event handle.

    For how to connect the event and task to an ETM channel, please refer to the :doc:`ETM </api-reference/peripherals/etm>` documentation.

    .. _mcpwm-power-management:

.. only:: not SOC_MCPWM_SUPPORT_ETM

    .. _mcpwm-power-management:

Power Management

When power management is enabled (i.e., :ref:`CONFIG_PM_ENABLE` is on), the system will adjust the PLL and APB frequency before going into Light-sleep, thus potentially changing the period of an MCPWM timers' counting step and leading to inaccurate time-keeping.

However, the driver can prevent the system from changing APB frequency by acquiring a power management lock of type :cpp:enumerator:`ESP_PM_APB_FREQ_MAX`. Whenever the driver creates an MCPWM timer instance that has selected :cpp:enumerator:`MCPWM_TIMER_CLK_SRC_PLL160M` as its clock source, the driver guarantees that the power management lock is acquired when enabling the timer by :cpp:func:`mcpwm_timer_enable`. On the contrary, the driver releases the lock when :cpp:func:`mcpwm_timer_disable` is called for that timer.

Likewise, whenever the driver creates an MCPWM capture timer instance that has selected :cpp:enumerator:`MCPWM_CAPTURE_CLK_SRC_APB` as its clock source, the driver guarantees that the power management lock is acquired when enabling the timer by :cpp:func:`mcpwm_capture_timer_enable`. And releases the lock in :cpp:func:`mcpwm_capture_timer_disable`.

IRAM Safe

By default, the MCPWM interrupt will be deferred when the Cache is disabled for reasons like writing/erasing Flash. Thus the event callback functions will not get executed in time, which is not expected in a real-time application.

There is a Kconfig option :ref:`CONFIG_MCPWM_ISR_IRAM_SAFE` that:

  • enables the interrupt to be serviced even when the cache is disabled
  • places all functions used by the ISR into IRAM [2]
  • places the driver object into DRAM (in case it is mapped to PSRAM by accident)

This allows the interrupt to run while the cache is disabled but comes at the cost of increased IRAM consumption.

There is another Kconfig option :ref:`CONFIG_MCPWM_CTRL_FUNC_IN_IRAM` that can put commonly used IO control functions into IRAM as well. So, these functions can also be executable when the cache is disabled. The IO control function is as follows:

Thread Safety

The factory functions like :cpp:func:`mcpwm_new_timer` are guaranteed to be thread-safe by the driver, which means, you can call it from different RTOS tasks without protection by extra locks.

The following function is allowed to run under the ISR context, as the driver uses a critical section to prevent them from being called concurrently in the task and ISR.

Other functions that are not related to Resource Allocation and Initialization, are not thread-safe. Thus, you should avoid calling them in different tasks without mutex protection.

Kconfig Options

Application Examples

API Reference

.. include-build-file:: inc/mcpwm_timer.inc
.. include-build-file:: inc/mcpwm_oper.inc
.. include-build-file:: inc/mcpwm_cmpr.inc
.. include-build-file:: inc/mcpwm_gen.inc
.. include-build-file:: inc/mcpwm_fault.inc
.. include-build-file:: inc/mcpwm_sync.inc
.. include-build-file:: inc/mcpwm_cap.inc
.. include-build-file:: inc/mcpwm_etm.inc
.. include-build-file:: inc/components/esp_driver_mcpwm/include/driver/mcpwm_types.inc
.. include-build-file:: inc/components/hal/include/hal/mcpwm_types.inc


[1](1, 2, 3, 4, 5, 6, 7, 8, 9) Different ESP chip series might have a different number of MCPWM resources (e.g., groups, timers, comparators, operators, generators, triggers and so on). Please refer to the [TRM] for details. The driver does not forbid you from applying for more MCPWM resources, but it returns an error when there are no hardware resources available. Please always check the return value when doing :ref:`mcpwm-resource-allocation-and-initialization`.
[2]The callback function and the sub-functions invoked by itself should also be placed in IRAM. You need to take care of this by yourself.