From 9155b5928c83d21f337ebf8191bc2bea99936c86 Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Tue, 30 Nov 2021 17:07:40 +0200 Subject: [PATCH 01/10] py/mpprint.h: Add DBG() macro. Debugging macro for developers. --- py/mpprint.h | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/py/mpprint.h b/py/mpprint.h index 0dff9a770ada7..ffb7680a1f37b 100644 --- a/py/mpprint.h +++ b/py/mpprint.h @@ -79,4 +79,44 @@ int mp_printf(const mp_print_t *print, const char *fmt, ...); int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args); #endif +// Debug messages during code developing. +// An approximate hierarchy of debug levels is: +// -1 - SUPPRES all messages in release version +// 0 - the most CRITICAL errors, often requiring a system reset, use message with this level if possible raising an exception +// 1 - ERROR requiring program restart, use message with this level before raising an exception +// 2 - WARNING, something went wrong, but you can fix it with additional operations in code right now +// 3 - INFO, it is interesting and useful for understanding a bug +// 4 - DEBUG, more detaled info, dig deeper +// 5 - TRACE, show algorithm flow +// In real you may use own classification of debug levels. +#define DBG(level, ...) \ + do { \ + if (level <= DBG_LEVEL) { \ + mp_printf(MP_PYTHON_PRINTER, "%d:: ", level); \ + mp_printf(MP_PYTHON_PRINTER, __VA_ARGS__); \ + mp_printf(MP_PYTHON_PRINTER, "\n"); \ + } \ + } while (0); +/* +// How to use: +// Set DBG_LEVEL in developed *.C or *.CPP file +#define DBG_LEVEL 1000 // show all messages +// Add DBG() macro in code, like +void foo() { + DBG(5, "Enter foo()") + ... + int value; + ... + // calculate value + DBG(3, "See a value=%d", value) + ... + DBG(5, "Exit foo()") +} +// See usage in ports/esp32/machine_pwm.c +// It is not a dogma. You may start debugging from level 3. +#define DBG_LEVEL 3 +// Then add DBG(3, ...) and when gets too much messages then change some messages to the next level DBG(4, ...), or DBG(2, ...) +// Then you may change DBG_LEVEL to 2, and finally to -1. +*/ + #endif // MICROPY_INCLUDED_PY_MPPRINT_H From b6d918355589b6dd6e78d2b46ef9643954e70736 Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Thu, 2 Dec 2021 19:31:11 +0200 Subject: [PATCH 02/10] py/mprint.h: Change names to MP_PRN & MP_PRN_LEVEL. --- py/mpprint.h | 59 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/py/mpprint.h b/py/mpprint.h index ffb7680a1f37b..23e677cba1994 100644 --- a/py/mpprint.h +++ b/py/mpprint.h @@ -79,44 +79,55 @@ int mp_printf(const mp_print_t *print, const char *fmt, ...); int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args); #endif -// Debug messages during code developing. -// An approximate hierarchy of debug levels is: -// -1 - SUPPRES all messages in release version -// 0 - the most CRITICAL errors, often requiring a system reset, use message with this level if possible raising an exception -// 1 - ERROR requiring program restart, use message with this level before raising an exception -// 2 - WARNING, something went wrong, but you can fix it with additional operations in code right now -// 3 - INFO, it is interesting and useful for understanding a bug -// 4 - DEBUG, more detaled info, dig deeper -// 5 - TRACE, show algorithm flow +// Debug messages during code developing with MP_PRN(level, ...) & MP_PRN_LEVEL. +// An approximate hierarchy of debug levels (MP_PRN_LEVEL) is: +// 0 - SUPPRES all messages. Use it in release version. +// 1 - The most CRITICAL errors, often requiring a system reset, use message with this level if possible raising an exception. +// 2 - ERROR requiring program restart, use message with this level before raising an exception. +// 3 - WARNING, something went wrong, but you can fix it with additional operations in code right now or may ignore it. +// 4 - INFO, it is interesting and useful for understanding a bug. +// 5 - DEBUG, more detaled info, dig deeper. +// 6 - TRACE, show algorithm flow, like enter/exit a function. // In real you may use own classification of debug levels. -#define DBG(level, ...) \ +#define MP_PRN(level, ...) \ do { \ - if (level <= DBG_LEVEL) { \ - mp_printf(MP_PYTHON_PRINTER, "%d:: ", level); \ - mp_printf(MP_PYTHON_PRINTER, __VA_ARGS__); \ - mp_printf(MP_PYTHON_PRINTER, "\n"); \ + if (MP_PRN_LEVEL > 0) { \ + if ((0 < level) && (level <= MP_PRN_LEVEL)) { \ + mp_printf(MP_PYTHON_PRINTER, "%d:: ", level); \ + mp_printf(MP_PYTHON_PRINTER, __VA_ARGS__); \ + mp_printf(MP_PYTHON_PRINTER, "\n"); \ + } \ } \ } while (0); /* // How to use: -// Set DBG_LEVEL in developed *.C or *.CPP file -#define DBG_LEVEL 1000 // show all messages -// Add DBG() macro in code, like +// Set MP_PRN_LEVEL in developed *.C or *.CPP file, for example + +#define MP_PRN_LEVEL 1000 // show all messages +// Add MP_PRN() macro in code, like void foo() { - DBG(5, "Enter foo()") + MP_PRN(6, "Enter foo()") ... int value; ... // calculate value - DBG(3, "See a value=%d", value) ... - DBG(5, "Exit foo()") + MP_PRN(4, "See a value=%d", value) + ... + MP_PRN(6, "Exit foo()") } -// See usage in ports/esp32/machine_pwm.c + +// See usage in ports/esp32/esp32_pcnt.c and ports/esp32/machine_pwm.c // It is not a dogma. You may start debugging from level 3. -#define DBG_LEVEL 3 -// Then add DBG(3, ...) and when gets too much messages then change some messages to the next level DBG(4, ...), or DBG(2, ...) -// Then you may change DBG_LEVEL to 2, and finally to -1. +#define MP_PRN_LEVEL 3 +// Then add MP_PRN(3, ...) and when gets too much messages then change some messages to the next level MP_PRN(4, ...), or MP_PRN(2, ...) +// Then you may change MP_PRN_LEVEL to 2(reduce printing), and finally to 0(supress printing). +// +// To switching off MP_PRN() from a compiled binary, use +#ifdef MP_PRN + #undef MP_PRN + #define MP_PRN(level, ...) +#endif */ #endif // MICROPY_INCLUDED_PY_MPPRINT_H From 43dade5359844d5819c1bea011a440c20aac18af Mon Sep 17 00:00:00 2001 From: Ihor Nehrutsa Date: Fri, 10 Dec 2021 09:50:20 +0200 Subject: [PATCH 03/10] esp32\machine_pwm.c: HOTFIX PWM duty. --- ports/esp32/machine_pwm.c | 75 +++++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 27 deletions(-) diff --git a/ports/esp32/machine_pwm.c b/ports/esp32/machine_pwm.c index 1cf3bc033a4fc..2dd04120daa35 100644 --- a/ports/esp32/machine_pwm.c +++ b/ports/esp32/machine_pwm.c @@ -31,12 +31,12 @@ #include "py/runtime.h" #include "py/mphal.h" +#include "py/mpprint.h" #include "driver/ledc.h" #include "esp_err.h" -#define PWM_DBG(...) -// #define PWM_DBG(...) mp_printf(&mp_plat_print, __VA_ARGS__); mp_printf(&mp_plat_print, "\n"); +#define MP_PRN_LEVEL 0 // Total number of channels #define PWM_CHANNEL_MAX (LEDC_SPEED_MODE_MAX * LEDC_CHANNEL_MAX) @@ -77,6 +77,23 @@ STATIC ledc_timer_config_t timers[PWM_TIMER_MAX]; // 10-bit resolution (compatible with esp8266 PWM) #define PWRES (LEDC_TIMER_10_BIT) +// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/ledc.html#supported-range-of-frequency-and-duty-resolutions +// duty() uses 10-bit resolution or less +// duty_u16() and duty_ns() use 16-bit resolution or less + +// possible highest resolution in device +#if CONFIG_IDF_TARGET_ESP32 +#define HIGHEST_PWM_RES (LEDC_TIMER_16_BIT) // 20 in fact, but 16 is used +#else +#define HIGHEST_PWM_RES (LEDC_TIMER_14_BIT) +#endif +// duty resolution of user interface in `duty_u16()` and `duty_u16` parameter in constructor/initializer +#define UI_RES_16_BIT (16) +// how much to shift from the HIGHEST_PWM_RES duty resolution to the user interface duty resolution UI_RES_16_BIT +#define UI_RES_SHIFT (16 - HIGHEST_PWM_RES) // 0 for ESP32, 2 for S2, S3, C3 + +// If the PWM frequency is less than EMPIRIC_FREQ, then LEDC_REF_CLK_HZ(1 MHz) is used, else LEDC_APB_CLK_HZ(80 MHz) is used +#define EMPIRIC_FREQ (10) // Hz // Maximum duty value on 10-bit resolution #define MAX_DUTY_U10 ((1 << PWRES) - 1) @@ -205,10 +222,8 @@ STATIC void configure_channel(machine_pwm_obj_t *self) { } STATIC void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_config_t *timer) { - // Even if the timer frequency is already set, - // the set_duty_x() is required to reconfigure the channel duty anyway if (freq != timer->freq_hz) { - PWM_DBG("set_freq(%d)", freq) + MP_PRN(6, "set_freq(%d)", freq) // Find the highest bit resolution for the requested frequency unsigned int i = LEDC_APB_CLK_HZ; // 80 MHz @@ -216,7 +231,7 @@ STATIC void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_conf i = LEDC_REF_CLK_HZ; // 1 MHz } - #if 1 + #if 0 // original code i /= freq; #else @@ -245,6 +260,7 @@ STATIC void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_conf } // Configure the new resolution and frequency + unsigned int save_duty_resolution = timer->duty_resolution; timer->duty_resolution = res; timer->freq_hz = freq; timer->clk_cfg = LEDC_USE_APB_CLK; @@ -256,7 +272,7 @@ STATIC void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_conf esp_err_t err = ledc_timer_config(timer); if (err != ESP_OK) { if (err == ESP_FAIL) { - PWM_DBG(" (timer timer->speed_mode %d, timer->timer_num %d, timer->clk_cfg %d, timer->freq_hz %d, timer->duty_resolution %d) ", timer->speed_mode, timer->timer_num, timer->clk_cfg, timer->freq_hz, timer->duty_resolution); + MP_PRN(2, "timer timer->speed_mode:%d, timer->timer_num:%d, timer->clk_cfg:%d, timer->freq_hz:%d, timer->duty_resolution:%d", timer->speed_mode, timer->timer_num, timer->clk_cfg, timer->freq_hz, timer->duty_resolution); mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("unreachable frequency %d"), freq); } else { check_esp_err(err); @@ -266,15 +282,17 @@ STATIC void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_conf if (self->mode == LEDC_LOW_SPEED_MODE) { check_esp_err(ledc_timer_rst(self->mode, self->timer)); } - } - // Save the same duty cycle when frequency or channel are changed - if (self->duty_x == HIGHEST_PWM_RES) { - set_duty_u16(self, self->duty_u16); - } else if (self->duty_x == PWRES) { - set_duty_u10(self, self->duty_u10); - } else if (self->duty_x == -HIGHEST_PWM_RES) { - set_duty_ns(self, self->duty_ns); + // Save the same duty cycle when frequency is changed + if (save_duty_resolution != timer->duty_resolution) { + if (self->duty_x == HIGHEST_PWM_RES) { + set_duty_u16(self, self->duty_u16); + } else if (self->duty_x == PWRES) { + set_duty_u10(self, self->duty_u10); + } else if (self->duty_x == -HIGHEST_PWM_RES) { + set_duty_ns(self, self->duty_ns); + } + } } } @@ -287,14 +305,12 @@ STATIC int ns_to_duty(machine_pwm_obj_t *self, int ns) { } else if (duty > UI_MAX_DUTY) { duty = UI_MAX_DUTY; } - // PWM_DBG(" ns_to_duty(UI_MAX_DUTY=%d freq_hz=%d duty=%d=%f <- ns=%d) ", UI_MAX_DUTY, timer.freq_hz, duty, (float)ns * UI_MAX_DUTY * timer.freq_hz / 1000000000.0, ns); return duty; } STATIC int duty_to_ns(machine_pwm_obj_t *self, int duty) { ledc_timer_config_t timer = timers[TIMER_IDX(self->mode, self->timer)]; int64_t ns = ((int64_t)duty * 1000000000LL + (int64_t)timer.freq_hz * UI_MAX_DUTY / 2) / ((int64_t)timer.freq_hz * UI_MAX_DUTY); - // PWM_DBG(" duty_to_ns(UI_MAX_DUTY=%d freq_hz=%d duty=%d -> ns=%f=%d) ", UI_MAX_DUTY, timer.freq_hz, duty, (float)duty * 1000000000.0 / ((float)timer.freq_hz * UI_MAX_DUTY), ns); return ns; } @@ -316,23 +332,28 @@ STATIC void set_duty_u16(machine_pwm_obj_t *self, int duty) { if ((duty < 0) || (duty > UI_MAX_DUTY)) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty_u16 must be from 0 to %d"), UI_MAX_DUTY); } - duty >>= HIGHEST_PWM_RES + UI_RES_SHIFT - timers[TIMER_IDX(self->mode, self->timer)].duty_resolution; - int max_duty = (1 << timers[TIMER_IDX(self->mode, self->timer)].duty_resolution) - 1; - if (duty < 0) { - duty = 0; - } else if (duty > max_duty) { - duty = max_duty; - } - check_esp_err(ledc_set_duty(self->mode, self->channel, duty)); + ledc_timer_config_t timer = timers[TIMER_IDX(self->mode, self->timer)]; + int _duty = duty >> (HIGHEST_PWM_RES + UI_RES_SHIFT - timer.duty_resolution); + int max_duty = (1 << timer.duty_resolution) - 1; + if (_duty < 0) { + _duty = 0; + } else if (_duty > max_duty) { + _duty = max_duty; + } + MP_PRN(5, "3| duty:%d, _duty:%d, max_duty:%d", duty, _duty, max_duty) + check_esp_err(ledc_set_duty(self->mode, self->channel, _duty)); check_esp_err(ledc_update_duty(self->mode, self->channel)); /* // Bug: Sometimes duty is not set right now. + // Not a bug. It's a feature. The duty is applied at the beginning of the next signal period. + // Bug: It has been experimentally established that the duty is setted during 2 signal periods, but 1 period is expected. // See https://github.com/espressif/esp-idf/issues/7288 if (duty != get_duty_u16(self)) { - ets_delay_us(100); + MP_PRN(4, "set_duty_u16(%u), get_duty_u16():%u, _duty:%d, duty_resolution:%d, freq_hz:%d", duty, get_duty_u16(self), _duty, timer.duty_resolution, timer.freq_hz); + ets_delay_us(2 * 1000000 / timer.freq_hz); if (duty != get_duty_u16(self)) { - PWM_DBG(" (set_duty_u16(%u) get_duty_u16()=%u duty_resolution=%d) ", duty, get_duty_u16(self), timers[TIMER_IDX(self->mode, self->timer)].duty_resolution); + MP_PRN(3, "set_duty_u16(%u), get_duty_u16():%u, _duty:%d, duty_resolution:%d, freq_hz:%d", duty, get_duty_u16(self), _duty, timer.duty_resolution, timer.freq_hz); } } */ From 9a4691858f5773923f2e4afeb4e1c2bfe81c80a8 Mon Sep 17 00:00:00 2001 From: IhorNehrutsa <70886343+IhorNehrutsa@users.noreply.github.com> Date: Fri, 10 Dec 2021 10:07:29 +0200 Subject: [PATCH 04/10] py\mpprint.h: Change MP_PRN() delimiter to '|'. --- py/mpprint.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/mpprint.h b/py/mpprint.h index 23e677cba1994..c12778c02c359 100644 --- a/py/mpprint.h +++ b/py/mpprint.h @@ -93,7 +93,7 @@ int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args); do { \ if (MP_PRN_LEVEL > 0) { \ if ((0 < level) && (level <= MP_PRN_LEVEL)) { \ - mp_printf(MP_PYTHON_PRINTER, "%d:: ", level); \ + mp_printf(MP_PYTHON_PRINTER, "%d| ", level); \ mp_printf(MP_PYTHON_PRINTER, __VA_ARGS__); \ mp_printf(MP_PYTHON_PRINTER, "\n"); \ } \ From c23d1da28d1b6d8eae183c44d24bd91ebef963c3 Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Sun, 12 Dec 2021 14:09:19 +0200 Subject: [PATCH 05/10] Revert "Merge branch 'DBG_macro' into HOTFIX_esp32_PWM_duty" This reverts commit 584fd3134e4d3755414dd6b5e7de1f4233723088, reversing changes made to 43dade5359844d5819c1bea011a440c20aac18af. --- py/mpprint.h | 51 --------------------------------------------------- 1 file changed, 51 deletions(-) diff --git a/py/mpprint.h b/py/mpprint.h index c12778c02c359..0dff9a770ada7 100644 --- a/py/mpprint.h +++ b/py/mpprint.h @@ -79,55 +79,4 @@ int mp_printf(const mp_print_t *print, const char *fmt, ...); int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args); #endif -// Debug messages during code developing with MP_PRN(level, ...) & MP_PRN_LEVEL. -// An approximate hierarchy of debug levels (MP_PRN_LEVEL) is: -// 0 - SUPPRES all messages. Use it in release version. -// 1 - The most CRITICAL errors, often requiring a system reset, use message with this level if possible raising an exception. -// 2 - ERROR requiring program restart, use message with this level before raising an exception. -// 3 - WARNING, something went wrong, but you can fix it with additional operations in code right now or may ignore it. -// 4 - INFO, it is interesting and useful for understanding a bug. -// 5 - DEBUG, more detaled info, dig deeper. -// 6 - TRACE, show algorithm flow, like enter/exit a function. -// In real you may use own classification of debug levels. -#define MP_PRN(level, ...) \ - do { \ - if (MP_PRN_LEVEL > 0) { \ - if ((0 < level) && (level <= MP_PRN_LEVEL)) { \ - mp_printf(MP_PYTHON_PRINTER, "%d| ", level); \ - mp_printf(MP_PYTHON_PRINTER, __VA_ARGS__); \ - mp_printf(MP_PYTHON_PRINTER, "\n"); \ - } \ - } \ - } while (0); -/* -// How to use: -// Set MP_PRN_LEVEL in developed *.C or *.CPP file, for example - -#define MP_PRN_LEVEL 1000 // show all messages -// Add MP_PRN() macro in code, like -void foo() { - MP_PRN(6, "Enter foo()") - ... - int value; - ... - // calculate value - ... - MP_PRN(4, "See a value=%d", value) - ... - MP_PRN(6, "Exit foo()") -} - -// See usage in ports/esp32/esp32_pcnt.c and ports/esp32/machine_pwm.c -// It is not a dogma. You may start debugging from level 3. -#define MP_PRN_LEVEL 3 -// Then add MP_PRN(3, ...) and when gets too much messages then change some messages to the next level MP_PRN(4, ...), or MP_PRN(2, ...) -// Then you may change MP_PRN_LEVEL to 2(reduce printing), and finally to 0(supress printing). -// -// To switching off MP_PRN() from a compiled binary, use -#ifdef MP_PRN - #undef MP_PRN - #define MP_PRN(level, ...) -#endif -*/ - #endif // MICROPY_INCLUDED_PY_MPPRINT_H From 3f200388347c4ce8c5112b2e706b06c77a69a10c Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Sun, 12 Dec 2021 14:16:53 +0200 Subject: [PATCH 06/10] docs\esp32\quickref.rst: Change order to get, set. --- docs/esp32/quickref.rst | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/docs/esp32/quickref.rst b/docs/esp32/quickref.rst index 97b6fba38d0ef..e74d3d81f891a 100644 --- a/docs/esp32/quickref.rst +++ b/docs/esp32/quickref.rst @@ -224,14 +224,18 @@ Use the :ref:`machine.PWM ` class:: from machine import Pin, PWM pwm0 = PWM(Pin(0)) # create PWM object from a pin - pwm0.freq() # get current frequency (default 5kHz) + freq = pwm0.freq() # get current frequency (default 5kHz) pwm0.freq(1000) # set PWM frequency from 1Hz to 40MHz - pwm0.duty() # get current duty cycle, range 0-1023 (default 512, 50%) + + duty = pwm0.duty() # get current duty cycle, range 0-1023 (default 512, 50%) pwm0.duty(256) # set duty cycle from 0 to 1023 as a ratio duty/1023, (now 25%) + + duty_u16 = pwm0.duty_u16() # get current duty cycle, range 0-65535 pwm0.duty_u16(2**16*3//4) # set duty cycle from 0 to 65535 as a ratio duty_u16/65535, (now 75%) - pwm0.duty_u16() # get current duty cycle, range 0-65535 + + duty_ns = pwm0.duty_ns() # get current pulse width in ns pwm0.duty_ns(250_000) # set pulse width in nanoseconds from 0 to 1_000_000_000/freq, (now 25%) - pwm0.duty_ns() # get current pulse width in ns + pwm0.deinit() # turn off PWM on the pin pwm2 = PWM(Pin(2), freq=20000, duty=512) # create and configure in one go @@ -246,7 +250,7 @@ Number of groups (speed modes) 2 1 Number of timers per group 4 4 4 Number of channels per group 8 8 6 ----------------------------------------------------- -------- -------- -------- -Different of PWM frequencies (groups * timers) 8 4 4 +Different PWM frequencies (groups * timers) 8 4 4 Total PWM channels (Pins, duties) (groups * channels) 16 8 6 ===================================================== ======== ======== ======== @@ -415,14 +419,14 @@ I2S bus See :ref:`machine.I2S `. :: from machine import I2S, Pin - + i2s = I2S(0, sck=Pin(13), ws=Pin(14), sd=Pin(34), mode=I2S.TX, bits=16, format=I2S.STEREO, rate=44100, ibuf=40000) # create I2S object i2s.write(buf) # write buffer of audio samples to I2S device - + i2s = I2S(1, sck=Pin(33), ws=Pin(25), sd=Pin(32), mode=I2S.RX, bits=16, format=I2S.MONO, rate=22050, ibuf=40000) # create I2S object i2s.readinto(buf) # fill buffer with audio samples from I2S device - -The I2S class is currently available as a Technical Preview. During the preview period, feedback from + +The I2S class is currently available as a Technical Preview. During the preview period, feedback from users is encouraged. Based on this feedback, the I2S class API and implementation may be changed. ESP32 has two I2S buses with id=0 and id=1 From bf424d5d2dca75f23bd9f2e8c3d6f28222caa094 Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Sun, 12 Dec 2021 14:20:43 +0200 Subject: [PATCH 07/10] esp32\machine_pwm.c: Remove MP_PRN() macro. --- ports/esp32/machine_pwm.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/ports/esp32/machine_pwm.c b/ports/esp32/machine_pwm.c index 2dd04120daa35..03671ce072d31 100644 --- a/ports/esp32/machine_pwm.c +++ b/ports/esp32/machine_pwm.c @@ -31,13 +31,10 @@ #include "py/runtime.h" #include "py/mphal.h" -#include "py/mpprint.h" #include "driver/ledc.h" #include "esp_err.h" -#define MP_PRN_LEVEL 0 - // Total number of channels #define PWM_CHANNEL_MAX (LEDC_SPEED_MODE_MAX * LEDC_CHANNEL_MAX) @@ -223,7 +220,6 @@ STATIC void configure_channel(machine_pwm_obj_t *self) { STATIC void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_config_t *timer) { if (freq != timer->freq_hz) { - MP_PRN(6, "set_freq(%d)", freq) // Find the highest bit resolution for the requested frequency unsigned int i = LEDC_APB_CLK_HZ; // 80 MHz @@ -272,7 +268,6 @@ STATIC void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_conf esp_err_t err = ledc_timer_config(timer); if (err != ESP_OK) { if (err == ESP_FAIL) { - MP_PRN(2, "timer timer->speed_mode:%d, timer->timer_num:%d, timer->clk_cfg:%d, timer->freq_hz:%d, timer->duty_resolution:%d", timer->speed_mode, timer->timer_num, timer->clk_cfg, timer->freq_hz, timer->duty_resolution); mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("unreachable frequency %d"), freq); } else { check_esp_err(err); @@ -340,7 +335,6 @@ STATIC void set_duty_u16(machine_pwm_obj_t *self, int duty) { } else if (_duty > max_duty) { _duty = max_duty; } - MP_PRN(5, "3| duty:%d, _duty:%d, max_duty:%d", duty, _duty, max_duty) check_esp_err(ledc_set_duty(self->mode, self->channel, _duty)); check_esp_err(ledc_update_duty(self->mode, self->channel)); From 687702ec83d224bb693db3ac5ab040128d3a8911 Mon Sep 17 00:00:00 2001 From: Ihor Nehrutsa Date: Tue, 14 Dec 2021 09:29:25 +0200 Subject: [PATCH 08/10] esp32\machine_pwm.c: Use rounded PWM divisor in ESP-IDF5. PWM frequency will more accuracy. --- ports/esp32/machine_pwm.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ports/esp32/machine_pwm.c b/ports/esp32/machine_pwm.c index 03671ce072d31..7e3fc47b9a8ed 100644 --- a/ports/esp32/machine_pwm.c +++ b/ports/esp32/machine_pwm.c @@ -227,13 +227,12 @@ STATIC void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_conf i = LEDC_REF_CLK_HZ; // 1 MHz } - #if 0 + #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) // original code i /= freq; #else // See https://github.com/espressif/esp-idf/issues/7722 - unsigned int divider = i / freq; // truncated - // int divider = (i + freq / 2) / freq; // rounded + int divider = (i + freq / 2) / freq; // rounded if (divider == 0) { divider = 1; } From 4ad2f32f5ab9d0bf63efb7432df4d487cc7cfd98 Mon Sep 17 00:00:00 2001 From: Ihor Nehrutsa Date: Tue, 14 Dec 2021 09:43:35 +0200 Subject: [PATCH 09/10] esp32\machine_pwm.c: Restore PWM_DBG. --- ports/esp32/machine_pwm.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ports/esp32/machine_pwm.c b/ports/esp32/machine_pwm.c index 7e3fc47b9a8ed..9a74befb732fc 100644 --- a/ports/esp32/machine_pwm.c +++ b/ports/esp32/machine_pwm.c @@ -35,6 +35,9 @@ #include "driver/ledc.h" #include "esp_err.h" +#define PWM_DBG(...) +// #define PWM_DBG(...) mp_printf(&mp_plat_print, __VA_ARGS__); mp_printf(&mp_plat_print, "\n"); + // Total number of channels #define PWM_CHANNEL_MAX (LEDC_SPEED_MODE_MAX * LEDC_CHANNEL_MAX) @@ -343,10 +346,10 @@ STATIC void set_duty_u16(machine_pwm_obj_t *self, int duty) { // Bug: It has been experimentally established that the duty is setted during 2 signal periods, but 1 period is expected. // See https://github.com/espressif/esp-idf/issues/7288 if (duty != get_duty_u16(self)) { - MP_PRN(4, "set_duty_u16(%u), get_duty_u16():%u, _duty:%d, duty_resolution:%d, freq_hz:%d", duty, get_duty_u16(self), _duty, timer.duty_resolution, timer.freq_hz); + PWM_DBG("set_duty_u16(%u), get_duty_u16():%u, _duty:%d, duty_resolution:%d, freq_hz:%d", duty, get_duty_u16(self), _duty, timer.duty_resolution, timer.freq_hz); ets_delay_us(2 * 1000000 / timer.freq_hz); if (duty != get_duty_u16(self)) { - MP_PRN(3, "set_duty_u16(%u), get_duty_u16():%u, _duty:%d, duty_resolution:%d, freq_hz:%d", duty, get_duty_u16(self), _duty, timer.duty_resolution, timer.freq_hz); + PWM_DBG("set_duty_u16(%u), get_duty_u16():%u, _duty:%d, duty_resolution:%d, freq_hz:%d", duty, get_duty_u16(self), _duty, timer.duty_resolution, timer.freq_hz); } } */ From 36918b43abafa1fae3d562c648c564a9dc12deb7 Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Wed, 15 Dec 2021 12:30:53 +0200 Subject: [PATCH 10/10] esp32/machine_pwm.c: Fix code review. --- ports/esp32/machine_pwm.c | 41 ++++++++++++--------------------------- 1 file changed, 12 insertions(+), 29 deletions(-) diff --git a/ports/esp32/machine_pwm.c b/ports/esp32/machine_pwm.c index 9a74befb732fc..091eeca489a3d 100644 --- a/ports/esp32/machine_pwm.c +++ b/ports/esp32/machine_pwm.c @@ -77,23 +77,6 @@ STATIC ledc_timer_config_t timers[PWM_TIMER_MAX]; // 10-bit resolution (compatible with esp8266 PWM) #define PWRES (LEDC_TIMER_10_BIT) -// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/ledc.html#supported-range-of-frequency-and-duty-resolutions -// duty() uses 10-bit resolution or less -// duty_u16() and duty_ns() use 16-bit resolution or less - -// possible highest resolution in device -#if CONFIG_IDF_TARGET_ESP32 -#define HIGHEST_PWM_RES (LEDC_TIMER_16_BIT) // 20 in fact, but 16 is used -#else -#define HIGHEST_PWM_RES (LEDC_TIMER_14_BIT) -#endif -// duty resolution of user interface in `duty_u16()` and `duty_u16` parameter in constructor/initializer -#define UI_RES_16_BIT (16) -// how much to shift from the HIGHEST_PWM_RES duty resolution to the user interface duty resolution UI_RES_16_BIT -#define UI_RES_SHIFT (16 - HIGHEST_PWM_RES) // 0 for ESP32, 2 for S2, S3, C3 - -// If the PWM frequency is less than EMPIRIC_FREQ, then LEDC_REF_CLK_HZ(1 MHz) is used, else LEDC_APB_CLK_HZ(80 MHz) is used -#define EMPIRIC_FREQ (10) // Hz // Maximum duty value on 10-bit resolution #define MAX_DUTY_U10 ((1 << PWRES) - 1) @@ -102,17 +85,17 @@ STATIC ledc_timer_config_t timers[PWM_TIMER_MAX]; // duty_u16() and duty_ns() use 16-bit resolution or less // Possible highest resolution in device -#if CONFIG_IDF_TARGET_ESP32 -#define HIGHEST_PWM_RES (LEDC_TIMER_16_BIT) // 20 bit in fact, but 16 bit is used +#if (LEDC_TIMER_BIT_MAX - 1) < LEDC_TIMER_16_BIT +#define HIGHEST_PWM_RES (LEDC_TIMER_BIT_MAX - 1) #else -#define HIGHEST_PWM_RES (LEDC_TIMER_14_BIT) +#define HIGHEST_PWM_RES (LEDC_TIMER_16_BIT) // 20 bit for ESP32, but 16 bit is used #endif // Duty resolution of user interface in `duty_u16()` and `duty_u16` parameter in constructor/initializer #define UI_RES_16_BIT (16) // Maximum duty value on highest user interface resolution #define UI_MAX_DUTY ((1 << UI_RES_16_BIT) - 1) // How much to shift from the HIGHEST_PWM_RES duty resolution to the user interface duty resolution UI_RES_16_BIT -#define UI_RES_SHIFT (16 - HIGHEST_PWM_RES) // 0 for ESP32, 2 for S2, S3, C3 +#define UI_RES_SHIFT (UI_RES_16_BIT - HIGHEST_PWM_RES) // 0 for ESP32, 2 for S2, S3, C3 // If the PWM frequency is less than EMPIRIC_FREQ, then LEDC_REF_CLK_HZ(1 MHz) source is used, else LEDC_APB_CLK_HZ(80 MHz) source is used #define EMPIRIC_FREQ (10) // Hz @@ -330,14 +313,14 @@ STATIC void set_duty_u16(machine_pwm_obj_t *self, int duty) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty_u16 must be from 0 to %d"), UI_MAX_DUTY); } ledc_timer_config_t timer = timers[TIMER_IDX(self->mode, self->timer)]; - int _duty = duty >> (HIGHEST_PWM_RES + UI_RES_SHIFT - timer.duty_resolution); + int channel_duty = duty >> (HIGHEST_PWM_RES + UI_RES_SHIFT - timer.duty_resolution); int max_duty = (1 << timer.duty_resolution) - 1; - if (_duty < 0) { - _duty = 0; - } else if (_duty > max_duty) { - _duty = max_duty; + if (channel_duty < 0) { + channel_duty = 0; + } else if (channel_duty > max_duty) { + channel_duty = max_duty; } - check_esp_err(ledc_set_duty(self->mode, self->channel, _duty)); + check_esp_err(ledc_set_duty(self->mode, self->channel, channel_duty)); check_esp_err(ledc_update_duty(self->mode, self->channel)); /* @@ -346,10 +329,10 @@ STATIC void set_duty_u16(machine_pwm_obj_t *self, int duty) { // Bug: It has been experimentally established that the duty is setted during 2 signal periods, but 1 period is expected. // See https://github.com/espressif/esp-idf/issues/7288 if (duty != get_duty_u16(self)) { - PWM_DBG("set_duty_u16(%u), get_duty_u16():%u, _duty:%d, duty_resolution:%d, freq_hz:%d", duty, get_duty_u16(self), _duty, timer.duty_resolution, timer.freq_hz); + PWM_DBG("set_duty_u16(%u), get_duty_u16():%u, channel_duty:%d, duty_resolution:%d, freq_hz:%d", duty, get_duty_u16(self), channel_duty, timer.duty_resolution, timer.freq_hz); ets_delay_us(2 * 1000000 / timer.freq_hz); if (duty != get_duty_u16(self)) { - PWM_DBG("set_duty_u16(%u), get_duty_u16():%u, _duty:%d, duty_resolution:%d, freq_hz:%d", duty, get_duty_u16(self), _duty, timer.duty_resolution, timer.freq_hz); + PWM_DBG("set_duty_u16(%u), get_duty_u16():%u, channel_duty:%d, duty_resolution:%d, freq_hz:%d", duty, get_duty_u16(self), channel_duty, timer.duty_resolution, timer.freq_hz); } } */ pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy