From a1f838cdf1239acc43d73ffc450cf98cad6a304a Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 4 Mar 2023 17:51:18 +0100 Subject: [PATCH] nrf/modules/machine/pwm: Use extmod/machine_pwm.c for PWM module. This is a breaking change, making the hardware PWM on the nrf port compatible with the other ports providing machine.PWM. Frequency range 4Hz - ~5.4 MHz. The base clock range is 125kHz to 16 MHz, and the divider range is 3 - 32767. The hardware supports up to four outputs per PWM device with different duty cycles, but only one output is (and was) supported. --- ports/nrf/Makefile | 1 - .../arduino_nano_33_ble_sense/mpconfigboard.h | 1 + .../nrf/boards/arduino_primo/mpconfigboard.h | 1 + .../boards/blueio_tag_evim/mpconfigboard.h | 1 + ports/nrf/boards/evk_nina_b3/mpconfigboard.h | 1 + ports/nrf/boards/feather52/mpconfigboard.h | 1 + .../nrf/boards/ibk_blyst_nano/mpconfigboard.h | 1 + .../nrf/boards/idk_blyst_nano/mpconfigboard.h | 1 + .../nrf52840-mdk-usb-dongle/mpconfigboard.h | 1 + .../nrf/boards/particle_xenon/mpconfigboard.h | 1 + ports/nrf/boards/pca10040/mpconfigboard.h | 1 + ports/nrf/boards/pca10056/mpconfigboard.h | 1 + ports/nrf/boards/pca10059/mpconfigboard.h | 1 + .../boards/seeed_xiao_nrf52/mpconfigboard.h | 1 + ports/nrf/main.c | 4 + ports/nrf/modules/machine/pwm.c | 465 +++++++++--------- ports/nrf/modules/machine/pwm.h | 1 + ports/nrf/mpconfigport.h | 8 + 18 files changed, 267 insertions(+), 225 deletions(-) diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index 9ba6267577..61ae72d714 100644 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -330,7 +330,6 @@ DRIVERS_SRC_C += $(addprefix modules/,\ machine/pin.c \ machine/timer.c \ machine/rtcounter.c \ - machine/pwm.c \ machine/temp.c \ uos/moduos.c \ uos/microbitfs.c \ diff --git a/ports/nrf/boards/arduino_nano_33_ble_sense/mpconfigboard.h b/ports/nrf/boards/arduino_nano_33_ble_sense/mpconfigboard.h index 0e6c5a44ab..71349e08c4 100644 --- a/ports/nrf/boards/arduino_nano_33_ble_sense/mpconfigboard.h +++ b/ports/nrf/boards/arduino_nano_33_ble_sense/mpconfigboard.h @@ -14,6 +14,7 @@ #define MICROPY_BOARD_ENTER_BOOTLOADER(nargs, args) NANO33_board_enter_bootloader() #define MICROPY_PY_MACHINE_UART (1) +#define MICROPY_PY_MACHINE_PWM (1) #define MICROPY_PY_MACHINE_HW_PWM (1) #define MICROPY_PY_MACHINE_HW_SPI (1) #define MICROPY_PY_MACHINE_RTCOUNTER (1) diff --git a/ports/nrf/boards/arduino_primo/mpconfigboard.h b/ports/nrf/boards/arduino_primo/mpconfigboard.h index 9d58a823cd..c9de2ff918 100644 --- a/ports/nrf/boards/arduino_primo/mpconfigboard.h +++ b/ports/nrf/boards/arduino_primo/mpconfigboard.h @@ -32,6 +32,7 @@ #define MICROPY_PY_MUSIC (1) #define MICROPY_PY_MACHINE_UART (1) +#define MICROPY_PY_MACHINE_PWM (1) #define MICROPY_PY_MACHINE_HW_PWM (1) #define MICROPY_PY_MACHINE_HW_SPI (1) #define MICROPY_PY_MACHINE_RTCOUNTER (1) diff --git a/ports/nrf/boards/blueio_tag_evim/mpconfigboard.h b/ports/nrf/boards/blueio_tag_evim/mpconfigboard.h index 1b58a4d8c1..03a1b893fd 100644 --- a/ports/nrf/boards/blueio_tag_evim/mpconfigboard.h +++ b/ports/nrf/boards/blueio_tag_evim/mpconfigboard.h @@ -32,6 +32,7 @@ #define MICROPY_PY_MUSIC (1) #define MICROPY_PY_MACHINE_UART (1) +#define MICROPY_PY_MACHINE_PWM (1) #define MICROPY_PY_MACHINE_HW_PWM (1) #define MICROPY_PY_MACHINE_HW_SPI (1) #define MICROPY_PY_MACHINE_RTCOUNTER (1) diff --git a/ports/nrf/boards/evk_nina_b3/mpconfigboard.h b/ports/nrf/boards/evk_nina_b3/mpconfigboard.h index e554b65207..eab232848b 100644 --- a/ports/nrf/boards/evk_nina_b3/mpconfigboard.h +++ b/ports/nrf/boards/evk_nina_b3/mpconfigboard.h @@ -46,6 +46,7 @@ // Peripherals Config #define MICROPY_PY_MACHINE_UART (1) +#define MICROPY_PY_MACHINE_PWM (1) #define MICROPY_PY_MACHINE_HW_PWM (1) #define MICROPY_PY_MACHINE_HW_SPI (1) #define MICROPY_PY_MACHINE_RTCOUNTER (1) diff --git a/ports/nrf/boards/feather52/mpconfigboard.h b/ports/nrf/boards/feather52/mpconfigboard.h index f653832c62..cef44ca403 100644 --- a/ports/nrf/boards/feather52/mpconfigboard.h +++ b/ports/nrf/boards/feather52/mpconfigboard.h @@ -29,6 +29,7 @@ #define MICROPY_PY_SYS_PLATFORM "nrf52" #define MICROPY_PY_MACHINE_UART (1) +#define MICROPY_PY_MACHINE_PWM (1) #define MICROPY_PY_MACHINE_HW_PWM (1) #define MICROPY_PY_MACHINE_HW_SPI (1) #define MICROPY_PY_MACHINE_RTCOUNTER (1) diff --git a/ports/nrf/boards/ibk_blyst_nano/mpconfigboard.h b/ports/nrf/boards/ibk_blyst_nano/mpconfigboard.h index c367c92b7f..c263ade7d6 100644 --- a/ports/nrf/boards/ibk_blyst_nano/mpconfigboard.h +++ b/ports/nrf/boards/ibk_blyst_nano/mpconfigboard.h @@ -32,6 +32,7 @@ #define MICROPY_PY_MUSIC (1) #define MICROPY_PY_MACHINE_UART (1) +#define MICROPY_PY_MACHINE_PWM (1) #define MICROPY_PY_MACHINE_HW_PWM (1) #define MICROPY_PY_MACHINE_HW_SPI (1) #define MICROPY_PY_MACHINE_RTCOUNTER (1) diff --git a/ports/nrf/boards/idk_blyst_nano/mpconfigboard.h b/ports/nrf/boards/idk_blyst_nano/mpconfigboard.h index f0760ec2c0..727a30cf10 100644 --- a/ports/nrf/boards/idk_blyst_nano/mpconfigboard.h +++ b/ports/nrf/boards/idk_blyst_nano/mpconfigboard.h @@ -32,6 +32,7 @@ #define MICROPY_PY_MUSIC (1) #define MICROPY_PY_MACHINE_UART (1) +#define MICROPY_PY_MACHINE_PWM (1) #define MICROPY_PY_MACHINE_HW_PWM (1) #define MICROPY_PY_MACHINE_HW_SPI (1) #define MICROPY_PY_MACHINE_RTCOUNTER (1) diff --git a/ports/nrf/boards/nrf52840-mdk-usb-dongle/mpconfigboard.h b/ports/nrf/boards/nrf52840-mdk-usb-dongle/mpconfigboard.h index d502c86575..feafe8a143 100644 --- a/ports/nrf/boards/nrf52840-mdk-usb-dongle/mpconfigboard.h +++ b/ports/nrf/boards/nrf52840-mdk-usb-dongle/mpconfigboard.h @@ -29,6 +29,7 @@ #define MICROPY_PY_SYS_PLATFORM "nrf52840-MDK-USB-Dongle" #define MICROPY_PY_MACHINE_UART (1) +#define MICROPY_PY_MACHINE_PWM (1) #define MICROPY_PY_MACHINE_HW_PWM (1) #define MICROPY_PY_MACHINE_HW_SPI (1) #define MICROPY_PY_MACHINE_RTCOUNTER (1) diff --git a/ports/nrf/boards/particle_xenon/mpconfigboard.h b/ports/nrf/boards/particle_xenon/mpconfigboard.h index d77e104ce2..012a04458e 100644 --- a/ports/nrf/boards/particle_xenon/mpconfigboard.h +++ b/ports/nrf/boards/particle_xenon/mpconfigboard.h @@ -29,6 +29,7 @@ #define MICROPY_PY_SYS_PLATFORM "PARTICLE-XENON" #define MICROPY_PY_MACHINE_UART (1) +#define MICROPY_PY_MACHINE_PWM (1) #define MICROPY_PY_MACHINE_HW_PWM (1) #define MICROPY_PY_MACHINE_HW_SPI (1) #define MICROPY_PY_MACHINE_RTCOUNTER (1) diff --git a/ports/nrf/boards/pca10040/mpconfigboard.h b/ports/nrf/boards/pca10040/mpconfigboard.h index 7202f6a0b1..00a56c2ea2 100644 --- a/ports/nrf/boards/pca10040/mpconfigboard.h +++ b/ports/nrf/boards/pca10040/mpconfigboard.h @@ -29,6 +29,7 @@ #define MICROPY_PY_SYS_PLATFORM "nrf52-DK" #define MICROPY_PY_MACHINE_UART (1) +#define MICROPY_PY_MACHINE_PWM (1) #define MICROPY_PY_MACHINE_HW_PWM (1) #define MICROPY_PY_MACHINE_HW_SPI (1) #define MICROPY_PY_MACHINE_RTCOUNTER (1) diff --git a/ports/nrf/boards/pca10056/mpconfigboard.h b/ports/nrf/boards/pca10056/mpconfigboard.h index f7daf48a62..fa39764a7e 100644 --- a/ports/nrf/boards/pca10056/mpconfigboard.h +++ b/ports/nrf/boards/pca10056/mpconfigboard.h @@ -29,6 +29,7 @@ #define MICROPY_PY_SYS_PLATFORM "nrf52840-PDK" #define MICROPY_PY_MACHINE_UART (1) +#define MICROPY_PY_MACHINE_PWM (1) #define MICROPY_PY_MACHINE_HW_PWM (1) #define MICROPY_PY_MACHINE_HW_SPI (1) #define MICROPY_PY_MACHINE_RTCOUNTER (1) diff --git a/ports/nrf/boards/pca10059/mpconfigboard.h b/ports/nrf/boards/pca10059/mpconfigboard.h index f4c78915c6..904a0871e4 100644 --- a/ports/nrf/boards/pca10059/mpconfigboard.h +++ b/ports/nrf/boards/pca10059/mpconfigboard.h @@ -29,6 +29,7 @@ #define MICROPY_PY_SYS_PLATFORM "nrf52840-Dongle" #define MICROPY_PY_MACHINE_UART (1) +#define MICROPY_PY_MACHINE_PWM (1) #define MICROPY_PY_MACHINE_HW_PWM (1) #define MICROPY_PY_MACHINE_HW_SPI (1) #define MICROPY_PY_MACHINE_RTCOUNTER (1) diff --git a/ports/nrf/boards/seeed_xiao_nrf52/mpconfigboard.h b/ports/nrf/boards/seeed_xiao_nrf52/mpconfigboard.h index 1a2d14f1d4..18fbe327d0 100644 --- a/ports/nrf/boards/seeed_xiao_nrf52/mpconfigboard.h +++ b/ports/nrf/boards/seeed_xiao_nrf52/mpconfigboard.h @@ -34,6 +34,7 @@ #define MICROPY_HW_USB_CDC (1) #define MICROPY_PY_MACHINE_UART (1) +#define MICROPY_PY_MACHINE_PWM (1) #define MICROPY_PY_MACHINE_HW_PWM (1) #define MICROPY_PY_MACHINE_HW_SPI (1) #define MICROPY_PY_MACHINE_RTCOUNTER (1) diff --git a/ports/nrf/main.c b/ports/nrf/main.c index 339633d863..197fea9ab6 100644 --- a/ports/nrf/main.c +++ b/ports/nrf/main.c @@ -280,6 +280,10 @@ soft_reset: } } + #if MICROPY_PY_MACHINE_HW_PWM + pwm_deinit_all(); + #endif + mp_deinit(); printf("MPY: soft reboot\n"); diff --git a/ports/nrf/modules/machine/pwm.c b/ports/nrf/modules/machine/pwm.c index 9ec36d1e14..525c3aba6b 100644 --- a/ports/nrf/modules/machine/pwm.c +++ b/ports/nrf/modules/machine/pwm.c @@ -42,278 +42,317 @@ #include "nrfx_pwm.h" #endif +#define PWM_MAX_BASE_FREQ (16000000) +#define PWM_MIN_BASE_FREQ (125000) +#define PWM_MAX_PERIOD (32768) + typedef enum { - MODE_LOW_HIGH, - MODE_HIGH_LOW + MODE_HIGH_LOW, + MODE_LOW_HIGH } pwm_mode_t; +typedef enum { + DUTY_NOT_SET, + DUTY_PERCENT, + DUTY_U16, + DUTY_NS +} pwm_duty_t; + typedef struct { - uint8_t pwm_pin; - uint8_t duty; - uint16_t pulse_width; - uint16_t period; - nrf_pwm_clk_t freq; - pwm_mode_t mode; + uint8_t pwm_pin; + uint8_t duty_mode; + int8_t freq_div; + bool defer_start; + uint32_t duty; + uint32_t freq; + bool mode; } machine_pwm_config_t; -typedef struct _machine_hard_pwm_obj_t { - mp_obj_base_t base; - const nrfx_pwm_t * p_pwm; - machine_pwm_config_t * p_config; -} machine_hard_pwm_obj_t; +typedef struct _machine_pwm_obj_t { + mp_obj_base_t base; + const nrfx_pwm_t *p_pwm; + machine_pwm_config_t *p_config; +} machine_pwm_obj_t; STATIC const nrfx_pwm_t machine_hard_pwm_instances[] = { -#if defined(NRF52_SERIES) + #if defined(NRF52_SERIES) NRFX_PWM_INSTANCE(0), NRFX_PWM_INSTANCE(1), NRFX_PWM_INSTANCE(2), -#if NRF52840 + #if NRF52840 NRFX_PWM_INSTANCE(3), -#endif -#endif + #endif + #endif }; STATIC machine_pwm_config_t hard_configs[MP_ARRAY_SIZE(machine_hard_pwm_instances)]; +STATIC uint8_t pwm_used[MP_ARRAY_SIZE(machine_hard_pwm_instances)]; -STATIC const machine_hard_pwm_obj_t machine_hard_pwm_obj[] = { -#if defined(NRF52_SERIES) +STATIC const machine_pwm_obj_t machine_hard_pwm_obj[] = { + #if defined(NRF52_SERIES) {{&machine_pwm_type}, .p_pwm = &machine_hard_pwm_instances[0], .p_config = &hard_configs[0]}, {{&machine_pwm_type}, .p_pwm = &machine_hard_pwm_instances[1], .p_config = &hard_configs[1]}, {{&machine_pwm_type}, .p_pwm = &machine_hard_pwm_instances[2], .p_config = &hard_configs[2]}, -#if NRF52840 + #if NRF52840 {{&machine_pwm_type}, .p_pwm = &machine_hard_pwm_instances[3], .p_config = &hard_configs[3]}, -#endif -#endif + #endif + #endif }; void pwm_init0(void) { } - -STATIC int hard_pwm_find(mp_obj_t id) { - if (mp_obj_is_int(id)) { - // given an integer id - int pwm_id = mp_obj_get_int(id); - if (pwm_id >= 0 && pwm_id < MP_ARRAY_SIZE(machine_hard_pwm_obj)) { - return pwm_id; +// Find a free PWM +STATIC int hard_pwm_find(int pin) { + // check, if a PWM object can be reused. + for (int i = 0; i < MP_ARRAY_SIZE(machine_hard_pwm_obj); i++) { + if (machine_hard_pwm_obj[i].p_config->pwm_pin == pin) { + return i; } } - return -1; + // if not, look for a free object. + for (int i = 0; i < MP_ARRAY_SIZE(machine_hard_pwm_obj); i++) { + if (pwm_used[i] == 0) { + return i; + } + } + mp_raise_ValueError(MP_ERROR_TEXT("no free PWM id")); } -STATIC void machine_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { - machine_hard_pwm_obj_t *self = self_in; - mp_printf(print, "PWM(%u)", self->p_pwm->drv_inst_idx); +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 = self_in; + static char *duty_suffix[] = { "", "", "_u16", "_ns" }; + mp_printf(print, "", + self->p_config->pwm_pin, self->p_config->freq, + duty_suffix[self->p_config->duty_mode], self->p_config->duty, + self->p_config->mode, self->p_pwm->drv_inst_idx); } /******************************************************************************/ /* MicroPython bindings for machine API */ -STATIC mp_obj_t machine_hard_pwm_make_new(mp_arg_val_t *args); -STATIC void machine_hard_pwm_init(mp_obj_t self, mp_arg_val_t *args); -STATIC void machine_hard_pwm_deinit(mp_obj_t self); -STATIC mp_obj_t machine_hard_pwm_freq(mp_obj_t self, mp_arg_val_t *args); +STATIC void machine_hard_pwm_start(const machine_pwm_obj_t *self); +STATIC void mp_machine_pwm_deinit(const machine_pwm_obj_t *self); +STATIC void mp_machine_pwm_freq_set(const machine_pwm_obj_t *self, mp_int_t freq); +STATIC void mp_machine_pwm_duty_set(const machine_pwm_obj_t *self, mp_int_t duty); +STATIC void mp_machine_pwm_duty_set_u16(const machine_pwm_obj_t *self, mp_int_t duty_u16); +STATIC void mp_machine_pwm_duty_set_ns(const machine_pwm_obj_t *self, mp_int_t duty_ns); -/* common code for both soft and hard implementations *************************/ +static const mp_arg_t allowed_args[] = { + { MP_QSTR_pin, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_duty, MP_ARG_KW_ONLY | 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_id, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, +}; -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) { - enum { ARG_id, ARG_pin, ARG_freq, ARG_period, ARG_duty, ARG_pulse_width, ARG_mode }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_id, MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_SMALL_INT(-1)} }, - { MP_QSTR_pin, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_period, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_duty, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_pulse_width, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_mode, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - }; +STATIC void mp_machine_pwm_init_helper(const machine_pwm_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_pin, ARG_freq, ARG_duty, ARG_duty_u16, ARG_duty_ns, ARG_invert, ARG_id }; + + 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); + + self->p_config->defer_start = true; + if (args[ARG_freq].u_int != -1) { + mp_machine_pwm_freq_set(self, args[ARG_freq].u_int); + } + if (args[ARG_duty].u_int != -1) { + mp_machine_pwm_duty_set(self, args[ARG_duty].u_int); + } + if (args[ARG_duty_u16].u_int != -1) { + mp_machine_pwm_duty_set_u16(self, args[ARG_duty_u16].u_int); + } + if (args[ARG_duty_ns].u_int != -1) { + mp_machine_pwm_duty_set_ns(self, args[ARG_duty_ns].u_int); + } + if (args[ARG_invert].u_int != -1) { + self->p_config->mode = args[ARG_invert].u_int ? MODE_LOW_HIGH : MODE_HIGH_LOW; + } + self->p_config->defer_start = false; + + machine_hard_pwm_start(self); +} + + +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, ARG_duty_u16, ARG_duty_ns, ARG_invert, ARG_id }; // parse args 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); - if (args[ARG_id].u_obj == MP_OBJ_NEW_SMALL_INT(-1)) { - // TODO: implement soft PWM - // return machine_soft_pwm_make_new(args); - return mp_const_none; - } else { - // hardware peripheral id given - return machine_hard_pwm_make_new(args); - } -} - -STATIC mp_obj_t machine_pwm_init(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_INIT_pin }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_pin, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} } - }; - - // parse args - mp_obj_t self = pos_args[0]; - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - // dispatch to specific implementation - if (mp_obj_get_type(self) == &machine_pwm_type) { - machine_hard_pwm_init(self, args); - } - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_pwm_init_obj, 1, machine_pwm_init); - -STATIC mp_obj_t machine_pwm_deinit(mp_obj_t self) { - // dispatch to specific implementation - if (mp_obj_get_type(self) == &machine_pwm_type) { - machine_hard_pwm_deinit(self); - } - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_pwm_deinit_obj, machine_pwm_deinit); - -STATIC mp_obj_t machine_pwm_freq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_FREQ_freq }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_freq, MP_ARG_INT, {.u_int = -1} }, - }; - - mp_obj_t self = pos_args[0]; - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - if (mp_obj_get_type(self) == &machine_pwm_type) { - machine_hard_pwm_freq(self, args); - } else { - // soft pwm - } - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mp_machine_pwm_freq_obj, 1, machine_pwm_freq); - -STATIC mp_obj_t machine_pwm_period(size_t n_args, const mp_obj_t *args) { - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_pwm_period_obj, 1, 2, machine_pwm_period); - -STATIC mp_obj_t machine_pwm_duty(size_t n_args, const mp_obj_t *args) { - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_pwm_duty_obj, 1, 2, machine_pwm_duty); - -STATIC const mp_rom_map_elem_t machine_pwm_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_pwm_init_obj) }, - { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_pwm_deinit_obj) }, - - { MP_ROM_QSTR(MP_QSTR_freq), MP_ROM_PTR(&mp_machine_pwm_freq_obj) }, - { MP_ROM_QSTR(MP_QSTR_period), MP_ROM_PTR(&mp_machine_pwm_period_obj) }, - { MP_ROM_QSTR(MP_QSTR_duty), MP_ROM_PTR(&mp_machine_pwm_duty_obj) }, - - { MP_ROM_QSTR(MP_QSTR_FREQ_16MHZ), MP_ROM_INT(NRF_PWM_CLK_16MHz) }, - { MP_ROM_QSTR(MP_QSTR_FREQ_8MHZ), MP_ROM_INT(NRF_PWM_CLK_8MHz) }, - { MP_ROM_QSTR(MP_QSTR_FREQ_4MHZ), MP_ROM_INT(NRF_PWM_CLK_4MHz) }, - { MP_ROM_QSTR(MP_QSTR_FREQ_2MHZ), MP_ROM_INT(NRF_PWM_CLK_2MHz) }, - { MP_ROM_QSTR(MP_QSTR_FREQ_1MHZ), MP_ROM_INT(NRF_PWM_CLK_1MHz) }, - { MP_ROM_QSTR(MP_QSTR_FREQ_500KHZ), MP_ROM_INT(NRF_PWM_CLK_500kHz) }, - { MP_ROM_QSTR(MP_QSTR_FREQ_250KHZ), MP_ROM_INT(NRF_PWM_CLK_250kHz) }, - { MP_ROM_QSTR(MP_QSTR_FREQ_125KHZ), MP_ROM_INT(NRF_PWM_CLK_125kHz) }, - - { MP_ROM_QSTR(MP_QSTR_MODE_LOW_HIGH), MP_ROM_INT(MODE_LOW_HIGH) }, - { MP_ROM_QSTR(MP_QSTR_MODE_HIGH_LOW), MP_ROM_INT(MODE_HIGH_LOW) }, -}; - -STATIC MP_DEFINE_CONST_DICT(machine_pwm_locals_dict, machine_pwm_locals_dict_table); - -/* code for hard implementation ***********************************************/ - -STATIC mp_obj_t machine_hard_pwm_make_new(mp_arg_val_t *args) { - enum { ARG_id, ARG_pin, ARG_freq, ARG_period, ARG_duty, ARG_pulse_width, ARG_mode }; - // get static peripheral object - int pwm_id = hard_pwm_find(args[ARG_id].u_obj); - if (pwm_id < 0) { - mp_raise_ValueError(MP_ERROR_TEXT("invalid or missing PWM id")); - } - const machine_hard_pwm_obj_t *self = &machine_hard_pwm_obj[pwm_id]; - - // check if PWM pin is set + // check if the PWM pin is given. + int pwm_pin; if (args[ARG_pin].u_obj != MP_OBJ_NULL) { - self->p_config->pwm_pin = mp_hal_get_pin_obj(args[ARG_pin].u_obj)->pin; + pwm_pin = mp_hal_get_pin_obj(args[ARG_pin].u_obj)->pin; } else { mp_raise_ValueError(MP_ERROR_TEXT("Pin missing")); } - if (args[ARG_freq].u_obj != MP_OBJ_NULL) { - self->p_config->freq = mp_obj_get_int(args[ARG_freq].u_obj); + int pwm_id = -1; + if (args[ARG_id].u_int != -1) { + // get static peripheral object + if (args[ARG_id].u_int >= 0 && args[ARG_id].u_int < MP_ARRAY_SIZE(machine_hard_pwm_obj)) { + pwm_id = args[ARG_id].u_int; + } } else { - self->p_config->freq = 2; // 4 MHz by default. + pwm_id = hard_pwm_find(pwm_pin); } + if (pwm_id < 0) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid PWM id")); + } + const machine_pwm_obj_t *self = &machine_hard_pwm_obj[pwm_id]; + self->p_config->pwm_pin = pwm_pin; + self->p_config->defer_start = false; + self->p_config->duty_mode = DUTY_NOT_SET; + self->p_config->duty = 0; + self->p_config->freq = 0; + self->p_config->freq_div = -1; + self->p_config->mode = MODE_HIGH_LOW; - if (args[ARG_period].u_obj != MP_OBJ_NULL) { - self->p_config->period = mp_obj_get_int(args[ARG_period].u_obj); - } else { - mp_raise_ValueError(MP_ERROR_TEXT("PWM period must be within 16000 cycles")); - } - - if (args[ARG_duty].u_obj != MP_OBJ_NULL) { - self->p_config->duty = mp_obj_get_int(args[ARG_duty].u_obj); - } else { - self->p_config->duty = 50; // 50% by default. - } - - if (args[ARG_pulse_width].u_obj != MP_OBJ_NULL) { - self->p_config->pulse_width = mp_obj_get_int(args[ARG_pulse_width].u_obj); - } else { - self->p_config->pulse_width = 0; - } - - if (args[ARG_mode].u_obj != MP_OBJ_NULL) { - self->p_config->mode = mp_obj_get_int(args[ARG_mode].u_obj); - } else { - self->p_config->mode = MODE_HIGH_LOW; - } + // start the PWM running for this channel + 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, all_args, &kw_args); return MP_OBJ_FROM_PTR(self); } -STATIC void machine_hard_pwm_init(mp_obj_t self_in, mp_arg_val_t *args) { - machine_hard_pwm_obj_t *self = self_in; +void pwm_deinit_all(void) { + for (int i = 0; i < MP_ARRAY_SIZE(machine_hard_pwm_obj); i++) { + mp_machine_pwm_deinit(&machine_hard_pwm_obj[i]); + } +} + +STATIC void mp_machine_pwm_deinit(const machine_pwm_obj_t *self) { + pwm_used[self->p_pwm->drv_inst_idx] = 0; + nrfx_pwm_stop(self->p_pwm, true); + nrfx_pwm_uninit(self->p_pwm); +} + +STATIC mp_obj_t mp_machine_pwm_freq_get(const machine_pwm_obj_t *self) { + return MP_OBJ_NEW_SMALL_INT(self->p_config->freq); +} + +STATIC void mp_machine_pwm_freq_set(const machine_pwm_obj_t *self, mp_int_t freq) { + + uint8_t div = 0; + if (freq > (PWM_MAX_BASE_FREQ / 3) || freq <= (PWM_MIN_BASE_FREQ / PWM_MAX_PERIOD)) { + mp_raise_ValueError(MP_ERROR_TEXT("frequency out of range")); + } + for (div = 0; div < 8; div++) { + if (PWM_MAX_BASE_FREQ / (1 << div) / freq < PWM_MAX_PERIOD) { + break; + } + } + self->p_config->freq_div = div; + self->p_config->freq = freq; + machine_hard_pwm_start(self); +} + +STATIC mp_obj_t mp_machine_pwm_duty_get(const machine_pwm_obj_t *self) { + if (self->p_config->duty_mode == DUTY_PERCENT) { + return MP_OBJ_NEW_SMALL_INT(self->p_config->duty); + } else if (self->p_config->duty_mode == DUTY_U16) { + return MP_OBJ_NEW_SMALL_INT(self->p_config->duty * 100 / 65536); + } else { + return MP_OBJ_NEW_SMALL_INT(-1); + } +} + +STATIC void mp_machine_pwm_duty_set(const machine_pwm_obj_t *self, mp_int_t duty) { + self->p_config->duty = duty; + self->p_config->duty_mode = DUTY_PERCENT; + machine_hard_pwm_start(self); +} + +STATIC mp_obj_t mp_machine_pwm_duty_get_u16(const machine_pwm_obj_t *self) { + if (self->p_config->duty_mode == DUTY_U16) { + return MP_OBJ_NEW_SMALL_INT(self->p_config->duty); + } else if (self->p_config->duty_mode == DUTY_PERCENT) { + return MP_OBJ_NEW_SMALL_INT(self->p_config->duty * 65536 / 100); + } else { + return MP_OBJ_NEW_SMALL_INT(-1); + } +} + +STATIC void mp_machine_pwm_duty_set_u16(const machine_pwm_obj_t *self, mp_int_t duty) { + self->p_config->duty = duty; + self->p_config->duty_mode = DUTY_U16; + machine_hard_pwm_start(self); +} + +STATIC mp_obj_t mp_machine_pwm_duty_get_ns(const machine_pwm_obj_t *self) { + if (self->p_config->duty_mode == DUTY_NS) { + return MP_OBJ_NEW_SMALL_INT(self->p_config->duty); + } else { + return MP_OBJ_NEW_SMALL_INT(-1); + } +} + +STATIC void mp_machine_pwm_duty_set_ns(const machine_pwm_obj_t *self, mp_int_t duty) { + self->p_config->duty = duty; + self->p_config->duty_mode = DUTY_NS; + machine_hard_pwm_start(self); +} + +/* code for hard implementation ***********************************************/ + +STATIC void machine_hard_pwm_start(const machine_pwm_obj_t *self) { nrfx_pwm_config_t config; + // check if ready to go + if (self->p_config->defer_start == true || self->p_config->freq_div < 0 || self->p_config->duty_mode == DUTY_NOT_SET) { + return; // Not ready yet. + } + pwm_used[self->p_pwm->drv_inst_idx] = 1; + config.output_pins[0] = self->p_config->pwm_pin; config.output_pins[1] = NRFX_PWM_PIN_NOT_USED; config.output_pins[2] = NRFX_PWM_PIN_NOT_USED; config.output_pins[3] = NRFX_PWM_PIN_NOT_USED; - config.irq_priority = 6; - config.base_clock = self->p_config->freq; - config.count_mode = NRF_PWM_MODE_UP; - config.top_value = self->p_config->period; - config.load_mode = NRF_PWM_LOAD_INDIVIDUAL; - config.step_mode = NRF_PWM_STEP_AUTO; + uint32_t tick_freq = PWM_MAX_BASE_FREQ / (1 << self->p_config->freq_div); + uint32_t period = tick_freq / self->p_config->freq; + + config.irq_priority = 6; + config.base_clock = self->p_config->freq_div; + config.count_mode = NRF_PWM_MODE_UP; + config.top_value = period; + config.load_mode = NRF_PWM_LOAD_INDIVIDUAL; + config.step_mode = NRF_PWM_STEP_AUTO; + + nrfx_pwm_stop(self->p_pwm, true); + nrfx_pwm_uninit(self->p_pwm); nrfx_pwm_init(self->p_pwm, &config, NULL, NULL); - uint16_t pulse_width = ((self->p_config->period * self->p_config->duty) / 100); - - // If manual pulse width has been set, override duty-cycle. - if (self->p_config->pulse_width > 0) { - pulse_width = self->p_config->pulse_width; + uint16_t pulse_width; + if (self->p_config->duty_mode == DUTY_PERCENT) { + pulse_width = ((period * self->p_config->duty) / 100); + } else if (self->p_config->duty_mode == DUTY_U16) { + pulse_width = ((period * self->p_config->duty) / 65536); + } + if (self->p_config->duty_mode == DUTY_NS) { + pulse_width = (uint64_t)self->p_config->duty * tick_freq / 1000000000ULL; } // TODO: Move DMA buffer to global memory. volatile static uint16_t pwm_seq[4]; if (self->p_config->mode == MODE_HIGH_LOW) { - pwm_seq[0] = self->p_config->period - pulse_width; - pwm_seq[1] = self->p_config->period - pulse_width; + pwm_seq[0] = 0x8000 | pulse_width; } else { - pwm_seq[0] = self->p_config->period - pulse_width; - pwm_seq[1] = self->p_config->period - pulse_width; + pwm_seq[0] = pulse_width; } - pwm_seq[2] = self->p_config->period - pulse_width; - pwm_seq[3] = self->p_config->period - pulse_width; + // Outputs 1..3 are not used for now + // pwm_seq[1] = 0x8000 | pulse_width; + // pwm_seq[2] = 0x8000 | pulse_width; + // pwm_seq[3] = 0x8000 | pulse_width; const nrf_pwm_sequence_t pwm_sequence = { .values.p_raw = (const uint16_t *)&pwm_seq, @@ -323,31 +362,9 @@ STATIC void machine_hard_pwm_init(mp_obj_t self_in, mp_arg_val_t *args) { }; nrfx_pwm_simple_playback(self->p_pwm, - &pwm_sequence, - 0, // Loop disabled. - 0); + &pwm_sequence, + 0, // Loop disabled. + 0); } -STATIC void machine_hard_pwm_deinit(mp_obj_t self_in) { - machine_hard_pwm_obj_t *self = self_in; - (void)self; - nrfx_pwm_stop(self->p_pwm, true); - nrfx_pwm_uninit(self->p_pwm); -} - -STATIC mp_obj_t machine_hard_pwm_freq(mp_obj_t self_in, mp_arg_val_t *args) { - machine_hard_pwm_obj_t *self = self_in; - (void)self; - return mp_const_none; -} - -MP_DEFINE_CONST_OBJ_TYPE( - machine_pwm_type, - MP_QSTR_PWM, - MP_TYPE_FLAG_NONE, - make_new, machine_pwm_make_new, - print, machine_pwm_print, - locals_dict, &machine_pwm_locals_dict - ); - #endif // MICROPY_PY_MACHINE_HW_PWM diff --git a/ports/nrf/modules/machine/pwm.h b/ports/nrf/modules/machine/pwm.h index ab2d927fa4..4c0528fb4c 100644 --- a/ports/nrf/modules/machine/pwm.h +++ b/ports/nrf/modules/machine/pwm.h @@ -25,5 +25,6 @@ */ void pwm_init0(void); +void pwm_deinit_all(void); extern const mp_obj_type_t machine_pwm_type; diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index 629ba5e946..852a744c1b 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -196,6 +196,14 @@ #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" +#endif + #ifndef MICROPY_PY_MACHINE_TIMER_NRF #define MICROPY_PY_MACHINE_TIMER_NRF (1) #endif