From 207369ec0978f87ceb5e57f42d30ae10025e5bc7 Mon Sep 17 00:00:00 2001 From: Lucian Copeland Date: Fri, 17 Jul 2020 17:28:23 -0400 Subject: [PATCH 1/3] Add boilerplate, create basic PWMOut test --- ports/esp32s2/common-hal/pulseio/PWMOut.c | 120 ++++++++++++++++++++ ports/esp32s2/common-hal/pulseio/PWMOut.h | 43 +++++++ ports/esp32s2/common-hal/pulseio/PulseIn.c | 75 ++++++++++++ ports/esp32s2/common-hal/pulseio/PulseIn.h | 53 +++++++++ ports/esp32s2/common-hal/pulseio/PulseOut.c | 60 ++++++++++ ports/esp32s2/common-hal/pulseio/PulseOut.h | 42 +++++++ ports/esp32s2/common-hal/pulseio/__init__.c | 1 + ports/esp32s2/mpconfigport.mk | 2 +- 8 files changed, 395 insertions(+), 1 deletion(-) create mode 100644 ports/esp32s2/common-hal/pulseio/PWMOut.c create mode 100644 ports/esp32s2/common-hal/pulseio/PWMOut.h create mode 100644 ports/esp32s2/common-hal/pulseio/PulseIn.c create mode 100644 ports/esp32s2/common-hal/pulseio/PulseIn.h create mode 100644 ports/esp32s2/common-hal/pulseio/PulseOut.c create mode 100644 ports/esp32s2/common-hal/pulseio/PulseOut.h create mode 100644 ports/esp32s2/common-hal/pulseio/__init__.c diff --git a/ports/esp32s2/common-hal/pulseio/PWMOut.c b/ports/esp32s2/common-hal/pulseio/PWMOut.c new file mode 100644 index 0000000000..8f96433c06 --- /dev/null +++ b/ports/esp32s2/common-hal/pulseio/PWMOut.c @@ -0,0 +1,120 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Lucian Copeland for Adafruit Industries + * Uses code from Micropython, Copyright (c) 2013-2016 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 "common-hal/pulseio/PWMOut.h" +#include "shared-bindings/pulseio/PWMOut.h" +#include "py/runtime.h" +#include "driver/ledc.h" + +#define LEDC_LS_TIMER LEDC_TIMER_1 +#define LEDC_LS_MODE LEDC_LOW_SPEED_MODE +#define LEDC_LS_CH0_GPIO (18) +#define LEDC_LS_CH0_CHANNEL LEDC_CHANNEL_0 +#define LEDC_LS_CH1_GPIO (19) +#define LEDC_LS_CH1_CHANNEL LEDC_CHANNEL_1 +#define LEDC_LS_CH2_GPIO (4) +#define LEDC_LS_CH2_CHANNEL LEDC_CHANNEL_2 +#define LEDC_LS_CH3_GPIO (5) +#define LEDC_LS_CH3_CHANNEL LEDC_CHANNEL_3 + +#define LEDC_TEST_CH_NUM (4) +#define LEDC_TEST_DUTY (4000) +#define LEDC_TEST_FADE_TIME (3000) + + + +void pwmout_reset(void) { +} + +pwmout_result_t common_hal_pulseio_pwmout_construct(pulseio_pwmout_obj_t* self, + const mcu_pin_obj_t* pin, + uint16_t duty, + uint32_t frequency, + bool variable_frequency) { + ledc_timer_config_t ledc_timer = { + .duty_resolution = LEDC_TIMER_16_BIT, // resolution of PWM duty + .freq_hz = frequency, // frequency of PWM signal + .speed_mode = LEDC_LS_MODE, // timer mode + .timer_num = LEDC_LS_TIMER, // timer index + .clk_cfg = LEDC_AUTO_CLK, // Auto select the source clock + }; + // Set configuration of timer0 for high speed channels + ledc_timer_config(&ledc_timer); + + ledc_channel_config_t pwm_channel = { + .channel = LEDC_LS_CH0_CHANNEL, + .duty = 0, + .gpio_num = pin->number, + .speed_mode = LEDC_LS_MODE, + .hpoint = 0, + .timer_sel = LEDC_LS_TIMER + }; + + // Set LED Controller with previously prepared configuration + for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++) { + ledc_channel_config(&ledc_channel[ch]); + } + + for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++) { + ledc_set_duty(ledc_channel[ch].speed_mode, ledc_channel[ch].channel, duty*2); + ledc_update_duty(ledc_channel[ch].speed_mode, ledc_channel[ch].channel); + } + + return PWMOUT_OK; +} + +void common_hal_pulseio_pwmout_never_reset(pulseio_pwmout_obj_t *self) { +} + +void common_hal_pulseio_pwmout_reset_ok(pulseio_pwmout_obj_t *self) { +} + +bool common_hal_pulseio_pwmout_deinited(pulseio_pwmout_obj_t* self) { + return false; +} + +void common_hal_pulseio_pwmout_deinit(pulseio_pwmout_obj_t* self) { +} + +void common_hal_pulseio_pwmout_set_duty_cycle(pulseio_pwmout_obj_t* self, uint16_t duty) { +} + +uint16_t common_hal_pulseio_pwmout_get_duty_cycle(pulseio_pwmout_obj_t* self) { + return false; +} + +void common_hal_pulseio_pwmout_set_frequency(pulseio_pwmout_obj_t* self, uint32_t frequency) { +} + +uint32_t common_hal_pulseio_pwmout_get_frequency(pulseio_pwmout_obj_t* self) { + return false; +} + +bool common_hal_pulseio_pwmout_get_variable_frequency(pulseio_pwmout_obj_t* self) { + return false; +} diff --git a/ports/esp32s2/common-hal/pulseio/PWMOut.h b/ports/esp32s2/common-hal/pulseio/PWMOut.h new file mode 100644 index 0000000000..28484e9238 --- /dev/null +++ b/ports/esp32s2/common-hal/pulseio/PWMOut.h @@ -0,0 +1,43 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Lucian Copeland for Adafruit Industries + * + * 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_ESP32S2_COMMON_HAL_PULSEIO_PWMOUT_H +#define MICROPY_INCLUDED_ESP32S2_COMMON_HAL_PULSEIO_PWMOUT_H + +#include "common-hal/microcontroller/Pin.h" + +typedef struct { + mp_obj_base_t base; + uint8_t channel; + bool variable_frequency: 1; + uint16_t duty_cycle; + uint32_t frequency; + uint32_t period; +} pulseio_pwmout_obj_t; + +void pwmout_reset(void); + +#endif // MICROPY_INCLUDED_ESP32S2_COMMON_HAL_PULSEIO_PWMOUT_H diff --git a/ports/esp32s2/common-hal/pulseio/PulseIn.c b/ports/esp32s2/common-hal/pulseio/PulseIn.c new file mode 100644 index 0000000000..65fc6631d4 --- /dev/null +++ b/ports/esp32s2/common-hal/pulseio/PulseIn.c @@ -0,0 +1,75 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Lucian Copeland for Adafruit Industries + * + * 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 "common-hal/pulseio/PulseIn.h" +#include "py/runtime.h" + +// STATIC void pulsein_handler(uint8_t num) { +// } + +void pulsein_reset(void) { +} + +void common_hal_pulseio_pulsein_construct(pulseio_pulsein_obj_t* self, const mcu_pin_obj_t* pin, + uint16_t maxlen, bool idle_state) { + mp_raise_NotImplementedError(translate("PulseIn not supported on this chip")); +} + +bool common_hal_pulseio_pulsein_deinited(pulseio_pulsein_obj_t* self) { + return false; +} + +void common_hal_pulseio_pulsein_deinit(pulseio_pulsein_obj_t* self) { +} + +void common_hal_pulseio_pulsein_pause(pulseio_pulsein_obj_t* self) { +} + +void common_hal_pulseio_pulsein_resume(pulseio_pulsein_obj_t* self, uint16_t trigger_duration) { +} + +void common_hal_pulseio_pulsein_clear(pulseio_pulsein_obj_t* self) { +} + +uint16_t common_hal_pulseio_pulsein_get_item(pulseio_pulsein_obj_t* self, int16_t index) { + return false; +} + +uint16_t common_hal_pulseio_pulsein_popleft(pulseio_pulsein_obj_t* self) { + return false; +} + +uint16_t common_hal_pulseio_pulsein_get_maxlen(pulseio_pulsein_obj_t* self) { + return false; +} + +bool common_hal_pulseio_pulsein_get_paused(pulseio_pulsein_obj_t* self) { + return false; +} + +uint16_t common_hal_pulseio_pulsein_get_len(pulseio_pulsein_obj_t* self) { + return false; +} diff --git a/ports/esp32s2/common-hal/pulseio/PulseIn.h b/ports/esp32s2/common-hal/pulseio/PulseIn.h new file mode 100644 index 0000000000..b317c516c1 --- /dev/null +++ b/ports/esp32s2/common-hal/pulseio/PulseIn.h @@ -0,0 +1,53 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Lucian Copeland for Adafruit Industries + * + * 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_ESP32S2_COMMON_HAL_PULSEIO_PULSEIN_H +#define MICROPY_INCLUDED_ESP32S2_COMMON_HAL_PULSEIO_PULSEIN_H + +#include "common-hal/microcontroller/Pin.h" + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; + + const mcu_pin_obj_t* pin; + bool idle_state; + bool paused; + volatile bool first_edge; + + uint16_t* buffer; + uint16_t maxlen; + + volatile uint16_t start; + volatile uint16_t len; + volatile uint32_t last_overflow; + volatile uint16_t last_count; +} pulseio_pulsein_obj_t; + +void pulsein_reset(void); + +#endif // MICROPY_INCLUDED_ESP32S2_COMMON_HAL_PULSEIO_PULSEIN_H diff --git a/ports/esp32s2/common-hal/pulseio/PulseOut.c b/ports/esp32s2/common-hal/pulseio/PulseOut.c new file mode 100644 index 0000000000..c448c578df --- /dev/null +++ b/ports/esp32s2/common-hal/pulseio/PulseOut.c @@ -0,0 +1,60 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Lucian Copeland for Adafruit Industries + * + * 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 "common-hal/pulseio/PulseOut.h" + +#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() { +} + +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")); +} + +bool common_hal_pulseio_pulseout_deinited(pulseio_pulseout_obj_t* self) { + return false; +} + +void common_hal_pulseio_pulseout_deinit(pulseio_pulseout_obj_t* self) { +} + +void common_hal_pulseio_pulseout_send(pulseio_pulseout_obj_t* self, uint16_t* pulses, uint16_t length) { +} diff --git a/ports/esp32s2/common-hal/pulseio/PulseOut.h b/ports/esp32s2/common-hal/pulseio/PulseOut.h new file mode 100644 index 0000000000..f465d00792 --- /dev/null +++ b/ports/esp32s2/common-hal/pulseio/PulseOut.h @@ -0,0 +1,42 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Lucian Copeland for Adafruit Industries + * + * 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_ESP32S2_COMMON_HAL_PULSEIO_PULSEOUT_H +#define MICROPY_INCLUDED_ESP32S2_COMMON_HAL_PULSEIO_PULSEOUT_H + +#include "common-hal/microcontroller/Pin.h" +#include "common-hal/pulseio/PWMOut.h" + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; + pulseio_pwmout_obj_t *pwmout; +} pulseio_pulseout_obj_t; + +void pulseout_reset(void); + +#endif // MICROPY_INCLUDED_ESP32S2_COMMON_HAL_PULSEIO_PULSEOUT_H diff --git a/ports/esp32s2/common-hal/pulseio/__init__.c b/ports/esp32s2/common-hal/pulseio/__init__.c new file mode 100644 index 0000000000..2bee925bc7 --- /dev/null +++ b/ports/esp32s2/common-hal/pulseio/__init__.c @@ -0,0 +1 @@ +// No pulseio module functions. diff --git a/ports/esp32s2/mpconfigport.mk b/ports/esp32s2/mpconfigport.mk index 5686579b45..fccb14dedf 100644 --- a/ports/esp32s2/mpconfigport.mk +++ b/ports/esp32s2/mpconfigport.mk @@ -25,7 +25,7 @@ CIRCUITPY_FREQUENCYIO = 0 CIRCUITPY_I2CPERIPHERAL = 0 CIRCUITPY_MICROCONTROLLER = 1 CIRCUITPY_NVM = 0 -CIRCUITPY_PULSEIO = 0 +CIRCUITPY_PULSEIO = 1 CIRCUITPY_ROTARYIO = 0 CIRCUITPY_RTC = 0 CIRCUITPY_TOUCHIO = 0 From 61a2e4f94b58d8f7ad09782b0a4e4bb4414b591f Mon Sep 17 00:00:00 2001 From: Lucian Copeland Date: Tue, 21 Jul 2020 20:43:28 -0400 Subject: [PATCH 2/3] Add PWMOut --- ports/esp32s2/Makefile | 2 +- ports/esp32s2/common-hal/pulseio/PWMOut.c | 169 ++++++++++++++++------ ports/esp32s2/common-hal/pulseio/PWMOut.h | 10 +- ports/esp32s2/supervisor/port.c | 4 + 4 files changed, 135 insertions(+), 50 deletions(-) diff --git a/ports/esp32s2/Makefile b/ports/esp32s2/Makefile index b296976885..09c35fd3ad 100644 --- a/ports/esp32s2/Makefile +++ b/ports/esp32s2/Makefile @@ -262,7 +262,7 @@ all: $(BUILD)/firmware.bin $(BUILD)/firmware.uf2 $(BUILD)/firmware.elf: $(OBJ) | $(ESP_IDF_COMPONENTS_EXPANDED) $(ESP_AUTOGEN_LD) $(STEPECHO) "LINK $@" - $(Q)$(CC) -o $@ $(LDFLAGS) $^ $(ESP_IDF_COMPONENTS_EXPANDED) $(BINARY_BLOBS) build-$(BOARD)/esp-idf/esp-idf/newlib/libnewlib.a -u newlib_include_pthread_impl + $(Q)$(CC) -o $@ $(LDFLAGS) $^ $(ESP_IDF_COMPONENTS_EXPANDED) $(BINARY_BLOBS) build-$(BOARD)/esp-idf/esp-idf/newlib/libnewlib.a -u newlib_include_pthread_impl -Wl,--start-group $(LIBS) -Wl,--end-group # $(Q)$(SIZE) $@ | $(PYTHON3) $(TOP)/tools/build_memory_info.py $(BUILD)/esp-idf/esp-idf/esp32s2/esp32s2_out.ld $(BUILD)/circuitpython-firmware.bin: $(BUILD)/firmware.elf diff --git a/ports/esp32s2/common-hal/pulseio/PWMOut.c b/ports/esp32s2/common-hal/pulseio/PWMOut.c index 8f96433c06..15fe96a229 100644 --- a/ports/esp32s2/common-hal/pulseio/PWMOut.c +++ b/ports/esp32s2/common-hal/pulseio/PWMOut.c @@ -24,31 +24,33 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ - +#include #include "common-hal/pulseio/PWMOut.h" #include "shared-bindings/pulseio/PWMOut.h" #include "py/runtime.h" #include "driver/ledc.h" -#define LEDC_LS_TIMER LEDC_TIMER_1 -#define LEDC_LS_MODE LEDC_LOW_SPEED_MODE -#define LEDC_LS_CH0_GPIO (18) -#define LEDC_LS_CH0_CHANNEL LEDC_CHANNEL_0 -#define LEDC_LS_CH1_GPIO (19) -#define LEDC_LS_CH1_CHANNEL LEDC_CHANNEL_1 -#define LEDC_LS_CH2_GPIO (4) -#define LEDC_LS_CH2_CHANNEL LEDC_CHANNEL_2 -#define LEDC_LS_CH3_GPIO (5) -#define LEDC_LS_CH3_CHANNEL LEDC_CHANNEL_3 - -#define LEDC_TEST_CH_NUM (4) -#define LEDC_TEST_DUTY (4000) -#define LEDC_TEST_FADE_TIME (3000) - +#define INDEX_EMPTY 0xFF +STATIC uint32_t reserved_timer_freq[LEDC_TIMER_MAX]; +STATIC uint8_t reserved_channels[LEDC_CHANNEL_MAX] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; +STATIC bool never_reset_tim[LEDC_TIMER_MAX]; +STATIC bool never_reset_chan[LEDC_CHANNEL_MAX]; void pwmout_reset(void) { + for (size_t i = 0; i < LEDC_CHANNEL_MAX; i++ ) { + ledc_stop(LEDC_LOW_SPEED_MODE, i, 0); + if (!never_reset_chan[i]) { + reserved_channels[i] = INDEX_EMPTY; + } + } + for (size_t i = 0; i < LEDC_TIMER_MAX; i++ ) { + ledc_timer_rst(LEDC_LOW_SPEED_MODE, i); + if (!never_reset_tim[i]) { + reserved_timer_freq[i] = 0; + } + } } pwmout_result_t common_hal_pulseio_pwmout_construct(pulseio_pwmout_obj_t* self, @@ -56,65 +58,142 @@ pwmout_result_t common_hal_pulseio_pwmout_construct(pulseio_pwmout_obj_t* self, uint16_t duty, uint32_t frequency, bool variable_frequency) { - ledc_timer_config_t ledc_timer = { - .duty_resolution = LEDC_TIMER_16_BIT, // resolution of PWM duty - .freq_hz = frequency, // frequency of PWM signal - .speed_mode = LEDC_LS_MODE, // timer mode - .timer_num = LEDC_LS_TIMER, // timer index - .clk_cfg = LEDC_AUTO_CLK, // Auto select the source clock - }; - // Set configuration of timer0 for high speed channels - ledc_timer_config(&ledc_timer); - - ledc_channel_config_t pwm_channel = { - .channel = LEDC_LS_CH0_CHANNEL, - .duty = 0, - .gpio_num = pin->number, - .speed_mode = LEDC_LS_MODE, - .hpoint = 0, - .timer_sel = LEDC_LS_TIMER - }; - - // Set LED Controller with previously prepared configuration - for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++) { - ledc_channel_config(&ledc_channel[ch]); + // Calculate duty cycle + uint32_t duty_bits = 0; + uint32_t interval = LEDC_APB_CLK_HZ/frequency; + for (size_t i = 0; i < 32; i++) { + if(!(interval >> i)) { + duty_bits = i - 1; + break; + } + } + if (duty_bits < 1) { + mp_raise_ValueError(translate("Invalid frequency")); + } else if (duty_bits >= LEDC_TIMER_14_BIT) { + duty_bits = LEDC_TIMER_13_BIT; } - for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++) { - ledc_set_duty(ledc_channel[ch].speed_mode, ledc_channel[ch].channel, duty*2); - ledc_update_duty(ledc_channel[ch].speed_mode, ledc_channel[ch].channel); + // Find a viable timer + size_t timer_index = INDEX_EMPTY; + size_t channel_index = INDEX_EMPTY; + for (size_t i = 0; i < LEDC_TIMER_MAX; i++) { + if ((reserved_timer_freq[i] == frequency) && !variable_frequency) { + //prioritize matched frequencies so we don't needlessly take slots + timer_index = i; + break; + } else if (reserved_timer_freq[i] == 0) { + timer_index = i; + break; + } } + if (timer_index == INDEX_EMPTY) { + // Running out of timers isn't pin related on ESP32S2 so we can't re-use error messages + mp_raise_ValueError(translate("No more timers available")); + } + + // Find a viable channel + for (size_t i = 0; i < LEDC_CHANNEL_MAX; i++) { + if (reserved_channels[i] == INDEX_EMPTY) { + channel_index = i; + break; + } + } + if (channel_index == INDEX_EMPTY) { + mp_raise_ValueError(translate("No more channels available")); + } + + // Run configuration + self->tim_handle.timer_num = timer_index; + self->tim_handle.duty_resolution = duty_bits; + self->tim_handle.freq_hz = frequency; + self->tim_handle.speed_mode = LEDC_LOW_SPEED_MODE; + self->tim_handle.clk_cfg = LEDC_AUTO_CLK; + + if (ledc_timer_config(&(self->tim_handle)) != ESP_OK) { + mp_raise_ValueError(translate("Could not initialize timer")); + } + + self->chan_handle.channel = channel_index; + self->chan_handle.duty = duty >> (16 - duty_bits); + self->chan_handle.gpio_num = pin->number; + self->chan_handle.speed_mode = LEDC_LOW_SPEED_MODE; // Only LS is allowed on ESP32-S2 + self->chan_handle.hpoint = 0; + self->chan_handle.timer_sel = timer_index; + + if (ledc_channel_config(&(self->chan_handle))) { + mp_raise_ValueError(translate("Could not initialize channel")); + } + + // Make reservations + reserved_timer_freq[timer_index] = frequency; + reserved_channels[channel_index] = timer_index; + + self->variable_frequency = variable_frequency; + self->pin_number = pin->number; + self->deinited = false; + self->duty_resolution = duty_bits; + claim_pin(pin); + + // Set initial duty + ledc_set_duty(LEDC_LOW_SPEED_MODE, self->chan_handle.channel, duty >> (16 - duty_bits)); + ledc_update_duty(LEDC_LOW_SPEED_MODE, self->chan_handle.channel); return PWMOUT_OK; } void common_hal_pulseio_pwmout_never_reset(pulseio_pwmout_obj_t *self) { + never_reset_tim[self->tim_handle.timer_num] = true; + never_reset_chan[self->chan_handle.channel] = true; } void common_hal_pulseio_pwmout_reset_ok(pulseio_pwmout_obj_t *self) { + never_reset_tim[self->tim_handle.timer_num] = false; + never_reset_chan[self->chan_handle.channel] = false; } bool common_hal_pulseio_pwmout_deinited(pulseio_pwmout_obj_t* self) { - return false; + return self->deinited == true; } void common_hal_pulseio_pwmout_deinit(pulseio_pwmout_obj_t* self) { + if (common_hal_pulseio_pwmout_deinited(self)) { + return; + } + ledc_stop(LEDC_LOW_SPEED_MODE, self->chan_handle.channel, 0); + ledc_timer_rst(LEDC_LOW_SPEED_MODE, self->tim_handle.timer_num); + // Search if any other channel is using the timer + bool taken = false; + for (size_t i =0; i < LEDC_CHANNEL_MAX; i++) { + if (reserved_channels[i] == self->tim_handle.timer_num) { + taken = true; + } + } + // Variable frequency means there's only one channel on the timer + if (!taken || self->variable_frequency) { + reserved_timer_freq[self->tim_handle.timer_num] = 0; + } + reset_pin_number(self->pin_number); + reserved_channels[self->chan_handle.channel] = INDEX_EMPTY; + self->deinited = true; } void common_hal_pulseio_pwmout_set_duty_cycle(pulseio_pwmout_obj_t* self, uint16_t duty) { + ledc_set_duty(LEDC_LOW_SPEED_MODE, self->chan_handle.channel, duty >> (16 - self->duty_resolution)); + ledc_update_duty(LEDC_LOW_SPEED_MODE, self->chan_handle.channel); } uint16_t common_hal_pulseio_pwmout_get_duty_cycle(pulseio_pwmout_obj_t* self) { - return false; + return ledc_get_duty(LEDC_LOW_SPEED_MODE, self->chan_handle.channel) << (16 - self->duty_resolution); } void common_hal_pulseio_pwmout_set_frequency(pulseio_pwmout_obj_t* self, uint32_t frequency) { + ledc_set_freq(LEDC_LOW_SPEED_MODE, self->tim_handle.timer_num, frequency); } uint32_t common_hal_pulseio_pwmout_get_frequency(pulseio_pwmout_obj_t* self) { - return false; + return ledc_get_freq(LEDC_LOW_SPEED_MODE, self->tim_handle.timer_num); } bool common_hal_pulseio_pwmout_get_variable_frequency(pulseio_pwmout_obj_t* self) { - return false; + return self->variable_frequency; } diff --git a/ports/esp32s2/common-hal/pulseio/PWMOut.h b/ports/esp32s2/common-hal/pulseio/PWMOut.h index 28484e9238..90a0c3ed96 100644 --- a/ports/esp32s2/common-hal/pulseio/PWMOut.h +++ b/ports/esp32s2/common-hal/pulseio/PWMOut.h @@ -28,14 +28,16 @@ #define MICROPY_INCLUDED_ESP32S2_COMMON_HAL_PULSEIO_PWMOUT_H #include "common-hal/microcontroller/Pin.h" +#include "driver/ledc.h" typedef struct { mp_obj_base_t base; - uint8_t channel; + ledc_timer_config_t tim_handle; + ledc_channel_config_t chan_handle; + uint16_t pin_number; + uint8_t duty_resolution; bool variable_frequency: 1; - uint16_t duty_cycle; - uint32_t frequency; - uint32_t period; + bool deinited: 1; } pulseio_pwmout_obj_t; void pwmout_reset(void); diff --git a/ports/esp32s2/supervisor/port.c b/ports/esp32s2/supervisor/port.c index a80550b309..e4767e5146 100644 --- a/ports/esp32s2/supervisor/port.c +++ b/ports/esp32s2/supervisor/port.c @@ -38,6 +38,7 @@ #include "common-hal/busio/I2C.h" #include "common-hal/busio/SPI.h" #include "common-hal/busio/UART.h" +#include "common-hal/pulseio/PWMOut.h" #include "supervisor/memory.h" #include "supervisor/shared/tick.h" @@ -64,6 +65,9 @@ void reset_port(void) { // A larger delay so the idle task can run and do any IDF cleanup needed. vTaskDelay(4); +#if CIRCUITPY_PULSEIO + pwmout_reset(); +#endif #if CIRCUITPY_BUSIO i2c_reset(); spi_reset(); From cf0a4d208e71260b6f2796ba4f1f0edc36f2dc04 Mon Sep 17 00:00:00 2001 From: Lucian Copeland Date: Thu, 23 Jul 2020 11:21:19 -0400 Subject: [PATCH 3/3] Fix unintended timer reset, style changes --- ports/esp32s2/common-hal/pulseio/PWMOut.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ports/esp32s2/common-hal/pulseio/PWMOut.c b/ports/esp32s2/common-hal/pulseio/PWMOut.c index 15fe96a229..c4f5e18dd4 100644 --- a/ports/esp32s2/common-hal/pulseio/PWMOut.c +++ b/ports/esp32s2/common-hal/pulseio/PWMOut.c @@ -34,7 +34,7 @@ #define INDEX_EMPTY 0xFF STATIC uint32_t reserved_timer_freq[LEDC_TIMER_MAX]; -STATIC uint8_t reserved_channels[LEDC_CHANNEL_MAX] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; +STATIC uint8_t reserved_channels[LEDC_CHANNEL_MAX]; STATIC bool never_reset_tim[LEDC_TIMER_MAX]; STATIC bool never_reset_chan[LEDC_CHANNEL_MAX]; @@ -135,8 +135,7 @@ pwmout_result_t common_hal_pulseio_pwmout_construct(pulseio_pwmout_obj_t* self, claim_pin(pin); // Set initial duty - ledc_set_duty(LEDC_LOW_SPEED_MODE, self->chan_handle.channel, duty >> (16 - duty_bits)); - ledc_update_duty(LEDC_LOW_SPEED_MODE, self->chan_handle.channel); + common_hal_pulseio_pwmout_set_duty_cycle(self, duty); return PWMOUT_OK; } @@ -160,7 +159,6 @@ void common_hal_pulseio_pwmout_deinit(pulseio_pwmout_obj_t* self) { return; } ledc_stop(LEDC_LOW_SPEED_MODE, self->chan_handle.channel, 0); - ledc_timer_rst(LEDC_LOW_SPEED_MODE, self->tim_handle.timer_num); // Search if any other channel is using the timer bool taken = false; for (size_t i =0; i < LEDC_CHANNEL_MAX; i++) { @@ -170,6 +168,7 @@ void common_hal_pulseio_pwmout_deinit(pulseio_pwmout_obj_t* self) { } // Variable frequency means there's only one channel on the timer if (!taken || self->variable_frequency) { + ledc_timer_rst(LEDC_LOW_SPEED_MODE, self->tim_handle.timer_num); reserved_timer_freq[self->tim_handle.timer_num] = 0; } reset_pin_number(self->pin_number); @@ -178,7 +177,7 @@ void common_hal_pulseio_pwmout_deinit(pulseio_pwmout_obj_t* self) { } void common_hal_pulseio_pwmout_set_duty_cycle(pulseio_pwmout_obj_t* self, uint16_t duty) { - ledc_set_duty(LEDC_LOW_SPEED_MODE, self->chan_handle.channel, duty >> (16 - self->duty_resolution)); + ledc_set_duty(LEDC_LOW_SPEED_MODE, self->chan_handle.channel, duty >> (16 - self->duty_resolution)); ledc_update_duty(LEDC_LOW_SPEED_MODE, self->chan_handle.channel); }