diff --git a/ports/rp2/machine_pwm.c b/ports/rp2/machine_pwm.c index 0c840eca94..5a0c4751e9 100644 --- a/ports/rp2/machine_pwm.c +++ b/ports/rp2/machine_pwm.c @@ -39,34 +39,37 @@ typedef struct _machine_pwm_obj_t { uint8_t slice; uint8_t channel; uint8_t duty_type; + bool freq_set; mp_int_t duty; } machine_pwm_obj_t; enum { + VALUE_NOT_SET = -1, DUTY_NOT_SET = 0, DUTY_U16, DUTY_NS }; STATIC machine_pwm_obj_t machine_pwm_obj[] = { - {{&machine_pwm_type}, 0, PWM_CHAN_A, DUTY_NOT_SET, 0}, - {{&machine_pwm_type}, 0, PWM_CHAN_B, DUTY_NOT_SET, 0}, - {{&machine_pwm_type}, 1, PWM_CHAN_A, DUTY_NOT_SET, 0}, - {{&machine_pwm_type}, 1, PWM_CHAN_B, DUTY_NOT_SET, 0}, - {{&machine_pwm_type}, 2, PWM_CHAN_A, DUTY_NOT_SET, 0}, - {{&machine_pwm_type}, 2, PWM_CHAN_B, DUTY_NOT_SET, 0}, - {{&machine_pwm_type}, 3, PWM_CHAN_A, DUTY_NOT_SET, 0}, - {{&machine_pwm_type}, 3, PWM_CHAN_B, DUTY_NOT_SET, 0}, - {{&machine_pwm_type}, 4, PWM_CHAN_A, DUTY_NOT_SET, 0}, - {{&machine_pwm_type}, 4, PWM_CHAN_B, DUTY_NOT_SET, 0}, - {{&machine_pwm_type}, 5, PWM_CHAN_A, DUTY_NOT_SET, 0}, - {{&machine_pwm_type}, 5, PWM_CHAN_B, DUTY_NOT_SET, 0}, - {{&machine_pwm_type}, 6, PWM_CHAN_A, DUTY_NOT_SET, 0}, - {{&machine_pwm_type}, 6, PWM_CHAN_B, DUTY_NOT_SET, 0}, - {{&machine_pwm_type}, 7, PWM_CHAN_A, DUTY_NOT_SET, 0}, - {{&machine_pwm_type}, 7, PWM_CHAN_B, DUTY_NOT_SET, 0}, + {{&machine_pwm_type}, 0, PWM_CHAN_A, DUTY_NOT_SET, 0, 0 }, + {{&machine_pwm_type}, 0, PWM_CHAN_B, DUTY_NOT_SET, 0, 0 }, + {{&machine_pwm_type}, 1, PWM_CHAN_A, DUTY_NOT_SET, 0, 0 }, + {{&machine_pwm_type}, 1, PWM_CHAN_B, DUTY_NOT_SET, 0, 0 }, + {{&machine_pwm_type}, 2, PWM_CHAN_A, DUTY_NOT_SET, 0, 0 }, + {{&machine_pwm_type}, 2, PWM_CHAN_B, DUTY_NOT_SET, 0, 0 }, + {{&machine_pwm_type}, 3, PWM_CHAN_A, DUTY_NOT_SET, 0, 0 }, + {{&machine_pwm_type}, 3, PWM_CHAN_B, DUTY_NOT_SET, 0, 0 }, + {{&machine_pwm_type}, 4, PWM_CHAN_A, DUTY_NOT_SET, 0, 0 }, + {{&machine_pwm_type}, 4, PWM_CHAN_B, DUTY_NOT_SET, 0, 0 }, + {{&machine_pwm_type}, 5, PWM_CHAN_A, DUTY_NOT_SET, 0, 0 }, + {{&machine_pwm_type}, 5, PWM_CHAN_B, DUTY_NOT_SET, 0, 0 }, + {{&machine_pwm_type}, 6, PWM_CHAN_A, DUTY_NOT_SET, 0, 0 }, + {{&machine_pwm_type}, 6, PWM_CHAN_B, DUTY_NOT_SET, 0, 0 }, + {{&machine_pwm_type}, 7, PWM_CHAN_A, DUTY_NOT_SET, 0, 0 }, + {{&machine_pwm_type}, 7, PWM_CHAN_B, DUTY_NOT_SET, 0, 0 }, }; +STATIC void mp_machine_pwm_freq_set(machine_pwm_obj_t *self, mp_int_t freq); STATIC void mp_machine_pwm_duty_set_u16(machine_pwm_obj_t *self, mp_int_t duty_u16); STATIC void mp_machine_pwm_duty_set_ns(machine_pwm_obj_t *self, mp_int_t duty_ns); @@ -75,10 +78,46 @@ STATIC void mp_machine_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_p mp_printf(print, "", self->slice, self->channel); } -// PWM(pin) +void machine_pwm_start(machine_pwm_obj_t *self) { + // Start the PWM if properly set. + if (self->freq_set && self->duty_type != DUTY_NOT_SET) { + pwm_set_enabled(self->slice, true); + } +} + +STATIC void mp_machine_pwm_init_helper(machine_pwm_obj_t *self, + size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_freq, ARG_duty_u16, ARG_duty_ns }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_freq, MP_ARG_INT, {.u_int = VALUE_NOT_SET} }, + { MP_QSTR_duty_u16, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = VALUE_NOT_SET} }, + { MP_QSTR_duty_ns, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = VALUE_NOT_SET} }, + }; + + // Parse the arguments. + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, + MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + if ((n_args + kw_args->used) > 0) { + if (args[ARG_freq].u_int != VALUE_NOT_SET) { + mp_machine_pwm_freq_set(self, args[ARG_freq].u_int); + } + if (args[ARG_duty_u16].u_int != VALUE_NOT_SET) { + mp_machine_pwm_duty_set_u16(self, args[ARG_duty_u16].u_int); + } + if (args[ARG_duty_ns].u_int != VALUE_NOT_SET) { + mp_machine_pwm_duty_set_ns(self, args[ARG_duty_ns].u_int); + } + } else { + machine_pwm_start(self); + } +} + +// PWM(pin [, args]) STATIC mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { // Check number of arguments - mp_arg_check_num(n_args, n_kw, 1, 1, false); + mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); // Get GPIO to connect to PWM. uint32_t gpio = mp_hal_get_pin_obj(all_args[0]); @@ -87,16 +126,28 @@ STATIC mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args uint slice = pwm_gpio_to_slice_num(gpio); uint8_t channel = pwm_gpio_to_channel(gpio); machine_pwm_obj_t *self = &machine_pwm_obj[slice * 2 + channel]; + self->freq_set = false; self->duty_type = DUTY_NOT_SET; // Select PWM function for given GPIO. gpio_set_function(gpio, GPIO_FUNC_PWM); + // Process the remaining parameters. + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, all_args + n_args); + mp_machine_pwm_init_helper(self, n_args - 1, all_args + 1, &kw_args); + return MP_OBJ_FROM_PTR(self); } +// Stop all active slices. +void machine_pwm_deinit_all(void) { + for (int i = 0; i < 8; i++) { + pwm_set_enabled(machine_pwm_obj[i].slice, false); + } +} + STATIC void mp_machine_pwm_deinit(machine_pwm_obj_t *self) { - self->duty_type = DUTY_NOT_SET; pwm_set_enabled(self->slice, false); } @@ -165,11 +216,16 @@ STATIC void mp_machine_pwm_freq_set(machine_pwm_obj_t *self, mp_int_t freq) { } pwm_hw->slice[self->slice].div = div16; pwm_hw->slice[self->slice].top = top; + self->freq_set = true; if (self->duty_type == DUTY_U16) { mp_machine_pwm_duty_set_u16(self, self->duty); } else if (self->duty_type == DUTY_NS) { mp_machine_pwm_duty_set_ns(self, self->duty); } + machine_pwm_obj_t *other = self->channel == PWM_CHAN_A ? self + 1 : self - 1; + if (other->duty_type == DUTY_NS) { + mp_machine_pwm_duty_set_ns(other, other->duty); + } } STATIC mp_obj_t mp_machine_pwm_duty_get_u16(machine_pwm_obj_t *self) { @@ -188,9 +244,9 @@ STATIC void mp_machine_pwm_duty_set_u16(machine_pwm_obj_t *self, mp_int_t duty_u // Use rounding here to set it as accurately as possible. uint32_t cc = (duty_u16 * (top + 1) + 65535 / 2) / 65535; pwm_set_chan_level(self->slice, self->channel, cc); - pwm_set_enabled(self->slice, true); self->duty = duty_u16; self->duty_type = DUTY_U16; + machine_pwm_start(self); } STATIC mp_obj_t mp_machine_pwm_duty_get_ns(machine_pwm_obj_t *self) { @@ -207,7 +263,7 @@ STATIC void mp_machine_pwm_duty_set_ns(machine_pwm_obj_t *self, mp_int_t duty_ns mp_raise_ValueError(MP_ERROR_TEXT("duty larger than period")); } pwm_set_chan_level(self->slice, self->channel, cc); - pwm_set_enabled(self->slice, true); self->duty = duty_ns; self->duty_type = DUTY_NS; + machine_pwm_start(self); } diff --git a/ports/rp2/main.c b/ports/rp2/main.c index 059449695c..0ecc09b346 100644 --- a/ports/rp2/main.c +++ b/ports/rp2/main.c @@ -206,6 +206,7 @@ int main(int argc, char **argv) { #if MICROPY_PY_BLUETOOTH mp_bluetooth_deinit(); #endif + machine_pwm_deinit_all(); machine_pin_deinit(); #if MICROPY_PY_THREAD mp_thread_deinit(); diff --git a/ports/rp2/modmachine.h b/ports/rp2/modmachine.h index 6ea4def91d..45ccd49e8c 100644 --- a/ports/rp2/modmachine.h +++ b/ports/rp2/modmachine.h @@ -16,6 +16,7 @@ extern const mp_obj_type_t machine_wdt_type; void machine_pin_init(void); void machine_pin_deinit(void); void machine_i2s_init0(void); +void machine_pwm_deinit_all(void); struct _machine_spi_obj_t *spi_from_mp_obj(mp_obj_t o); NORETURN mp_obj_t machine_bootloader(size_t n_args, const mp_obj_t *args); diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index 17586e5092..653b51091a 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -114,6 +114,7 @@ #define MICROPY_PY_MACHINE_BITSTREAM (1) #define MICROPY_PY_MACHINE_PULSE (1) #define MICROPY_PY_MACHINE_PWM (1) +#define MICROPY_PY_MACHINE_PWM_INIT (1) #define MICROPY_PY_MACHINE_PWM_DUTY_U16_NS (1) #define MICROPY_PY_MACHINE_PWM_INCLUDEFILE "ports/rp2/machine_pwm.c" #define MICROPY_PY_MACHINE_I2C (1)