diff --git a/ports/esp32s2/common-hal/pulseio/PulseOut.c b/ports/esp32s2/common-hal/pulseio/PulseOut.c index c448c578df..dc93d7e964 100644 --- a/ports/esp32s2/common-hal/pulseio/PulseOut.c +++ b/ports/esp32s2/common-hal/pulseio/PulseOut.c @@ -29,32 +29,52 @@ #include "shared-bindings/pulseio/PWMOut.h" #include "py/runtime.h" -// STATIC void turn_on(pulseio_pulseout_obj_t *pulseout) { -// } - -// STATIC void turn_off(pulseio_pulseout_obj_t *pulseout) { -// } - -// STATIC void start_timer(void) { -// } - -// STATIC void pulseout_event_handler(void) { -// } - -void pulseout_reset() { -} +// Requires rmt.c void esp32s2_peripherals_reset_all(void) to reset void common_hal_pulseio_pulseout_construct(pulseio_pulseout_obj_t* self, - const pulseio_pwmout_obj_t* carrier) { - mp_raise_NotImplementedError(translate("PulseOut not supported on this chip")); + const mcu_pin_obj_t* pin, + uint32_t frequency) { + + rmt_channel_t channel = esp32s2_peripherals_find_and_reserve_rmt(); + + // Configure Channel + rmt_config_t config = RMT_DEFAULT_CONFIG_TX(pin->number, channel); + config.tx_config.carrier_en = true; + config.tx_config.carrier_duty_percent = 50; + config.tx_config.carrier_freq_hz = frequency; + config.clk_div = 80; + + rmt_config(&config); + rmt_driver_install(channel, 0, 0); + + self->channel = channel; } bool common_hal_pulseio_pulseout_deinited(pulseio_pulseout_obj_t* self) { - return false; + return (self->channel == RMT_CHANNEL_MAX); } void common_hal_pulseio_pulseout_deinit(pulseio_pulseout_obj_t* self) { + esp32s2_peripherals_free_rmt(self->channel); + self->channel = RMT_CHANNEL_MAX; + } void common_hal_pulseio_pulseout_send(pulseio_pulseout_obj_t* self, uint16_t* pulses, uint16_t length) { + rmt_item32_t items[length]; + + // Circuitpython allows 16 bit pulse values, while ESP32 only allows 15 bits + // Thus, we use entire items for one pulse, rather than switching inside each item + for (size_t i = 0; i < length; i++) { + // Setting the RMT duration to 0 has undefined behavior, so avoid that pre-emptively. + if (pulses[i] == 0) { + pulses[i] = 1; + } + uint32_t level = (i % 2) ? 0 : 1; + const rmt_item32_t item = {{{ (pulses[i] & 0x8000 ? 0x7FFF : 1), level, (pulses[i] & 0x7FFF), level}}}; + items[i] = item; + } + + rmt_write_items(self->channel, items, length, true); + rmt_wait_tx_done(self->channel, pdMS_TO_TICKS(100)); } diff --git a/ports/esp32s2/common-hal/pulseio/PulseOut.h b/ports/esp32s2/common-hal/pulseio/PulseOut.h index f465d00792..5139059926 100644 --- a/ports/esp32s2/common-hal/pulseio/PulseOut.h +++ b/ports/esp32s2/common-hal/pulseio/PulseOut.h @@ -29,14 +29,14 @@ #include "common-hal/microcontroller/Pin.h" #include "common-hal/pulseio/PWMOut.h" +#include "driver/rmt.h" +#include "rmt.h" #include "py/obj.h" typedef struct { mp_obj_base_t base; - pulseio_pwmout_obj_t *pwmout; + rmt_channel_t channel; } pulseio_pulseout_obj_t; -void pulseout_reset(void); - #endif // MICROPY_INCLUDED_ESP32S2_COMMON_HAL_PULSEIO_PULSEOUT_H diff --git a/ports/esp32s2/mpconfigport.h b/ports/esp32s2/mpconfigport.h index d340bb4805..6b070afea6 100644 --- a/ports/esp32s2/mpconfigport.h +++ b/ports/esp32s2/mpconfigport.h @@ -28,19 +28,20 @@ #ifndef ESP32S2_MPCONFIGPORT_H__ #define ESP32S2_MPCONFIGPORT_H__ -#define CIRCUITPY_INTERNAL_NVM_SIZE (0) -#define MICROPY_NLR_THUMB (0) +#define CIRCUITPY_INTERNAL_NVM_SIZE (0) +#define MICROPY_NLR_THUMB (0) -#define MICROPY_PY_UJSON (0) -#define MICROPY_USE_INTERNAL_PRINTF (0) +#define MICROPY_PY_UJSON (0) +#define MICROPY_USE_INTERNAL_PRINTF (0) #include "py/circuitpy_mpconfig.h" +#define CPY_PULSEOUT_USES_DIGITALIO (1) #define MICROPY_PORT_ROOT_POINTERS \ CIRCUITPY_COMMON_ROOT_POINTERS -#define MICROPY_NLR_SETJMP (1) -#define CIRCUITPY_DEFAULT_STACK_SIZE 0x6000 +#define MICROPY_NLR_SETJMP (1) +#define CIRCUITPY_DEFAULT_STACK_SIZE (0x6000) #endif // __INCLUDED_ESP32S2_MPCONFIGPORT_H diff --git a/ports/esp32s2/peripherals/rmt.c b/ports/esp32s2/peripherals/rmt.c index f17957c1c4..16605edf5e 100644 --- a/ports/esp32s2/peripherals/rmt.c +++ b/ports/esp32s2/peripherals/rmt.c @@ -29,6 +29,14 @@ bool rmt_reserved_channels[RMT_CHANNEL_MAX]; +void esp32s2_peripherals_rmt_reset(void) { + for (size_t i = 0; i < RMT_CHANNEL_MAX; i++) { + if (rmt_reserved_channels[i]) { + esp32s2_peripherals_free_rmt(i); + } + } +} + rmt_channel_t esp32s2_peripherals_find_and_reserve_rmt(void) { for (size_t i = 0; i < RMT_CHANNEL_MAX; i++) { if (!rmt_reserved_channels[i]) { diff --git a/ports/esp32s2/peripherals/rmt.h b/ports/esp32s2/peripherals/rmt.h index 4741a5bc59..01ed09907a 100644 --- a/ports/esp32s2/peripherals/rmt.h +++ b/ports/esp32s2/peripherals/rmt.h @@ -31,6 +31,7 @@ #include "driver/rmt.h" #include +void esp32s2_peripherals_rmt_reset(void); rmt_channel_t esp32s2_peripherals_find_and_reserve_rmt(void); void esp32s2_peripherals_free_rmt(rmt_channel_t chan); diff --git a/ports/esp32s2/supervisor/port.c b/ports/esp32s2/supervisor/port.c index e4767e5146..d287079b79 100644 --- a/ports/esp32s2/supervisor/port.c +++ b/ports/esp32s2/supervisor/port.c @@ -42,6 +42,8 @@ #include "supervisor/memory.h" #include "supervisor/shared/tick.h" +#include "rmt.h" + STATIC esp_timer_handle_t _tick_timer; void tick_timer_cb(void* arg) { @@ -66,6 +68,7 @@ void reset_port(void) { vTaskDelay(4); #if CIRCUITPY_PULSEIO + esp32s2_peripherals_rmt_reset(); pwmout_reset(); #endif #if CIRCUITPY_BUSIO diff --git a/shared-bindings/pulseio/PulseOut.c b/shared-bindings/pulseio/PulseOut.c index 3d35f05445..10524f51b3 100644 --- a/shared-bindings/pulseio/PulseOut.c +++ b/shared-bindings/pulseio/PulseOut.c @@ -64,19 +64,33 @@ //| pulse.send(pulses)""" //| ... //| -STATIC mp_obj_t pulseio_pulseout_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { - mp_arg_check_num(n_args, kw_args, 1, 1, false); - mp_obj_t carrier_obj = args[0]; +STATIC mp_obj_t pulseio_pulseout_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - if (!MP_OBJ_IS_TYPE(carrier_obj, &pulseio_pwmout_type)) { - mp_raise_TypeError_varg(translate("Expected a %q"), pulseio_pwmout_type.name); - } - - // create Pulse object from the given pin pulseio_pulseout_obj_t *self = m_new_obj(pulseio_pulseout_obj_t); self->base.type = &pulseio_pulseout_type; + #ifndef CPY_PULSEOUT_USES_DIGITALIO + // Most ports pass a PWMOut + mp_arg_check_num(n_args, kw_args, 1, 1, false); + mp_obj_t carrier_obj = args[0]; + if (!MP_OBJ_IS_TYPE(carrier_obj, &pulseio_pwmout_type)) { + mp_raise_TypeError_varg(translate("Expected a %q"), pulseio_pwmout_type.name); + } common_hal_pulseio_pulseout_construct(self, (pulseio_pwmout_obj_t *)MP_OBJ_TO_PTR(carrier_obj)); + #else + // ESP32-S2 Special Case + enum { ARG_pin, ARG_frequency}; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_pin, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_frequency, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 38000} }, + }; + 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); + + const mcu_pin_obj_t* pin = validate_obj_is_free_pin(args[ARG_pin].u_obj); + + common_hal_pulseio_pulseout_construct(self, pin, args[ARG_frequency].u_int); + #endif return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/pulseio/PulseOut.h b/shared-bindings/pulseio/PulseOut.h index 390910ff62..090f6d15c6 100644 --- a/shared-bindings/pulseio/PulseOut.h +++ b/shared-bindings/pulseio/PulseOut.h @@ -33,8 +33,14 @@ extern const mp_obj_type_t pulseio_pulseout_type; +#ifndef CPY_PULSEOUT_USES_DIGITALIO extern void common_hal_pulseio_pulseout_construct(pulseio_pulseout_obj_t* self, const pulseio_pwmout_obj_t* carrier); +#else +extern void common_hal_pulseio_pulseout_construct(pulseio_pulseout_obj_t* self, + const mcu_pin_obj_t* pin, uint32_t frequency); +#endif + extern void common_hal_pulseio_pulseout_deinit(pulseio_pulseout_obj_t* self); extern bool common_hal_pulseio_pulseout_deinited(pulseio_pulseout_obj_t* self); extern void common_hal_pulseio_pulseout_send(pulseio_pulseout_obj_t* self,