esp32/machine_pwm: Add support for all PWM timers and channels.
This commit allows using all the available PWM timers (up to 8) and channels (up to 16), without affecting the PWM API. If a new frequency is set, first it checks if another timer is using the same frequency. If yes, then it uses this timer, otherwise, it creates a new one. If all timers are used, the user should set an already used frequency, or de-init a channel. This work is based on #6276 and #3608.
This commit is contained in:
parent
0d9429f44c
commit
52636fa692
|
@ -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
|
||||
};
|
||||
|
||||
// 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);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
#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.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
self->channel = channel;
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
self->mode = mode;
|
||||
self->timer = TIMER_IDX_TO_TIMER(timer_idx);
|
||||
self->channel = CHANNEL_IDX_TO_CHANNEL(channel_idx);
|
||||
|
||||
// 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);
|
||||
}
|
||||
chan_gpio[channel] = self->pin;
|
||||
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;
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue