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 value like speed, distance
- Generate Space Vector PWM (SVPWM) signals for Field Oriented Control (FOC)
The main submodules are listed in the following diagram:
/../_static/diagrams/mcpwm/mcpwm_overview.diag
- 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.
Description of the MCPWM functionality is divided into the following sections:
- 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.
- Timer Operations and Events - describes control functions and event callbacks that supported by the MCPWM timer.
- Comparator Operations and Events - describes control functions and event callbacks that supported by the MCPWM comparator.
- Generator Actions on Events - describes how to set actions for MCPWM generators on particular events that generated by the MCPWM timer and comparators.
- Classical PWM Waveforms and Generator Configurations - demonstrates some classical PWM waveforms that can be achieved by configuring generator actions.
- Dead Time - describes how to set dead time for MCPWM generators.
- Classical PWM Waveforms and Dead Time Configurations - demonstrates some classical PWM waveforms that can be achieved by configuring dead time.
- Carrier Modulation - describes how to set modulate a high frequency onto the final PWM waveforms.
- Faults and Brake Actions - describes how to set brake actions for MCPWM operators on particular fault event.
- Generator Force Actions - describes how to control the generator output level asynchronously in a forceful way.
- Synchronization - describes how to synchronize the MCPWM timers and get a fixed phase difference between the generated PWM signals.
- Capture - describes how to use the MCPWM capture module to measure the pulse width of a signal.
- Power Management - describes how different source clock will affect power consumption.
- IRAM Safe - describes tips on how to make the RMT interrupt work better along with a disabled cache.
- Thread Safety - lists which APIs are guaranteed to be thread safe by the driver.
- Kconfig Options - lists the supported Kconfig options that can bring different effects to the driver.
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.
You can allocate a MCPWM timer object by calling :cppmcpwm_new_timer
function, with a configuration structure :cppmcpwm_timer_config_t
as the parameter. The configuration structure is defined as:
- :cpp
mcpwm_timer_config_t::group_id
specifies the MCPWM group ID. The ID should belong to [0, :cSOC_MCPWM_GROUPS
- 1] range. Please note, timers located in different groups are totally independent. - :cpp
mcpwm_timer_config_t::intr_priority
sets the priority of the interrupt. If it is set to0
, the driver will allocate an interrupt with a default priority. Otherwise, the driver will use the given priority. - :cpp
mcpwm_timer_config_t::clk_src
sets the clock source of the timer. - :cpp
mcpwm_timer_config_t::resolution_hz
set the expected resolution of the timer, the driver internally will set a proper divider based on the clock source and the resolution. - :cpp
mcpwm_timer_config_t::count_mode
sets the count mode of the timer. - :cpp
mcpwm_timer_config_t::period_ticks
sets the period of the timer, in ticks (the tick resolution is set in the :cppmcpwm_timer_config_t::resolution_hz
). - :cpp
mcpwm_timer_config_t::update_period_on_empty
sets whether to update the period value when the timer counts to zero. - :cpp
mcpwm_timer_config_t::update_period_on_sync
sets whether to update the period value when the timer takes a sync signal.
The :cppmcpwm_new_timer
will return a pointer to the allocated timer object if the allocation succeeds. Otherwise, it will return error code. Specifically, when there are no more free timers in the MCPWM group, this function will return :cESP_ERR_NOT_FOUND
error.1
On the contrary, calling :cppmcpwm_del_timer
function will free the allocated timer object.
You can allocate a MCPWM operator object by calling :cppmcpwm_new_operator
function, with a configuration structure :cppmcpwm_operator_config_t
as the parameter. The configuration structure is defined as:
- :cpp
mcpwm_operator_config_t::group_id
specifies the MCPWM group ID. The ID should belong to [0, :cSOC_MCPWM_GROUPS
- 1] range. Please note, operators located in different groups are totally independent. - :cpp
mcpwm_operator_config_t::intr_priority
sets the priority of the interrupt. If it is set to0
, the driver will allocate an interrupt with a default priority. Otherwise, the driver will use the given priority. - :cpp
mcpwm_operator_config_t::update_gen_action_on_tez
sets whether to update the generator action when the timer counts to zero. Here and below, the timer refers to the one that is connected to the operator by :cppmcpwm_operator_connect_timer
. - :cpp
mcpwm_operator_config_t::update_gen_action_on_tep
sets whether to update the generator action when the timer counts to peak. - :cpp
mcpwm_operator_config_t::update_gen_action_on_sync
sets whether to update the generator action when the timer takes a sync signal. - :cpp
mcpwm_operator_config_t::update_dead_time_on_tez
sets whether to update the dead time when the timer counts to zero. - :cpp
mcpwm_operator_config_t::update_dead_time_on_tep
sets whether to update the dead time when the timer counts to peak. - :cpp
mcpwm_operator_config_t::update_dead_time_on_sync
sets whether to update the dead time when the timer takes a sync signal.
The :cppmcpwm_new_operator
will return a pointer to the allocated operator object if the allocation succeeds. Otherwise, it will return error code. Specifically, when there are no more free operators in the MCPWM group, this function will return :cESP_ERR_NOT_FOUND
error.2
On the contrary, calling :cppmcpwm_del_operator
function will free the allocated operator object.
You can allocate a MCPWM comparator object by calling :cppmcpwm_new_comparator
function, with a MCPWM operator handle and configuration structure :cppmcpwm_comparator_config_t
as the parameter. The operator handle is created by :cppmcpwm_new_operator
. The configuration structure is defined as:
- :cpp
mcpwm_comparator_config_t::intr_priority
sets the priority of the interrupt. If it is set to0
, the driver will allocate an interrupt with a default priority. Otherwise, the driver will use the given priority. - :cpp
mcpwm_comparator_config_t::update_cmp_on_tez
sets whether to update the compare threshold when the timer counts to zero. - :cpp
mcpwm_comparator_config_t::update_cmp_on_tep
sets whether to update the compare threshold when the timer counts to peak. - :cpp
mcpwm_comparator_config_t::update_cmp_on_sync
sets whether to update the compare threshold when the timer takes a sync signal.
The :cppmcpwm_new_comparator
will return a pointer to the allocated comparator object if the allocation succeeds. Otherwise, it will return error code. Specifically, when there are no more free comparators in the MCPWM operator, this function will return :cESP_ERR_NOT_FOUND
error.3
On the contrary, calling :cppmcpwm_del_comparator
function will free the allocated comparator object.
You can allocate a MCPWM generator object by calling :cppmcpwm_new_generator
function, with a MCPWM operator handle and configuration structure :cppmcpwm_generator_config_t
as the parameter. The operator handle is created by :cppmcpwm_new_operator
. The configuration structure is defined as:
- :cpp
mcpwm_generator_config_t::gen_gpio_num
sets the GPIO number used by the generator. - :cpp
mcpwm_generator_config_t::invert_pwm
sets whether to invert the PWM signal. - :cpp
mcpwm_generator_config_t::io_loop_back
sets whether to enable the Loop-back mode. It is for debugging purposes only. It enables both the GPIO's input and output ability through the GPIO matrix peripheral. - :cpp
mcpwm_generator_config_t::io_od_mode
configures the PWM GPIO as open-drain output. - :cpp
mcpwm_generator_config_t::pull_up
and :cppmcpwm_generator_config_t::pull_down
controls whether to enable the internal pull-up and pull-down resistors accordingly.
The :cppmcpwm_new_generator
will return a pointer to the allocated generator object if the allocation succeeds. Otherwise, it will return error code. Specifically, when there are no more free generators in the MCPWM operator, this function will return :cESP_ERR_NOT_FOUND
error.4
On the contrary, calling :cppmcpwm_del_generator
function will free the allocated generator object.
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 :cppmcpwm_new_gpio_fault
function, with configuration structure :cppmcpwm_gpio_fault_config_t
as the parameter. The configuration structure is defined as:
- :cpp
mcpwm_gpio_fault_config_t::group_id
sets the MCPWM group ID. The ID should belong to [0, :cSOC_MCPWM_GROUPS
- 1] range. Please note, GPIO fault located in different groups are totally independent, i.e. GPIO fault in group 0 can not be detected by the operator in group 1. - :cpp
mcpwm_gpio_fault_config_t::gpio_num
sets the GPIO number used by the fault. - :cpp
mcpwm_gpio_fault_config_t::active_level
sets the active level of the fault signal. - :cpp
mcpwm_gpio_fault_config_t::pull_up
and :cppmcpwm_gpio_fault_config_t::pull_down
set whether to pull up and/or pull down the GPIO internally. - :cpp
mcpwm_gpio_fault_config_t::io_loop_back
sets whether to enable the loop back mode. It is for debugging purposes only. It enables both the GPIO's input and output ability through the GPIO matrix peripheral.
The :cppmcpwm_new_gpio_fault
will return a pointer to the allocated fault object if the allocation succeeds. Otherwise, it will return error code. Specifically, when there are no more free GPIO faults in the MCPWM group, this function will return :cESP_ERR_NOT_FOUND
error.5
Software fault object can be used to trigger a fault by calling a function :cppmcpwm_soft_fault_activate
instead of waiting for a real fault signal on the GPIO. A software fault object can be allocated by calling :cppmcpwm_new_soft_fault
function, with configuration structure :cppmcpwm_soft_fault_config_t
as the parameter. Currently this configuration structure is left for future purpose. :cppmcpwm_new_soft_fault
function will return a pointer to the allocated fault object if the allocation succeeds. Otherwise, it will return error code. Specifically, when there are no memory left for the fault object, this function will return :cESP_ERR_NO_MEM
error. Although the software fault and GPIO fault are of different types, but the returned fault handle is of the same type.
On the contrary, calling :cppmcpwm_del_fault
function will free the allocated fault object, this function works for both software and GPIO fault.
The sync source is what can be used to synchronize the MCPWM timer and MCPWM capture timer. There're three types of sync sources: A sync source reflected from the GPIO, a sync source generated by software and a sync source generated by MCPWM timer event.
To allocate a GPIO sync source, you can call :cppmcpwm_new_gpio_sync_src
function, with configuration structure :cppmcpwm_gpio_sync_src_config_t
as the parameter. The configuration structure is defined as:
- :cpp
mcpwm_gpio_sync_src_config_t::group_id
sets the MCPWM group ID. The ID should belong to [0, :cSOC_MCPWM_GROUPS
- 1] range. Please note, GPIO sync source located in different groups are totally independent, i.e. GPIO sync source in group 0 can not be detected by the timers in group 1. - :cpp
mcpwm_gpio_sync_src_config_t::gpio_num
sets the GPIO number used by the sync source. - :cpp
mcpwm_gpio_sync_src_config_t::active_neg
sets whether the sync signal is active on falling edges. - :cpp
mcpwm_gpio_sync_src_config_t::pull_up
and :cppmcpwm_gpio_sync_src_config_t::pull_down
set whether to pull up and/or pull down the GPIO internally. - :cpp
mcpwm_gpio_sync_src_config_t::io_loop_back
sets whether to enable the loop back mode. It is for debugging purposes only. It enables both the GPIO's input and output ability through the GPIO matrix peripheral.
The :cppmcpwm_new_gpio_sync_src
will return a pointer to the allocated sync source object if the allocation succeeds. Otherwise, it will return error code. Specifically, when there are no more free GPIO sync sources in the MCPWM group, this function will return :cESP_ERR_NOT_FOUND
error.6
To allocate a Timer event sync source, you can call :cppmcpwm_new_timer_sync_src
function, with configuration structure :cppmcpwm_timer_sync_src_config_t
as the parameter. The configuration structure is defined as:
- :cpp
mcpwm_timer_sync_src_config_t::timer_event
specifies on what timer event to generate the sync signal. - :cpp
mcpwm_timer_sync_src_config_t::propagate_input_sync
sets whether to propagate the input sync signal (i.e. the input sync signal will be routed to its sync output).
The :cppmcpwm_new_timer_sync_src
will return a pointer to the allocated sync source object if the allocation succeeds. Otherwise, it will return error code. Specifically, if a sync source has been allocated from the same timer before, this function will return :cESP_ERR_INVALID_STATE
error.
Last but not least, to allocate a software sync source, you can call :cppmcpwm_new_soft_sync_src
function, with configuration structure :cppmcpwm_soft_sync_config_t
as the parameter. Currently this configuration structure is left for future purpose. :cppmcpwm_new_soft_sync_src
will return a pointer to the allocated sync source object if the allocation succeeds. Otherwise, it will return error code. Specifically, when there are no memory left for the sync source object, this function will return :cESP_ERR_NO_MEM
error. Please note, to make a software sync source take effect, don't forget to call :cppmcpwm_soft_sync_activate
.
On the contrary, calling :cppmcpwm_del_sync_src
function will free the allocated sync source object, this function works for all types of sync sources.
The MCPWM group has a dedicated timer which is used to capture the timestamp when specific event occurred. The capture timer is connected with several independent channels, each channel is assigned with a GPIO.
To allocate a capture timer, you can call :cppmcpwm_new_capture_timer
function, with configuration structure :cppmcpwm_capture_timer_config_t
as the parameter. The configuration structure is defined as:
- :cpp
mcpwm_capture_timer_config_t::group_id
sets the MCPWM group ID. The ID should belong to [0, :cSOC_MCPWM_GROUPS
- 1] range. - :cpp
mcpwm_capture_timer_config_t::clk_src
sets the clock source of the capture timer.
The :cppmcpwm_new_capture_timer
will return a pointer to the allocated capture timer object if the allocation succeeds. Otherwise, it will return error code. Specifically, when there are no free capture timer left in the MCPWM group, this function will return :cESP_ERR_NOT_FOUND
error.7
Next, to allocate a capture channel, you can call :cppmcpwm_new_capture_channel
function, with a capture timer handle and configuration structure :cppmcpwm_capture_channel_config_t
as the parameter. The configuration structure is defined as:
- :cpp
mcpwm_capture_channel_config_t::intr_priority
sets the priority of the interrupt. If it is set to0
, the driver will allocate an interrupt with a default priority. Otherwise, the driver will use the given priority. - :cpp
mcpwm_capture_channel_config_t::gpio_num
sets the GPIO number used by the capture channel. - :cpp
mcpwm_capture_channel_config_t::prescale
sets the prescaler of the input signal. - :cpp
mcpwm_capture_channel_config_t::pos_edge
and :cppmcpwm_capture_channel_config_t::neg_edge
set whether to capture on the positive and/or falling edge of the input signal. - :cpp
mcpwm_capture_channel_config_t::pull_up
and :cppmcpwm_capture_channel_config_t::pull_down
set whether to pull up and/or pull down the GPIO internally. - :cpp
mcpwm_capture_channel_config_t::invert_cap_signal
sets whether to invert the capture signal. - :cpp
mcpwm_capture_channel_config_t::io_loop_back
sets whether to enable the loop back mode. It is for debugging purposes only. It enables both the GPIO's input and output ability through the GPIO matrix peripheral.
The :cppmcpwm_new_capture_channel
will return a pointer to the allocated capture channel object if the allocation succeeds. Otherwise, it will return error code. Specifically, when there are no free capture channel left in the capture timer, this function will return :cESP_ERR_NOT_FOUND
error.
On the contrary, calling :cppmcpwm_del_capture_channel
and :cppmcpwm_del_capture_timer
will free the allocated capture channel and timer object accordingly.
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.
The timer period is initialized by the :cppmcpwm_timer_config_t::period_ticks
parameter in :cppmcpwm_timer_config_t
. You can update the period at runtime by calling :cppmcpwm_timer_set_period
function. The new period will take effect based on how you set the :cppmcpwm_timer_config_t::update_period_on_empty
and :cppmcpwm_timer_config_t::update_period_on_sync
parameters in :cppmcpwm_timer_config_t
. If none of them are set, the timer period will take effect immediately.
The MCPWM timer can generate different events at runtime. If you have some function that should be called when particular event happens, you should hook your function to the interrupt service routine by calling :cppmcpwm_timer_register_event_callbacks
. The callback function prototype is declared in :cppmcpwm_timer_event_cb_t
. All supported event callbacks are listed in the :cppmcpwm_timer_event_callbacks_t
:
- :cpp
mcpwm_timer_event_callbacks_t::on_full
sets callback function for timer when it counts to peak value. - :cpp
mcpwm_timer_event_callbacks_t::on_empty
sets callback function for timer when it counts to zero. - :cpp
mcpwm_timer_event_callbacks_t::on_stop
sets callback function for timer when it is stopped.
The callback functions above are called within the ISR context, so they should not attempt to block (e.g., make sure that only FreeRTOS APIs with ISR
suffix is called within the function).
The parameter user_data
of :cppmcpwm_timer_register_event_callbacks
function is used to save user's own context, it will be passed to each callback function directly.
This function will lazy install interrupt service for the MCPWM timer without enabling it. It is only allowed to be called before before :cppmcpwm_timer_enable
, otherwise the :cESP_ERR_INVALID_STATE
error will be returned. See also Enable and Disable timer for more information.
Before doing IO control to the timer, user needs to enable the timer first, by calling :cppmcpwm_timer_enable
. Internally, this function will:
- switch the timer state from init to enable.
- enable the interrupt service if it has been lazy installed by :cpp
mcpwm_timer_register_event_callbacks
. - acquire a proper power management lock if a specific clock source (e.g. PLL_160M clock) is selected. See also Power management for more information.
On the contrary, calling :cppmcpwm_timer_disable
will put the timer driver back to init state, disable the interrupts service and release the power management lock.
The basic IO operation of a timer is to start and stop. Calling :cppmcpwm_timer_start_stop
with different :cppmcpwm_timer_start_stop_cmd_t
commands can start the timer immediately or stop the timer at a specific event. What're more, you can even start the timer for only one round, that means, the timer will count to peak value or zero, and then stop itself.
The allocated MCPWM Timer should be connected with a MCPWM operator by calling :cppmcpwm_operator_connect_timer
, so that the operator can take that timer as its time base, and generate the required PWM waves. Make sure the MCPWM timer and operator are in the same group, otherwise, this function will return :cESP_ERR_INVALID_ARG
error.
The MCPWM comparator can inform the user when the timer counter equals to 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 :cppmcpwm_comparator_register_event_callbacks
. The callback function prototype is declared in :cppmcpwm_compare_event_cb_t
. All supported event callbacks are listed in the :cppmcpwm_comparator_event_callbacks_t
:
- :cpp
mcpwm_comparator_event_callbacks_t::on_reach
sets callback function for comparator when the timer counter equals to the compare value.
The callback function will provide event specific data of type :cppmcpwm_compare_event_data_t
to the user. The callback function is called within the ISR context, so is should not attempt to block (e.g., make sure that only FreeRTOS APIs with ISR
suffix is called within the function).
The parameter user_data
of :cppmcpwm_comparator_register_event_callbacks
function is used to save user's own context, it will be passed to the callback function directly.
This function will lazy install interrupt service for the MCPWM comparator, whereas the service can only be removed in :cppmcpwm_del_comparator
.
You can set the compare value for the MCPWM comparator at runtime by calling :cppmcpwm_comparator_set_compare_value
. There're a few points to note:
- New compare value might won't take effect immediately. The update time for the compare value is set by :cpp
mcpwm_comparator_config_t::update_cmp_on_tez
or :cppmcpwm_comparator_config_t::update_cmp_on_tep
or :cppmcpwm_comparator_config_t::update_cmp_on_sync
. - Make sure the operator has connected to one MCPWM timer already by :cpp
mcpwm_operator_connect_timer
. Otherwise, it will return error code :cESP_ERR_INVALID_STATE
. - The compare value shouldn't exceed timer's count peak, otherwise, the compare event will never got triggered.
One generator can set multiple actions on different timer events, by calling :cppmcpwm_generator_set_actions_on_timer_event
with variable number of action configurations. The action configuration is defined in :cppmcpwm_gen_timer_event_action_t
:
- :cpp
mcpwm_gen_timer_event_action_t::direction
specific the timer direction. The supported directions are listed in :cppmcpwm_timer_direction_t
. - :cpp
mcpwm_gen_timer_event_action_t::event
specifies the timer event. The supported timer events are listed in :cppmcpwm_timer_event_t
. - :cpp
mcpwm_gen_timer_event_action_t::action
specifies the generator action to be taken. The supported actions are listed in :cppmcpwm_generator_action_t
.
There's a helper macro :cMCPWM_GEN_TIMER_EVENT_ACTION
to simplify the construction of a timer event action entry.
Please note, the argument list of :cppmcpwm_generator_set_actions_on_timer_event
must be terminated by :cMCPWM_GEN_TIMER_EVENT_ACTION_END
.
You can also set the timer action one by one by calling :cppmcpwm_generator_set_action_on_timer_event
without varargs.
One generator can set multiple actions on different compare events, by calling :cppmcpwm_generator_set_actions_on_compare_event
with variable number of action configurations. The action configuration is defined in :cppmcpwm_gen_compare_event_action_t
:
- :cpp
mcpwm_gen_compare_event_action_t::direction
specific the timer direction. The supported directions are listed in :cppmcpwm_timer_direction_t
. - :cpp
mcpwm_gen_compare_event_action_t::comparator
specifies the comparator handle. See MCPWM Comparators for how to allocate a comparator. - :cpp
mcpwm_gen_compare_event_action_t::action
specifies the generator action to be taken. The supported actions are listed in :cppmcpwm_generator_action_t
.
There's a helper macro :cMCPWM_GEN_COMPARE_EVENT_ACTION
to simplify the construction of a compare event action entry.
Please note, the argument list of :cppmcpwm_generator_set_actions_on_compare_event
must be terminated by :cMCPWM_GEN_COMPARE_EVENT_ACTION_END
.
You can also set the compare action one by one by calling :cppmcpwm_generator_set_action_on_compare_event
without varargs.
One generator can set action on fault based trigger events, by calling :cppmcpwm_generator_set_action_on_fault_event
with an action configurations. The action configuration is defined in :cppmcpwm_gen_fault_event_action_t
:
- :cpp
mcpwm_gen_fault_event_action_t::direction
specifies the timer direction. The supported directions are listed in :cppmcpwm_timer_direction_t
. - :cpp
mcpwm_gen_fault_event_action_t::fault
specifies the fault used for the trigger. See MCPWM Faults for how to allocate a fault. - :cpp
mcpwm_gen_fault_event_action_t::action
specifies the generator action to be taken. The supported actions are listed in :cppmcpwm_generator_action_t
.
When no free trigger slot is left in the operator to which the generator belongs, this function will return the :cESP_ERR_NOT_FOUND
error.8
The trigger only support GPOI fault. when the input is not a GPIO fault, this function will return the :cESP_ERR_NOT_SUPPORTED
error.
There's a helper macro :cMCPWM_GEN_FAULT_EVENT_ACTION
to simplify the construction of a trigger event action entry.
Please note, fault event does not have variadic function like :cppmcpwm_generator_set_actions_on_fault_event
.
One generator can set action on sync based trigger events, by calling :cppmcpwm_generator_set_action_on_sync_event
with an action configurations. The action configuration is defined in :cppmcpwm_gen_sync_event_action_t
:
- :cpp
mcpwm_gen_sync_event_action_t::direction
specifies the timer direction. The supported directions are listed in :cppmcpwm_timer_direction_t
. - :cpp
mcpwm_gen_sync_event_action_t::sync
specifies the sync source used for the trigger. See MCPWM Sync Sources for how to allocate a sync source. - :cpp
mcpwm_gen_sync_event_action_t::action
specifies the generator action to be taken. The supported actions are listed in :cppmcpwm_generator_action_t
.
When no free trigger slot is left in the operator to which the generator belongs, this function will return the :cESP_ERR_NOT_FOUND
error.9
The trigger only support one sync action, regardless of the kinds. When set sync actions more than once, this function will return the :cESP_ERR_INVALID_STATE
error.
There's a helper macro :cMCPWM_GEN_SYNC_EVENT_ACTION
to simplify the construction of a trigger event action entry.
Please note, sync event does not have variadic function like :cppmcpwm_generator_set_actions_on_sync_event
.
This section will demonstrate the classical PWM waveforms that can be generated by the pair of the 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 are 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.
/../_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)));
}
/../_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)));
}
/../_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()));
}
/../_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()));
}
/../_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()));
}
/../_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()));
}
In power electronics, the rectifier and inverter are commonly used. This requires the use of rectifier bridge and inverter bridge. Each bridge arm has two power electronic devices, such as MOSFET, IGBT, etc. The two MOSFETs on the same arm can't 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, but 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 that generated by setting Generator Actions on Events.
The dead time driver works like a decorator. This is also reflected in the function parameters of :cppmcpwm_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 :cppmcpwm_dead_time_config_t
structure:
- :cpp
mcpwm_dead_time_config_t::posedge_delay_ticks
and :cppmcpwm_dead_time_config_t::negedge_delay_ticks
set the number of ticks to delay the PWM waveform on the rising and falling edge. Specifically, setting both of them to zero means to bypass the dead-time module. The resolution of the dead-time tick is the same to the timer that is connected with the operator by :cppmcpwm_operator_connect_timer
. - :cpp
mcpwm_dead_time_config_t::invert_output
: Whether to invert the signal after applying the dead-time, which can be used to control the delay edge polarity.
Warning
Due to the hardware limitation, one delay module (either posedge delay or negedge delay) can't 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't 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.
This section will demonstrate 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.
/../_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));
}
/../_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));
}
/../_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));
}
/../_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));
}
/../_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));
}
/../_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));
}
/../_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));
}
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 PWM output signals may be at 100% duty and not changing whenever motor is required to run steady at the full load. Coupling of 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 :cppmcpwm_operator_apply_carrier
, and provide configuration structure :cppmcpwm_carrier_config_t
:
- :cpp
mcpwm_carrier_config_t::clk_src
sets the clock source of the carrier. - :cpp
mcpwm_carrier_config_t::frequency_hz
indicates carrier frequency in Hz. - :cpp
mcpwm_carrier_config_t::duty_cycle
indicates the duty cycle of the carrier. Note that, the supported choices of the duty cycle are discrete, the driver will search for the nearest one based on your configuration. - :cpp
mcpwm_carrier_config_t::first_pulse_duration_us
indicates the duration of the first pulse in microseconds. The resolution of the first pulse duration is determined by the carrier frequency you set in the :cppmcpwm_carrier_config_t::frequency_hz
. The first pulse duration can't be zero, and it has to be at least one period of the carrier. A longer pulse width can help conduct the inductance quicker. - :cpp
mcpwm_carrier_config_t::invert_before_modulate
and :cppmcpwm_carrier_config_t::invert_after_modulate
set whether to invert the carrier output before and after modulation.
Specifically, the carrier submodule can be disabled by calling :cppmcpwm_operator_apply_carrier
with a NULL
configuration.
The MCPWM operator is able to sense external signals with information about 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.
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 :cppmcpwm_operator_set_brake_on_fault
. Brake specific configuration is passed as a structure :cppmcpwm_brake_config_t
:
- :cpp
mcpwm_brake_config_t::fault
set which fault that the operator should react to. - :cpp
mcpwm_brake_config_t::brake_mode
set the brake mode that should be used for the fault. The supported brake modes are listed in the :cppmcpwm_operator_brake_mode_t
. For :cppMCPWM_OPER_BRAKE_MODE_CBC
mode, the operator will recover itself automatically as long as the fault disappears. You can specify the recovery time in :cppmcpwm_brake_config_t::cbc_recover_on_tez
and :cppmcpwm_brake_config_t::cbc_recover_on_tep
. For :cppMCPWM_OPER_BRAKE_MODE_OST
mode, the operator can't recover even though the fault disappears. User has to call :cppmcpwm_operator_recover_from_fault
to manually recover it.
One generator can set multiple actions on different brake events, by calling :cppmcpwm_generator_set_actions_on_brake_event
with variable number of action configurations. The action configuration is defined in :cppmcpwm_gen_brake_event_action_t
:
- :cpp
mcpwm_gen_brake_event_action_t::direction
specific the timer direction. The supported directions are listed in :cppmcpwm_timer_direction_t
. - :cpp
mcpwm_gen_brake_event_action_t::brake_mode
specifies the brake mode. The supported brake modes are listed in the :cppmcpwm_operator_brake_mode_t
. - :cpp
mcpwm_gen_brake_event_action_t::action
specifies the generator action to be taken. The supported actions are listed in :cppmcpwm_generator_action_t
.
There's a helper macro :cMCPWM_GEN_BRAKE_EVENT_ACTION
to simplify the construction of a brake event action entry.
Please note, the argument list of :cppmcpwm_generator_set_actions_on_brake_event
must be terminated by :cMCPWM_GEN_BRAKE_EVENT_ACTION_END
.
You can also set the brake action one by one by calling :cppmcpwm_generator_set_action_on_brake_event
without varargs.
The MCPWM fault detector can inform the user when it detects a valid fault or a fault signal disappears. If you have some function that should be called when such event happens, you should hook your function to the interrupt service routine by calling :cppmcpwm_fault_register_event_callbacks
. The callback function prototype is declared in :cppmcpwm_fault_event_cb_t
. All supported event callbacks are listed in the :cppmcpwm_fault_event_callbacks_t
:
- :cpp
mcpwm_fault_event_callbacks_t::on_fault_enter
sets callback function that will be called when a fault is detected. - :cpp
mcpwm_fault_event_callbacks_t::on_fault_exit
sets callback function that will be called when a fault is cleared.
The callback function is called within the ISR context, so is should not attempt to block (e.g., make sure that only FreeRTOS APIs with ISR
suffix is called within the function).
The parameter user_data
of :cppmcpwm_fault_register_event_callbacks
function is used to save user's own context, it will be passed to the callback function directly.
This function will lazy install interrupt service for the MCPWM fault, whereas the service can only be removed in :cppmcpwm_del_fault
.
The MCPWM operator can inform the user when it 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 :cppmcpwm_operator_register_event_callbacks
. The callback function prototype is declared in :cppmcpwm_brake_event_cb_t
. All supported event callbacks are listed in the :cppmcpwm_operator_event_callbacks_t
:
- :cpp
mcpwm_operator_event_callbacks_t::on_brake_cbc
sets callback function that will be called when the operator is going to take a CBC action. - :cpp
mcpwm_operator_event_callbacks_t::on_brake_ost
sets callback function that will be called when the operator is going to take an OST action.
The callback function is called within the ISR context, so is should not attempt to block (e.g., make sure that only FreeRTOS APIs with ISR
suffix is called within the function).
The parameter user_data
of :cppmcpwm_operator_register_event_callbacks
function is used to save user's own context, it will be passed to the callback function directly.
This function will lazy install interrupt service for the MCPWM operator, whereas the service can only be removed in :cppmcpwm_del_operator
.
Software can override generator output level at runtime, by calling :cppmcpwm_generator_set_force_level
. The software force level always has a higher priority than other event actions set in e.g. :cppmcpwm_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, the force output level will keep alive, until it's removed by assigninglevel
to -1. - Set the
hole_on
to false, the force output level will only be active for a short time, any upcoming event can override it.
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 :cppmcpwm_timer_set_phase_on_sync
. The sync phase configuration is defined in :cppmcpwm_timer_sync_phase_config_t
structure:
- :cpp
mcpwm_timer_sync_phase_config_t::sync_src
sets the sync signal source. See MCPWM Sync Sources for how to create a sync source object. Specifically, if this is set toNULL
, the driver will disable the sync feature for the MCPWM timer. - :cpp
mcpwm_timer_sync_phase_config_t::count_value
sets the count value to load when the sync signal is taken. - :cpp
mcpwm_timer_sync_phase_config_t::direction
sets the count direction when the sync signal is taken.
Likewise, the MCPWM capture timer MCPWM Capture Timer can be synced as well. You can set the sync phase for the capture timer by calling :cppmcpwm_capture_timer_set_phase_on_sync
. The sync phase configuration is defined in :cppmcpwm_capture_timer_sync_phase_config_t
structure:
- :cpp
mcpwm_capture_timer_sync_phase_config_t::sync_src
sets the sync signal source. See MCPWM Sync Sources for how to create a sync source object. Specifically, if this is set toNULL
, the driver will disable the sync feature for the MCPWM capture timer. - :cpp
mcpwm_capture_timer_sync_phase_config_t::count_value
sets the count value to load when the sync signal is taken. - :cpp
mcpwm_capture_timer_sync_phase_config_t::direction
sets the count direction when the sync signal is taken. Note that, different from MCPWM Timer, the capture timer can only support one count direction: :cppMCPWM_TIMER_DIRECTION_UP
.
- 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));
}
}
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 SensorThe capture timer is usually connected with several capture channels, please refer to MCPWM Capture Timer and Channels for resource allocation.
The MCPWM capture channel can inform the user when there's a valid edge detected on the signal. You have to register a callback function to get the timer count value of the capture moment, by calling :cppmcpwm_capture_channel_register_event_callbacks
. The callback function prototype is declared in :cppmcpwm_capture_event_cb_t
. All supported capture callbacks are listed in the :cppmcpwm_capture_event_callbacks_t
:
- :cpp
mcpwm_capture_event_callbacks_t::on_cap
sets callback function for the capture channel when a valid edge is detected.
The callback function will provide event specific data of type :cppmcpwm_capture_event_data_t
, so that you can get the edge of the capture signal in :cppmcpwm_capture_event_data_t::cap_edge
and the count value of that moment in :cppmcpwm_capture_event_data_t::cap_value
. To convert the capture count into timestamp, you need to know the resolution of the capture timer by calling :cppmcpwm_capture_timer_get_resolution
.
The callback function is called within the ISR context, so is should not attempt to block (e.g., make sure that only FreeRTOS APIs with ISR
suffix is called within the function).
The parameter user_data
of :cppmcpwm_capture_channel_register_event_callbacks
function is used to save user's own context, it will be 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 :cppmcpwm_del_capture_channel
.
The capture channel is not enabled after allocation by :cppmcpwm_new_capture_channel
. You should call :cppmcpwm_capture_channel_enable
and :cppmcpwm_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 :cppmcpwm_capture_channel_register_event_callbacks
, :cppmcpwm_capture_channel_enable
will enable the interrupt service as well.
Before doing IO control to the capture timer, user needs to enable the timer first, by calling :cppmcpwm_capture_timer_enable
. Internally, this function will:
- switch the capture timer state from init to enable.
- acquire 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 :cppmcpwm_capture_timer_disable
will put the timer driver back to init state, and release the power management lock.
The basic IO operation of a capture timer is to start and stop. Calling :cppmcpwm_capture_timer_start
can start the timer and calling :cppmcpwm_capture_timer_stop
can stop the timer immediately.
Sometime, the software also wants to trigger a "fake" capture event. The :cppmcpwm_capture_channel_trigger_soft_catch
is provided for that purpose. Please note that, even though it's a "fake" capture event, it can still cause an interrupt, thus your capture event callback function will get invoked as well.
When power management is enabled (i.e. CONFIG_PM_ENABLE
is on), the system will adjust the PLL, APB frequency before going into light sleep, thus potentially changing the period of a 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 :cppESP_PM_APB_FREQ_MAX
. Whenever the driver creates a MCPWM timer instance that has selected :cppMCPWM_TIMER_CLK_SRC_PLL160M
as its clock source, the driver will guarantee that the power management lock is acquired when enable the timer by :cppmcpwm_timer_enable
. Likewise, the driver releases the lock when :cppmcpwm_timer_disable
is called for that timer.
Likewise, Whenever the driver creates a MCPWM capture timer instance that has selected :cppMCPWM_CAPTURE_CLK_SRC_APB
as its clock source, the driver will guarantee that the power management lock is acquired when enable the timer by :cppmcpwm_capture_timer_enable
. And will release the lock in :cppmcpwm_capture_timer_disable
.
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's a Kconfig option CONFIG_MCPWM_ISR_IRAM_SAFE
that will:
- Enable the interrupt being serviced even when cache is disabled
- Place all functions that used by the ISR into IRAM10
- Place driver object into DRAM (in case it's mapped to PSRAM by accident)
This will allow the interrupt to run while the cache is disabled but will come at the cost of increased IRAM consumption.
There is another Kconfig option 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. These IO control functions are as follows:
- :cpp
mcpwm_comparator_set_compare_value
- :cpp
mcpwm_timer_set_period
The factory functions like :cppmcpwm_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 functions are allowed to run under ISR context, as the driver uses a critical section to prevent them being called concurrently in the task and ISR.
- :cpp
mcpwm_comparator_set_compare_value
- :cpp
mcpwm_timer_set_period
Other functions that are not related to Resource Allocation, are not thread safe. Thus, you should avoid calling them in different tasks without mutex protection.
CONFIG_MCPWM_ISR_IRAM_SAFE
controls whether the default ISR handler can work when cache is disabled, see IRAM Safe for more information.CONFIG_MCPWM_CTRL_FUNC_IN_IRAM
controls where to place the MCPWM control functions (IRAM or flash), see IRAM Safe for more information.CONFIG_MCPWM_ENABLE_DEBUG_LOG
is used to enabled the debug log output. Enable this option will increase the firmware binary size.
- Brushed DC motor speed control by PID algorithm:
peripherals/mcpwm/mcpwm_bdc_speed_control
- BLDC motor control with hall sensor feedback:
peripherals/mcpwm/mcpwm_bldc_hall_control
- Ultrasonic sensor (HC-SR04) distance measurement:
peripherals/mcpwm/mcpwm_capture_hc_sr04
- Servo motor angle control:
peripherals/mcpwm/mcpwm_servo_control
- MCPWM synchronization between timers:
peripherals/mcpwm/mcpwm_sync
inc/mcpwm_timer.inc
inc/mcpwm_oper.inc
inc/mcpwm_cmpr.inc
inc/mcpwm_gen.inc
inc/mcpwm_fault.inc
inc/mcpwm_sync.inc
inc/mcpwm_cap.inc
inc/components/driver/include/driver/mcpwm_types.inc
inc/components/hal/include/hal/mcpwm_types.inc
Different ESP chip series might have different number of MCPWM resources (e.g. groups, timers, comparators, operators, generators and so on). Please refer to the [TRM] for details. The driver won't forbid you from applying for more MCPWM resources, but it will return error when there's no hardware resources available. Please always check the return value when doing Resource Allocation.↩
Different ESP chip series might have different number of MCPWM resources (e.g. groups, timers, comparators, operators, generators and so on). Please refer to the [TRM] for details. The driver won't forbid you from applying for more MCPWM resources, but it will return error when there's no hardware resources available. Please always check the return value when doing Resource Allocation.↩
Different ESP chip series might have different number of MCPWM resources (e.g. groups, timers, comparators, operators, generators and so on). Please refer to the [TRM] for details. The driver won't forbid you from applying for more MCPWM resources, but it will return error when there's no hardware resources available. Please always check the return value when doing Resource Allocation.↩
Different ESP chip series might have different number of MCPWM resources (e.g. groups, timers, comparators, operators, generators and so on). Please refer to the [TRM] for details. The driver won't forbid you from applying for more MCPWM resources, but it will return error when there's no hardware resources available. Please always check the return value when doing Resource Allocation.↩
Different ESP chip series might have different number of MCPWM resources (e.g. groups, timers, comparators, operators, generators and so on). Please refer to the [TRM] for details. The driver won't forbid you from applying for more MCPWM resources, but it will return error when there's no hardware resources available. Please always check the return value when doing Resource Allocation.↩
Different ESP chip series might have different number of MCPWM resources (e.g. groups, timers, comparators, operators, generators and so on). Please refer to the [TRM] for details. The driver won't forbid you from applying for more MCPWM resources, but it will return error when there's no hardware resources available. Please always check the return value when doing Resource Allocation.↩
Different ESP chip series might have different number of MCPWM resources (e.g. groups, timers, comparators, operators, generators and so on). Please refer to the [TRM] for details. The driver won't forbid you from applying for more MCPWM resources, but it will return error when there's no hardware resources available. Please always check the return value when doing Resource Allocation.↩
Different ESP chip series might have different number of MCPWM resources (e.g. groups, timers, comparators, operators, generators and so on). Please refer to the [TRM] for details. The driver won't forbid you from applying for more MCPWM resources, but it will return error when there's no hardware resources available. Please always check the return value when doing Resource Allocation.↩
Different ESP chip series might have different number of MCPWM resources (e.g. groups, timers, comparators, operators, generators and so on). Please refer to the [TRM] for details. The driver won't forbid you from applying for more MCPWM resources, but it will return error when there's no hardware resources available. Please always check the return value when doing Resource Allocation.↩
Callback function and the sub-functions invoked by itself should also be placed in IRAM, users need to take care of this by themselves.↩