feat(mcpwm): support RC_FAST group clock source

Expose RC_FAST as a shared MCPWM group clock source for timers,
capture timers, and carriers on ESP32-C5, ESP32-C6, ESP32-H2,
and ESP32-P4. Fix the ESP32-S31 MCPWM clock selector mapping.

Add a hardware runtime test that verifies MCPWM Timer counting
resolution for every supported clock source.
This commit is contained in:
Steven Yang
2026-07-29 20:20:47 +08:00
parent 102122904a
commit 8a982614a2
9 changed files with 100 additions and 14 deletions
@@ -24,5 +24,5 @@ endif()
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
# the component can be registered as WHOLE_ARCHIVE
idf_component_register(SRCS ${srcs}
PRIV_REQUIRES unity esp_driver_mcpwm esp_driver_gpio esp_driver_gptimer esp_psram
PRIV_REQUIRES unity esp_driver_mcpwm esp_driver_gpio esp_driver_gptimer esp_psram esp_timer esp_rom
WHOLE_ARCHIVE)
@@ -7,6 +7,8 @@
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "unity.h"
#include "esp_timer.h"
#include "esp_rom_sys.h"
#include "hal/mcpwm_ll.h"
#include "driver/mcpwm_timer.h"
#include "esp_private/mcpwm.h"
@@ -88,6 +90,72 @@ TEST_CASE("mcpwm_timer_start_stop", "[mcpwm]")
}
}
TEST_CASE("mcpwm_timer_various_clk_src", "[mcpwm]")
{
mcpwm_timer_clock_source_t clk_srcs[] = SOC_MCPWM_TIMER_CLKS;
const int num_timers = MCPWM_LL_GET(TIMERS_PER_GROUP) * MCPWM_LL_GET(GROUP_NUM);
const uint32_t resolution_test_duration_us = 5000;
for (size_t clk_src_idx = 0; clk_src_idx < sizeof(clk_srcs) / sizeof(clk_srcs[0]); clk_src_idx++) {
mcpwm_timer_clock_source_t clk_src = clk_srcs[clk_src_idx];
const uint32_t tolerance_percent = (soc_module_clk_t)clk_src == SOC_MOD_CLK_RC_FAST ? 10 : 1;
mcpwm_timer_config_t config = {
.clk_src = clk_src,
.resolution_hz = 1000000, // 1MHz
.period_ticks = 12000,
.count_mode = MCPWM_TIMER_COUNT_MODE_UP_DOWN,
};
printf("create MCPWM timers with clock source: %d\r\n", clk_src);
mcpwm_timer_handle_t timers[num_timers];
for (int group_id = 0; group_id < MCPWM_LL_GET(GROUP_NUM); group_id++) {
config.group_id = group_id;
for (int timer_id = 0; timer_id < MCPWM_LL_GET(TIMERS_PER_GROUP); timer_id++) {
int timer_index = group_id * MCPWM_LL_GET(TIMERS_PER_GROUP) + timer_id;
TEST_ESP_OK(mcpwm_new_timer(&config, &timers[timer_index]));
}
}
printf("enable timers\r\n");
for (int i = 0; i < num_timers; i++) {
TEST_ESP_OK(mcpwm_timer_enable(timers[i]));
}
printf("check timer resolution\r\n");
for (int i = 0; i < num_timers; i++) {
TEST_ESP_OK(mcpwm_timer_start_stop(timers[i], MCPWM_TIMER_START_NO_STOP));
uint32_t start_count = 0;
uint32_t end_count = 0;
mcpwm_timer_direction_t direction;
TEST_ESP_OK(mcpwm_timer_get_phase(timers[i], &start_count, &direction));
int64_t start_time_us = esp_timer_get_time();
esp_rom_delay_us(resolution_test_duration_us);
TEST_ESP_OK(mcpwm_timer_get_phase(timers[i], &end_count, &direction));
int64_t elapsed_time_us = esp_timer_get_time() - start_time_us;
uint32_t expected_ticks = (uint64_t)config.resolution_hz * elapsed_time_us / 1000000;
uint32_t actual_ticks = end_count - start_count;
uint32_t tolerance_ticks = expected_ticks * tolerance_percent / 100;
TEST_ASSERT_EQUAL(MCPWM_TIMER_DIRECTION_UP, direction);
TEST_ASSERT_UINT32_WITHIN(tolerance_ticks, expected_ticks, actual_ticks);
// make sure the timer has stopped
TEST_ESP_OK(mcpwm_timer_start_stop(timers[i], MCPWM_TIMER_STOP_EMPTY));
vTaskDelay(pdMS_TO_TICKS(10));
check_mcpwm_timer_phase(&timers[i], 1, 0, MCPWM_TIMER_DIRECTION_UP);
}
printf("disable and delete timers\r\n");
for (int i = 0; i < num_timers; i++) {
TEST_ESP_OK(mcpwm_timer_disable(timers[i]));
TEST_ESP_OK(mcpwm_del_timer(timers[i]));
}
}
}
#define TEST_MCPWM_TIMER_EVENT_BIT_FULL (1 << 0)
#define TEST_MCPWM_TIMER_EVENT_BIT_EMPTY (1 << 1)
#define TEST_MCPWM_TIMER_EVENT_BIT_STOP (1 << 2)