Skip to content
This repository was archived by the owner on Sep 6, 2023. It is now read-only.

esp32/machine_pwm: Add support for all PWM timers and modes. #245

Closed
wants to merge 1 commit into from
Closed
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
125 changes: 78 additions & 47 deletions ports/esp32/machine_pwm.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,56 +35,52 @@
// Forward dec'l
extern const mp_obj_type_t machine_pwm_type;


typedef struct _esp32_pwm_obj_t {
mp_obj_base_t base;
gpio_num_t pin;
uint8_t active;
uint8_t channel;
uint8_t timer;
uint8_t mode;
} esp32_pwm_obj_t;

// Number of available timers
#define LEDC_TIMER_MAX 4

// Which channel has which GPIO pin assigned?
// (-1 if not assigned)
STATIC int chan_gpio[LEDC_CHANNEL_MAX];

// Params for PW operation
// 5khz
#define PWFREQ (5000)
// High speed mode
#define PWMODE (LEDC_HIGH_SPEED_MODE)
// 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 = {
.bit_num = PWRES,
.freq_hz = PWFREQ,
.speed_mode = PWMODE,
.timer_num = PWTIMER
};

STATIC void pwm_init(void) {

// Initial condition: no channels assigned
for (int x = 0; x < LEDC_CHANNEL_MAX; ++x) {
chan_gpio[x] = -1;
}

// Init with default timer params
ledc_timer_config(&timer_cfg);
}

STATIC int set_freq(int newval) {
int oval = timer_cfg.freq_hz;

timer_cfg.freq_hz = newval;
if (ledc_timer_config(&timer_cfg) != ESP_OK) {
timer_cfg.freq_hz = oval;
return 0;
// prepare all timers
ledc_timer_config_t timer_cfg = {
.bit_num = PWRES,
.freq_hz = PWFREQ,
.speed_mode = LEDC_LOW_SPEED_MODE,
.timer_num = 0,
};
for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) {
for (int idx = 0; idx < LEDC_TIMER_MAX; ++idx) {
timer_cfg.timer_num = idx;
timer_cfg.speed_mode = mode;
ledc_timer_config(&timer_cfg);
}
}
return 1;
}

/******************************************************************************/
Expand All @@ -93,20 +89,23 @@ STATIC int set_freq(int newval) {

STATIC void esp32_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
esp32_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in);
mp_printf(print, "PWM(%u", self->pin);
mp_printf(print, "PWM(%u, timer=%u, speed_mode=%u", self->pin, self->timer, self->mode);
if (self->active) {
mp_printf(print, ", freq=%u, duty=%u", timer_cfg.freq_hz,
ledc_get_duty(PWMODE, self->channel));
mp_printf(print, ", freq=%u, duty=%u",
ledc_get_freq(self->mode, self->timer),
ledc_get_duty(self->mode, self->channel));
}
mp_printf(print, ")");
}

STATIC void esp32_pwm_init_helper(esp32_pwm_obj_t *self,
size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_freq, ARG_duty };
enum { ARG_freq, ARG_duty, ARG_timer, ARG_mode };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_freq, MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_duty, MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_timer, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_speed_mode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = LEDC_HIGH_SPEED_MODE} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args,
Expand All @@ -133,16 +132,31 @@ STATIC void esp32_pwm_init_helper(esp32_pwm_obj_t *self,
}
self->channel = channel;

// set timer
if (0 <= args[ARG_timer].u_int && args[ARG_timer].u_int <= 3) {
self->timer = args[ARG_timer].u_int;
} else {
mp_raise_ValueError("invalid timer");
}

// set speed mode
if (0 <= args[ARG_mode].u_int && args[ARG_mode].u_int <= LEDC_SPEED_MODE_MAX) {
self->mode = args[ARG_mode].u_int;
} else {
mp_raise_ValueError("invalid speed_mode");
}

// New PWM assignment
self->active = 1;
if (chan_gpio[channel] == -1) {
// configure channel
ledc_channel_config_t cfg = {
.channel = channel,
.duty = (1 << PWRES) / 2,
.gpio_num = self->pin,
.intr_type = LEDC_INTR_DISABLE,
.speed_mode = PWMODE,
.timer_sel = PWTIMER,
.speed_mode = self->mode,
.timer_sel = self->timer,
};
if (ledc_channel_config(&cfg) != ESP_OK) {
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError,
Expand All @@ -152,22 +166,23 @@ STATIC void esp32_pwm_init_helper(esp32_pwm_obj_t *self,
}

// Maybe change PWM timer
int tval = args[ARG_freq].u_int;
if (tval != -1) {
if (tval != timer_cfg.freq_hz) {
if (!set_freq(tval)) {
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError,
"Bad frequency %d", tval));
}
}
int fval = args[ARG_freq].u_int;
if (fval != -1 && ledc_set_freq(self->mode, self->timer, fval) != ESP_OK) {
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError,
"Bad frequency %d", fval));
}

// Set duty cycle?
int dval = args[ARG_duty].u_int;
if (dval != -1) {
dval &= ((1 << PWRES)-1);
ledc_set_duty(PWMODE, channel, dval);
ledc_update_duty(PWMODE, channel);
ledc_set_duty(self->mode, channel, dval);
ledc_update_duty(self->mode, channel);
}

// Reset the timer if low speed
if (self->mode == LEDC_LOW_SPEED_MODE) {
ledc_timer_rst(self->mode, self->timer);
}
}

Expand Down Expand Up @@ -210,28 +225,42 @@ STATIC mp_obj_t esp32_pwm_deinit(mp_obj_t self_in) {

// Valid channel?
if ((chan >= 0) && (chan < LEDC_CHANNEL_MAX)) {
// Mark it unused, and tell the hardware to stop routing
// Mark it unused, and tell the hardware to stop
chan_gpio[chan] = -1;
ledc_stop(PWMODE, chan, 0);
ledc_stop(self->mode, chan, 0);
self->active = 0;
self->channel = -1;
// Disable ledc signal for the pin
if (self->mode == LEDC_LOW_SPEED_MODE) {
gpio_matrix_out(self->pin, LEDC_LS_SIG_OUT0_IDX + self->channel, false, true);
} else {
gpio_matrix_out(self->pin, LEDC_HS_SIG_OUT0_IDX + self->channel, false, true);
}
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp32_pwm_deinit_obj, esp32_pwm_deinit);

STATIC mp_obj_t esp32_pwm_freq(size_t n_args, const mp_obj_t *args) {
esp32_pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]);

if (n_args == 1) {
// get
return MP_OBJ_NEW_SMALL_INT(timer_cfg.freq_hz);
return MP_OBJ_NEW_SMALL_INT(ledc_get_freq(self->mode, self->timer));
}

// set
int tval = mp_obj_get_int(args[1]);
if (!set_freq(tval)) {
if (tval <= 0 || ledc_set_freq(self->mode, self->timer, tval) != ESP_OK) {
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError,
"Bad frequency %d", tval));
"bad frequency %d", tval));
}

// Reset the timer if low speed
if (self->mode == LEDC_LOW_SPEED_MODE) {
ledc_timer_rst(self->mode, self->timer);
}

return mp_const_none;
}

Expand All @@ -243,15 +272,15 @@ STATIC mp_obj_t esp32_pwm_duty(size_t n_args, const mp_obj_t *args) {

if (n_args == 1) {
// get
duty = ledc_get_duty(PWMODE, self->channel);
duty = ledc_get_duty(self->mode, self->channel);
return MP_OBJ_NEW_SMALL_INT(duty);
}

// set
duty = mp_obj_get_int(args[1]);
duty &= ((1 << PWRES)-1);
ledc_set_duty(PWMODE, self->channel, duty);
ledc_update_duty(PWMODE, self->channel);
ledc_set_duty(self->mode, self->channel, duty);
ledc_update_duty(self->mode, self->channel);

return mp_const_none;
}
Expand All @@ -263,6 +292,8 @@ STATIC const mp_rom_map_elem_t esp32_pwm_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&esp32_pwm_deinit_obj) },
{ MP_ROM_QSTR(MP_QSTR_freq), MP_ROM_PTR(&esp32_pwm_freq_obj) },
{ MP_ROM_QSTR(MP_QSTR_duty), MP_ROM_PTR(&esp32_pwm_duty_obj) },
{ MP_ROM_QSTR(MP_QSTR_HIGH_SPEED_MODE), MP_ROM_INT(LEDC_HIGH_SPEED_MODE) },
{ MP_ROM_QSTR(MP_QSTR_LOW_SPEED_MODE), MP_ROM_INT(LEDC_LOW_SPEED_MODE) },
};

STATIC MP_DEFINE_CONST_DICT(esp32_pwm_locals_dict,
Expand Down