diff --git a/extmod/extmod.cmake b/extmod/extmod.cmake index c6b45b0d3e..69ec39759b 100644 --- a/extmod/extmod.cmake +++ b/extmod/extmod.cmake @@ -10,6 +10,7 @@ set(MICROPY_SOURCE_EXTMOD ${MICROPY_EXTMOD_DIR}/machine_i2c.c ${MICROPY_EXTMOD_DIR}/machine_mem.c ${MICROPY_EXTMOD_DIR}/machine_pulse.c + ${MICROPY_EXTMOD_DIR}/machine_pwm.c ${MICROPY_EXTMOD_DIR}/machine_signal.c ${MICROPY_EXTMOD_DIR}/machine_spi.c ${MICROPY_EXTMOD_DIR}/modbluetooth.c diff --git a/extmod/machine_pwm.c b/extmod/machine_pwm.c new file mode 100644 index 0000000000..ddf49c1358 --- /dev/null +++ b/extmod/machine_pwm.c @@ -0,0 +1,143 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/runtime.h" + +#if MICROPY_PY_MACHINE_PWM + +#include "extmod/machine_pwm.h" + +#ifdef MICROPY_PY_MACHINE_PWM_INCLUDEFILE +#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) { + machine_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_machine_pwm_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_pwm_deinit_obj, machine_pwm_deinit); + +// PWM.freq([value]) +STATIC mp_obj_t machine_pwm_freq(size_t n_args, const mp_obj_t *args) { + machine_pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]); + if (n_args == 1) { + // Get frequency. + return mp_machine_pwm_freq_get(self); + } else { + // Set the frequency. + mp_int_t freq = mp_obj_get_int(args[1]); + mp_machine_pwm_freq_set(self, freq); + return mp_const_none; + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pwm_freq_obj, 1, 2, machine_pwm_freq); + +#if MICROPY_PY_MACHINE_PWM_DUTY +// PWM.duty([duty]) +STATIC mp_obj_t machine_pwm_duty(size_t n_args, const mp_obj_t *args) { + machine_pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]); + if (n_args == 1) { + // Get duty cycle. + return mp_machine_pwm_duty_get(self); + } else { + // Set duty cycle. + mp_int_t duty = mp_obj_get_int(args[1]); + mp_machine_pwm_duty_set(self, duty); + return mp_const_none; + } +} +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]); + if (n_args == 1) { + // Get duty cycle. + return mp_machine_pwm_duty_get_u16(self); + } else { + // Set duty cycle. + mp_int_t duty_u16 = mp_obj_get_int(args[1]); + mp_machine_pwm_duty_set_u16(self, duty_u16); + return mp_const_none; + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pwm_duty_u16_obj, 1, 2, machine_pwm_duty_u16); + +// PWM.duty_ns([value]) +STATIC mp_obj_t machine_pwm_duty_ns(size_t n_args, const mp_obj_t *args) { + machine_pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]); + if (n_args == 1) { + // Get duty cycle. + return mp_machine_pwm_duty_get_ns(self); + } else { + // Set duty cycle. + mp_int_t duty_ns = mp_obj_get_int(args[1]); + mp_machine_pwm_duty_set_ns(self, duty_ns); + return mp_const_none; + } +} +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); + +const mp_obj_type_t machine_pwm_type = { + { &mp_type_type }, + .name = MP_QSTR_PWM, + .print = mp_machine_pwm_print, + .make_new = mp_machine_pwm_make_new, + .locals_dict = (mp_obj_dict_t *)&machine_pwm_locals_dict, +}; + +#endif // MICROPY_PY_MACHINE_PWM diff --git a/extmod/machine_pwm.h b/extmod/machine_pwm.h new file mode 100644 index 0000000000..f0953014ca --- /dev/null +++ b/extmod/machine_pwm.h @@ -0,0 +1,55 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_EXTMOD_MACHINE_PWM_H +#define MICROPY_INCLUDED_EXTMOD_MACHINE_PWM_H + +#include "py/obj.h" + +// A port must provide this type, but it's otherwise opaque. +typedef struct _machine_pwm_obj_t machine_pwm_obj_t; + +// This PWM class is implemented by machine_pwm.c. +extern const mp_obj_type_t machine_pwm_type; + +// A port must provide implementations of these low-level PWM functions, either as global +// linker symbols, or included directly if MICROPY_PY_MACHINE_PWM_INCLUDEFILE is defined. +#ifndef MICROPY_PY_MACHINE_PWM_INCLUDEFILE +void mp_machine_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind); +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); +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); +void mp_machine_pwm_deinit(machine_pwm_obj_t *self); +mp_obj_t mp_machine_pwm_freq_get(machine_pwm_obj_t *self); +void mp_machine_pwm_freq_set(machine_pwm_obj_t *self, mp_int_t freq); +mp_obj_t mp_machine_pwm_duty_get(machine_pwm_obj_t *self); +void mp_machine_pwm_duty_set(machine_pwm_obj_t *self, mp_int_t duty); +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_u16); +mp_obj_t mp_machine_pwm_duty_get_ns(machine_pwm_obj_t *self); +void mp_machine_pwm_duty_set_ns(machine_pwm_obj_t *self, mp_int_t duty_ns); +#endif + +#endif // MICROPY_INCLUDED_EXTMOD_MACHINE_PWM_H diff --git a/ports/esp32/machine_pwm.c b/ports/esp32/machine_pwm.c index a7d7d29df8..9fe06aa699 100644 --- a/ports/esp32/machine_pwm.c +++ b/ports/esp32/machine_pwm.c @@ -23,25 +23,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include + +#include "py/runtime.h" +#include "py/mphal.h" + #include "driver/ledc.h" #include "esp_err.h" -#include "py/nlr.h" -#include "py/runtime.h" -#include "modmachine.h" -#include "mphalport.h" - -// 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; -} esp32_pwm_obj_t; - // Which channel has which GPIO pin assigned? // (-1 if not assigned) STATIC int chan_gpio[LEDC_CHANNEL_MAX]; @@ -110,11 +98,17 @@ STATIC int set_freq(int newval) { } /******************************************************************************/ - // MicroPython bindings for PWM -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); +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); if (self->active) { mp_printf(print, ", freq=%u, duty=%u", timer_cfg.freq_hz, @@ -123,7 +117,7 @@ STATIC void esp32_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_print_ mp_printf(print, ")"); } -STATIC void esp32_pwm_init_helper(esp32_pwm_obj_t *self, +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 }; static const mp_arg_t allowed_args[] = { @@ -192,13 +186,13 @@ STATIC void esp32_pwm_init_helper(esp32_pwm_obj_t *self, } } -STATIC mp_obj_t esp32_pwm_make_new(const mp_obj_type_t *type, +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) { mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); gpio_num_t pin_id = machine_pin_get_id(args[0]); // create PWM object from the given pin - esp32_pwm_obj_t *self = m_new_obj(esp32_pwm_obj_t); + 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; @@ -213,20 +207,12 @@ STATIC mp_obj_t esp32_pwm_make_new(const mp_obj_type_t *type, // start the PWM running for this channel mp_map_t kw_args; mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); - esp32_pwm_init_helper(self, n_args - 1, args + 1, &kw_args); + mp_machine_pwm_init_helper(self, n_args - 1, args + 1, &kw_args); return MP_OBJ_FROM_PTR(self); } -STATIC mp_obj_t esp32_pwm_init(size_t n_args, - const mp_obj_t *args, mp_map_t *kw_args) { - esp32_pwm_init_helper(args[0], n_args - 1, args + 1, kw_args); - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_KW(esp32_pwm_init_obj, 1, esp32_pwm_init); - -STATIC mp_obj_t esp32_pwm_deinit(mp_obj_t self_in) { - esp32_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in); +STATIC void mp_machine_pwm_deinit(machine_pwm_obj_t *self) { int chan = self->channel; // Valid channel? @@ -238,63 +224,27 @@ STATIC mp_obj_t esp32_pwm_deinit(mp_obj_t self_in) { self->channel = -1; gpio_matrix_out(self->pin, SIG_GPIO_OUT_IDX, false, false); } - 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) { - if (n_args == 1) { - // get - return MP_OBJ_NEW_SMALL_INT(timer_cfg.freq_hz); - } - - // set - int tval = mp_obj_get_int(args[1]); - if (!set_freq(tval)) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("bad frequency %d"), tval); - } - return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp32_pwm_freq_obj, 1, 2, esp32_pwm_freq); +STATIC mp_obj_t mp_machine_pwm_freq_get(machine_pwm_obj_t *self) { + return MP_OBJ_NEW_SMALL_INT(timer_cfg.freq_hz); +} -STATIC mp_obj_t esp32_pwm_duty(size_t n_args, const mp_obj_t *args) { - esp32_pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]); - int duty; - - if (n_args == 1) { - // get - duty = ledc_get_duty(PWMODE, self->channel); - duty <<= PWRES - timer_cfg.duty_resolution; - return MP_OBJ_NEW_SMALL_INT(duty); +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); } +} - // set - duty = mp_obj_get_int(args[1]); +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); +} + +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); - - return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp32_pwm_duty_obj, - 1, 2, esp32_pwm_duty); - -STATIC const mp_rom_map_elem_t esp32_pwm_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&esp32_pwm_init_obj) }, - { 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) }, -}; - -STATIC MP_DEFINE_CONST_DICT(esp32_pwm_locals_dict, - esp32_pwm_locals_dict_table); - -const mp_obj_type_t machine_pwm_type = { - { &mp_type_type }, - .name = MP_QSTR_PWM, - .print = esp32_pwm_print, - .make_new = esp32_pwm_make_new, - .locals_dict = (mp_obj_dict_t *)&esp32_pwm_locals_dict, -}; diff --git a/ports/esp32/main/CMakeLists.txt b/ports/esp32/main/CMakeLists.txt index 057ec02441..75e123f4e0 100644 --- a/ports/esp32/main/CMakeLists.txt +++ b/ports/esp32/main/CMakeLists.txt @@ -62,7 +62,6 @@ set(MICROPY_SOURCE_PORT ${PROJECT_DIR}/machine_dac.c ${PROJECT_DIR}/machine_i2c.c ${PROJECT_DIR}/machine_i2s.c - ${PROJECT_DIR}/machine_pwm.c ${PROJECT_DIR}/machine_uart.c ${PROJECT_DIR}/modmachine.c ${PROJECT_DIR}/modnetwork.c diff --git a/ports/esp32/modmachine.c b/ports/esp32/modmachine.c index 5f94007d49..00271a37ed 100644 --- a/ports/esp32/modmachine.c +++ b/ports/esp32/modmachine.c @@ -53,6 +53,7 @@ #include "extmod/machine_mem.h" #include "extmod/machine_signal.h" #include "extmod/machine_pulse.h" +#include "extmod/machine_pwm.h" #include "extmod/machine_i2c.h" #include "extmod/machine_spi.h" #include "modmachine.h" diff --git a/ports/esp32/modmachine.h b/ports/esp32/modmachine.h index 7bf03b0cab..afc2ab07f4 100644 --- a/ports/esp32/modmachine.h +++ b/ports/esp32/modmachine.h @@ -15,7 +15,6 @@ extern const mp_obj_type_t machine_pin_type; extern const mp_obj_type_t machine_touchpad_type; extern const mp_obj_type_t machine_adc_type; extern const mp_obj_type_t machine_dac_type; -extern const mp_obj_type_t machine_pwm_type; extern const mp_obj_type_t machine_hw_i2c_type; extern const mp_obj_type_t machine_hw_spi_type; extern const mp_obj_type_t machine_i2s_type; diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index b306be937d..6a48ce5023 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -158,6 +158,10 @@ #define MICROPY_PY_MACHINE_PIN_MAKE_NEW mp_pin_make_new #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_INCLUDEFILE "ports/esp32/machine_pwm.c" #define MICROPY_PY_MACHINE_I2C (1) #define MICROPY_PY_MACHINE_SOFTI2C (1) #define MICROPY_PY_MACHINE_SPI (1) diff --git a/ports/esp8266/Makefile b/ports/esp8266/Makefile index 9ea4498f41..e771d8f7d3 100644 --- a/ports/esp8266/Makefile +++ b/ports/esp8266/Makefile @@ -96,7 +96,6 @@ SRC_C = \ modmachine.c \ machine_bitstream.c \ machine_pin.c \ - machine_pwm.c \ machine_rtc.c \ machine_adc.c \ machine_uart.c \ diff --git a/ports/esp8266/machine_pwm.c b/ports/esp8266/machine_pwm.c index 1d6a6cfe58..f8cd937b80 100644 --- a/ports/esp8266/machine_pwm.c +++ b/ports/esp8266/machine_pwm.c @@ -24,28 +24,25 @@ * THE SOFTWARE. */ -#include -#include - -#include "esppwm.h" - #include "py/runtime.h" #include "modmachine.h" -typedef struct _pyb_pwm_obj_t { +#include "esppwm.h" + +typedef struct _machine_pwm_obj_t { mp_obj_base_t base; pyb_pin_obj_t *pin; uint8_t active; uint8_t channel; -} pyb_pwm_obj_t; +} machine_pwm_obj_t; STATIC bool pwm_inited = false; /******************************************************************************/ // MicroPython bindings for PWM -STATIC void pyb_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { - pyb_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in); +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->phys_port); if (self->active) { mp_printf(print, ", freq=%u, duty=%u", @@ -54,7 +51,7 @@ STATIC void pyb_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_print_ki mp_printf(print, ")"); } -STATIC void pyb_pwm_init_helper(pyb_pwm_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { +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 }; static const mp_arg_t allowed_args[] = { { MP_QSTR_freq, MP_ARG_INT, {.u_int = -1} }, @@ -80,13 +77,13 @@ STATIC void pyb_pwm_init_helper(pyb_pwm_obj_t *self, size_t n_args, const mp_obj pwm_start(); } -STATIC mp_obj_t pyb_pwm_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *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 *args) { mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); pyb_pin_obj_t *pin = mp_obj_get_pin_obj(args[0]); // create PWM object from the given pin - pyb_pwm_obj_t *self = m_new_obj(pyb_pwm_obj_t); - self->base.type = &pyb_pwm_type; + machine_pwm_obj_t *self = m_new_obj(machine_pwm_obj_t); + self->base.type = &machine_pwm_type; self->pin = pin; self->active = 0; self->channel = -1; @@ -100,71 +97,39 @@ STATIC mp_obj_t pyb_pwm_make_new(const mp_obj_type_t *type, size_t n_args, size_ // start the PWM running for this channel mp_map_t kw_args; mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); - pyb_pwm_init_helper(self, n_args - 1, args + 1, &kw_args); + mp_machine_pwm_init_helper(self, n_args - 1, args + 1, &kw_args); return MP_OBJ_FROM_PTR(self); } -STATIC mp_obj_t pyb_pwm_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { - pyb_pwm_init_helper(args[0], n_args - 1, args + 1, kw_args); - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_KW(pyb_pwm_init_obj, 1, pyb_pwm_init); - -STATIC mp_obj_t pyb_pwm_deinit(mp_obj_t self_in) { - pyb_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in); +STATIC void mp_machine_pwm_deinit(machine_pwm_obj_t *self) { pwm_delete(self->channel); self->active = 0; pwm_start(); - return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_pwm_deinit_obj, pyb_pwm_deinit); -STATIC mp_obj_t pyb_pwm_freq(size_t n_args, const mp_obj_t *args) { - // pyb_pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]); - if (n_args == 1) { - // get - return MP_OBJ_NEW_SMALL_INT(pwm_get_freq(0)); - } else { - // set - pwm_set_freq(mp_obj_get_int(args[1]), 0); - pwm_start(); - return mp_const_none; - } +STATIC mp_obj_t mp_machine_pwm_freq_get(machine_pwm_obj_t *self) { + return MP_OBJ_NEW_SMALL_INT(pwm_get_freq(0)); } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_pwm_freq_obj, 1, 2, pyb_pwm_freq); -STATIC mp_obj_t pyb_pwm_duty(size_t n_args, const mp_obj_t *args) { - pyb_pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]); +STATIC void mp_machine_pwm_freq_set(machine_pwm_obj_t *self, mp_int_t freq) { + pwm_set_freq(freq, 0); + pwm_start(); +} + +STATIC mp_obj_t mp_machine_pwm_duty_get(machine_pwm_obj_t *self) { if (!self->active) { pwm_add(self->pin->phys_port, self->pin->periph, self->pin->func); self->active = 1; } - if (n_args == 1) { - // get - return MP_OBJ_NEW_SMALL_INT(pwm_get_duty(self->channel)); - } else { - // set - pwm_set_duty(mp_obj_get_int(args[1]), self->channel); - pwm_start(); - return mp_const_none; - } + return MP_OBJ_NEW_SMALL_INT(pwm_get_duty(self->channel)); } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_pwm_duty_obj, 1, 2, pyb_pwm_duty); -STATIC const mp_rom_map_elem_t pyb_pwm_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&pyb_pwm_init_obj) }, - { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&pyb_pwm_deinit_obj) }, - { MP_ROM_QSTR(MP_QSTR_freq), MP_ROM_PTR(&pyb_pwm_freq_obj) }, - { MP_ROM_QSTR(MP_QSTR_duty), MP_ROM_PTR(&pyb_pwm_duty_obj) }, -}; - -STATIC MP_DEFINE_CONST_DICT(pyb_pwm_locals_dict, pyb_pwm_locals_dict_table); - -const mp_obj_type_t pyb_pwm_type = { - { &mp_type_type }, - .name = MP_QSTR_PWM, - .print = pyb_pwm_print, - .make_new = pyb_pwm_make_new, - .locals_dict = (mp_obj_dict_t *)&pyb_pwm_locals_dict, -}; +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; + } + pwm_set_duty(duty, self->channel); + pwm_start(); +} diff --git a/ports/esp8266/modmachine.c b/ports/esp8266/modmachine.c index 6cd4583431..39a890f56f 100644 --- a/ports/esp8266/modmachine.c +++ b/ports/esp8266/modmachine.c @@ -39,6 +39,7 @@ #include "extmod/machine_mem.h" #include "extmod/machine_signal.h" #include "extmod/machine_pulse.h" +#include "extmod/machine_pwm.h" #include "extmod/machine_i2c.h" #include "extmod/machine_spi.h" #include "modmachine.h" @@ -423,7 +424,7 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_WDT), MP_ROM_PTR(&esp_wdt_type) }, { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&pyb_pin_type) }, { MP_ROM_QSTR(MP_QSTR_Signal), MP_ROM_PTR(&machine_signal_type) }, - { MP_ROM_QSTR(MP_QSTR_PWM), MP_ROM_PTR(&pyb_pwm_type) }, + { MP_ROM_QSTR(MP_QSTR_PWM), MP_ROM_PTR(&machine_pwm_type) }, { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&machine_adc_type) }, { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&pyb_uart_type) }, #if MICROPY_PY_MACHINE_I2C diff --git a/ports/esp8266/modmachine.h b/ports/esp8266/modmachine.h index be4debd335..4a73d3b8e8 100644 --- a/ports/esp8266/modmachine.h +++ b/ports/esp8266/modmachine.h @@ -4,7 +4,6 @@ #include "py/obj.h" extern const mp_obj_type_t pyb_pin_type; -extern const mp_obj_type_t pyb_pwm_type; extern const mp_obj_type_t machine_adc_type; extern const mp_obj_type_t pyb_rtc_type; extern const mp_obj_type_t pyb_uart_type; diff --git a/ports/esp8266/mpconfigport.h b/ports/esp8266/mpconfigport.h index 78263efec5..7d6d0a34c3 100644 --- a/ports/esp8266/mpconfigport.h +++ b/ports/esp8266/mpconfigport.h @@ -81,6 +81,10 @@ #define MICROPY_PY_MACHINE_PIN_MAKE_NEW mp_pin_make_new #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_INCLUDEFILE "ports/esp8266/machine_pwm.c" #define MICROPY_PY_MACHINE_I2C (1) #define MICROPY_PY_MACHINE_SOFTI2C (1) #define MICROPY_PY_MACHINE_SPI (1) diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt index fd7ec65e0f..2e7347fd53 100644 --- a/ports/rp2/CMakeLists.txt +++ b/ports/rp2/CMakeLists.txt @@ -85,7 +85,6 @@ set(MICROPY_SOURCE_PORT machine_adc.c machine_i2c.c machine_pin.c - machine_pwm.c machine_rtc.c machine_spi.c machine_timer.c @@ -113,7 +112,6 @@ set(MICROPY_SOURCE_QSTR ${PROJECT_SOURCE_DIR}/machine_adc.c ${PROJECT_SOURCE_DIR}/machine_i2c.c ${PROJECT_SOURCE_DIR}/machine_pin.c - ${PROJECT_SOURCE_DIR}/machine_pwm.c ${PROJECT_SOURCE_DIR}/machine_rtc.c ${PROJECT_SOURCE_DIR}/machine_spi.c ${PROJECT_SOURCE_DIR}/machine_timer.c diff --git a/ports/rp2/machine_pwm.c b/ports/rp2/machine_pwm.c index ff40c5503f..2952a43062 100644 --- a/ports/rp2/machine_pwm.c +++ b/ports/rp2/machine_pwm.c @@ -34,8 +34,6 @@ /******************************************************************************/ // MicroPython bindings for machine.PWM -const mp_obj_type_t machine_pwm_type; - typedef struct _machine_pwm_obj_t { mp_obj_base_t base; uint8_t slice; @@ -61,13 +59,13 @@ STATIC machine_pwm_obj_t machine_pwm_obj[] = { {{&machine_pwm_type}, 7, PWM_CHAN_B}, }; -STATIC void machine_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { +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); } // PWM(pin) -STATIC mp_obj_t machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_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); @@ -85,113 +83,78 @@ STATIC mp_obj_t machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args, s return MP_OBJ_FROM_PTR(self); } -STATIC mp_obj_t machine_pwm_deinit(mp_obj_t self_in) { - machine_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in); +STATIC void mp_machine_pwm_deinit(machine_pwm_obj_t *self) { pwm_set_enabled(self->slice, false); - return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_pwm_deinit_obj, machine_pwm_deinit); -// PWM.freq([value]) -STATIC mp_obj_t machine_pwm_freq(size_t n_args, const mp_obj_t *args) { - machine_pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]); +STATIC mp_obj_t mp_machine_pwm_freq_get(machine_pwm_obj_t *self) { uint32_t source_hz = clock_get_hz(clk_sys); - if (n_args == 1) { - // Get frequency. - uint32_t div16 = pwm_hw->slice[self->slice].div; - uint32_t top = pwm_hw->slice[self->slice].top; - uint32_t pwm_freq = 16 * source_hz / div16 / top; - return MP_OBJ_NEW_SMALL_INT(pwm_freq); - } else { - // Set the frequency, making "top" as large as possible for maximum resolution. - // Maximum "top" is set at 65534 to be able to achieve 100% duty with 65535. - #define TOP_MAX 65534 - mp_int_t freq = mp_obj_get_int(args[1]); - uint32_t div16_top = 16 * source_hz / freq; - uint32_t top = 1; - for (;;) { - // Try a few small prime factors to get close to the desired frequency. - if (div16_top >= 16 * 5 && div16_top % 5 == 0 && top * 5 <= TOP_MAX) { - div16_top /= 5; - top *= 5; - } else if (div16_top >= 16 * 3 && div16_top % 3 == 0 && top * 3 <= TOP_MAX) { - div16_top /= 3; - top *= 3; - } else if (div16_top >= 16 * 2 && top * 2 <= TOP_MAX) { - div16_top /= 2; - top *= 2; - } else { - break; - } - } - if (div16_top < 16) { - mp_raise_ValueError(MP_ERROR_TEXT("freq too large")); - } else if (div16_top >= 256 * 16) { - mp_raise_ValueError(MP_ERROR_TEXT("freq too small")); - } - pwm_hw->slice[self->slice].div = div16_top; - pwm_hw->slice[self->slice].top = top; - return mp_const_none; - } -} -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pwm_freq_obj, 1, 2, machine_pwm_freq); - -// 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]); + uint32_t div16 = pwm_hw->slice[self->slice].div; uint32_t top = pwm_hw->slice[self->slice].top; - if (n_args == 1) { - // Get duty cycle. - 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(cc * 65535 / (top + 1)); - } else { - // Set duty cycle. - mp_int_t duty_u16 = mp_obj_get_int(args[1]); - uint32_t cc = duty_u16 * (top + 1) / 65535; - pwm_set_chan_level(self->slice, self->channel, cc); - pwm_set_enabled(self->slice, true); - return mp_const_none; - } + uint32_t pwm_freq = 16 * source_hz / div16 / top; + return MP_OBJ_NEW_SMALL_INT(pwm_freq); } -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pwm_duty_u16_obj, 1, 2, machine_pwm_duty_u16); -// PWM.duty_ns([value]) -STATIC mp_obj_t machine_pwm_duty_ns(size_t n_args, const mp_obj_t *args) { - machine_pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]); +STATIC void mp_machine_pwm_freq_set(machine_pwm_obj_t *self, mp_int_t freq) { + // Set the frequency, making "top" as large as possible for maximum resolution. + // Maximum "top" is set at 65534 to be able to achieve 100% duty with 65535. + #define TOP_MAX 65534 + uint32_t source_hz = clock_get_hz(clk_sys); + uint32_t div16_top = 16 * source_hz / freq; + uint32_t top = 1; + for (;;) { + // Try a few small prime factors to get close to the desired frequency. + if (div16_top >= 16 * 5 && div16_top % 5 == 0 && top * 5 <= TOP_MAX) { + div16_top /= 5; + top *= 5; + } else if (div16_top >= 16 * 3 && div16_top % 3 == 0 && top * 3 <= TOP_MAX) { + div16_top /= 3; + top *= 3; + } else if (div16_top >= 16 * 2 && top * 2 <= TOP_MAX) { + div16_top /= 2; + top *= 2; + } else { + break; + } + } + if (div16_top < 16) { + mp_raise_ValueError(MP_ERROR_TEXT("freq too large")); + } else if (div16_top >= 256 * 16) { + mp_raise_ValueError(MP_ERROR_TEXT("freq too small")); + } + pwm_hw->slice[self->slice].div = div16_top; + pwm_hw->slice[self->slice].top = top; +} + +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; + return MP_OBJ_NEW_SMALL_INT(cc * 65535 / (top + 1)); +} + +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; + uint32_t cc = duty_u16 * (top + 1) / 65535; + pwm_set_chan_level(self->slice, self->channel, cc); + pwm_set_enabled(self->slice, true); +} + +STATIC mp_obj_t mp_machine_pwm_duty_get_ns(machine_pwm_obj_t *self) { uint32_t source_hz = clock_get_hz(clk_sys); uint32_t slice_hz = 16 * source_hz / pwm_hw->slice[self->slice].div; - if (n_args == 1) { - // Get duty cycle. - 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); - } else { - // Set duty cycle. - mp_int_t duty_ns = mp_obj_get_int(args[1]); - uint32_t cc = (uint64_t)duty_ns * slice_hz / 1000000000ULL; - if (cc > 65535) { - 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); - return mp_const_none; - } + 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); } -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pwm_duty_ns_obj, 1, 2, machine_pwm_duty_ns); -STATIC const mp_rom_map_elem_t machine_pwm_locals_dict_table[] = { - { 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) }, - { 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) }, -}; -STATIC MP_DEFINE_CONST_DICT(machine_pwm_locals_dict, machine_pwm_locals_dict_table); - -const mp_obj_type_t machine_pwm_type = { - { &mp_type_type }, - .name = MP_QSTR_PWM, - .print = machine_pwm_print, - .make_new = machine_pwm_make_new, - .locals_dict = (mp_obj_dict_t *)&machine_pwm_locals_dict, -}; +STATIC void mp_machine_pwm_duty_set_ns(machine_pwm_obj_t *self, mp_int_t duty_ns) { + uint32_t source_hz = clock_get_hz(clk_sys); + uint32_t slice_hz = 16 * source_hz / pwm_hw->slice[self->slice].div; + uint32_t cc = (uint64_t)duty_ns * slice_hz / 1000000000ULL; + if (cc > 65535) { + 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); +} diff --git a/ports/rp2/modmachine.c b/ports/rp2/modmachine.c index 1b2b2adad7..32dc3d22cc 100644 --- a/ports/rp2/modmachine.c +++ b/ports/rp2/modmachine.c @@ -30,6 +30,7 @@ #include "extmod/machine_i2c.h" #include "extmod/machine_mem.h" #include "extmod/machine_pulse.h" +#include "extmod/machine_pwm.h" #include "extmod/machine_signal.h" #include "extmod/machine_spi.h" diff --git a/ports/rp2/modmachine.h b/ports/rp2/modmachine.h index c79bcfdad1..0c48565299 100644 --- a/ports/rp2/modmachine.h +++ b/ports/rp2/modmachine.h @@ -6,7 +6,6 @@ extern const mp_obj_type_t machine_adc_type; extern const mp_obj_type_t machine_hw_i2c_type; extern const mp_obj_type_t machine_pin_type; -extern const mp_obj_type_t machine_pwm_type; extern const mp_obj_type_t machine_rtc_type; extern const mp_obj_type_t machine_spi_type; extern const mp_obj_type_t machine_timer_type; diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index 4da34cd038..dea3775a85 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -131,6 +131,9 @@ #define MICROPY_PY_MACHINE (1) #define MICROPY_PY_MACHINE_PIN_MAKE_NEW mp_pin_make_new #define MICROPY_PY_MACHINE_PULSE (1) +#define MICROPY_PY_MACHINE_PWM (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) #define MICROPY_PY_MACHINE_SPI (1) diff --git a/py/py.mk b/py/py.mk index 609ba6cae3..be8296e5e8 100644 --- a/py/py.mk +++ b/py/py.mk @@ -189,6 +189,7 @@ PY_EXTMOD_O_BASENAME = \ extmod/machine_pinbase.o \ extmod/machine_signal.o \ extmod/machine_pulse.o \ + extmod/machine_pwm.o \ extmod/machine_i2c.o \ extmod/machine_spi.o \ extmod/modbluetooth.o \