From 9c7ad68165bcd224c94ca6d8f172362cf8000d99 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 23 Feb 2023 21:52:47 +0100 Subject: [PATCH 1/9] mimxrt/machine_pwm: Start PWM only if freq and duty are set. And also fix/improve the following: - Simplify the duty handling a little bit. - Allow duty_u16(65536), which sets the output high. - Rename machine_pwm_start() to mp_machine_pwm_start(), in preparation for a possible start/stop method pair. --- ports/mimxrt/machine_pwm.c | 94 +++++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 42 deletions(-) diff --git a/ports/mimxrt/machine_pwm.c b/ports/mimxrt/machine_pwm.c index c1d6bdf7a6e65..b8b526ae02618 100644 --- a/ports/mimxrt/machine_pwm.c +++ b/ports/mimxrt/machine_pwm.c @@ -39,6 +39,7 @@ #define PWM_CHANNEL1 (1) #define PWM_CHANNEL2 (2) +#define VALUE_NOT_SET (-1) typedef struct _machine_pwm_obj_t { mp_obj_base_t base; @@ -51,10 +52,10 @@ typedef struct _machine_pwm_obj_t { uint8_t channel2; uint8_t invert; bool sync; - uint32_t freq; + int32_t freq; int16_t prescale; - uint16_t duty_u16; - uint32_t duty_ns; + int32_t duty_u16; + int32_t duty_ns; uint16_t center; uint32_t deadtime; bool output_enable_1; @@ -68,7 +69,7 @@ static char *ERRMSG_FREQ = "PWM frequency too low"; static char *ERRMSG_INIT = "PWM set-up failed"; static char *ERRMSG_VALUE = "value larger than period"; -STATIC void machine_pwm_start(machine_pwm_obj_t *self); +STATIC void mp_machine_pwm_start(machine_pwm_obj_t *self); 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); @@ -79,36 +80,40 @@ STATIC void mp_machine_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_p } else { mp_printf(print, "channel=%c", channel_char[self->channel1]); } - if (self->duty_ns != 0) { - mp_printf(print, " duty_ns=%u", self->duty_ns); + if (self->duty_ns != VALUE_NOT_SET) { + mp_printf(print, " duty_ns=%d", self->duty_ns); } else { - mp_printf(print, " duty_u16=%u", self->duty_u16); + mp_printf(print, " duty_u16=%d", self->duty_u16); } - mp_printf(print, " freq=%u center=%u, deadtime=%u, sync=%u>", + mp_printf(print, " freq=%d center=%u, deadtime=%u, sync=%u>", self->freq, self->center, self->deadtime, self->sync); #ifdef FSL_FEATURE_SOC_TMR_COUNT } else { - mp_printf(print, "module, self->channel1, self->freq); - if (self->duty_ns != 0) { - mp_printf(print, "duty_ns=%u>", self->duty_ns); + if (self->duty_ns == VALUE_NOT_SET) { + mp_printf(print, "duty_ns=%d>", self->duty_ns); } else { - mp_printf(print, "duty_u16=%u>", self->duty_u16); + mp_printf(print, "duty_u16=%d>", self->duty_u16); } #endif } } -// Utility functions for decoding and convertings +// Utility functions for decoding and converting // STATIC uint32_t duty_ns_to_duty_u16(uint32_t freq, uint32_t duty_ns) { uint64_t duty = (uint64_t)duty_ns * freq * PWM_FULL_SCALE / 1000000000ULL; - if (duty >= PWM_FULL_SCALE) { + if (duty > PWM_FULL_SCALE) { mp_raise_ValueError(MP_ERROR_TEXT(ERRMSG_VALUE)); } return (uint32_t)duty; } +STATIC uint32_t duty_u16_to_duty_ns(machine_pwm_obj_t *self) { + return 1000000000ULL * (uint64_t)self->duty_u16 / self->freq / PWM_FULL_SCALE; +} + STATIC uint8_t module_decode(char channel) { switch (channel) { case '0': @@ -340,15 +345,20 @@ STATIC void configure_pwm(machine_pwm_obj_t *self) { set_frequency = false; } - if (self->duty_ns != 0) { - self->duty_u16 = duty_ns_to_duty_u16(self->freq, self->duty_ns); - } - if (self->is_flexpwm) { - configure_flexpwm(self); - #ifdef FSL_FEATURE_SOC_TMR_COUNT - } else { - configure_qtmr(self); - #endif + // Enable the PWM only if both freq and duty value are set + if (self->freq != VALUE_NOT_SET && (self->duty_u16 != VALUE_NOT_SET || self->duty_ns != VALUE_NOT_SET)) { + if (self->duty_ns != VALUE_NOT_SET) { + self->duty_u16 = duty_ns_to_duty_u16(self->freq, self->duty_ns); + } else { + self->duty_ns = duty_u16_to_duty_ns(self); + } + if (self->is_flexpwm) { + configure_flexpwm(self); + #ifdef FSL_FEATURE_SOC_TMR_COUNT + } else { + configure_qtmr(self); + #endif + } } } @@ -359,9 +369,9 @@ STATIC void mp_machine_pwm_init_helper(machine_pwm_obj_t *self, enum { ARG_freq, ARG_duty_u16, ARG_duty_ns, ARG_center, ARG_align, ARG_invert, ARG_sync, ARG_xor, ARG_deadtime }; static const mp_arg_t allowed_args[] = { - { MP_QSTR_freq, MP_ARG_INT, {.u_int = 0} }, - { MP_QSTR_duty_u16, MP_ARG_INT, {.u_int = 0} }, - { MP_QSTR_duty_ns, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_freq, MP_ARG_INT, {.u_int = VALUE_NOT_SET} }, + { MP_QSTR_duty_u16, MP_ARG_INT, {.u_int = VALUE_NOT_SET} }, + { MP_QSTR_duty_ns, MP_ARG_INT, {.u_int = VALUE_NOT_SET} }, { MP_QSTR_center, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_align, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1}}, { MP_QSTR_invert, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1}}, @@ -380,24 +390,24 @@ STATIC void mp_machine_pwm_init_helper(machine_pwm_obj_t *self, } // Set duty_u16 cycle? - uint32_t duty = args[ARG_duty_u16].u_int; - if (duty != 0) { - if (duty >= PWM_FULL_SCALE) { + int32_t duty = args[ARG_duty_u16].u_int; + if (duty >= 0) { + if (duty > PWM_FULL_SCALE) { mp_raise_ValueError(MP_ERROR_TEXT(ERRMSG_VALUE)); } self->duty_u16 = duty; - self->duty_ns = 0; + self->duty_ns = VALUE_NOT_SET; } // Set duty_ns value? duty = args[ARG_duty_ns].u_int; - if (duty != 0) { + if (duty >= 0) { self->duty_ns = duty; - self->duty_u16 = duty_ns_to_duty_u16(self->freq, self->duty_ns); + self->duty_u16 = VALUE_NOT_SET; } // Set center value? int32_t center = args[ARG_center].u_int; if (center >= 0) { - if (center >= PWM_FULL_SCALE) { + if (center > PWM_FULL_SCALE) { mp_raise_ValueError(MP_ERROR_TEXT(ERRMSG_VALUE)); } self->center = center; @@ -432,7 +442,7 @@ STATIC void mp_machine_pwm_init_helper(machine_pwm_obj_t *self, configure_pwm(self); self->is_init = true; } else { - machine_pwm_start(self); + mp_machine_pwm_start(self); } } @@ -512,10 +522,10 @@ STATIC mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args self->submodule = submodule1; self->channel1 = channel1; self->invert = 0; - self->freq = 1000; + self->freq = VALUE_NOT_SET; self->prescale = -1; - self->duty_u16 = 32768; - self->duty_ns = 0; + self->duty_u16 = VALUE_NOT_SET; + self->duty_ns = VALUE_NOT_SET; self->center = 32768; self->output_enable_1 = is_board_pin(pin1); self->sync = false; @@ -571,7 +581,7 @@ void machine_pwm_deinit_all(void) { #endif } -STATIC void machine_pwm_start(machine_pwm_obj_t *self) { +STATIC void mp_machine_pwm_start(machine_pwm_obj_t *self) { if (self->is_flexpwm) { PWM_StartTimer(self->instance, 1 << self->submodule); #ifdef FSL_FEATURE_SOC_TMR_COUNT @@ -606,23 +616,23 @@ mp_obj_t mp_machine_pwm_duty_get_u16(machine_pwm_obj_t *self) { void mp_machine_pwm_duty_set_u16(machine_pwm_obj_t *self, mp_int_t duty) { if (duty >= 0) { - if (duty >= PWM_FULL_SCALE) { + if (duty > PWM_FULL_SCALE) { mp_raise_ValueError(MP_ERROR_TEXT(ERRMSG_VALUE)); } self->duty_u16 = duty; - self->duty_ns = 0; + self->duty_ns = VALUE_NOT_SET; configure_pwm(self); } } mp_obj_t mp_machine_pwm_duty_get_ns(machine_pwm_obj_t *self) { - return MP_OBJ_NEW_SMALL_INT(1000000000ULL / self->freq * self->duty_u16 / PWM_FULL_SCALE); + return MP_OBJ_NEW_SMALL_INT(self->duty_ns); } void mp_machine_pwm_duty_set_ns(machine_pwm_obj_t *self, mp_int_t duty) { if (duty >= 0) { self->duty_ns = duty; - self->duty_u16 = duty_ns_to_duty_u16(self->freq, self->duty_ns); + self->duty_u16 = VALUE_NOT_SET; configure_pwm(self); } } From 250757716a91a05d28e4af8e07adda278f491199 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 24 Feb 2023 14:51:36 +0100 Subject: [PATCH 2/9] samd/machine_pwm: Add init() method to PWM and simplify the PWM code. The PWM.init() method has been added. Calling init() without arguments restarts a PWM channel stopped with deinit(). Otherwise single parameters except for "device=n" can be changed again. The device can only be specified once, either in the constructor or the first init() call. Also simplify get_pwm_config() and get_adc_config(), and shrink the PWM object. --- docs/samd/quickref.rst | 2 +- ports/samd/machine_pwm.c | 219 ++++++++++++++++++-------------------- ports/samd/mpconfigport.h | 2 +- ports/samd/pin_af.c | 31 ++---- 4 files changed, 118 insertions(+), 136 deletions(-) diff --git a/docs/samd/quickref.rst b/docs/samd/quickref.rst index cdfb0a4750724..7a0786d4cadc6 100644 --- a/docs/samd/quickref.rst +++ b/docs/samd/quickref.rst @@ -191,7 +191,7 @@ It supports all basic methods listed for that class. :: PWM Constructor ``````````````` -.. class:: PWM(dest, freq, duty_u16, duty_ns, *, invert, device) +.. class:: PWM(dest, *, freq, duty_u16, duty_ns, invert, device) :noindex: Construct and return a new PWM object using the following parameters: diff --git a/ports/samd/machine_pwm.c b/ports/samd/machine_pwm.c index f6b417631e1de..d01ce2ff1ad5b 100644 --- a/ports/samd/machine_pwm.c +++ b/ports/samd/machine_pwm.c @@ -40,15 +40,14 @@ typedef struct _machine_pwm_obj_t { mp_obj_base_t base; Tcc *instance; + bool defer_start; uint8_t pin_id; uint8_t alt_fct; - uint8_t device; + int8_t device; uint8_t channel; uint8_t output; uint16_t prescaler; - uint32_t period; // full period count ticks - uint32_t duty_ns; // just for reporting - uint16_t duty_u16; // just for reporting + int32_t freq; // for re-init. } machine_pwm_obj_t; #define PWM_NOT_INIT (0) @@ -58,6 +57,8 @@ typedef struct _machine_pwm_obj_t { #define PWM_FULL_SCALE (65536) #define PWM_UPDATE_TIMEOUT (2000) +#define VALUE_NOT_SET (-1) + static Tcc *tcc_instance[] = TCC_INSTS; #if defined(MCU_SAMD21) @@ -104,10 +105,12 @@ static uint8_t device_status[TCC_INST_NUM]; static uint8_t output_active[TCC_INST_NUM]; const uint16_t prescaler_table[] = {1, 2, 4, 8, 16, 64, 256, 1024}; -STATIC void pwm_stop_device(int device); 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); +STATIC void mp_machine_pwm_start(machine_pwm_obj_t *self); +STATIC void mp_machine_pwm_stop(machine_pwm_obj_t *self); + 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); @@ -115,48 +118,39 @@ STATIC void mp_machine_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_p pin_name(self->pin_id), self->device, self->channel, self->output); } -// PWM(pin) -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) { - enum { ARG_pin, ARG_freq, ARG_duty_u16, ARG_duty_ns, ARG_invert, ARG_device }; +// called by the constructor and init() +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, ARG_invert, ARG_device }; static const mp_arg_t allowed_args[] = { - { MP_QSTR_pin, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_freq, MP_ARG_INT, {.u_int = -1} }, - { MP_QSTR_duty_u16, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, - { MP_QSTR_duty_ns, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, - { MP_QSTR_invert, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, - { MP_QSTR_device, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { 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} }, + { MP_QSTR_invert, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = VALUE_NOT_SET} }, + { MP_QSTR_device, 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_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - // Get GPIO and optional device to connect to PWM. - uint32_t pin_id = mp_hal_get_pin_obj(args[ARG_pin].u_obj); - int32_t wanted_dev = args[ARG_device].u_int; // -1 = any - - // Get the peripheral object and populate it - - pwm_config_t config = get_pwm_config(pin_id, wanted_dev, device_status); - uint8_t device = config.device_channel >> 4; - if (device >= TCC_INST_NUM) { - mp_raise_ValueError(MP_ERROR_TEXT("wrong device")); + mp_arg_parse_all(n_args, pos_args, kw_args, + MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + int8_t device = self->device; + if (device == VALUE_NOT_SET) { // Device not set, just get & set + int32_t wanted_dev = args[ARG_device].u_int; // -1 = any + pwm_config_t config = get_pwm_config(self->pin_id, wanted_dev, device_status); + device = config.device_channel >> 4; + self->instance = tcc_instance[device]; + self->device = device; + self->alt_fct = config.alt_fct; + self->channel = (config.device_channel & 0x0f) % tcc_channel_count[device]; + self->output = config.device_channel & 0x0f; + put_duty_value(device, self->channel, 0); } - machine_pwm_obj_t *self = mp_obj_malloc(machine_pwm_obj_t, &machine_pwm_type); - self->instance = tcc_instance[device]; - self->device = device; - self->pin_id = pin_id; - self->alt_fct = config.alt_fct; - self->channel = (config.device_channel & 0x0f) % tcc_channel_count[device]; - self->output = config.device_channel & 0x0f; - self->prescaler = 1; - self->period = 1; // Use an invalid but safe value - self->duty_u16 = self->duty_ns = 0; - put_duty_value(self->device, self->channel, 0); - Tcc *tcc = self->instance; + // Initialize the hardware if needed if (device_status[device] == PWM_NOT_INIT) { // Enable the device clock at first use. #if defined(MCU_SAMD21) @@ -203,10 +197,11 @@ STATIC mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args device_status[device] = PWM_CLK_READY; } - if (args[ARG_invert].u_int != -1) { + self->defer_start = true; + if (args[ARG_invert].u_int != VALUE_NOT_SET) { bool invert = !!args[ARG_invert].u_int; if (device_status[device] != PWM_CLK_READY) { - pwm_stop_device(device); + mp_machine_pwm_stop(self); } uint32_t mask = 1 << (self->output + TCC_DRVCTRL_INVEN0_Pos); if (invert) { @@ -215,24 +210,47 @@ STATIC mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args tcc->DRVCTRL.reg &= ~(mask); } } - if (args[ARG_duty_u16].u_int != -1) { + 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 != -1) { + if (args[ARG_duty_ns].u_int != VALUE_NOT_SET) { mp_machine_pwm_duty_set_ns(self, args[ARG_duty_ns].u_int); } - if (args[ARG_freq].u_int != -1) { - mp_machine_pwm_freq_set(self, args[ARG_freq].u_int); - } + self->defer_start = false; + // Start the PWM if properly set. + mp_machine_pwm_start(self); +} + +// PWM(pin) +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 *args) { + + // Check number of arguments + mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); + + // Get the peripheral object and populate it + machine_pwm_obj_t *self = mp_obj_malloc(machine_pwm_obj_t, &machine_pwm_type); + self->pin_id = mp_hal_get_pin_obj(args[0]); + self->device = VALUE_NOT_SET; + self->prescaler = 1; + self->freq = VALUE_NOT_SET; + + // Process the remaining parameters. + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + mp_machine_pwm_init_helper(self, n_args - 1, args + 1, &kw_args); + return MP_OBJ_FROM_PTR(self); } -STATIC void pwm_stop_device(int device) { - Tcc *tcc = tcc_instance[device]; +STATIC void mp_machine_pwm_stop(machine_pwm_obj_t *self) { + Tcc *tcc = tcc_instance[self->device]; tcc->CTRLA.bit.ENABLE = 0; while (tcc->SYNCBUSY.reg & TCC_SYNCBUSY_ENABLE) { } - device_status[device] = PWM_CLK_READY; + device_status[self->device] = PWM_CLK_READY; } // Stop all TTC devices @@ -252,13 +270,13 @@ void pwm_deinit_all(void) { // Switch off an output. If all outputs of a device are off, // switch off that device. // This stops all channels, but keeps the configuration -// Calling pwm.freq(n) will start an instance again. +// Calling pwm.freq(n), pwm.duty_x() or pwm.init() will start it again. STATIC void mp_machine_pwm_deinit(machine_pwm_obj_t *self) { mp_hal_clr_pin_mux(self->pin_id); // Switch the output off output_active[self->device] &= ~(1 << self->output); // clear output flasg // Stop the device, if no output is active. if (output_active[self->device] == 0) { - pwm_stop_device(self->device); + mp_machine_pwm_stop(self); } } @@ -275,80 +293,52 @@ STATIC void wait_for_register_update(Tcc *tcc) { tcc->INTFLAG.reg = TCC_INTFLAG_OVF; } -STATIC mp_obj_t mp_machine_pwm_freq_get(machine_pwm_obj_t *self) { - if (self->instance->CTRLA.reg & TCC_CTRLA_ENABLE) { - return MP_OBJ_NEW_SMALL_INT(PWM_MASTER_CLK / self->prescaler / self->period); - } else { - return MP_OBJ_NEW_SMALL_INT(0); - } -} - -STATIC void mp_machine_pwm_freq_set(machine_pwm_obj_t *self, mp_int_t freq) { - // Set the frequency. The period counter is 24 bit or 16 bit with a pre-scaling +STATIC void mp_machine_pwm_start(machine_pwm_obj_t *self) { + // Start the PWM. The period counter is 24 bit or 16 bit with a pre-scaling // of up to 1024, allowing a range from 24 MHz down to 1 Hz. static const uint32_t max_period[5] = {1 << 24, 1 << 24, 1 << 16, 1 << 16, 1 << 16}; - Tcc *tcc = self->instance; - if (freq < 1) { - pwm_stop_device(self->device); + if (self->freq < 1 || self->defer_start == true) { return; } - - // Get the actual settings of prescaler & period from the unit - // To be able for cope for changes. - uint32_t prev_period = tcc->PER.reg + 1; + Tcc *tcc = self->instance; // Check for the right prescaler uint8_t index; for (index = 0; index < 8; index++) { - uint32_t temp = PWM_MASTER_CLK / prescaler_table[index] / freq; + uint32_t temp = PWM_MASTER_CLK / prescaler_table[index] / self->freq; if (temp < max_period[self->device]) { break; } } self->prescaler = prescaler_table[index]; - uint32_t period = PWM_MASTER_CLK / self->prescaler / freq; + uint32_t period = PWM_MASTER_CLK / self->prescaler / self->freq; if (period < 2) { mp_raise_ValueError(MP_ERROR_TEXT("freq too large")); } // If the PWM is running, ensure that a cycle has passed since the - // previous setting before setting a new frequency/duty value + // previous setting before setting frequency and duty. if (tcc->CTRLA.reg & TCC_CTRLA_ENABLE) { wait_for_register_update(tcc); } // Check, if the prescaler has to be changed and stop the device if so. if (index != tcc->CTRLA.bit.PRESCALER) { - // stop the device - pwm_stop_device(self->device); - // update the prescaler + mp_machine_pwm_stop(self); tcc->CTRLA.bit.PRESCALER = index; } // Lock the update to get a glitch-free change of period and duty cycle tcc->CTRLBSET.reg = TCC_CTRLBSET_LUPD; tcc->PERBUF.reg = period - 1; - self->period = period; - - // Check if the Duty rate has to be aligned again when freq or prescaler were changed. - // This condition is as well true on first call after instantiation. So (re-)configure - // all channels with a duty_u16 setting. - if (period != prev_period) { - for (uint16_t ch = 0; ch < tcc_channel_count[self->device]; ch++) { - if ((duty_type_flags[self->device] & (1 << ch)) != 0) { // duty_u16 type? - tcc->CCBUF[ch].reg = (uint64_t)get_duty_value(self->device, ch) * period / - PWM_FULL_SCALE; - } - } - } - // If the prescaler was changed, the device is disabled. So this condition is true - // after the instantiation and after a prescaler change. - // (re-)configure all channels with a duty_ns setting. - if (!(tcc->CTRLA.reg & TCC_CTRLA_ENABLE)) { - for (uint16_t ch = 0; ch < tcc_channel_count[self->device]; ch++) { - if ((duty_type_flags[self->device] & (1 << ch)) == 0) { // duty_ns type? - tcc->CCBUF[ch].reg = (uint64_t)get_duty_value(self->device, ch) * PWM_MASTER_CLK / - self->prescaler / 1000000000ULL; - } + + // (re-) configure the duty type settings. + for (uint16_t ch = 0; ch < tcc_channel_count[self->device]; ch++) { + if ((duty_type_flags[self->device] & (1 << ch)) != 0) { // duty_u16 type? + tcc->CCBUF[ch].reg = (uint64_t)get_duty_value(self->device, ch) * period / + PWM_FULL_SCALE; + } else { // duty_ns type + tcc->CCBUF[ch].reg = (uint64_t)get_duty_value(self->device, ch) * PWM_MASTER_CLK / + self->prescaler / 1000000000ULL; } } // Remember the output as active. @@ -367,38 +357,39 @@ STATIC void mp_machine_pwm_freq_set(machine_pwm_obj_t *self, mp_int_t freq) { tcc->CTRLBCLR.reg = TCC_CTRLBCLR_LUPD; } +STATIC mp_obj_t mp_machine_pwm_freq_get(machine_pwm_obj_t *self) { + return MP_OBJ_NEW_SMALL_INT(self->freq); +} + +STATIC void mp_machine_pwm_freq_set(machine_pwm_obj_t *self, mp_int_t freq) { + self->freq = freq; + mp_machine_pwm_start(self); +} + STATIC mp_obj_t mp_machine_pwm_duty_get_u16(machine_pwm_obj_t *self) { - return MP_OBJ_NEW_SMALL_INT(self->duty_u16); + if (duty_type_flags[self->device] & (1 << self->channel)) { + return MP_OBJ_NEW_SMALL_INT(get_duty_value(self->device, self->channel)); + } else { + return MP_OBJ_NEW_SMALL_INT(-1); + } } STATIC void mp_machine_pwm_duty_set_u16(machine_pwm_obj_t *self, mp_int_t duty_u16) { - // Remember the values for update & reporting put_duty_value(self->device, self->channel, duty_u16); - self->duty_u16 = duty_u16; - self->duty_ns = 0; - // If the device is enabled, than the period is set and we get a reasonable value for - // the duty cycle, set to the CCBUF register. Otherwise, PWM does not start. - if (self->instance->CTRLA.reg & TCC_CTRLA_ENABLE) { - // Ensure that a cycle has passed updating the registers - // since the previous setting before setting a new duty value - wait_for_register_update(self->instance); - self->instance->CCBUF[self->channel].reg = (uint64_t)duty_u16 * (self->period) / PWM_FULL_SCALE; - } duty_type_flags[self->device] |= 1 << self->channel; + mp_machine_pwm_start(self); } STATIC mp_obj_t mp_machine_pwm_duty_get_ns(machine_pwm_obj_t *self) { - return MP_OBJ_NEW_SMALL_INT(self->duty_ns); + if (!(duty_type_flags[self->device] & (1 << self->channel))) { + return MP_OBJ_NEW_SMALL_INT(get_duty_value(self->device, self->channel)); + } else { + return MP_OBJ_NEW_SMALL_INT(-1); + } } STATIC void mp_machine_pwm_duty_set_ns(machine_pwm_obj_t *self, mp_int_t duty_ns) { - // Remember the values for update & reporting put_duty_value(self->device, self->channel, duty_ns); - self->duty_ns = duty_ns; - self->duty_u16 = 0; - // Ensure that a cycle has passed updating the registers - // since the previous setting before setting a new duty value - wait_for_register_update(self->instance); - self->instance->CCBUF[self->channel].reg = (uint64_t)duty_ns * PWM_MASTER_CLK / self->prescaler / 1000000000ULL; duty_type_flags[self->device] &= ~(1 << self->channel); + mp_machine_pwm_start(self); } diff --git a/ports/samd/mpconfigport.h b/ports/samd/mpconfigport.h index 779bc4713e10b..6ed8186ec00ec 100644 --- a/ports/samd/mpconfigport.h +++ b/ports/samd/mpconfigport.h @@ -107,7 +107,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 (0) +#define MICROPY_PY_MACHINE_PWM_INIT (1) #define MICROPY_PY_MACHINE_PWM_DUTY_U16_NS (1) #define MICROPY_PY_MACHINE_PWM_INCLUDEFILE "ports/samd/machine_pwm.c" #define MICROPY_PY_MACHINE_PIN_MAKE_NEW mp_pin_make_new diff --git a/ports/samd/pin_af.c b/ports/samd/pin_af.c index 2664fd4109947..15743d1dfe6e4 100644 --- a/ports/samd/pin_af.c +++ b/ports/samd/pin_af.c @@ -142,11 +142,6 @@ sercom_pad_config_t get_sercom_config(int pin_id, uint8_t sercom_nr) { adc_config_t get_adc_config(int pin_id, int32_t flag) { const machine_pin_obj_t *pct_ptr = get_pin_obj_ptr(pin_id); - #if defined(MCU_SAMD51) - if (pct_ptr->adc1 != 0xff && (flag & (1 << (pct_ptr->adc1 + 16))) == 0) { - return (adc_config_t) {1, pct_ptr->adc1}; - } else - #endif if (pct_ptr->adc0 != 0xff && (flag & (1 << pct_ptr->adc0)) == 0) { return (adc_config_t) {0, pct_ptr->adc0}; #if defined(MUC_SAMD51) @@ -174,26 +169,22 @@ pwm_config_t get_pwm_config(int pin_id, int wanted_dev, uint8_t device_status[]) return (pwm_config_t) {ALT_FCT_TCC1, tcc1}; } else if ((tcc2 >> 4) == wanted_dev) { return (pwm_config_t) {ALT_FCT_TCC2, tcc2}; - } else { - mp_raise_ValueError(MP_ERROR_TEXT("wrong device or channel")); } } else { - pwm_config_t ret = {}; + // Try to get a unused PWM device at the pin + if (((tcc1 >> 4) < TCC_INST_NUM) && (device_status[tcc1 >> 4] == 0)) { + return (pwm_config_t) {ALT_FCT_TCC1, tcc1}; + } + if (((tcc2 >> 4) < TCC_INST_NUM) && (device_status[tcc2 >> 4] == 0)) { + return (pwm_config_t) {ALT_FCT_TCC2, tcc2}; + } + // If all devices are used, return one from the pin if available if ((tcc1 >> 4) < TCC_INST_NUM) { - ret = (pwm_config_t) {ALT_FCT_TCC1, tcc1}; - if (tcc2 == 0xff) { - return ret; - } + return (pwm_config_t) {ALT_FCT_TCC1, tcc1}; } if ((tcc2 >> 4) < TCC_INST_NUM) { - // if a device in slot 1 is not available or already in use, use the one in slot 2 - if (tcc1 == 0xff || device_status[(ret.device_channel >> 4)] != 0) { - return (pwm_config_t) {ALT_FCT_TCC2, tcc2}; - } else { - return ret; - } - } else { - mp_raise_ValueError(MP_ERROR_TEXT("not a PWM pin")); + return (pwm_config_t) {ALT_FCT_TCC2, tcc2}; } } + mp_raise_ValueError(MP_ERROR_TEXT("not a PWM Pin")); } From 84302b2854334371217015a5924978c009a94d30 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 24 Feb 2023 17:06:34 +0100 Subject: [PATCH 3/9] rp2/machine_pwm: Enable keyword args in constructor and add init method. This adds support for freq/duty_u16/duty_ns keyword arguments in the PWM constructor, and adds the PWM.init() method. Using init() without arguments enables a previously deinit-ed PWM again. Further changes in this commit: - Do not start PWM output if only duty was set. - Stop all PWM slices on soft-reset. - Fix a bug when changing the freq on a channel pair with duty_ns set. --- ports/rp2/machine_pwm.c | 98 +++++++++++++++++++++++++++++++--------- ports/rp2/main.c | 1 + ports/rp2/modmachine.h | 1 + ports/rp2/mpconfigport.h | 1 + 4 files changed, 80 insertions(+), 21 deletions(-) diff --git a/ports/rp2/machine_pwm.c b/ports/rp2/machine_pwm.c index 0c840eca94360..5a0c4751e9222 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 059449695cb62..0ecc09b3461c8 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 6ea4def91d974..45ccd49e8ce60 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 17586e50928fe..653b51091a00f 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) From dc8f9d22ca2ec9328b519e79013c97bbbe18b6c6 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 25 Feb 2023 10:51:56 +0100 Subject: [PATCH 4/9] docs: Update the PWM examples based on recent API improvements. This adds the freq and duty_u16 keyword settings to the constructor, and sometimes other details in the PWM section. For mimxrt a clarification regarding the PWM invert argument was added, and for rp2 a few words were spent on PWM output pairs of a channel/slice. --- docs/esp32/quickref.rst | 4 ++-- docs/library/machine.PWM.rst | 3 ++- docs/mimxrt/quickref.rst | 16 +++++++++------- docs/rp2/quickref.rst | 27 +++++++++++++++++++-------- docs/samd/quickref.rst | 14 ++++++-------- 5 files changed, 38 insertions(+), 26 deletions(-) diff --git a/docs/esp32/quickref.rst b/docs/esp32/quickref.rst index f74926c374602..e19c6ecb40893 100644 --- a/docs/esp32/quickref.rst +++ b/docs/esp32/quickref.rst @@ -305,8 +305,8 @@ Use the :ref:`machine.PWM ` class:: from machine import Pin, PWM - pwm0 = PWM(Pin(0)) # create PWM object from a pin - freq = pwm0.freq() # get current frequency (default 5kHz) + pwm0 = PWM(Pin(0), freq=5000, duty_u16=32768) # create PWM object from a pin + freq = pwm0.freq() # get current frequency pwm0.freq(1000) # set PWM frequency from 1Hz to 40MHz duty = pwm0.duty() # get current duty cycle, range 0-1023 (default 512, 50%) diff --git a/docs/library/machine.PWM.rst b/docs/library/machine.PWM.rst index 4b74355775602..d6c5de4b240ca 100644 --- a/docs/library/machine.PWM.rst +++ b/docs/library/machine.PWM.rst @@ -10,7 +10,8 @@ Example usage:: from machine import PWM - pwm = PWM(pin) # create a PWM object on a pin + pwm = PWM(pin, freq=50, duty_u16=8192) # create a PWM object on a pin + # and set freq and duty pwm.duty_u16(32768) # set duty to 50% # reinitialise with a period of 200us, duty of 5us diff --git a/docs/mimxrt/quickref.rst b/docs/mimxrt/quickref.rst index 06f91f7f50312..ab8bf8831bbb0 100644 --- a/docs/mimxrt/quickref.rst +++ b/docs/mimxrt/quickref.rst @@ -145,11 +145,12 @@ handling signal groups. :: from machine import Pin, PWM - pwm2 = PWM(Pin(2)) # create PWM object from a pin - pwm2.freq() # get current frequency - pwm2.freq(1000) # set frequency - pwm2.duty_u16() # get current duty cycle, range 0-65535 - pwm2.duty_u16(200) # set duty cycle, range 0-65535 + # create PWM object from a pin and set the frequency and duty cycle + pwm2 = PWM(Pin(2), freq=2000, duty_u16=32768) + pwm2.freq() # get the current frequency + pwm2.freq(1000) # set/change the frequency + pwm2.duty_u16() # get the current duty cycle, range 0-65535 + pwm2.duty_u16(200) # set the duty cycle, range 0-65535 pwm2.deinit() # turn off PWM on the pin # create a complementary signal pair on Pin 2 and 3 pwm2 = PWM((2, 3), freq=2000, duty_ns=20000) @@ -206,8 +207,9 @@ PWM Constructor - *align*\=value. Shortcuts for the pulse center setting, causing the pulse either at the center of the frame (value=0), the leading edge at the begin (value=1) or the trailing edge at the end of a pulse period (value=2). - - *invert*\=True|False channel_mask. Setting a bit in the mask inverts the respective channel. - Bit 0 inverts the first specified channel, bit 2 the second. The default is 0. + - *invert*\=value channel_mask. Setting a bit in the mask inverts the respective channel. + Bit 0 inverts the first specified channel, bit 1 the second. The default is 0. For a + PWM object with a single channel, True and False may be used as values. - *sync*\=True|False. If a channel of a module's submodule 0 is already active, other submodules of the same module can be forced to be synchronous to submodule 0. Their pulse period start then at at same clock cycle. The default is False. diff --git a/docs/rp2/quickref.rst b/docs/rp2/quickref.rst index 67a5cc0dfc68e..11a808c110278 100644 --- a/docs/rp2/quickref.rst +++ b/docs/rp2/quickref.rst @@ -146,19 +146,30 @@ See :ref:`machine.UART `. :: PWM (pulse width modulation) ---------------------------- -There are 8 independent channels each of which have 2 outputs making it 16 -PWM channels in total which can be clocked from 7Hz to 125Mhz. +There are 8 independent PWM generators called slices, which each have two +channels making it 16 PWM channels in total which can be clocked from +8Hz to 62.5Mhz at a machine.freq() of 125Mhz. The two channels of a +slice run at the same frequency, but can have a different duty rate. +The two channels are usually assigned to adjacent GPIO pin pairs with +even/odd numbers. So GPIO0 and GPIO1 are at slice 0, GPIO2 and GPIO3 +are at slice 1, and so on. A certain channel can be assigned to +different GPIO pins (see Pinout). For instance slice 0, channel A can be assigned +to both GPIO0 and GPIO16. 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(1000) # set frequency - pwm0.duty_u16() # get current duty cycle, range 0-65535 - pwm0.duty_u16(200) # set duty cycle, range 0-65535 - pwm0.deinit() # turn off PWM on the pin + # create PWM object from a pin and set the frequency of slice 0 + # and duty cycle for channel A + pwm0 = PWM(Pin(0), freq=2000, duty_u16=32768) + pwm0.freq() # get the current frequency of slice 0 + pwm0.freq(1000) # set/change the frequency of slice 0 + pwm0.duty_u16() # get the current duty cycle of channel A, range 0-65535 + pwm0.duty_u16(200) # set the duty cycle of channel A, range 0-65535 + pwm0.duty_u16(0) # stop the output at channel A + print(pwm0) # show the properties of the PWM object. + pwm0.deinit() # turn off PWM of slice 0, stopping channels A and B ADC (analog to digital conversion) ---------------------------------- diff --git a/docs/samd/quickref.rst b/docs/samd/quickref.rst index 7a0786d4cadc6..7da855cb37f74 100644 --- a/docs/samd/quickref.rst +++ b/docs/samd/quickref.rst @@ -178,11 +178,12 @@ It supports all basic methods listed for that class. :: from machine import Pin, PWM - pwm = PWM(Pin(7)) # create PWM object from a pin - pwm.freq() # get current frequency - pwm.freq(1000) # set frequency - pwm.duty_u16() # get current duty cycle, range 0-65535 - pwm.duty_u16(200) # set duty cycle, range 0-65535 + # create PWM object from a pin and set the frequency and duty cycle + pwm = PWM(Pin('D7'), freq=2000, duty_u16=32768) + pwm.freq() # get the current frequency + pwm.freq(1000) # set/change the frequency + pwm.duty_u16() # get the current duty cycle, range 0-65535 + pwm.duty_u16(200) # set the duty cycle, range 0-65535 pwm.deinit() # turn off PWM on the pin pwm # show the PWM objects properties @@ -213,9 +214,6 @@ PWM Constructor - *freq* should be an integer which sets the frequency in Hz for the PWM cycle. The valid frequency range is 1 Hz to 24 MHz. - *duty_u16* sets the duty cycle as a ratio ``duty_u16 / 65536``. - The duty cycle of a X channel can only be changed, if the A and B channel - of the respective submodule is not used. Otherwise the duty_16 value of the - X channel is 32768 (50%). - *duty_ns* sets the pulse width in nanoseconds. The limitation for X channels apply as well. - *invert*\=True|False. Setting a bit inverts the respective output. From 2ac643c15bec8c88ece0e944ce58f36d02dfd2dd Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 25 Feb 2023 14:19:32 +0100 Subject: [PATCH 5/9] esp8266/machine_pwm: Implement duty_u16() and duty_ns() for consistency. Also adds these keyword arguments to the constructor and init method. --- ports/esp8266/machine_pwm.c | 64 +++++++++++++++++++++++++++++++----- ports/esp8266/mpconfigport.h | 1 + 2 files changed, 56 insertions(+), 9 deletions(-) diff --git a/ports/esp8266/machine_pwm.c b/ports/esp8266/machine_pwm.c index 6b7728deadeee..58c8fa915fd56 100644 --- a/ports/esp8266/machine_pwm.c +++ b/ports/esp8266/machine_pwm.c @@ -35,8 +35,11 @@ typedef struct _machine_pwm_obj_t { pyb_pin_obj_t *pin; uint8_t active; uint8_t channel; + int32_t duty_ns; } machine_pwm_obj_t; +STATIC void mp_machine_pwm_duty_set_ns(machine_pwm_obj_t *self, mp_int_t duty); + STATIC bool pwm_inited = false; /******************************************************************************/ @@ -53,10 +56,12 @@ STATIC void mp_machine_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_p } 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 }; + enum { ARG_freq, ARG_duty, ARG_duty_u16, ARG_duty_ns }; 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_duty_u16, MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_duty_ns, MP_ARG_INT, {.u_int = -1} }, }; 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); @@ -74,6 +79,15 @@ STATIC void mp_machine_pwm_init_helper(machine_pwm_obj_t *self, size_t n_args, c if (args[ARG_duty].u_int != -1) { pwm_set_duty(args[ARG_duty].u_int, self->channel); } + if (args[ARG_duty_u16].u_int != -1) { + pwm_set_duty(args[ARG_duty_u16].u_int * 1000 / 65536, self->channel); + } + if (args[ARG_duty_ns].u_int != -1) { + uint32_t freq = pwm_get_freq(0); + if (freq > 0) { + pwm_set_duty((uint64_t)args[ARG_duty_ns].u_int * freq / 1000000, self->channel); + } + } if (pin_mode[self->pin->phys_port] == GPIO_MODE_OPEN_DRAIN) { mp_hal_pin_open_drain(self->pin->phys_port); @@ -91,6 +105,7 @@ STATIC mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args self->pin = pin; self->active = 0; self->channel = -1; + self->duty_ns = -1; // start the PWM subsystem if it's not already running if (!pwm_inited) { @@ -118,26 +133,57 @@ STATIC mp_obj_t mp_machine_pwm_freq_get(machine_pwm_obj_t *self) { STATIC void mp_machine_pwm_freq_set(machine_pwm_obj_t *self, mp_int_t freq) { pwm_set_freq(freq, 0); - pwm_start(); + if (self->duty_ns != -1) { + mp_machine_pwm_duty_set_ns(self, self->duty_ns); + } else { + pwm_start(); + } } -STATIC mp_obj_t mp_machine_pwm_duty_get(machine_pwm_obj_t *self) { +STATIC void set_active(machine_pwm_obj_t *self, bool set_pin) { if (!self->active) { pwm_add(self->pin->phys_port, self->pin->periph, self->pin->func); self->active = 1; - - if (pin_mode[self->pin->phys_port] == GPIO_MODE_OPEN_DRAIN) { + if (set_pin && pin_mode[self->pin->phys_port] == GPIO_MODE_OPEN_DRAIN) { mp_hal_pin_open_drain(self->pin->phys_port); } } +} + +STATIC mp_obj_t mp_machine_pwm_duty_get(machine_pwm_obj_t *self) { + set_active(self, true); return MP_OBJ_NEW_SMALL_INT(pwm_get_duty(self->channel)); } STATIC void mp_machine_pwm_duty_set(machine_pwm_obj_t *self, mp_int_t duty) { - if (!self->active) { - pwm_add(self->pin->phys_port, self->pin->periph, self->pin->func); - self->active = 1; - } + set_active(self, false); + self->duty_ns = -1; pwm_set_duty(duty, self->channel); pwm_start(); } + +STATIC mp_obj_t mp_machine_pwm_duty_get_u16(machine_pwm_obj_t *self) { + set_active(self, true); + return MP_OBJ_NEW_SMALL_INT(pwm_get_duty(self->channel) * 65536 / 1024); +} + +STATIC void mp_machine_pwm_duty_set_u16(machine_pwm_obj_t *self, mp_int_t duty) { + set_active(self, false); + self->duty_ns = -1; + pwm_set_duty(duty * 1024 / 65536, self->channel); + pwm_start(); +} + +STATIC mp_obj_t mp_machine_pwm_duty_get_ns(machine_pwm_obj_t *self) { + set_active(self, true); + uint32_t freq = pwm_get_freq(0); + return MP_OBJ_NEW_SMALL_INT(pwm_get_duty(self->channel) * 976563 / freq); +} + +STATIC void mp_machine_pwm_duty_set_ns(machine_pwm_obj_t *self, mp_int_t duty) { + set_active(self, false); + self->duty_ns = duty; + uint32_t freq = pwm_get_freq(0); + pwm_set_duty(duty * freq / 976562, self->channel); // 1e9/1024 = 976562.5 + pwm_start(); +} diff --git a/ports/esp8266/mpconfigport.h b/ports/esp8266/mpconfigport.h index 2111d33e953cd..54549614e543d 100644 --- a/ports/esp8266/mpconfigport.h +++ b/ports/esp8266/mpconfigport.h @@ -68,6 +68,7 @@ #define MICROPY_PY_MACHINE_PWM (1) #define MICROPY_PY_MACHINE_PWM_INIT (1) #define MICROPY_PY_MACHINE_PWM_DUTY (1) +#define MICROPY_PY_MACHINE_PWM_DUTY_U16_NS (1) #define MICROPY_PY_MACHINE_PWM_INCLUDEFILE "ports/esp8266/machine_pwm.c" #define MICROPY_PY_MACHINE_I2C (1) #define MICROPY_PY_MACHINE_SOFTI2C (1) From 0b3b508d1d681e705c4c228cb846d10549e644af Mon Sep 17 00:00:00 2001 From: robert-hh Date: Tue, 28 Feb 2023 12:43:52 +0100 Subject: [PATCH 6/9] rp2/machine_pwm: Add support for inverting a PWM channel output. Using the invert=True|False keyword option with the constructor or init(). --- docs/library/machine.PWM.rst | 4 +- ports/rp2/machine_pwm.c | 86 +++++++++++++++++++++--------------- 2 files changed, 54 insertions(+), 36 deletions(-) diff --git a/docs/library/machine.PWM.rst b/docs/library/machine.PWM.rst index d6c5de4b240ca..b9cf00240314c 100644 --- a/docs/library/machine.PWM.rst +++ b/docs/library/machine.PWM.rst @@ -24,7 +24,7 @@ Example usage:: Constructors ------------ -.. class:: PWM(dest, *, freq, duty_u16, duty_ns) +.. class:: PWM(dest, *, freq, duty_u16, duty_ns, invert) Construct and return a new PWM object using the following parameters: @@ -35,10 +35,12 @@ Constructors PWM cycle. - *duty_u16* sets the duty cycle as a ratio ``duty_u16 / 65535``. - *duty_ns* sets the pulse width in nanoseconds. + - *invert* inverts the respective output if the value is True Setting *freq* may affect other PWM objects if the objects share the same underlying PWM generator (this is hardware specific). Only one of *duty_u16* and *duty_ns* should be specified at a time. + *invert* is not available at all ports. Methods ------- diff --git a/ports/rp2/machine_pwm.c b/ports/rp2/machine_pwm.c index 5a0c4751e9222..5ab1efe0aacdc 100644 --- a/ports/rp2/machine_pwm.c +++ b/ports/rp2/machine_pwm.c @@ -38,8 +38,8 @@ typedef struct _machine_pwm_obj_t { mp_obj_base_t base; uint8_t slice; uint8_t channel; + uint8_t invert; uint8_t duty_type; - bool freq_set; mp_int_t duty; } machine_pwm_obj_t; @@ -51,47 +51,57 @@ enum { }; STATIC machine_pwm_obj_t machine_pwm_obj[] = { - {{&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 }, + {{&machine_pwm_type}, 0, PWM_CHAN_A, 0, DUTY_NOT_SET, 0 }, + {{&machine_pwm_type}, 0, PWM_CHAN_B, 0, DUTY_NOT_SET, 0 }, + {{&machine_pwm_type}, 1, PWM_CHAN_A, 0, DUTY_NOT_SET, 0 }, + {{&machine_pwm_type}, 1, PWM_CHAN_B, 0, DUTY_NOT_SET, 0 }, + {{&machine_pwm_type}, 2, PWM_CHAN_A, 0, DUTY_NOT_SET, 0 }, + {{&machine_pwm_type}, 2, PWM_CHAN_B, 0, DUTY_NOT_SET, 0 }, + {{&machine_pwm_type}, 3, PWM_CHAN_A, 0, DUTY_NOT_SET, 0 }, + {{&machine_pwm_type}, 3, PWM_CHAN_B, 0, DUTY_NOT_SET, 0 }, + {{&machine_pwm_type}, 4, PWM_CHAN_A, 0, DUTY_NOT_SET, 0 }, + {{&machine_pwm_type}, 4, PWM_CHAN_B, 0, DUTY_NOT_SET, 0 }, + {{&machine_pwm_type}, 5, PWM_CHAN_A, 0, DUTY_NOT_SET, 0 }, + {{&machine_pwm_type}, 5, PWM_CHAN_B, 0, DUTY_NOT_SET, 0 }, + {{&machine_pwm_type}, 6, PWM_CHAN_A, 0, DUTY_NOT_SET, 0 }, + {{&machine_pwm_type}, 6, PWM_CHAN_B, 0, DUTY_NOT_SET, 0 }, + {{&machine_pwm_type}, 7, PWM_CHAN_A, 0, DUTY_NOT_SET, 0 }, + {{&machine_pwm_type}, 7, PWM_CHAN_B, 0, DUTY_NOT_SET, 0 }, }; +STATIC bool defer_start; +STATIC bool slice_freq_set[8]; + 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); 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, "", self->slice, self->channel); + mp_printf(print, "", + self->slice, self->channel, self->invert); } 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) { + if (defer_start == false && slice_freq_set[self->slice] == true && self->duty_type != DUTY_NOT_SET) { + if (self->channel == PWM_CHAN_A) { + pwm_set_output_polarity(self->slice, self->invert, (self + 1)->invert); + } else { + pwm_set_output_polarity(self->slice, (self - 1)->invert, self->invert); + } 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 }; + enum { ARG_freq, ARG_duty_u16, ARG_duty_ns, ARG_invert }; 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} }, + { MP_QSTR_invert, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = VALUE_NOT_SET} }, }; // Parse the arguments. @@ -99,19 +109,22 @@ 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); - 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); + // defer starting PWM until all provided args are checked. + defer_start = true; + 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); } + if (args[ARG_invert].u_int != VALUE_NOT_SET) { + self->invert = !!args[ARG_invert].u_int; + } + defer_start = false; + machine_pwm_start(self); } // PWM(pin [, args]) @@ -126,7 +139,7 @@ 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->invert = 0; self->duty_type = DUTY_NOT_SET; // Select PWM function for given GPIO. @@ -143,6 +156,7 @@ STATIC mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args // Stop all active slices. void machine_pwm_deinit_all(void) { for (int i = 0; i < 8; i++) { + slice_freq_set[i] = false; pwm_set_enabled(machine_pwm_obj[i].slice, false); } } @@ -216,14 +230,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; + slice_freq_set[self->slice] = 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) { + if (other->duty_type == DUTY_U16) { + mp_machine_pwm_duty_set_u16(other, other->duty); + } else if (other->duty_type == DUTY_NS) { mp_machine_pwm_duty_set_ns(other, other->duty); } } From 7952694a3cbf54ddc07268b84c45c80a8c6169e8 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 27 Apr 2023 21:21:56 +0200 Subject: [PATCH 7/9] rp2/machine_pwm: Add duty_x() checks and return 0 if PWM is not started. Changes in this commit: - Limit duty_u16() to 65535 and duty_ns() to the period duration. - Return 0 for pwm.freq() if the frequency has not been set yet. - Return 0 for pwm.duty_us16() and duty_ns() unless both frequency and duty cycle have been set. - Initialize the pin to PWM at the very end of the constructor, to avoid possible glitches on the pin when setting up the PWM. --- ports/rp2/machine_pwm.c | 57 ++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/ports/rp2/machine_pwm.c b/ports/rp2/machine_pwm.c index 5ab1efe0aacdc..a14ba0a43abb9 100644 --- a/ports/rp2/machine_pwm.c +++ b/ports/rp2/machine_pwm.c @@ -142,14 +142,14 @@ STATIC mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args self->invert = 0; 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); + // Select PWM function for given GPIO. + gpio_set_function(gpio, GPIO_FUNC_PWM); + return MP_OBJ_FROM_PTR(self); } @@ -189,10 +189,14 @@ uint32_t get_slice_hz_ceil(uint32_t div16) { } STATIC mp_obj_t mp_machine_pwm_freq_get(machine_pwm_obj_t *self) { - uint32_t div16 = pwm_hw->slice[self->slice].div; - uint32_t top = pwm_hw->slice[self->slice].top; - uint32_t pwm_freq = get_slice_hz_round(div16 * (top + 1)); - return MP_OBJ_NEW_SMALL_INT(pwm_freq); + if (slice_freq_set[self->slice] == true) { + uint32_t div16 = pwm_hw->slice[self->slice].div; + uint32_t top = pwm_hw->slice[self->slice].top; + uint32_t pwm_freq = get_slice_hz_round(div16 * (top + 1)); + return MP_OBJ_NEW_SMALL_INT(pwm_freq); + } else { + return MP_OBJ_NEW_SMALL_INT(0); + } } STATIC void mp_machine_pwm_freq_set(machine_pwm_obj_t *self, mp_int_t freq) { @@ -245,19 +249,27 @@ STATIC void mp_machine_pwm_freq_set(machine_pwm_obj_t *self, mp_int_t freq) { } STATIC mp_obj_t mp_machine_pwm_duty_get_u16(machine_pwm_obj_t *self) { - uint32_t top = pwm_hw->slice[self->slice].top; - uint32_t cc = pwm_hw->slice[self->slice].cc; - cc = (cc >> (self->channel ? PWM_CH0_CC_B_LSB : PWM_CH0_CC_A_LSB)) & 0xffff; - - // Use rounding (instead of flooring) here to give as accurate an - // estimate as possible. - return MP_OBJ_NEW_SMALL_INT((cc * 65535 + (top + 1) / 2) / (top + 1)); + if (self->duty_type != DUTY_NOT_SET && slice_freq_set[self->slice] == true) { + uint32_t top = pwm_hw->slice[self->slice].top; + uint32_t cc = pwm_hw->slice[self->slice].cc; + cc = (cc >> (self->channel ? PWM_CH0_CC_B_LSB : PWM_CH0_CC_A_LSB)) & 0xffff; + + // Use rounding (instead of flooring) here to give as accurate an + // estimate as possible. + return MP_OBJ_NEW_SMALL_INT((cc * 65535 + (top + 1) / 2) / (top + 1)); + } else { + return MP_OBJ_NEW_SMALL_INT(0); + } } STATIC void mp_machine_pwm_duty_set_u16(machine_pwm_obj_t *self, mp_int_t duty_u16) { uint32_t top = pwm_hw->slice[self->slice].top; + // Limit duty_u16 to 65535 // Use rounding here to set it as accurately as possible. + if (duty_u16 > 65535) { + duty_u16 = 65535; + } uint32_t cc = (duty_u16 * (top + 1) + 65535 / 2) / 65535; pwm_set_chan_level(self->slice, self->channel, cc); self->duty = duty_u16; @@ -266,17 +278,22 @@ STATIC void mp_machine_pwm_duty_set_u16(machine_pwm_obj_t *self, mp_int_t duty_u } STATIC mp_obj_t mp_machine_pwm_duty_get_ns(machine_pwm_obj_t *self) { - uint32_t slice_hz = get_slice_hz_round(pwm_hw->slice[self->slice].div); - uint32_t cc = pwm_hw->slice[self->slice].cc; - cc = (cc >> (self->channel ? PWM_CH0_CC_B_LSB : PWM_CH0_CC_A_LSB)) & 0xffff; - return MP_OBJ_NEW_SMALL_INT(((uint64_t)cc * 1000000000ULL + slice_hz / 2) / slice_hz); + if (self->duty_type != DUTY_NOT_SET && slice_freq_set[self->slice] == true) { + uint32_t slice_hz = get_slice_hz_round(pwm_hw->slice[self->slice].div); + uint32_t cc = pwm_hw->slice[self->slice].cc; + cc = (cc >> (self->channel ? PWM_CH0_CC_B_LSB : PWM_CH0_CC_A_LSB)) & 0xffff; + return MP_OBJ_NEW_SMALL_INT(((uint64_t)cc * 1000000000ULL + slice_hz / 2) / slice_hz); + } else { + return MP_OBJ_NEW_SMALL_INT(0); + } } STATIC void mp_machine_pwm_duty_set_ns(machine_pwm_obj_t *self, mp_int_t duty_ns) { uint32_t slice_hz = get_slice_hz_round(pwm_hw->slice[self->slice].div); uint32_t cc = ((uint64_t)duty_ns * slice_hz + 500000000ULL) / 1000000000ULL; - if (cc > 65535) { - mp_raise_ValueError(MP_ERROR_TEXT("duty larger than period")); + uint32_t top = pwm_hw->slice[self->slice].top; + if (cc > (top + 1)) { + cc = top + 1; } pwm_set_chan_level(self->slice, self->channel, cc); self->duty = duty_ns; From 38243cd8e0792fd6e597e4761f9d9949a9d41599 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 4 May 2023 12:58:04 +1000 Subject: [PATCH 8/9] extmod/machine_pwm: Remove PWM_INIT and PWM_DUTY_U16_NS config options. All ports that enable MICROPY_PY_MACHINE_PWM now enable these two sub-options, so remove these sub-options altogether to force consistency in new ports that implement machine.PWM. Signed-off-by: Damien George --- extmod/machine_pwm.c | 10 ---------- ports/esp32/mpconfigport.h | 2 -- ports/esp8266/mpconfigport.h | 2 -- ports/mimxrt/mpconfigport.h | 2 -- ports/nrf/mpconfigport.h | 2 -- ports/rp2/mpconfigport.h | 2 -- ports/samd/mpconfigport.h | 2 -- 7 files changed, 22 deletions(-) diff --git a/extmod/machine_pwm.c b/extmod/machine_pwm.c index 220d34d7da582..8a633b37939d4 100644 --- a/extmod/machine_pwm.c +++ b/extmod/machine_pwm.c @@ -34,13 +34,11 @@ #include MICROPY_PY_MACHINE_PWM_INCLUDEFILE #endif -#if MICROPY_PY_MACHINE_PWM_INIT STATIC mp_obj_t machine_pwm_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { mp_machine_pwm_init_helper(args[0], n_args - 1, args + 1, kw_args); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_pwm_init_obj, 1, machine_pwm_init); -#endif // PWM.deinit() STATIC mp_obj_t machine_pwm_deinit(mp_obj_t self_in) { @@ -82,8 +80,6 @@ STATIC mp_obj_t machine_pwm_duty(size_t n_args, const mp_obj_t *args) { STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pwm_duty_obj, 1, 2, machine_pwm_duty); #endif -#if MICROPY_PY_MACHINE_PWM_DUTY_U16_NS - // PWM.duty_u16([value]) STATIC mp_obj_t machine_pwm_duty_u16(size_t n_args, const mp_obj_t *args) { machine_pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]); @@ -114,21 +110,15 @@ STATIC mp_obj_t machine_pwm_duty_ns(size_t n_args, const mp_obj_t *args) { } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pwm_duty_ns_obj, 1, 2, machine_pwm_duty_ns); -#endif - STATIC const mp_rom_map_elem_t machine_pwm_locals_dict_table[] = { - #if MICROPY_PY_MACHINE_PWM_INIT { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_pwm_init_obj) }, - #endif { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_pwm_deinit_obj) }, { MP_ROM_QSTR(MP_QSTR_freq), MP_ROM_PTR(&machine_pwm_freq_obj) }, #if MICROPY_PY_MACHINE_PWM_DUTY { MP_ROM_QSTR(MP_QSTR_duty), MP_ROM_PTR(&machine_pwm_duty_obj) }, #endif - #if MICROPY_PY_MACHINE_PWM_DUTY_U16_NS { MP_ROM_QSTR(MP_QSTR_duty_u16), MP_ROM_PTR(&machine_pwm_duty_u16_obj) }, { MP_ROM_QSTR(MP_QSTR_duty_ns), MP_ROM_PTR(&machine_pwm_duty_ns_obj) }, - #endif }; STATIC MP_DEFINE_CONST_DICT(machine_pwm_locals_dict, machine_pwm_locals_dict_table); diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index 807ae23b0cd0c..ea3487bcd8e33 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -98,9 +98,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 (1) -#define MICROPY_PY_MACHINE_PWM_DUTY_U16_NS (1) #define MICROPY_PY_MACHINE_PWM_INCLUDEFILE "ports/esp32/machine_pwm.c" #define MICROPY_PY_MACHINE_I2C (1) #define MICROPY_PY_MACHINE_I2C_TRANSFER_WRITE1 (1) diff --git a/ports/esp8266/mpconfigport.h b/ports/esp8266/mpconfigport.h index 54549614e543d..12ea3716d49d3 100644 --- a/ports/esp8266/mpconfigport.h +++ b/ports/esp8266/mpconfigport.h @@ -66,9 +66,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 (1) -#define MICROPY_PY_MACHINE_PWM_DUTY_U16_NS (1) #define MICROPY_PY_MACHINE_PWM_INCLUDEFILE "ports/esp8266/machine_pwm.c" #define MICROPY_PY_MACHINE_I2C (1) #define MICROPY_PY_MACHINE_SOFTI2C (1) diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index b1fdf14f22207..1dd7711efed53 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -83,8 +83,6 @@ uint32_t trng_random_u32(void); #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/mimxrt/machine_pwm.c" #define MICROPY_PY_MACHINE_I2C (1) #ifndef MICROPY_PY_MACHINE_I2S diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index a3ceb023ee649..23f7e560dc542 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -197,9 +197,7 @@ #define MICROPY_PY_MACHINE_SOFT_PWM (0) #endif -#define MICROPY_PY_MACHINE_PWM_INIT (1) #define MICROPY_PY_MACHINE_PWM_DUTY (1) -#define MICROPY_PY_MACHINE_PWM_DUTY_U16_NS (1) #if MICROPY_PY_MACHINE_HW_PWM #define MICROPY_PY_MACHINE_PWM_INCLUDEFILE "ports/nrf/modules/machine/pwm.c" diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index 653b51091a00f..b4487a23d7094 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -114,8 +114,6 @@ #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) #define MICROPY_PY_MACHINE_SOFTI2C (1) diff --git a/ports/samd/mpconfigport.h b/ports/samd/mpconfigport.h index 6ed8186ec00ec..bc1ad3d9889eb 100644 --- a/ports/samd/mpconfigport.h +++ b/ports/samd/mpconfigport.h @@ -107,8 +107,6 @@ #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/samd/machine_pwm.c" #define MICROPY_PY_MACHINE_PIN_MAKE_NEW mp_pin_make_new From 786013d467d8beace7c7a8a2cd1122c799d05dae Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 25 Feb 2023 10:51:56 +0100 Subject: [PATCH 9/9] docs/samd: Make use of pin names more consistent in examples. This keeps up with the changed Pin naming scheme. --- docs/samd/pinout.rst | 2 +- docs/samd/quickref.rst | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/samd/pinout.rst b/docs/samd/pinout.rst index 46d9d7a29d1b1..702da2c1784eb 100644 --- a/docs/samd/pinout.rst +++ b/docs/samd/pinout.rst @@ -105,7 +105,7 @@ or other combinations. SAMD21 SPI assignments `````````````````````` -The I2C devices and signals must be chosen according to the following rules: +The SPI devices and signals must be chosen according to the following rules: - The following pad number pairs are suitable for MOSI/SCK: 0/1, 2/3, 3/1, and 0/3. - The MISO signal must be at a Pin with a different pad number than MOSI or SCK. diff --git a/docs/samd/quickref.rst b/docs/samd/quickref.rst index 7da855cb37f74..60c57b3a47a31 100644 --- a/docs/samd/quickref.rst +++ b/docs/samd/quickref.rst @@ -132,7 +132,7 @@ Use the :ref:`machine.Pin ` class:: print(p2.value()) # get value, 0 or 1 p4 = Pin('D4', Pin.IN, Pin.PULL_UP) # enable internal pull-up resistor - p7 = Pin("PA07", Pin.OUT, value=1) # set pin high on creation + p7 = Pin('PA07', Pin.OUT, value=1) # set pin high on creation Pins can be denoted by a string or a number. The string is either the pin label of the respective board, like "D0" or "SDA", or in the form @@ -157,7 +157,7 @@ See :ref:`machine.UART `. :: # Use UART 3 on a ItsyBitsy M4 board from machine import UART - uart3 = UART(3, tx=Pin(1), rx=Pin(0), baudrate=115200) + uart3 = UART(3, tx=Pin('D1'), rx=Pin('D0'), baudrate=115200) uart3.write('hello') # write 5 bytes uart3.read(5) # read up to 5 bytes @@ -243,9 +243,9 @@ Use the :ref:`machine.ADC ` class:: from machine import ADC - adc0 = ADC(Pin("A0")) # create ADC object on ADC pin, average=16 + adc0 = ADC(Pin('A0')) # create ADC object on ADC pin, average=16 adc0.read_u16() # read value, 0-65536 across voltage range 0.0v - 3.3v - adc1 = ADC(Pin("A1"), average=1) # create ADC object on ADC pin, average=1 + adc1 = ADC(Pin('A1'), average=1) # create ADC object on ADC pin, average=1 The resolution of the ADC is 12 bit with 12 bit accuracy, irrespective of the value returned by read_u16(). If you need a higher resolution or better accuracy, use @@ -339,7 +339,7 @@ Software SPI (using bit-banging) works on all pins, and is accessed via the # construct a SoftSPI bus on the given pins # polarity is the idle state of SCK # phase=0 means sample on the first edge of SCK, phase=1 means the second - spi = SoftSPI(baudrate=100000, polarity=1, phase=0, sck=Pin(7), mosi=Pin(9), miso=Pin(10)) + spi = SoftSPI(baudrate=100000, polarity=1, phase=0, sck=Pin('D7'), mosi=Pin('D9'), miso=Pin('D10')) spi.init(baudrate=200000) # set the baud rate @@ -386,7 +386,7 @@ accessed via the :ref:`machine.SoftI2C ` class:: from machine import Pin, SoftI2C - i2c = SoftI2C(scl=Pin(10), sda=Pin(11), freq=100000) + i2c = SoftI2C(scl=Pin('D10'), sda=Pin('D11'), freq=100000) i2c.scan() # scan for devices @@ -422,7 +422,7 @@ The OneWire driver is implemented in software and works on all pins:: from machine import Pin import onewire - ow = onewire.OneWire(Pin(12)) # create a OneWire bus on GPIO12 + ow = onewire.OneWire(Pin('D12')) # create a OneWire bus on GPIO12 ow.scan() # return a list of devices on the bus ow.reset() # reset the bus ow.readbyte() # read a byte @@ -452,12 +452,12 @@ The DHT driver is implemented in software and works on all pins:: import dht import machine - d = dht.DHT11(machine.Pin(4)) + d = dht.DHT11(machine.Pin('D4')) d.measure() d.temperature() # eg. 23 (°C) d.humidity() # eg. 41 (% RH) - d = dht.DHT22(machine.Pin(4)) + d = dht.DHT22(machine.Pin('D4')) d.measure() d.temperature() # eg. 23.6 (°C) d.humidity() # eg. 41.3 (% RH) @@ -472,7 +472,7 @@ The APA102 on some Adafruit boards can be controlled using SoftSPI:: from machine import SoftSPI, Pin # create the SPI object. miso can be any unused pin. - spi=SoftSPI(sck=Pin(25), mosi=Pin(26), miso=Pin(14)) + spi=SoftSPI(sck=Pin('D25'), mosi=Pin('D26'), miso=Pin('D14')) # define a little function that writes the data with # preamble and postfix @@ -497,7 +497,7 @@ with the Neopixel driver from the MicroPython driver library:: import machine # 1 LED connected to Pin D8 on Adafruit Feather boards - p = machine.Pin(8, machine.Pin.OUT) + p = machine.Pin('D8', machine.Pin.OUT) n = neopixel.NeoPixel(p, 1) # set the led to red. 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