diff --git a/docs/esp32/quickref.rst b/docs/esp32/quickref.rst index 3153ebd57130f..e8931ce825815 100644 --- a/docs/esp32/quickref.rst +++ b/docs/esp32/quickref.rst @@ -17,6 +17,7 @@ working with this board it may be useful to get an overview of the microcontroll general.rst tutorial/intro.rst + tutorial/index.rst Installing MicroPython ---------------------- @@ -225,13 +226,30 @@ Use the ``machine.PWM`` class:: from machine import Pin, PWM pwm0 = PWM(Pin(0)) # create PWM object from a pin - pwm0.freq() # get current frequency + pwm0.freq() # get current frequency (default 5kHz) pwm0.freq(1000) # set frequency - pwm0.duty() # get current duty cycle + pwm0.duty() # get current duty cycle (default 512 (50%)) pwm0.duty(200) # set duty cycle pwm0.deinit() # turn off PWM on the pin - pwm2 = PWM(Pin(2), freq=20000, duty=512) # create and configure in one go + pwm2 = PWM(Pin(2), freq=20000, duty=512) # create and configure in one go + +ESP chips have different hardware peripherals: + +===================================================== ======== ======== ======== +Hardware specification ESP32 ESP32-S2 ESP32-C3 +----------------------------------------------------- -------- -------- -------- +Number of groups (speed modes) 2 1 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 +Total PWM channels (Pins, duties) (groups * channels) 16 8 6 +===================================================== ======== ======== ======== + +The maximum number of PWM channels (Pins) are available on ESP32 chip - 16 channels, +but only 8 different PWM frequencies are available, the remaining 8 channels must have the same frequency. +On the other hand, 16 independent PWM duty cycles are possible at the same frequency. ADC (analog to digital conversion) ---------------------------------- @@ -391,14 +409,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 diff --git a/docs/esp32/tutorial/index.rst b/docs/esp32/tutorial/index.rst new file mode 100644 index 0000000000000..2a68b328cbc1e --- /dev/null +++ b/docs/esp32/tutorial/index.rst @@ -0,0 +1,22 @@ +.. _esp32_tutorial: + +MicroPython tutorial for ESP32 +================================ + +This tutorial is intended to get you started using MicroPython on the ESP32 +system-on-a-chip. If it is your first time it is recommended to follow the +tutorial through in the order below. Otherwise the sections are mostly self +contained, so feel free to skip to those that interest you. + +The tutorial does not assume that you know Python, but it also does not attempt +to explain any of the details of the Python language. Instead it provides you +with commands that are ready to run, and hopes that you will gain a bit of +Python knowledge along the way. To learn more about Python itself please refer +to ``__. + +.. toctree:: + :maxdepth: 1 + :numbered: + + intro.rst + pwm.rst diff --git a/docs/esp32/tutorial/pwm.rst b/docs/esp32/tutorial/pwm.rst new file mode 100644 index 0000000000000..fd25185573586 --- /dev/null +++ b/docs/esp32/tutorial/pwm.rst @@ -0,0 +1,50 @@ +.. _esp32_pwm: + +Pulse Width Modulation +====================== + +Pulse width modulation (PWM) is a way to get an artificial analog output on a +digital pin. It achieves this by rapidly toggling the pin from low to high. +There are two parameters associated with this: the frequency of the toggling, +and the duty cycle. The duty cycle is defined to be how long the pin is high +compared with the length of a single period (low plus high time). Maximum +duty cycle is when the pin is high all of the time, and minimum is when it is +low all of the time. + +More comprehensive example with all 16 PWM channels and 8 timers:: + + from machine import Pin, PWM + try: + f = 100 # Hz + d = 1024 // 16 # 6.25% + pins = (15, 2, 4, 16, 18, 19, 22, 23, 25, 26, 27, 14 , 12, 13, 32, 33) + pwms = [] + for i, pin in enumerate(pins): + pwms.append(PWM(Pin(pin), freq=f * (i // 2 + 1), duty= 1023 if i==15 else d * (i + 1))) + print(pwms[i]) + finally: + for pwm in pwms: + try: + pwm.deinit() + except: + pass + +Output is:: + + PWM(pin=15, freq=100, duty=64, resolution=10, mode=0, channel=0, timer=0) + PWM(pin=2, freq=100, duty=128, resolution=10, mode=0, channel=1, timer=0) + PWM(pin=4, freq=200, duty=192, resolution=10, mode=0, channel=2, timer=1) + PWM(pin=16, freq=200, duty=256, resolution=10, mode=0, channel=3, timer=1) + PWM(pin=18, freq=300, duty=320, resolution=10, mode=0, channel=4, timer=2) + PWM(pin=19, freq=300, duty=384, resolution=10, mode=0, channel=5, timer=2) + PWM(pin=22, freq=400, duty=448, resolution=10, mode=0, channel=6, timer=3) + PWM(pin=23, freq=400, duty=512, resolution=10, mode=0, channel=7, timer=3) + PWM(pin=25, freq=500, duty=576, resolution=10, mode=1, channel=0, timer=0) + PWM(pin=26, freq=500, duty=640, resolution=10, mode=1, channel=1, timer=0) + PWM(pin=27, freq=600, duty=704, resolution=10, mode=1, channel=2, timer=1) + PWM(pin=14, freq=600, duty=768, resolution=10, mode=1, channel=3, timer=1) + PWM(pin=12, freq=700, duty=832, resolution=10, mode=1, channel=4, timer=2) + PWM(pin=13, freq=700, duty=896, resolution=10, mode=1, channel=5, timer=2) + PWM(pin=32, freq=800, duty=960, resolution=10, mode=1, channel=6, timer=3) + PWM(pin=33, freq=800, duty=1023, resolution=10, mode=1, channel=7, timer=3) + diff --git a/ports/esp32/machine_pwm.c b/ports/esp32/machine_pwm.c index 9fe06aa6999d3..41b8dbcbfe5b9 100644 --- a/ports/esp32/machine_pwm.c +++ b/ports/esp32/machine_pwm.c @@ -3,7 +3,10 @@ * * The MIT License (MIT) * - * Copyright (c) 2016 Damien P. George + * Copyright (c) 2016-2021 Damien P. George + * Copyright (c) 2018 Alan Dragomirecky + * Copyright (c) 2020 Antoine Aubert + * Copyright (c) 2021 Ihor Nehrutsa * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -30,54 +33,105 @@ #include "driver/ledc.h" #include "esp_err.h" -// Which channel has which GPIO pin assigned? -// (-1 if not assigned) -STATIC int chan_gpio[LEDC_CHANNEL_MAX]; +#define PWM_DBG(...) +// #define PWM_DBG(...) mp_printf(&mp_plat_print, __VA_ARGS__) + +// Total number of channels +#define PWM_CHANNEL_MAX (LEDC_SPEED_MODE_MAX * LEDC_CHANNEL_MAX) +typedef struct _chan_t { + // Which channel has which GPIO pin assigned? + // (-1 if not assigned) + gpio_num_t pin; + // Which channel has which timer assigned? + // (-1 if not assigned) + int timer_idx; +} chan_t; +// List of PWM channels +STATIC chan_t chans[PWM_CHANNEL_MAX]; + +// channel_idx is an index (end-to-end sequential numbering) for all channels +// available on the chip and described in chans[] +#define CHANNEL_IDX(mode, channel) (mode * LEDC_CHANNEL_MAX + channel) +#define CHANNEL_IDX_TO_MODE(channel_idx) (channel_idx / LEDC_CHANNEL_MAX) +#define CHANNEL_IDX_TO_CHANNEL(channel_idx) (channel_idx % LEDC_CHANNEL_MAX) + +// Total number of timers +#define PWM_TIMER_MAX (LEDC_SPEED_MODE_MAX * LEDC_TIMER_MAX) +// List of timer configs +STATIC ledc_timer_config_t timers[PWM_TIMER_MAX]; + +// timer_idx is an index (end-to-end sequential numbering) for all timers +// available on the chip and configured in timers[] +#define TIMER_IDX(mode, timer) (mode * LEDC_TIMER_MAX + timer) +#define TIMER_IDX_TO_MODE(timer_idx) (timer_idx / LEDC_TIMER_MAX) +#define TIMER_IDX_TO_TIMER(timer_idx) (timer_idx % LEDC_TIMER_MAX) // Params for PW operation -// 5khz +// 5khz is default frequency #define PWFREQ (5000) -// High speed mode -#if CONFIG_IDF_TARGET_ESP32 -#define PWMODE (LEDC_HIGH_SPEED_MODE) -#else -#define PWMODE (LEDC_LOW_SPEED_MODE) -#endif + // 10-bit resolution (compatible with esp8266 PWM) #define PWRES (LEDC_TIMER_10_BIT) -// Timer 1 -#define PWTIMER (LEDC_TIMER_1) // Config of timer upon which we run all PWM'ed GPIO pins STATIC bool pwm_inited = false; -STATIC ledc_timer_config_t timer_cfg = { - .duty_resolution = PWRES, - .freq_hz = PWFREQ, - .speed_mode = PWMODE, - .timer_num = PWTIMER -}; -STATIC void pwm_init(void) { +// MicroPython PWM object struct +typedef struct _machine_pwm_obj_t { + mp_obj_base_t base; + gpio_num_t pin; + bool active; + int mode; + int channel; + int timer; +} machine_pwm_obj_t; +STATIC void pwm_init(void) { // Initial condition: no channels assigned - for (int x = 0; x < LEDC_CHANNEL_MAX; ++x) { - chan_gpio[x] = -1; + for (int i = 0; i < PWM_CHANNEL_MAX; ++i) { + chans[i].pin = -1; + chans[i].timer_idx = -1; } - // Init with default timer params - ledc_timer_config(&timer_cfg); + // Prepare all timers config + // Initial condition: no timers assigned + for (int i = 0; i < PWM_TIMER_MAX; ++i) { + timers[i].duty_resolution = PWRES; + // unset timer is -1 + timers[i].freq_hz = -1; + timers[i].speed_mode = TIMER_IDX_TO_MODE(i); + timers[i].timer_num = TIMER_IDX_TO_TIMER(i); + timers[i].clk_cfg = LEDC_AUTO_CLK; + } } -STATIC int set_freq(int newval) { - int ores = timer_cfg.duty_resolution; - int oval = timer_cfg.freq_hz; +STATIC void configure_channel(machine_pwm_obj_t *self) { + ledc_channel_config_t cfg = { + .channel = self->channel, + .duty = (1 << (timers[TIMER_IDX(self->mode, self->timer)].duty_resolution)) / 2, + .gpio_num = self->pin, + .intr_type = LEDC_INTR_DISABLE, + .speed_mode = self->mode, + .timer_sel = self->timer, + }; + if (ledc_channel_config(&cfg) != ESP_OK) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("PWM not supported on Pin(%d)"), self->pin); + } +} + +STATIC void set_freq(int newval, ledc_timer_config_t *timer) { + // If already set, do nothing + if (newval == timer->freq_hz) { + return; + } // Find the highest bit resolution for the requested frequency if (newval <= 0) { newval = 1; } unsigned int res = 0; - for (unsigned int i = LEDC_APB_CLK_HZ / newval; i > 1; i >>= 1, ++res) { + for (unsigned int i = LEDC_APB_CLK_HZ / newval; i > 1; i >>= 1) { + ++res; } if (res == 0) { res = 1; @@ -87,32 +141,113 @@ STATIC int set_freq(int newval) { } // Configure the new resolution and frequency - timer_cfg.duty_resolution = res; - timer_cfg.freq_hz = newval; - if (ledc_timer_config(&timer_cfg) != ESP_OK) { - timer_cfg.duty_resolution = ores; - timer_cfg.freq_hz = oval; - return 0; + timer->duty_resolution = res; + timer->freq_hz = newval; + + // set freq + 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_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("bad frequency %d"), newval); + } else { + check_esp_err(err); + } } - return 1; +} + +STATIC int get_duty(machine_pwm_obj_t *self) { + uint32_t duty = ledc_get_duty(self->mode, self->channel); + duty <<= PWRES - timers[TIMER_IDX(self->mode, self->timer)].duty_resolution; + return duty; +} + +STATIC void set_duty(machine_pwm_obj_t *self, int duty) { + if ((duty < 0) || (duty > (1 << PWRES) - 1)) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty must be between 0 and %u"), (1 << PWRES) - 1); + } + duty &= (1 << PWRES) - 1; + duty >>= PWRES - timers[TIMER_IDX(self->mode, self->timer)].duty_resolution; + check_esp_err(ledc_set_duty(self->mode, self->channel, duty)); + check_esp_err(ledc_update_duty(self->mode, self->channel)); + // check_esp_err(ledc_set_duty_and_update(self->mode, self->channel, duty, (1 << PWRES) - 1)); // thread safe function ??? + + // Bug: Sometimes duty is not set right now. + // See https://github.com/espressif/esp-idf/issues/7288 + /* + if (duty != get_duty(self)) { + PWM_DBG("\n duty_set %u %u %d %d \n", duty, get_duty(self), PWRES, timers[TIMER_IDX(self->mode, self->timer)].duty_resolution); + } + */ } /******************************************************************************/ -// MicroPython bindings for PWM +#define SAME_FREQ_ONLY (true) +#define SAME_FREQ_OR_FREE (false) +#define ANY_MODE (-1) +// Return timer_idx. Use TIMER_IDX_TO_MODE(timer_idx) and TIMER_IDX_TO_TIMER(timer_idx) to get mode and timer +STATIC int find_timer(int freq, bool same_freq_only, int mode) { + int free_timer_idx_found = -1; + // Find a free PWM Timer using the same freq + for (int timer_idx = 0; timer_idx < PWM_TIMER_MAX; ++timer_idx) { + if ((mode == ANY_MODE) || (mode == TIMER_IDX_TO_MODE(timer_idx))) { + if (timers[timer_idx].freq_hz == freq) { + // A timer already uses the same freq. Use it now. + return timer_idx; + } + if (!same_freq_only && (free_timer_idx_found == -1) && (timers[timer_idx].freq_hz == -1)) { + free_timer_idx_found = timer_idx; + // Continue to check if a channel with the same freq is in use. + } + } + } -typedef struct _machine_pwm_obj_t { - mp_obj_base_t base; - gpio_num_t pin; - uint8_t active; - uint8_t channel; -} machine_pwm_obj_t; + return free_timer_idx_found; +} + +// Return true if the timer is in use in addition to current channel +STATIC bool is_timer_in_use(int current_channel_idx, int timer_idx) { + for (int i = 0; i < PWM_CHANNEL_MAX; ++i) { + if ((i != current_channel_idx) && (chans[i].timer_idx == timer_idx)) { + return true; + } + } + + return false; +} + +// Find a free PWM channel, also spot if our pin is already mentioned. +// Return channel_idx. Use CHANNEL_IDX_TO_MODE(channel_idx) and CHANNEL_IDX_TO_CHANNEL(channel_idx) to get mode and channel +STATIC int find_channel(int pin, int mode) { + int avail_idx = -1; + int channel_idx; + for (channel_idx = 0; channel_idx < PWM_CHANNEL_MAX; ++channel_idx) { + if ((mode == ANY_MODE) || (mode == CHANNEL_IDX_TO_MODE(channel_idx))) { + if (chans[channel_idx].pin == pin) { + break; + } + if ((avail_idx == -1) && (chans[channel_idx].pin == -1)) { + avail_idx = channel_idx; + } + } + } + if (channel_idx >= PWM_CHANNEL_MAX) { + channel_idx = avail_idx; + } + return channel_idx; +} + +/******************************************************************************/ +// MicroPython bindings for PWM STATIC void mp_machine_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { machine_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_printf(print, "PWM(%u", self->pin); + mp_printf(print, "PWM(pin=%u", self->pin); if (self->active) { - mp_printf(print, ", freq=%u, duty=%u", timer_cfg.freq_hz, - ledc_get_duty(PWMODE, self->channel)); + int duty = get_duty(self); + mp_printf(print, ", freq=%u, duty=%u", ledc_get_freq(self->mode, self->timer), duty); + mp_printf(print, ", resolution=%u", timers[TIMER_IDX(self->mode, self->timer)].duty_resolution); + mp_printf(print, ", mode=%d, channel=%d, timer=%d", self->mode, self->channel, self->timer); } mp_printf(print, ")"); } @@ -128,61 +263,72 @@ STATIC void mp_machine_pwm_init_helper(machine_pwm_obj_t *self, mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - int channel; - int avail = -1; + int channel_idx = find_channel(self->pin, ANY_MODE); + if (channel_idx == -1) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("out of PWM channels:%d"), PWM_CHANNEL_MAX); // in all modes + } - // Find a free PWM channel, also spot if our pin is - // already mentioned. - for (channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) { - if (chan_gpio[channel] == self->pin) { - break; + int freq = args[ARG_freq].u_int; + if ((freq < -1) || (freq > 40000000)) { + mp_raise_ValueError(MP_ERROR_TEXT("freqency must be between 1Hz and 40MHz")); + } + // Check if freq wasn't passed as an argument + if (freq == -1) { + // Check if already set, otherwise use the default freq. + // Possible case: + // pwm = PWM(pin, freq=1000, duty=256) + // pwm = PWM(pin, duty=128) + if (chans[channel_idx].timer_idx != -1) { + freq = timers[chans[channel_idx].timer_idx].freq_hz; } - if ((avail == -1) && (chan_gpio[channel] == -1)) { - avail = channel; + if (freq < 0) { + freq = PWFREQ; } } - if (channel >= LEDC_CHANNEL_MAX) { - if (avail == -1) { - mp_raise_ValueError(MP_ERROR_TEXT("out of PWM channels")); - } - channel = avail; + + int timer_idx = find_timer(freq, SAME_FREQ_OR_FREE, CHANNEL_IDX_TO_MODE(channel_idx)); + if (timer_idx == -1) { + timer_idx = find_timer(freq, SAME_FREQ_OR_FREE, ANY_MODE); + } + if (timer_idx == -1) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("out of PWM timers:%d"), PWM_TIMER_MAX); // in all modes } - self->channel = channel; - // New PWM assignment - self->active = 1; - if (chan_gpio[channel] == -1) { - ledc_channel_config_t cfg = { - .channel = channel, - .duty = (1 << timer_cfg.duty_resolution) / 2, - .gpio_num = self->pin, - .intr_type = LEDC_INTR_DISABLE, - .speed_mode = PWMODE, - .timer_sel = PWTIMER, - }; - if (ledc_channel_config(&cfg) != ESP_OK) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("PWM not supported on pin %d"), self->pin); + int mode = TIMER_IDX_TO_MODE(timer_idx); + if (CHANNEL_IDX_TO_MODE(channel_idx) != mode) { + // unregister old channel + chans[channel_idx].pin = -1; + chans[channel_idx].timer_idx = -1; + // find new channel + channel_idx = find_channel(self->pin, mode); + if (CHANNEL_IDX_TO_MODE(channel_idx) != mode) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("out of PWM channels:%d"), PWM_CHANNEL_MAX); // in current mode } - chan_gpio[channel] = self->pin; } + self->mode = mode; + self->timer = TIMER_IDX_TO_TIMER(timer_idx); + self->channel = CHANNEL_IDX_TO_CHANNEL(channel_idx); - // Maybe change PWM timer - int tval = args[ARG_freq].u_int; - if (tval != -1) { - if (tval != timer_cfg.freq_hz) { - if (!set_freq(tval)) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("bad frequency %d"), tval); - } - } + // New PWM assignment + if ((chans[channel_idx].pin == -1) || (chans[channel_idx].timer_idx != timer_idx)) { + configure_channel(self); + chans[channel_idx].pin = self->pin; } + chans[channel_idx].timer_idx = timer_idx; + self->active = true; + + // Set timer frequency + set_freq(freq, &timers[timer_idx]); // Set duty cycle? - int dval = args[ARG_duty].u_int; - if (dval != -1) { - dval &= ((1 << PWRES) - 1); - dval >>= PWRES - timer_cfg.duty_resolution; - ledc_set_duty(PWMODE, channel, dval); - ledc_update_duty(PWMODE, channel); + int duty = args[ARG_duty].u_int; + if (duty != -1) { + set_duty(self, duty); + } + + // Reset the timer if low speed + if (self->mode == LEDC_LOW_SPEED_MODE) { + check_esp_err(ledc_timer_rst(self->mode, self->timer)); } } @@ -195,8 +341,10 @@ STATIC mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, machine_pwm_obj_t *self = m_new_obj(machine_pwm_obj_t); self->base.type = &machine_pwm_type; self->pin = pin_id; - self->active = 0; + self->active = false; + self->mode = -1; self->channel = -1; + self->timer = -1; // start the PWM subsystem if it's not already running if (!pwm_inited) { @@ -213,38 +361,99 @@ STATIC mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, } STATIC void mp_machine_pwm_deinit(machine_pwm_obj_t *self) { - int chan = self->channel; + int chan = CHANNEL_IDX(self->mode, self->channel); // Valid channel? - if ((chan >= 0) && (chan < LEDC_CHANNEL_MAX)) { + if ((chan >= 0) && (chan < PWM_CHANNEL_MAX)) { + // Clean up timer if necessary + if (!is_timer_in_use(chan, chans[chan].timer_idx)) { + check_esp_err(ledc_timer_rst(self->mode, self->timer)); + // Flag it unused + timers[chans[chan].timer_idx].freq_hz = -1; + } + // Mark it unused, and tell the hardware to stop routing - chan_gpio[chan] = -1; - ledc_stop(PWMODE, chan, 0); - self->active = 0; + check_esp_err(ledc_stop(self->mode, chan, 0)); + // Disable ledc signal for the pin + // gpio_matrix_out(self->pin, SIG_GPIO_OUT_IDX, false, false); + if (self->mode == LEDC_LOW_SPEED_MODE) { + gpio_matrix_out(self->pin, LEDC_LS_SIG_OUT0_IDX + self->channel, false, true); + } else { + #if LEDC_SPEED_MODE_MAX > 1 + #if CONFIG_IDF_TARGET_ESP32 + gpio_matrix_out(self->pin, LEDC_HS_SIG_OUT0_IDX + self->channel, false, true); + #else + #error Add supported CONFIG_IDF_TARGET_ESP32_xxx + #endif + #endif + } + chans[chan].pin = -1; + chans[chan].timer_idx = -1; + self->active = false; + self->mode = -1; self->channel = -1; - gpio_matrix_out(self->pin, SIG_GPIO_OUT_IDX, false, false); + self->timer = -1; } } STATIC mp_obj_t mp_machine_pwm_freq_get(machine_pwm_obj_t *self) { - return MP_OBJ_NEW_SMALL_INT(timer_cfg.freq_hz); + return MP_OBJ_NEW_SMALL_INT(ledc_get_freq(self->mode, self->timer)); } STATIC void mp_machine_pwm_freq_set(machine_pwm_obj_t *self, mp_int_t freq) { - if (!set_freq(freq)) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("bad frequency %d"), freq); + if (freq == timers[TIMER_IDX(self->mode, self->timer)].freq_hz) { + return; + } + + int current_timer_idx = chans[CHANNEL_IDX(self->mode, self->channel)].timer_idx; + bool current_in_use = is_timer_in_use(CHANNEL_IDX(self->mode, self->channel), current_timer_idx); + + // Check if an already running timer with the same freq is running + int new_timer_idx = find_timer(freq, SAME_FREQ_ONLY, self->mode); + + // If no existing timer was found, and the current one is in use, then find a new one + if ((new_timer_idx == -1) && current_in_use) { + // Have to find a new timer + new_timer_idx = find_timer(freq, SAME_FREQ_OR_FREE, self->mode); + + if (new_timer_idx == -1) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("out of PWM timers:%d"), PWM_TIMER_MAX); // in current mode + } + } + + if ((new_timer_idx != -1) && (new_timer_idx != current_timer_idx)) { + // Bind the channel to the new timer + chans[self->channel].timer_idx = new_timer_idx; + + if (ledc_bind_channel_timer(self->mode, self->channel, TIMER_IDX_TO_TIMER(new_timer_idx)) != ESP_OK) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("failed to bind timer to channel")); + } + + if (!current_in_use) { + // Free the old timer + check_esp_err(ledc_timer_rst(self->mode, self->timer)); + // Flag it unused + timers[current_timer_idx].freq_hz = -1; + } + + current_timer_idx = new_timer_idx; + } + self->mode = TIMER_IDX_TO_MODE(current_timer_idx); + self->timer = TIMER_IDX_TO_TIMER(current_timer_idx); + + // Set the freq + set_freq(freq, &timers[current_timer_idx]); + + // Reset the timer if low speed + if (self->mode == LEDC_LOW_SPEED_MODE) { + check_esp_err(ledc_timer_rst(self->mode, self->timer)); } } STATIC mp_obj_t mp_machine_pwm_duty_get(machine_pwm_obj_t *self) { - int duty = ledc_get_duty(PWMODE, self->channel); - duty <<= PWRES - timer_cfg.duty_resolution; - return MP_OBJ_NEW_SMALL_INT(duty); + return MP_OBJ_NEW_SMALL_INT(get_duty(self)); } STATIC void mp_machine_pwm_duty_set(machine_pwm_obj_t *self, mp_int_t duty) { - duty &= ((1 << PWRES) - 1); - duty >>= PWRES - timer_cfg.duty_resolution; - ledc_set_duty(PWMODE, self->channel, duty); - ledc_update_duty(PWMODE, self->channel); + set_duty(self, duty); } 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