8000 esp32: Fix machine.PWM on IDF <5.2. by projectgus · Pull Request #16127 · micropython/micropython · GitHub
[go: up one dir, main page]

Skip to content

esp32: Fix machine.PWM on IDF <5.2. #16127

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 5, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 50 additions & 2 deletions ports/esp32/machine_pwm.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,12 @@
#include "py/mphal.h"
#include "driver/ledc.h"
#include "esp_err.h"
#include "esp_clk_tree.h"
#include "soc/gpio_sig_map.h"

#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
#include "esp_clk_tree.h"
#endif

#define PWM_DBG(...)
// #define PWM_DBG(...) mp_printf(&mp_plat_print, __VA_ARGS__); mp_printf(&mp_plat_print, "\n");

Expand Down Expand Up @@ -208,7 +211,41 @@ static void configure_channel(machine_pwm_obj_t *self) {
}
}

// Temporary workaround for ledc_find_suitable_duty_resolution function only being added in IDF V5.2
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 2, 0)
static uint32_t ledc_find_suitable_duty_resolution(uint32_t src_clk_freq, uint32_t timer_freq) {
// This implementation is based on the one used in Micropython v1.23

// Find the highest bit resolution for the requested frequency
unsigned int freq = src_clk_freq;

int divider = (freq + timer_freq / 2) / timer_freq; // rounded
if (divider == 0) {
divider = 1;
}
float f = (float)freq / divider; // actual frequency
if (f <= 1.0) {
f = 1.0;
}
freq = (unsigned int)roundf((float)freq / f);

unsigned int res = 0;
for (; freq > 1; freq >>= 1) {
++res;
}
if (res == 0) {
res = 1;
} else if (res > HIGHEST_PWM_RES) {
// Limit resolution to HIGHEST_PWM_RES to match units of our duty
res = HIGHEST_PWM_RES;
}

return res;
}
#endif

static void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_config_t *timer) {
esp_err_t err;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason this was moved here, instead of just keeping it as it was, defined at the place of use below?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the first place of use is now inside an #ifdef block.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see, I missed that it's also used for ledc_timer_config().

I would have put this within the if (freq != timer->freq_hz) block to keep its scope limited, but here is also fine considering it's a generic error return variable.

if (freq != timer->freq_hz) {
// Configure the new frequency and resolution
timer->freq_hz = freq;
Expand All @@ -228,10 +265,21 @@ static void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_conf
}
#endif
uint32_t src_clk_freq = 0;
esp_err_t err = esp_clk_tree_src_get_freq_hz(timer->clk_cfg, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &src_clk_freq);
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
err = esp_clk_tree_src_get_freq_hz(timer->clk_cfg, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &src_clk_freq);
if (err != ESP_OK) {
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("unable to query source clock frequency %d"), (int)timer->clk_cfg);
}
#else
// Simplified fallback logic for IDF V5.0.x, for targets with APB only.
src_clk_freq = APB_CLK_FREQ; // 80 MHz
#if SOC_LEDC_SUPPORT_REF_TICK
if (timer->clk_cfg == LEDC_USE_REF_TICK) {
src_clk_freq = REF_CLK_FREQ; // 1 MHz
}
#endif // SOC_LEDC_SUPPORT_REF_TICK
#endif // ESP_IDF_VERSION

timer->duty_resolution = ledc_find_suitable_duty_resolution(src_clk_freq, timer->freq_hz);

// Set frequency
Expand Down
Loading
0