From 2b8d115ebc08ee3ac03d680eff40d2b8079c2b44 Mon Sep 17 00:00:00 2001 From: John Robertson Date: Sat, 4 Jun 2022 07:26:08 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20PWM=20for=20ESP32=20I2S=20?= =?UTF-8?q?expander=20(#24193)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Marlin/Configuration_adv.h | 5 ++- Marlin/Makefile | 2 +- Marlin/src/HAL/ESP32/HAL.cpp | 50 ++++++++++++++++----- Marlin/src/HAL/ESP32/HAL.h | 11 +++++ Marlin/src/HAL/ESP32/i2s.cpp | 20 +++++++++ Marlin/src/HAL/ESP32/inc/Conditionals_adv.h | 7 +++ Marlin/src/HAL/ESP32/inc/SanityCheck.h | 4 ++ Marlin/src/module/stepper.cpp | 4 +- Marlin/src/pins/esp32/pins_E4D.h | 9 +--- Marlin/src/pins/esp32/pins_ENWI_ESPNP.h | 8 ++-- Marlin/src/pins/esp32/pins_ESP32.h | 8 ++-- Marlin/src/pins/esp32/pins_ESPA_common.h | 8 ---- Marlin/src/pins/esp32/pins_MKS_TINYBEE.h | 5 +-- Marlin/src/pins/esp32/pins_MRR_ESPE.h | 9 ++-- 14 files changed, 104 insertions(+), 46 deletions(-) diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h index d25355d8a6..9b70dbbb65 100644 --- a/Marlin/Configuration_adv.h +++ b/Marlin/Configuration_adv.h @@ -3472,7 +3472,10 @@ #if ENABLED(SPINDLE_LASER_USE_PWM) #define SPINDLE_LASER_PWM_INVERT false // Set to "true" if the speed/power goes up when you want it to go slower #define SPINDLE_LASER_FREQUENCY 2500 // (Hz) Spindle/laser frequency (only on supported HALs: AVR, ESP32, and LPC) - #endif + // ESP32: If SPINDLE_LASER_PWM_PIN is onboard then <=78125Hz. For I2S expander + // the frequency determines the PWM resolution. 2500Hz = 0-100, 977Hz = 0-255, ... + // (250000 / SPINDLE_LASER_FREQUENCY) = max value. +#endif //#define AIR_EVACUATION // Cutter Vacuum / Laser Blower motor control with G-codes M10-M11 #if ENABLED(AIR_EVACUATION) diff --git a/Marlin/Makefile b/Marlin/Makefile index 417c979f88..563354fdbe 100644 --- a/Marlin/Makefile +++ b/Marlin/Makefile @@ -109,7 +109,7 @@ LIQUID_TWI2 ?= 0 # This defines if Wire is needed WIRE ?= 0 -# This defines if Tone is needed (i.e SPEAKER is defined in Configuration.h) +# This defines if Tone is needed (i.e., SPEAKER is defined in Configuration.h) # Disabling this (and SPEAKER) saves approximately 350 bytes of memory. TONE ?= 1 diff --git a/Marlin/src/HAL/ESP32/HAL.cpp b/Marlin/src/HAL/ESP32/HAL.cpp index 65af39786e..29f3be3c02 100644 --- a/Marlin/src/HAL/ESP32/HAL.cpp +++ b/Marlin/src/HAL/ESP32/HAL.cpp @@ -65,6 +65,7 @@ portMUX_TYPE MarlinHAL::spinlock = portMUX_INITIALIZER_UNLOCKED; // ------------------------ uint16_t MarlinHAL::adc_result; +pwm_pin_t MarlinHAL::pwm_pin_data[MAX_EXPANDER_BITS]; // ------------------------ // Private Variables @@ -330,21 +331,46 @@ int8_t get_pwm_channel(const pin_t pin, const uint32_t freq, const uint16_t res) } void MarlinHAL::set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size/*=_BV(PWM_RESOLUTION)-1*/, const bool invert/*=false*/) { - const int8_t cid = get_pwm_channel(pin, PWM_FREQUENCY, PWM_RESOLUTION); - if (cid >= 0) { - uint32_t duty = map(invert ? v_size - v : v, 0, v_size, 0, _BV(PWM_RESOLUTION)-1); - ledcWrite(cid, duty); - } + #if ENABLED(I2S_STEPPER_STREAM) + if (pin > 127) { + const uint8_t pinlo = pin & 0x7F; + pwm_pin_t &pindata = pwm_pin_data[pinlo]; + const uint32_t duty = map(invert ? v_size - v : v, 0, v_size, 0, pindata.pwm_cycle_ticks); + if (duty == 0 || duty == pindata.pwm_cycle_ticks) { // max or min (i.e., on/off) + pindata.pwm_duty_ticks = 0; // turn off PWM for this pin + duty ? SBI32(i2s_port_data, pinlo) : CBI32(i2s_port_data, pinlo); // set pin level + } + else + pindata.pwm_duty_ticks = duty; // PWM duty count = # of 4µs ticks per full PWM cycle + } + else + #endif + { + const int8_t cid = get_pwm_channel(pin, PWM_FREQUENCY, PWM_RESOLUTION); + if (cid >= 0) { + const uint32_t duty = map(invert ? v_size - v : v, 0, v_size, 0, _BV(PWM_RESOLUTION)-1); + ledcWrite(cid, duty); + } + } } int8_t MarlinHAL::set_pwm_frequency(const pin_t pin, const uint32_t f_desired) { - const int8_t cid = channel_for_pin(pin); - if (cid >= 0) { - if (f_desired == ledcReadFreq(cid)) return cid; // no freq change - ledcDetachPin(chan_pin[cid]); - chan_pin[cid] = 0; // remove old freq channel - } - return get_pwm_channel(pin, f_desired, PWM_RESOLUTION); // try for new one + #if ENABLED(I2S_STEPPER_STREAM) + if (pin > 127) { + pwm_pin_data[pin & 0x7F].pwm_cycle_ticks = 1000000UL / f_desired / 4; // # of 4µs ticks per full PWM cycle + return 0; + } + else + #endif + { + const int8_t cid = channel_for_pin(pin); + if (cid >= 0) { + if (f_desired == ledcReadFreq(cid)) return cid; // no freq change + ledcDetachPin(chan_pin[cid]); + chan_pin[cid] = 0; // remove old freq channel + } + return get_pwm_channel(pin, f_desired, PWM_RESOLUTION); // try for new one + } } // use hardware PWM if avail, if not then ISR diff --git a/Marlin/src/HAL/ESP32/HAL.h b/Marlin/src/HAL/ESP32/HAL.h index a07303d489..600ca8f5ee 100644 --- a/Marlin/src/HAL/ESP32/HAL.h +++ b/Marlin/src/HAL/ESP32/HAL.h @@ -68,6 +68,9 @@ #define PWM_RESOLUTION 10u // Default PWM bit resolution #define CHANNEL_MAX_NUM 15u // max PWM channel # to allocate (7 to only use low speed, 15 to use low & high) #define MAX_PWM_IOPIN 33u // hardware pwm pins < 34 +#ifndef MAX_EXPANDER_BITS + #define MAX_EXPANDER_BITS 32 // I2S expander bit width (max 32) +#endif // ------------------------ // Types @@ -76,6 +79,12 @@ typedef double isr_float_t; // FPU ops are used for single-precision, so use double for ISRs. typedef int16_t pin_t; +typedef struct pwm_pin { + uint32_t pwm_cycle_ticks = 1000000UL / (PWM_FREQUENCY) / 4; // # ticks per pwm cycle + uint32_t pwm_tick_count = 0; // current tick count + uint32_t pwm_duty_ticks = 0; // # of ticks for current duty cycle +} pwm_pin_t; + class Servo; typedef Servo hal_servo_t; @@ -197,6 +206,8 @@ public: // Free SRAM static int freeMemory(); + static pwm_pin_t pwm_pin_data[MAX_EXPANDER_BITS]; + // // ADC Methods // diff --git a/Marlin/src/HAL/ESP32/i2s.cpp b/Marlin/src/HAL/ESP32/i2s.cpp index 3e77b65836..cf337eeb46 100644 --- a/Marlin/src/HAL/ESP32/i2s.cpp +++ b/Marlin/src/HAL/ESP32/i2s.cpp @@ -337,6 +337,26 @@ uint8_t i2s_state(uint8_t pin) { } void i2s_push_sample() { + // Every 4µs (when space in DMA buffer) toggle each expander PWM output using + // the current duty cycle/frequency so they sync with any steps (once + // through the DMA/FIFO buffers). PWM signal inversion handled by other functions + LOOP_L_N(p, MAX_EXPANDER_BITS) { + if (hal.pwm_pin_data[p].pwm_duty_ticks > 0) { // pin has active pwm? + if (hal.pwm_pin_data[p].pwm_tick_count == 0) { + if (TEST32(i2s_port_data, p)) { // hi->lo + CBI32(i2s_port_data, p); + hal.pwm_pin_data[p].pwm_tick_count = hal.pwm_pin_data[p].pwm_cycle_ticks - hal.pwm_pin_data[p].pwm_duty_ticks; + } + else { // lo->hi + SBI32(i2s_port_data, p); + hal.pwm_pin_data[p].pwm_tick_count = hal.pwm_pin_data[p].pwm_duty_ticks; + } + } + else + hal.pwm_pin_data[p].pwm_tick_count--; + } + } + dma.current[dma.rw_pos++] = i2s_port_data; } diff --git a/Marlin/src/HAL/ESP32/inc/Conditionals_adv.h b/Marlin/src/HAL/ESP32/inc/Conditionals_adv.h index 5f1c4b1601..3ca806897a 100644 --- a/Marlin/src/HAL/ESP32/inc/Conditionals_adv.h +++ b/Marlin/src/HAL/ESP32/inc/Conditionals_adv.h @@ -20,3 +20,10 @@ * */ #pragma once + +// +// Board-specific options need to be defined before HAL.h +// +#if MB(MKS_TINYBEE) + #define MAX_EXPANDER_BITS 24 // TinyBee has 3 x HC595 +#endif diff --git a/Marlin/src/HAL/ESP32/inc/SanityCheck.h b/Marlin/src/HAL/ESP32/inc/SanityCheck.h index 04d70ec14f..3ccb15989f 100644 --- a/Marlin/src/HAL/ESP32/inc/SanityCheck.h +++ b/Marlin/src/HAL/ESP32/inc/SanityCheck.h @@ -48,3 +48,7 @@ #if USING_PULLDOWNS #error "PULLDOWN pin mode is not available on ESP32 boards." #endif + +#if BOTH(I2S_STEPPER_STREAM, LIN_ADVANCE) + #error "I2S stream is currently incompatible with LIN_ADVANCE." +#endif diff --git a/Marlin/src/module/stepper.cpp b/Marlin/src/module/stepper.cpp index 2ed97b7e0c..7624359c87 100644 --- a/Marlin/src/module/stepper.cpp +++ b/Marlin/src/module/stepper.cpp @@ -1863,9 +1863,7 @@ void Stepper::pulse_phase_isr() { #endif #endif - #if ENABLED(I2S_STEPPER_STREAM) - i2s_push_sample(); - #endif + TERN_(I2S_STEPPER_STREAM, i2s_push_sample()); // TODO: need to deal with MINIMUM_STEPPER_PULSE over i2s #if ISR_MULTI_STEPS diff --git a/Marlin/src/pins/esp32/pins_E4D.h b/Marlin/src/pins/esp32/pins_E4D.h index 02a5fe4e33..86ba1a7594 100644 --- a/Marlin/src/pins/esp32/pins_E4D.h +++ b/Marlin/src/pins/esp32/pins_E4D.h @@ -40,19 +40,14 @@ #define BOARD_WEBSITE_URL "github.com/Exilaus/E4d@box" #define DEFAULT_MACHINE_NAME BOARD_INFO_NAME -// -// Disable I2S stepper stream -// -#undef I2S_STEPPER_STREAM - // // Redefine I2S for ESP32 // #undef I2S_WS -#define I2S_WS 23 #undef I2S_BCK -#define I2S_BCK 22 #undef I2S_DATA +#define I2S_WS 23 +#define I2S_BCK 22 #define I2S_DATA 21 // diff --git a/Marlin/src/pins/esp32/pins_ENWI_ESPNP.h b/Marlin/src/pins/esp32/pins_ENWI_ESPNP.h index aff7f742ed..80923d972d 100644 --- a/Marlin/src/pins/esp32/pins_ENWI_ESPNP.h +++ b/Marlin/src/pins/esp32/pins_ENWI_ESPNP.h @@ -35,9 +35,11 @@ // I2S (steppers & other output-only pins) // #define I2S_STEPPER_STREAM -#define I2S_WS 17 -#define I2S_BCK 22 -#define I2S_DATA 21 +#if ENABLED(I2S_STEPPER_STREAM) + #define I2S_WS 17 + #define I2S_BCK 22 + #define I2S_DATA 21 +#endif // // Servos diff --git a/Marlin/src/pins/esp32/pins_ESP32.h b/Marlin/src/pins/esp32/pins_ESP32.h index 6578770ba0..266de7e9f6 100644 --- a/Marlin/src/pins/esp32/pins_ESP32.h +++ b/Marlin/src/pins/esp32/pins_ESP32.h @@ -33,9 +33,11 @@ // I2S (steppers & other output-only pins) // #define I2S_STEPPER_STREAM -#define I2S_WS 25 -#define I2S_BCK 26 -#define I2S_DATA 27 +#if ENABLED(I2S_STEPPER_STREAM) + #define I2S_WS 25 + #define I2S_BCK 26 + #define I2S_DATA 27 +#endif // // Limit Switches diff --git a/Marlin/src/pins/esp32/pins_ESPA_common.h b/Marlin/src/pins/esp32/pins_ESPA_common.h index 2fcacb3002..ca949cdf97 100644 --- a/Marlin/src/pins/esp32/pins_ESPA_common.h +++ b/Marlin/src/pins/esp32/pins_ESPA_common.h @@ -32,14 +32,6 @@ #define DEFAULT_MACHINE_NAME BOARD_INFO_NAME #endif -// -// Disable I2S stepper stream, by default -// -#undef I2S_STEPPER_STREAM -#undef I2S_WS -#undef I2S_BCK -#undef I2S_DATA - // // Limit Switches // diff --git a/Marlin/src/pins/esp32/pins_MKS_TINYBEE.h b/Marlin/src/pins/esp32/pins_MKS_TINYBEE.h index 025650fef5..10e8fbc0c6 100644 --- a/Marlin/src/pins/esp32/pins_MKS_TINYBEE.h +++ b/Marlin/src/pins/esp32/pins_MKS_TINYBEE.h @@ -40,6 +40,8 @@ #define BOARD_WEBSITE_URL "https://github.com/makerbase-mks" #define DEFAULT_MACHINE_NAME BOARD_INFO_NAME +// MAX_EXPANDER_BITS is defined for MKS TinyBee in HAL/ESP32/inc/Conditionals_adv.h + // // Servos // @@ -61,9 +63,6 @@ #define I2S_WS 26 #define I2S_BCK 25 #define I2S_DATA 27 - #if ENABLED(LIN_ADVANCE) - #error "I2S stream is currently incompatible with LIN_ADVANCE." - #endif #endif // diff --git a/Marlin/src/pins/esp32/pins_MRR_ESPE.h b/Marlin/src/pins/esp32/pins_MRR_ESPE.h index 9b9b54e3ae..140be0a286 100644 --- a/Marlin/src/pins/esp32/pins_MRR_ESPE.h +++ b/Marlin/src/pins/esp32/pins_MRR_ESPE.h @@ -52,11 +52,10 @@ // Enable I2S stepper stream // #define I2S_STEPPER_STREAM -#define I2S_WS 26 -#define I2S_BCK 25 -#define I2S_DATA 27 -#if ENABLED(LIN_ADVANCE) - #error "I2S stream is currently incompatible with LIN_ADVANCE." +#if ENABLED(I2S_STEPPER_STREAM) + #define I2S_WS 26 + #define I2S_BCK 25 + #define I2S_DATA 27 #endif //