Merge branch 'feat/change_mcpwm_prescale' into 'master'

change(mcpwm): change default mcpwm group prescale

Closes IDFGH-13989 and IDF-12790

See merge request espressif/esp-idf!34897
This commit is contained in:
Chen Ji Chang
2025-05-27 19:27:43 +08:00
6 changed files with 70 additions and 10 deletions

View File

@ -93,7 +93,8 @@ esp_err_t mcpwm_new_capture_timer(const mcpwm_capture_timer_config_t *config, mc
ESP_GOTO_ON_ERROR(esp_clk_tree_src_get_freq_hz((soc_module_clk_t)clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &periph_src_clk_hz), err, TAG, "get clock source freq failed");
ESP_LOGD(TAG, "periph_src_clk_hz %"PRIu32"", periph_src_clk_hz);
// when resolution_hz set to zero, use default resolution_hz
uint32_t resolution_hz = config->resolution_hz ? config->resolution_hz : periph_src_clk_hz / MCPWM_GROUP_CLOCK_DEFAULT_PRESCALE;
uint32_t cap_timer_prescale = group->prescale > 0 ? group->prescale : MCPWM_GROUP_CLOCK_DEFAULT_PRESCALE;
uint32_t resolution_hz = config->resolution_hz ? config->resolution_hz : periph_src_clk_hz / cap_timer_prescale;
ESP_GOTO_ON_ERROR(mcpwm_set_prescale(group, resolution_hz, MCPWM_LL_MAX_CAPTURE_TIMER_PRESCALE, NULL), err, TAG, "set prescale failed");
cap_timer->resolution_hz = group->resolution_hz;

View File

@ -53,7 +53,7 @@ extern "C" {
#define MCPWM_ALLOW_INTR_PRIORITY_MASK ESP_INTR_FLAG_LOWMED
#define MCPWM_GROUP_CLOCK_DEFAULT_PRESCALE 2
#define MCPWM_GROUP_CLOCK_DEFAULT_PRESCALE 1
///!< Logging settings
#define TAG "mcpwm"

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -73,7 +73,7 @@ TEST_CASE("mcpwm_capture_ext_gpio", "[mcpwm]")
printf("init a gpio to simulate the external capture signal\r\n");
const int cap_gpio = TEST_CAP_GPIO;
gpio_config_t ext_gpio_conf = {
.mode = GPIO_MODE_OUTPUT,
.mode = GPIO_MODE_INPUT_OUTPUT,
.pin_bit_mask = BIT(cap_gpio),
};
TEST_ESP_OK(gpio_config(&ext_gpio_conf));
@ -110,6 +110,12 @@ TEST_CASE("mcpwm_capture_ext_gpio", "[mcpwm]")
printf("enable capture channel\r\n");
TEST_ESP_OK(mcpwm_capture_channel_enable(pps_channel));
printf("check input function before starting capture\r\n");
gpio_set_level(cap_gpio, 1);
TEST_ASSERT_EQUAL(1, gpio_get_level(cap_gpio));
gpio_set_level(cap_gpio, 0);
TEST_ASSERT_EQUAL(0, gpio_get_level(cap_gpio));
printf("enable and start capture timer\r\n");
TEST_ESP_OK(mcpwm_capture_timer_enable(cap_timer));
TEST_ESP_OK(mcpwm_capture_timer_start(cap_timer));
@ -131,6 +137,13 @@ TEST_CASE("mcpwm_capture_ext_gpio", "[mcpwm]")
TEST_ESP_OK(mcpwm_del_capture_channel(pps_channel));
TEST_ESP_OK(mcpwm_capture_timer_disable(cap_timer));
TEST_ESP_OK(mcpwm_del_capture_timer(cap_timer));
printf("check input function after removing capture\r\n");
gpio_set_level(cap_gpio, 1);
TEST_ASSERT_EQUAL(1, gpio_get_level(cap_gpio));
gpio_set_level(cap_gpio, 0);
TEST_ASSERT_EQUAL(0, gpio_get_level(cap_gpio));
TEST_ESP_OK(gpio_reset_pin(cap_gpio));
}

View File

@ -356,7 +356,7 @@ static void mcpwm_gen_action_test_template(uint32_t timer_resolution, uint32_t p
mcpwm_capture_timer_config_t cap_timer_config = {
.clk_src = MCPWM_CAPTURE_CLK_SRC_DEFAULT,
.group_id = 0,
.resolution_hz = clk_src_freq_hz / 2,
.resolution_hz = clk_src_freq_hz,
};
TEST_ESP_OK(mcpwm_new_capture_timer(&cap_timer_config, &cap_timer));
@ -602,7 +602,7 @@ static void mcpwm_deadtime_test_template(uint32_t timer_resolution, uint32_t per
mcpwm_capture_timer_config_t cap_timer_config = {
.clk_src = MCPWM_CAPTURE_CLK_SRC_DEFAULT,
.group_id = 0,
.resolution_hz = clk_src_freq_hz / 2,
.resolution_hz = clk_src_freq_hz,
};
TEST_ESP_OK(mcpwm_new_capture_timer(&cap_timer_config, &cap_timer));

View File

@ -50,6 +50,7 @@ Description of the MCPWM functionality is divided into the following sections:
- :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-resolution-config` - describes the resolution configuration rules for the MCPWM submodule.
- :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.
@ -79,6 +80,10 @@ The :cpp:func:`mcpwm_new_timer` will return a pointer to the allocated timer obj
On the contrary, calling the :cpp:func:`mcpwm_del_timer` function will free the allocated timer object.
.. note::
The prescale for the MCPWM group will be calculated with the resolution of the first timer, and the driver will find the appropriate prescale from low to high. If there is a prescale conflict when allocating multiple timers, allocate timers in order of their target resolution, either from highest to lowest or lowest to highest. For more information, please refer to :ref:`mcpwm-resolution-config`.
MCPWM Operators
~~~~~~~~~~~~~~~
@ -193,6 +198,12 @@ To allocate a capture timer, you can call the :cpp:func:`mcpwm_new_capture_timer
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`.
.. only:: SOC_MCPWM_CAPTURE_CLK_FROM_GROUP
.. note::
Timers and capture timers share the MCPWM group clock source. The prescale for the MCPWM group will be calculated with the resolution of the first allocated (capture)timer. The driver will search for the appropriate prescale from low to high. If there is a prescale conflict when allocating multiple (capture)timers, allocate (capture)timers in order of their target resolution, either from highest to lowest or lowest to highest. For more information, please refer to :ref:`mcpwm-resolution-config`.
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:
@ -758,8 +769,8 @@ The MCPWM operator has a carrier submodule that can be used if galvanic isolatio
To configure the carrier submodule, you can call :cpp:func:`mcpwm_operator_apply_carrier`, and provide configuration structure :cpp:type:`mcpwm_carrier_config_t`:
- :cpp:member:`mcpwm_carrier_config_t::clk_src` sets the clock source of the carrier.
- :cpp:member:`mcpwm_carrier_config_t::frequency_hz` indicates carrier frequency in Hz.
- :cpp:member:`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 searches for the nearest one based on your configuration.
- :cpp:member:`mcpwm_carrier_config_t::frequency_hz` indicates carrier frequency in Hz. For more information, please refer to :ref:`mcpwm-resolution-config`.
- :cpp:member:`mcpwm_carrier_config_t::duty_cycle` indicates the duty cycle of the carrier. Note that, there are only 7 supported duty cycles: 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875.
- :cpp:member:`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 :cpp:member:`mcpwm_carrier_config_t::frequency_hz`. The first pulse duration can not 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:member:`mcpwm_carrier_config_t::invert_before_modulate` and :cpp:member:`mcpwm_carrier_config_t::invert_after_modulate` set whether to invert the carrier output before and after modulation.
@ -981,6 +992,18 @@ Likewise, whenever the driver creates an MCPWM capture timer instance, the drive
This feature can be enabled by setting the flag :cpp:member:`mcpwm_timer_config_t::allow_pd` or :cpp:member:`mcpwm_capture_timer_config_t::allow_pd`. It will allow the system to power down the MCPWM in Light-sleep, meanwhile save the MCPWM register context. It can help to save more power consumption with some extra cost of the memory.
.. _mcpwm-resolution-config:
Resolution Configuration
^^^^^^^^^^^^^^^^^^^^^^^^
The MCPWM group has clock dividers and some sub-modules will have their own clock dividers. The final clock frequency of the sub-module depends on the group clock divider and its own divider (if any). The group clock divider affects all submodules. When configuring the clock frequency (also called resolution) of an MCPWM submodule, the driver sets the divider according to the following rules:
1. If the clock frequency of the submodule can be divisible by the clock source, the frequency of the submodule is prioritized to ensure the accuracy of the submodule.
2. If it cannot be divisible by the clock source, the group clock is guaranteed to have the highest frequency possible, and the submodule frequency is adjusted to the closest frequency that can be divisible by the clock source.
When multiple MCPWM submodules coexist, you need to consider whether there is a clock divider conflict. When there is a group clock divider conflict, try adjusting the submodule allocation order. See [`TRM <{IDF_TARGET_TRM_EN_URL}#mcpwm>`__] for details on group frequency divider and submodule frequency divider ranges.
.. _mcpwm-iram-safe:
IRAM Safe

View File

@ -50,6 +50,7 @@ MCPWM 外设是一个多功能 PWM 生成器,集成多个子模块,在电力
- :ref:`mcpwm-capture` - 介绍如何使用 MCPWM 捕获模块测量信号脉宽。
:SOC_MCPWM_SUPPORT_ETM: - :ref:`mcpwm-etm-event-and-task` - MCPWM 提供了哪些事件和任务可以连接到 ETM 通道上。
- :ref:`mcpwm-power-management` - 介绍不同的时钟源对功耗的影响。
- :ref:`mcpwm-resolution-config` - 介绍 MCPWM 子模块的分辨率配置规则。
- :ref:`mcpwm-iram-safe` - 介绍如何协调 RMT 中断与禁用缓存。
- :ref:`mcpwm-thread-safety` - 列出了由驱动程序认证为线程安全的 API。
- :ref:`mcpwm-kconfig-options` - 列出了针对驱动的数个 Kconfig 支持选项。
@ -79,6 +80,10 @@ MCPWM 定时器
反之,调用 :cpp:func:`mcpwm_del_timer` 函数将释放已分配的定时器。
.. note::
同时分配多个 MCPWM 定时器时MCPWM 组的分频系数将以第一个定时器的分辨率来计算,驱动会从低到高寻找合适的分频系数。若分配多个定时器时出现分频系数冲突,请调整分配定时器的顺序,按照目标分辨率的大小按序申请定时器(从大到小或者从小到大)。更多相关内容请参阅 :ref:`mcpwm-resolution-config`
MCPWM 操作器
~~~~~~~~~~~~~~~
@ -193,6 +198,12 @@ MCPWM 组有一个专用定时器,用于捕获特定事件发生时的时间
在 {IDF_TARGET_NAME} 中,:cpp:member:`mcpwm_capture_timer_config_t::resolution_hz` 参数无效,捕获定时器的分辨率始终等于 :cpp:enumerator:`MCPWM_CAPTURE_CLK_SRC_APB`
.. only:: SOC_MCPWM_CAPTURE_CLK_FROM_GROUP
.. note::
定时器和捕获定时器共享 MCPWM 组时钟源。MCPWM 组的分频系数将以第一个分配的(捕获)定时器的分辨率来计算,驱动会从低到高寻找合适的分频系数。若分配多个(捕获)定时器时出现分频系数冲突,请调整分配定时器的顺序,按照目标分辨率的大小按序申请(捕获)定时器(从大到小或者从小到大)。更多相关内容请参阅 :ref:`mcpwm-resolution-config`
分配成功后,:cpp:func:`mcpwm_new_capture_timer` 将返回一个指向已分配捕获定时器的指针。否则,函数将返回错误代码。具体来说,当 MCPWM 组中没有空闲捕获定时器时,将返回 :c:macro:`ESP_ERR_NOT_FOUND` 错误。[1]_
接下来,可以调用 :cpp:func:`mcpwm_new_capture_channel` 函数,以一个捕获定时器句柄和配置结构体 :cpp:type:`mcpwm_capture_channel_config_t` 为参数,分配一个捕获通道。结构体定义为:
@ -758,8 +769,8 @@ MCPWM 操作器具有载波子模块,可以根据需要(例如隔离式数
调用 :cpp:func:`mcpwm_operator_apply_carrier`,并提供配置结构体 :cpp:type:`mcpwm_carrier_config_t`,配置载波子模块:
- :cpp:member:`mcpwm_carrier_config_t::clk_src` 设置载波的时钟源。
- :cpp:member:`mcpwm_carrier_config_t::frequency_hz` 表示载波频率,单位为赫兹。内部驱动将根据时钟源和载波频率设置合适的分频器。
- :cpp:member:`mcpwm_carrier_config_t::duty_cycle` 表示载波的占空比。需注意,支持的占空比选项并不连续,驱动程序将根据配置查找最接近的占空比
- :cpp:member:`mcpwm_carrier_config_t::frequency_hz` 表示载波频率,单位为赫兹。内部驱动将根据时钟源和载波频率设置合适的分频器。有关频率限制的相关内容请参阅 :ref:`mcpwm-resolution-config`
- :cpp:member:`mcpwm_carrier_config_t::duty_cycle` 表示载波的占空比。需注意,占空比仅支持的7种分别为 0.125、0.25、0.375、0.5、0.625、0.75、0.875
- :cpp:member:`mcpwm_carrier_config_t::first_pulse_duration_us` 表示第一个脉冲的脉宽,单位为微秒。该脉冲的分辨率由 :cpp:member:`mcpwm_carrier_config_t::frequency_hz` 中的配置决定。第一个脉冲的脉宽不能为零,且至少为一个载波周期。脉宽越长,电感传导越快。
- :cpp:member:`mcpwm_carrier_config_t::invert_before_modulate` 和 :cpp:member:`mcpwm_carrier_config_t::invert_after_modulate` 设置是否在调制前和调制后取反载波输出。
@ -981,6 +992,18 @@ MCPWM 捕获通道支持在信号上检测到有效边沿时发送通知。须
该特性可以通过置位配置中的 :cpp:member:`mcpwm_timer_config_t::allow_pd`:cpp:member:`mcpwm_capture_timer_config_t::allow_pd` 标志位启用。启用后驱动允许系统在 Light-sleep 时对 MCPWM 掉电,同时保存 MCPWM 的寄存器内容。它可以帮助降低 Light-sleep 时的功耗,但需要花费一些额外的存储来保存寄存器的配置。
.. _mcpwm-resolution-config:
分辨率配置
^^^^^^^^^^^^^
MCPWM 组拥有时钟分频器,并且部分子模块会有自己的时钟分频器,子模块最终的时钟频率取决于组时钟分频器和自身的分频器(如果有)。而组时钟分频器会影响所有子模块。当配置 MCPWM 子模块的时钟频率(或者叫分辨率)时,驱动会按照以下规则设置分频器:
1. 如果子模块的时钟频率可以被时钟源整除,优先保证子模块的频率准确。
2. 如果无法被时钟源整除,则尽可能保证组时钟的频率最高,将子模块频率调整到最接近的能够被时钟源整除的频率。
当多个 MCPWM 子模块共存时,需要考虑是否存在时钟分频器冲突。当出现组时钟分频器冲突,可以尝试调整子模块分配顺序。组分频器和子模块分频器范围详情请参见 [`TRM <{IDF_TARGET_TRM_EN_URL}#mcpwm>`__]。
.. _mcpwm-iram-safe:
IRAM 安全