MCPWM 同步:对齐 PWM 相位
为什么需要同步
每个 MCPWM 定时器是独立的硬件计数器。当你调用 mcpwm_timer_start() 启动两个定时器时,CPU 会依次发出两条写指令——第二个定时器比第一个晚几十个 CPU 周期才启动。即使两者的周期配置完全相同,它们的计数器在周期中的位置也是不同的,PWM 输出的相位关系无法预测。
同步通过在同步边沿到达时给**正在运行**的定时器加载指定的计数值和方向来解决这个问题。定时器必须已经在运行;同步不会启动或停止定时器。它是在运行时修正相位的一种机制。
如果同步边沿在每个周期都到达(例如来自 TEZ 处的定时器同步源),那么修正每周期重复一次,相位关系可以无限期保持。这就是典型的用法:一个定时器作为参考,其他定时器在每个周期都重新对齐到它。
MCPWM 提供三种同步源。所有源都产生 mcpwm_sync_handle_t 类型的句柄,且任何源都可以供给同组内的任意定时器。
GPIO 同步源
GPIO 同步源对外部引脚上的边沿做出反应——当外部控制器、传感器或编码器提供周期性参考信号时非常有用。
mcpwm_sync_handle_t sync = NULL;
ESP_ERROR_CHECK(mcpwm_new_gpio_sync_src(
&(mcpwm_gpio_sync_src_config_t) {
.group_id = 0,
.gpio_num = 5,
.flags.active_neg = false,
}, &sync));
GPIO 同步源配置很简单:
软件同步源
软件同步源由应用代码按需产生同步边沿。它没有配置字段;创建后即可在需要时激活。
mcpwm_sync_handle_t soft_sync = NULL;
ESP_ERROR_CHECK(mcpwm_new_soft_sync_src(NULL, &soft_sync));
// 后续当应用决定同步时:
ESP_ERROR_CHECK(mcpwm_soft_sync_activate(soft_sync));
备注
必须先通过 mcpwm_timer_set_phase_on_sync() 或 mcpwm_capture_timer_set_phase_on_sync() 将软件同步源绑定到定时器,再调用激活。驱动在创建时不会分配定时器;在绑定前调用 mcpwm_soft_sync_activate() 属于未定义行为。
这在定时器已经在运行、应用需要触发一次性的相位修正时有用——例如故障恢复后,或开始新的控制周期之前。由于软件同步是一次性的,如果后续没有更多同步边沿到来,相位关系会随时间漂移。如需持续锁相,应使用周期性源(GPIO 或定时器同步源)。
定时器同步源
定时器同步源在定时器到达指定事件时产生同步边沿——例如每次定时器计到零(TEZ)。这可以让一个定时器作为其他定时器的周期性参考,每周期都保持相位锁定。
mcpwm_sync_handle_t timer_sync = NULL;
ESP_ERROR_CHECK(mcpwm_new_timer_sync_src(
timer_a,
&(mcpwm_timer_sync_src_config_t) {
.timer_event = MCPWM_TIMER_EVENT_EMPTY,
},
&timer_sync));
timer_event— 触发同步输出的定时器事件。常用MCPWM_TIMER_EVENT_EMPTY(零)表示每个周期开始,或MCPWM_TIMER_EVENT_PEAK表示峰值位置。在向上计数模式中峰值就是周期边界;在向上-向下计数模式中峰值是周期的中点。propagate_input_sync— 设置后,该定时器会将其接收到的输入同步转发到其输出,无需额外 GPIO 接线即可实现定时器同步链。此模式下硬件选择输入同步作为输出源,因此timer_event字段会被忽略。
每个定时器最多只能创建一个同步源。多个定时器可以接收同一个同步源。
由于定时器同步源每周期都会触发,接收定时器在每个周期都会得到修正。这是维持多路 PWM 通道间稳定相位关系最常用的方式。
设置接收相位
无论选择哪种同步源,接收定时器都使用相同的 API。调用 mcpwm_timer_set_phase_on_sync() 配置同步边沿到达时的行为。定时器必须已经在运行,同步才会生效。
ESP_ERROR_CHECK(mcpwm_timer_set_phase_on_sync(timer,
&(mcpwm_timer_sync_phase_config_t) {
.sync_src = sync,
.count_value = 25,
.direction = MCPWM_TIMER_DIRECTION_UP,
}));
sync_src— 源对象。设为NULL可取消同步。count_value— 同步事件到达时加载的计数值。应保持在定时器周期范围内。direction— 加载后的计数方向。
两路 90 度移相
现在你已经了解了三种同步源以及如何设置接收相位,下面是一个完整示例。它使用定时器同步源:timer_a 每次到达零时发出同步,timer_b 收到后加载 count_value = 25,产生 90 度相位滞后。由于同步每周期重复一次,两路输出的相位关系可以无限期保持。
mcpwm_timer_handle_t timer_a = NULL;
mcpwm_timer_handle_t timer_b = NULL;
mcpwm_sync_handle_t timer_a_sync = NULL;
// timer_a 和 timer_b 均已创建,period_ticks = 100
ESP_ERROR_CHECK(mcpwm_new_timer_sync_src(
timer_a,
&(mcpwm_timer_sync_src_config_t) {
.timer_event = MCPWM_TIMER_EVENT_EMPTY,
},
&timer_a_sync));
ESP_ERROR_CHECK(mcpwm_timer_set_phase_on_sync(timer_b,
&(mcpwm_timer_sync_phase_config_t) {
.sync_src = timer_a_sync,
.count_value = 25,
.direction = MCPWM_TIMER_DIRECTION_UP,
}));
// timer_a 在 TEZ 输出同步;timer_b 收到后从 Tick 25 开始继续计数。
理解领先与滞后
下面的示意中,PWM_A 先开始一个周期,PWM_B 在其后四分之一周期出现,因此 PWM_B 滞后 PWM_A 90 度;反过来说,PWM_A 领先 PWM_B 90 度。
PWM_A 与 PWM_B 之间的 90 度相移:PWM_B 在 PWM_A 之后 25 个 tick 处开始上升。
其他注意事项
捕获定时器也可通过 mcpwm_capture_timer_set_phase_on_sync() 使用同一同步源,捕获始终向上计数。接收端和源必须保留在同一组中。删除源之前,应先取消同步或删除所有使用它的对象。
API 参考
MCPWM 同步驱动函数
Header File
This header file can be included with:
#include "driver/mcpwm_sync.h"
This header file is a part of the API provided by the
esp_driver_mcpwmcomponent. To declare that your component depends onesp_driver_mcpwm, add the following to your CMakeLists.txt:REQUIRES esp_driver_mcpwm
or
PRIV_REQUIRES esp_driver_mcpwm
Functions
-
esp_err_t mcpwm_new_timer_sync_src(mcpwm_timer_handle_t timer, const mcpwm_timer_sync_src_config_t *config, mcpwm_sync_handle_t *ret_sync)
Create MCPWM timer sync source.
- 参数:
timer -- [in] MCPWM timer handle, allocated by
mcpwm_new_timer()config -- [in] MCPWM timer sync source configuration
ret_sync -- [out] Returned MCPWM sync handle
- 返回:
ESP_OK: Create MCPWM timer sync source successfully
ESP_ERR_INVALID_ARG: Create MCPWM timer sync source failed because of invalid argument
ESP_ERR_NO_MEM: Create MCPWM timer sync source failed because out of memory
ESP_ERR_INVALID_STATE: Create MCPWM timer sync source failed because the timer has created a sync source before
ESP_FAIL: Create MCPWM timer sync source failed because of other error
-
esp_err_t mcpwm_new_gpio_sync_src(const mcpwm_gpio_sync_src_config_t *config, mcpwm_sync_handle_t *ret_sync)
Create MCPWM GPIO sync source.
- 参数:
config -- [in] MCPWM GPIO sync source configuration
ret_sync -- [out] Returned MCPWM GPIO sync handle
- 返回:
ESP_OK: Create MCPWM GPIO sync source successfully
ESP_ERR_INVALID_ARG: Create MCPWM GPIO sync source failed because of invalid argument
ESP_ERR_NO_MEM: Create MCPWM GPIO sync source failed because out of memory
ESP_ERR_NOT_FOUND: Create MCPWM GPIO sync source failed because can't find free resource
ESP_FAIL: Create MCPWM GPIO sync source failed because of other error
-
esp_err_t mcpwm_new_soft_sync_src(const mcpwm_soft_sync_config_t *config, mcpwm_sync_handle_t *ret_sync)
Create MCPWM software sync source.
- 参数:
config -- [in] MCPWM software sync source configuration
ret_sync -- [out] Returned software sync handle
- 返回:
ESP_OK: Create MCPWM software sync successfully
ESP_ERR_INVALID_ARG: Create MCPWM software sync failed because of invalid argument
ESP_ERR_NO_MEM: Create MCPWM software sync failed because out of memory
ESP_FAIL: Create MCPWM software sync failed because of other error
-
esp_err_t mcpwm_del_sync_src(mcpwm_sync_handle_t sync)
Delete MCPWM sync source.
- 参数:
sync -- [in] MCPWM sync handle, allocated by
mcpwm_new_timer_sync_src()ormcpwm_new_gpio_sync_src()ormcpwm_new_soft_sync_src()- 返回:
ESP_OK: Delete MCPWM sync source successfully
ESP_ERR_INVALID_ARG: Delete MCPWM sync source failed because of invalid argument
ESP_FAIL: Delete MCPWM sync source failed because of other error
-
esp_err_t mcpwm_soft_sync_activate(mcpwm_sync_handle_t sync)
Activate the software sync, trigger the sync event for once.
- 参数:
sync -- [in] MCPWM soft sync handle, allocated by
mcpwm_new_soft_sync_src()- 返回:
ESP_OK: Trigger MCPWM software sync event successfully
ESP_ERR_INVALID_ARG: Trigger MCPWM software sync event failed because of invalid argument
ESP_FAIL: Trigger MCPWM software sync event failed because of other error
Structures
-
struct mcpwm_timer_sync_src_config_t
MCPWM timer sync source configuration.
Public Members
-
mcpwm_timer_event_t timer_event
Timer event, upon which MCPWM timer will generate the sync signal
-
struct mcpwm_timer_sync_src_config_t::extra_mcpwm_timer_sync_src_flags flags
Extra configuration flags for timer sync source
-
mcpwm_timer_event_t timer_event
-
struct mcpwm_gpio_sync_src_config_t
MCPWM GPIO sync source configuration.
Public Members
-
int group_id
MCPWM group ID
-
int gpio_num
GPIO used by sync source
-
struct mcpwm_gpio_sync_src_config_t::extra_mcpwm_gpio_sync_src_flags flags
Extra configuration flags for GPIO sync source
-
int group_id
-
struct mcpwm_soft_sync_config_t
MCPWM software sync configuration structure.