# 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 同步源配置很简单：

- `group_id` — 同步源所属的 MCPWM 组。必须与所有接收该同步的定时器所在组一致。

- `gpio_num` — 承载同步信号的 GPIO。

- `active_neg` — 默认上升沿为有效边沿；设置后改为下降沿有效。

## 软件同步源

软件同步源由应用代码按需产生同步边沿。它没有配置字段；创建后即可在需要时激活。

    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

- components/esp_driver_mcpwm/include/driver/mcpwm_sync.h

- 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_mcpwm` component. To declare that your component depends on `esp_driver_mcpwm`, add the following to your CMakeLists.txt:

  >     REQUIRES esp_driver_mcpwm

  > or

  >     PRIV_REQUIRES esp_driver_mcpwm

#### Functions

esp_err_t mcpwm_new_timer_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()` or `mcpwm_new_gpio_sync_src()` or `mcpwm_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

  struct extra_mcpwm_timer_sync_src_flags

    Extra configuration flags for timer sync source.

    Public Members

    uint32_t propagate_input_sync

      The input sync signal would be routed to its sync output

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

  struct extra_mcpwm_gpio_sync_src_flags

    Extra configuration flags for GPIO sync source.

    Public Members

    uint32_t active_neg

      Whether the sync signal is active on negedge, by default, the sync signal's posedge is treated as active

struct mcpwm_soft_sync_config_t

  MCPWM software sync configuration structure.

此文档对您有帮助吗？

- 反馈已收到，谢谢！
- 如果您有其他意见，欢迎填写乐鑫文档反馈表。

- 我们重视您的反馈。
- 您可以填写乐鑫文档反馈表告诉我们如何改进该文档。