diff --git a/Grbl_Esp32/spindle_control.cpp b/Grbl_Esp32/spindle_control.cpp index 5a6a4072..1c4f960f 100644 --- a/Grbl_Esp32/spindle_control.cpp +++ b/Grbl_Esp32/spindle_control.cpp @@ -24,4 +24,4 @@ // This saves me from touching the grbl_esp32 code as much right now. // define a spindle type -DacSpindle my_spindle; +NullSpindle my_spindle; diff --git a/Grbl_Esp32/spindle_control.h b/Grbl_Esp32/spindle_control.h index 8719838e..d8892224 100644 --- a/Grbl_Esp32/spindle_control.h +++ b/Grbl_Esp32/spindle_control.h @@ -24,6 +24,6 @@ #include "grbl.h" #include "tools/SpindleClass.h" -extern DacSpindle my_spindle; +extern NullSpindle my_spindle; #endif diff --git a/Grbl_Esp32/tools/DacSpindle.cpp b/Grbl_Esp32/tools/DacSpindle.cpp new file mode 100644 index 00000000..1410262d --- /dev/null +++ b/Grbl_Esp32/tools/DacSpindle.cpp @@ -0,0 +1,107 @@ +/* + DacSpindle.cpp + + This uses the Analog DAC in the ESP32 to generate a voltage + proportional to the GCode S value desired. Some spindle uses + a 0-5V or 0-10V value to control the spindle. You would use + an Op Amp type circuit to get from the 0.3.3V of the ESP32 to that voltage. + + Part of Grbl_ESP32 + 2020 - Bart Dring + + Grbl is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . + +*/ + +#include "grbl.h" +#include "SpindleClass.h" + +// ======================================== DacSpindle ====================================== +void DacSpindle :: init() { + get_pin_numbers(); + if (_output_pin == UNDEFINED_PIN) + return; + + _min_rpm = settings.rpm_min; + _max_rpm = settings.rpm_max; + _pwm_min_value = 0; // not actually PWM...DAC counts + _pwm_max_value = 255; // not actually PWM...DAC counts + _gpio_ok = true; + + if (_output_pin != GPIO_NUM_25 && _output_pin != GPIO_NUM_26) { // DAC can only be used on these pins + _gpio_ok = false; + grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "DAC spindle pin invalid GPIO_NUM_%d", _output_pin); + return; + } + + if (_enable_pin != UNDEFINED_PIN) + pinMode(_enable_pin, OUTPUT); + + if (_direction_pin != UNDEFINED_PIN) + pinMode(_direction_pin, OUTPUT); + + config_message(); +} + +void DacSpindle :: config_message() { + grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "DAC spindle on GPIO %d", _output_pin); +} + +float DacSpindle::set_rpm(float rpm) { + if (_output_pin == UNDEFINED_PIN) + return rpm; + + uint32_t pwm_value; + + //grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Set RPM %5.1f", rpm); + + // apply overrides and limits + rpm *= (0.010 * sys.spindle_speed_ovr); // Scale by spindle speed override value (percent) + + // Calculate PWM register value based on rpm max/min settings and programmed rpm. + if ((_min_rpm >= _max_rpm) || (rpm >= _max_rpm)) { + // No PWM range possible. Set simple on/off spindle control pin state. + sys.spindle_speed = _max_rpm; + pwm_value = 255; + } else if (rpm <= _min_rpm) { + if (rpm == 0.0) { // S0 disables spindle + sys.spindle_speed = 0.0; + pwm_value = 0; + } else { // Set minimum PWM output + rpm = _min_rpm; + sys.spindle_speed = rpm; + pwm_value = 0; + grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Spindle RPM less than min RPM:%5.2f %d", rpm, pwm_value); + } + } else { + // Compute intermediate PWM value with linear spindle speed model. + // NOTE: A nonlinear model could be installed here, if required, but keep it VERY light-weight. + sys.spindle_speed = rpm; + + pwm_value = (uint32_t)map_float(rpm, _min_rpm, _max_rpm, _pwm_min_value, _pwm_max_value); + } + +#ifdef SPINDLE_ENABLE_OFF_WITH_ZERO_SPEED + set_enable_pin(rpm != 0); +#endif + + set_pwm(pwm_value); + + return rpm; +} + +void DacSpindle :: set_pwm(uint32_t duty) { + if (_gpio_ok) { + dacWrite(_output_pin, (uint8_t)duty); + } + +} \ No newline at end of file diff --git a/Grbl_Esp32/tools/Laser.cpp b/Grbl_Esp32/tools/Laser.cpp new file mode 100644 index 00000000..68078f7d --- /dev/null +++ b/Grbl_Esp32/tools/Laser.cpp @@ -0,0 +1,42 @@ +/* + Laser.cpp + + This is similar the the PWM Spindle except that it allows the + M4 speed vs. power copensation. + + Part of Grbl_ESP32 + 2020 - Bart Dring + + Grbl is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . + +*/ + +#include "grbl.h" +#include "SpindleClass.h" + +// ===================================== Laser ============================================== + + +bool Laser :: isRateAdjusted() { + // must be in $32=1 (laser mode) + return (settings.flags & BITFLAG_LASER_MODE); +} + +void Laser :: config_message() { + grbl_msg_sendf(CLIENT_SERIAL, + MSG_LEVEL_INFO, + "Laser spindle on GPIO:%d, Freq:%.2fHz, Res:%dbits Laser mode:$32=%d", + _output_pin, + _pwm_freq, + SPINDLE_PWM_BIT_PRECISION, + isRateAdjusted()); // the current mode +} \ No newline at end of file diff --git a/Grbl_Esp32/tools/NullSpindle.cpp b/Grbl_Esp32/tools/NullSpindle.cpp new file mode 100644 index 00000000..8be38ca6 --- /dev/null +++ b/Grbl_Esp32/tools/NullSpindle.cpp @@ -0,0 +1,40 @@ +/* + NullSpindle.cpp + + This is used when you don't want to use a spindle. No I/O will be used + and most methods don't do anything + + Part of Grbl_ESP32 + 2020 - Bart Dring + + Grbl is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . + +*/ +#include "grbl.h" +#include "SpindleClass.h" + +// ======================= NullSpindle ============================== +// NullSpindle is just bunch of do nothing (ignore) methods to be used when you don't want a spindle +void NullSpindle :: init() { + config_message(); +} +float NullSpindle :: set_rpm(float rpm) { + return rpm; +} +void NullSpindle :: set_state(uint8_t state, float rpm) {} +uint8_t NullSpindle :: get_state() { + return (SPINDLE_STATE_DISABLE); +} +void NullSpindle :: stop() {} +void NullSpindle :: config_message() { + grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "No spindle"); +} \ No newline at end of file diff --git a/Grbl_Esp32/tools/PWMSpindle.cpp b/Grbl_Esp32/tools/PWMSpindle.cpp new file mode 100644 index 00000000..94168723 --- /dev/null +++ b/Grbl_Esp32/tools/PWMSpindle.cpp @@ -0,0 +1,215 @@ +/* + PWMSpindle.cpp + + This is a full featured TTL PWM spindle. This does not include speed/power + compensation. Use the Laser class for that. + + Part of Grbl_ESP32 + 2020 - Bart Dring + + Grbl is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . + +*/ +#include "grbl.h" +#include "SpindleClass.h" + +// ======================= PWMSpindle ============================== +void PWMSpindle::init() { + + get_pin_numbers(); + + if (_output_pin == UNDEFINED_PIN) { + return; // We cannot continue without the output pin + } + + _pwm_freq = settings.spindle_pwm_freq; + _pwm_period = ((1 << SPINDLE_PWM_BIT_PRECISION) - 1); + + if (settings.spindle_pwm_min_value > settings.spindle_pwm_min_value) + grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Warning: Spindle min pwm is greater than max. Check $35 and $36"); + + if ((F_TIMERS / _pwm_freq) < _pwm_period) + grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Warning spindle PWM precision (%d bits) too high for frequency (%.2f Hz)", SPINDLE_PWM_BIT_PRECISION, _pwm_freq); + + // pre-caculate some PWM count values + _pwm_off_value = (_pwm_period * settings.spindle_pwm_off_value / 100.0); + _pwm_min_value = (_pwm_period * settings.spindle_pwm_min_value / 100.0); + _pwm_max_value = (_pwm_period * settings.spindle_pwm_max_value / 100.0); + +#ifdef ENABLE_PIECEWISE_LINEAR_SPINDLE + _min_rpm = RPM_MIN; + _max_rpm = RPM_MAX; +#else + _min_rpm = settings.rpm_min; + _max_rpm = settings.rpm_max; +#endif + // The pwm_gradient is the pwm duty cycle units per rpm + _pwm_gradient = (_pwm_max_value - _pwm_min_value) / (_max_rpm - _min_rpm); + + _spindle_pwm_chan_num = sys_get_next_PWM_chan_num(); + ledcSetup(_spindle_pwm_chan_num, (double)_pwm_freq, SPINDLE_PWM_BIT_PRECISION); // setup the channel + ledcAttachPin(_output_pin, _spindle_pwm_chan_num); // attach the PWM to the pin + + if (_enable_pin != UNDEFINED_PIN) + pinMode(_enable_pin, OUTPUT); + + if (_direction_pin != UNDEFINED_PIN) + pinMode(_direction_pin, OUTPUT); + + config_message(); +} + +// Get the GPIO from the machine definition +void PWMSpindle :: get_pin_numbers() { + // setup all the pins + +#ifdef SPINDLE_PWM_PIN + _output_pin = SPINDLE_PWM_PIN; +#else + _output_pin = UNDEFINED_PIN; +#endif + +#ifdef SPINDLE_ENABLE_PIN + _enable_pin = SPINDLE_ENABLE_PIN; +#else + _enable_pin = UNDEFINED_PIN; +#endif + +#ifdef SPINDLE_DIR_PIN + _direction_pin = SPINDLE_DIR_PIN; +#else + _direction_pin = UNDEFINED_PIN; +#endif + + if (_output_pin == UNDEFINED_PIN) + grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Warning: Spindle output pin not defined"); + +} + +float PWMSpindle::set_rpm(float rpm) { + if (_output_pin == UNDEFINED_PIN) + return rpm; + + uint32_t pwm_value; + + // apply overrides and limits + rpm *= (0.010 * sys.spindle_speed_ovr); // Scale by spindle speed override value (percent) + + // Calculate PWM register value based on rpm max/min settings and programmed rpm. + if ((_min_rpm >= _max_rpm) || (rpm >= _max_rpm)) { + // No PWM range possible. Set simple on/off spindle control pin state. + sys.spindle_speed = _max_rpm; + pwm_value = _pwm_max_value; + } else if (rpm <= _min_rpm) { + if (rpm == 0.0) { // S0 disables spindle + sys.spindle_speed = 0.0; + pwm_value = _pwm_off_value; + } else { // Set minimum PWM output + rpm = _min_rpm; + sys.spindle_speed = rpm; + pwm_value = _pwm_min_value; + grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Spindle RPM less than min RPM:%5.2f %d", rpm, pwm_value); + } + } else { + // Compute intermediate PWM value with linear spindle speed model. + // NOTE: A nonlinear model could be installed here, if required, but keep it VERY light-weight. + sys.spindle_speed = rpm; +#ifdef ENABLE_PIECEWISE_LINEAR_SPINDLE + pwm_value = piecewise_linear_fit(rpm); +#else + pwm_value = floor((rpm - _min_rpm) * _pwm_gradient) + _pwm_min_value; +#endif + } + +#ifdef SPINDLE_ENABLE_OFF_WITH_ZERO_SPEED + set_enable_pin(rpm != 0); +#endif + + set_pwm(pwm_value); + + return rpm; +} + +void PWMSpindle::set_state(uint8_t state, float rpm) { + + if (sys.abort) + return; // Block during abort. + + if (state == SPINDLE_DISABLE) { // Halt or set spindle direction and rpm. + sys.spindle_speed = 0.0; + stop(); + } else { + set_spindle_dir_pin(state == SPINDLE_ENABLE_CW); + set_enable_pin(true); + set_rpm(rpm); + } + sys.report_ovr_counter = 0; // Set to report change immediately +} + +uint8_t PWMSpindle::get_state() { + + + if (_current_pwm_duty == 0 || _output_pin == UNDEFINED_PIN) + return (SPINDLE_STATE_DISABLE); + else { + if (_direction_pin != UNDEFINED_PIN) { + if (digitalRead(_direction_pin)) + return (SPINDLE_STATE_CW); + else + return (SPINDLE_STATE_CCW); + } else + return (SPINDLE_STATE_CW); + } +} + +void PWMSpindle::stop() { + // inverts are delt with in methods + set_enable_pin(false); + set_pwm(0); +} + +// prints the startup message of the spindle config +void PWMSpindle :: config_message() { + grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "PWM spindle on GPIO %d, freq %.2fHz, Res %d bits", _output_pin, _pwm_freq, SPINDLE_PWM_BIT_PRECISION); +} + + +void PWMSpindle::set_pwm(uint32_t duty) { + if (_output_pin == UNDEFINED_PIN) + return; + + // to prevent excessive calls to ledcWrite, make sure duty hass changed + if (duty == _current_pwm_duty) + return; + + _current_pwm_duty = duty; + +#ifdef INVERT_SPINDLE_PWM + duty = (1 << SPINDLE_PWM_BIT_PRECISION) - duty; +#endif + ledcWrite(_spindle_pwm_chan_num, duty); +} + +void PWMSpindle::set_enable_pin(bool enable) { + if (_enable_pin == UNDEFINED_PIN) + return; +#ifndef INVERT_SPINDLE_ENABLE_PIN + digitalWrite(_enable_pin, enable); +#else + digitalWrite(_enable_pin, !enable); +#endif +} + +void PWMSpindle::set_spindle_dir_pin(bool Clockwise) { + if (_direction_pin != UNDEFINED_PIN) + digitalWrite(_direction_pin, Clockwise); +} \ No newline at end of file diff --git a/Grbl_Esp32/tools/RelaySpindle.cpp b/Grbl_Esp32/tools/RelaySpindle.cpp new file mode 100644 index 00000000..74485358 --- /dev/null +++ b/Grbl_Esp32/tools/RelaySpindle.cpp @@ -0,0 +1,55 @@ +/* + RelaySpindle.cpp + + This is used for a basic on/off spindle. All S Values about 1 + will turn the spindle on. + + Part of Grbl_ESP32 + 2020 - Bart Dring + + Grbl is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . + +*/ +#include "grbl.h" +#include "SpindleClass.h" + +// ========================= RelaySpindle ================================== +/* + This is the same as a PWM spindle, but is a digital rather than PWM output +*/ +void RelaySpindle::init() { + get_pin_numbers(); + if (_output_pin == UNDEFINED_PIN) + return; + + pinMode(_output_pin, OUTPUT); + + if (_enable_pin != UNDEFINED_PIN) + pinMode(SPINDLE_ENABLE_PIN, OUTPUT); + + if (_direction_pin != UNDEFINED_PIN) + pinMode(_direction_pin, OUTPUT); + + config_message(); +} + +// prints the startup message of the spindle config +void RelaySpindle :: config_message() { + grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Relay spindle on GPIO %d", _output_pin); +} + +void RelaySpindle::set_pwm(uint32_t duty) { +#ifdef INVERT_SPINDLE_PWM + duty = (duty == 0); // flip duty +#endif + digitalWrite(_output_pin, duty > 0); // anything greater +} \ No newline at end of file diff --git a/Grbl_Esp32/tools/SpindleClass.cpp b/Grbl_Esp32/tools/SpindleClass.cpp index 74e7bc48..34db4de7 100644 --- a/Grbl_Esp32/tools/SpindleClass.cpp +++ b/Grbl_Esp32/tools/SpindleClass.cpp @@ -35,6 +35,11 @@ */ #include "grbl.h" #include "SpindleClass.h" +#include "NullSpindle.cpp" +#include "PWMSpindle.cpp" +#include "DacSpindle.cpp" +#include "RelaySpindle.cpp" +#include "Laser.cpp" bool Spindle::isRateAdjusted() { return false; // default for basic spindles is false @@ -47,345 +52,4 @@ void Spindle :: spindle_sync(uint8_t state, float rpm) { set_state(state, rpm); } -// ======================= NullSpindle ============================== -// NullSpindle is just bunch of do nothing (ignore) methods to be used when you don't want a spindle -void NullSpindle :: init() { - config_message(); -} -float NullSpindle :: set_rpm(float rpm) { - return rpm; -} -void NullSpindle :: set_state(uint8_t state, float rpm) {} -uint8_t NullSpindle :: get_state() { - return (SPINDLE_STATE_DISABLE); -} -void NullSpindle :: stop() {} -void NullSpindle :: config_message() { - grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "No spindle"); -} - - -// ======================= PWMSpindle ============================== -void PWMSpindle::init() { - - get_pin_numbers(); - - if (_output_pin == UNDEFINED_PIN) { - return; // We cannot continue without the output pin - } - - _pwm_freq = settings.spindle_pwm_freq; - _pwm_period = ((1 << SPINDLE_PWM_BIT_PRECISION) - 1); - - if (settings.spindle_pwm_min_value > settings.spindle_pwm_min_value) - grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Warning: Spindle min pwm is greater than max. Check $35 and $36"); - - if ((F_TIMERS / _pwm_freq) < _pwm_period) - grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Warning spindle PWM precision (%d bits) too high for frequency (%.2f Hz)", SPINDLE_PWM_BIT_PRECISION, _pwm_freq); - - // pre-caculate some PWM count values - _pwm_off_value = (_pwm_period * settings.spindle_pwm_off_value / 100.0); - _pwm_min_value = (_pwm_period * settings.spindle_pwm_min_value / 100.0); - _pwm_max_value = (_pwm_period * settings.spindle_pwm_max_value / 100.0); - -#ifdef ENABLE_PIECEWISE_LINEAR_SPINDLE - _min_rpm = RPM_MIN; - _max_rpm = RPM_MAX; -#else - _min_rpm = settings.rpm_min; - _max_rpm = settings.rpm_max; -#endif - // The pwm_gradient is the pwm duty cycle units per rpm - _pwm_gradient = (_pwm_max_value - _pwm_min_value) / (_max_rpm - _min_rpm); - - _spindle_pwm_chan_num = sys_get_next_PWM_chan_num(); - ledcSetup(_spindle_pwm_chan_num, (double)_pwm_freq, SPINDLE_PWM_BIT_PRECISION); // setup the channel - ledcAttachPin(_output_pin, _spindle_pwm_chan_num); // attach the PWM to the pin - - if (_enable_pin != UNDEFINED_PIN) - pinMode(_enable_pin, OUTPUT); - - if (_direction_pin != UNDEFINED_PIN) - pinMode(_direction_pin, OUTPUT); - - config_message(); -} - -// Get the GPIO from the machine definition -void PWMSpindle :: get_pin_numbers() { - // setup all the pins - -#ifdef SPINDLE_PWM_PIN - _output_pin = SPINDLE_PWM_PIN; -#else - _output_pin = UNDEFINED_PIN; -#endif - -#ifdef SPINDLE_ENABLE_PIN - _enable_pin = SPINDLE_ENABLE_PIN; -#else - _enable_pin = UNDEFINED_PIN; -#endif - -#ifdef SPINDLE_DIR_PIN - _direction_pin = SPINDLE_DIR_PIN; -#else - _direction_pin = UNDEFINED_PIN; -#endif - - if (_output_pin == UNDEFINED_PIN) - grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Warning: Spindle output pin not defined"); - -} - -float PWMSpindle::set_rpm(float rpm) { - if (_output_pin == UNDEFINED_PIN) - return rpm; - - uint32_t pwm_value; - - // apply overrides and limits - rpm *= (0.010 * sys.spindle_speed_ovr); // Scale by spindle speed override value (percent) - - // Calculate PWM register value based on rpm max/min settings and programmed rpm. - if ((_min_rpm >= _max_rpm) || (rpm >= _max_rpm)) { - // No PWM range possible. Set simple on/off spindle control pin state. - sys.spindle_speed = _max_rpm; - pwm_value = _pwm_max_value; - } else if (rpm <= _min_rpm) { - if (rpm == 0.0) { // S0 disables spindle - sys.spindle_speed = 0.0; - pwm_value = _pwm_off_value; - } else { // Set minimum PWM output - rpm = _min_rpm; - sys.spindle_speed = rpm; - pwm_value = _pwm_min_value; - grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Spindle RPM less than min RPM:%5.2f %d", rpm, pwm_value); - } - } else { - // Compute intermediate PWM value with linear spindle speed model. - // NOTE: A nonlinear model could be installed here, if required, but keep it VERY light-weight. - sys.spindle_speed = rpm; -#ifdef ENABLE_PIECEWISE_LINEAR_SPINDLE - pwm_value = piecewise_linear_fit(rpm); -#else - pwm_value = floor((rpm - _min_rpm) * _pwm_gradient) + _pwm_min_value; -#endif - } - -#ifdef SPINDLE_ENABLE_OFF_WITH_ZERO_SPEED - set_enable_pin(rpm != 0); -#endif - - set_pwm(pwm_value); - - return rpm; -} - -void PWMSpindle::set_state(uint8_t state, float rpm) { - - if (sys.abort) - return; // Block during abort. - - if (state == SPINDLE_DISABLE) { // Halt or set spindle direction and rpm. - sys.spindle_speed = 0.0; - stop(); - } else { - set_spindle_dir_pin(state == SPINDLE_ENABLE_CW); - set_enable_pin(true); - set_rpm(rpm); - } - sys.report_ovr_counter = 0; // Set to report change immediately -} - -uint8_t PWMSpindle::get_state() { - - - if (_current_pwm_duty == 0 || _output_pin == UNDEFINED_PIN) - return (SPINDLE_STATE_DISABLE); - else { - if (_direction_pin != UNDEFINED_PIN) { - if (digitalRead(_direction_pin)) - return (SPINDLE_STATE_CW); - else - return (SPINDLE_STATE_CCW); - } else - return (SPINDLE_STATE_CW); - } -} - -void PWMSpindle::stop() { - // inverts are delt with in methods - set_enable_pin(false); - set_pwm(0); -} - -// prints the startup message of the spindle config -void PWMSpindle :: config_message() { - grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "PWM spindle on GPIO %d, freq %.2fHz, Res %d bits", _output_pin, _pwm_freq, SPINDLE_PWM_BIT_PRECISION); -} - - -void PWMSpindle::set_pwm(uint32_t duty) { - if (_output_pin == UNDEFINED_PIN) - return; - - // to prevent excessive calls to ledcWrite, make sure duty hass changed - if (duty == _current_pwm_duty) - return; - - _current_pwm_duty = duty; - -#ifdef INVERT_SPINDLE_PWM - duty = (1 << SPINDLE_PWM_BIT_PRECISION) - duty; -#endif - ledcWrite(_spindle_pwm_chan_num, duty); -} - -void PWMSpindle::set_enable_pin(bool enable) { - if (_enable_pin == UNDEFINED_PIN) - return; -#ifndef INVERT_SPINDLE_ENABLE_PIN - digitalWrite(_enable_pin, enable); -#else - digitalWrite(_enable_pin, !enable); -#endif -} - -void PWMSpindle::set_spindle_dir_pin(bool Clockwise) { - if (_direction_pin != UNDEFINED_PIN) - digitalWrite(_direction_pin, Clockwise); -} - -// ========================= RelaySpindle ================================== -/* - This is the same as a PWM spindle, but is a digital rather than PWM output -*/ -void RelaySpindle::init() { - get_pin_numbers(); - if (_output_pin == UNDEFINED_PIN) - return; - - pinMode(_output_pin, OUTPUT); - - if (_enable_pin != UNDEFINED_PIN) - pinMode(SPINDLE_ENABLE_PIN, OUTPUT); - - if (_direction_pin != UNDEFINED_PIN) - pinMode(_direction_pin, OUTPUT); - - config_message(); -} - -// prints the startup message of the spindle config -void RelaySpindle :: config_message() { - grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Relay spindle on GPIO %d", _output_pin); -} - -void RelaySpindle::set_pwm(uint32_t duty) { -#ifdef INVERT_SPINDLE_PWM - duty = (duty == 0); // flip duty -#endif - digitalWrite(_output_pin, duty > 0); // anything greater -} - - -// ===================================== Laser ============================================== - - -bool Laser :: isRateAdjusted() { - // must be in $32=1 (laser mode) - return (settings.flags & BITFLAG_LASER_MODE); -} - -void Laser :: config_message() { - grbl_msg_sendf(CLIENT_SERIAL, - MSG_LEVEL_INFO, - "Laser spindle on GPIO:%d, Freq:%.2fHz, Res:%dbits Laser mode:$32=%d", - _output_pin, - _pwm_freq, - SPINDLE_PWM_BIT_PRECISION, - isRateAdjusted()); // the current mode -} - -// ======================================== DacSpindle ====================================== -void DacSpindle :: init() { - get_pin_numbers(); - if (_output_pin == UNDEFINED_PIN) - return; - - _min_rpm = settings.rpm_min; - _max_rpm = settings.rpm_max; - _pwm_min_value = 0; // not actually PWM...DAC counts - _pwm_max_value = 255; // not actually PWM...DAC counts - _gpio_ok = true; - - if (_output_pin != GPIO_NUM_25 && _output_pin != GPIO_NUM_26) { // DAC can only be used on these pins - _gpio_ok = false; - grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "DAC spindle pin invalid GPIO_NUM_%d", _output_pin); - return; - } - - if (_enable_pin != UNDEFINED_PIN) - pinMode(_enable_pin, OUTPUT); - - if (_direction_pin != UNDEFINED_PIN) - pinMode(_direction_pin, OUTPUT); - - config_message(); -} - -void DacSpindle :: config_message() { - grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "DAC spindle on GPIO %d", _output_pin); -} - -float DacSpindle::set_rpm(float rpm) { - if (_output_pin == UNDEFINED_PIN) - return rpm; - - uint32_t pwm_value; - - //grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Set RPM %5.1f", rpm); - - // apply overrides and limits - rpm *= (0.010 * sys.spindle_speed_ovr); // Scale by spindle speed override value (percent) - - // Calculate PWM register value based on rpm max/min settings and programmed rpm. - if ((_min_rpm >= _max_rpm) || (rpm >= _max_rpm)) { - // No PWM range possible. Set simple on/off spindle control pin state. - sys.spindle_speed = _max_rpm; - pwm_value = 255; - } else if (rpm <= _min_rpm) { - if (rpm == 0.0) { // S0 disables spindle - sys.spindle_speed = 0.0; - pwm_value = 0; - } else { // Set minimum PWM output - rpm = _min_rpm; - sys.spindle_speed = rpm; - pwm_value = 0; - grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Spindle RPM less than min RPM:%5.2f %d", rpm, pwm_value); - } - } else { - // Compute intermediate PWM value with linear spindle speed model. - // NOTE: A nonlinear model could be installed here, if required, but keep it VERY light-weight. - sys.spindle_speed = rpm; - - pwm_value = (uint32_t)map_float(rpm, _min_rpm, _max_rpm, _pwm_min_value, _pwm_max_value); - } - -#ifdef SPINDLE_ENABLE_OFF_WITH_ZERO_SPEED - set_enable_pin(rpm != 0); -#endif - - set_pwm(pwm_value); - - return rpm; -} - -void DacSpindle :: set_pwm(uint32_t duty) { - if (_gpio_ok) { - dacWrite(_output_pin, (uint8_t)duty); - } - -} diff --git a/Grbl_Esp32/tools/SpindleClass.h b/Grbl_Esp32/tools/SpindleClass.h index 5f2b0a64..7dae8c8c 100644 --- a/Grbl_Esp32/tools/SpindleClass.h +++ b/Grbl_Esp32/tools/SpindleClass.h @@ -37,15 +37,12 @@ class Spindle { public: virtual void init(); // not in constructor because this also gets called when $$ settings change virtual float set_rpm(float rpm); - //virtual void set_pwm(uint32_t duty); virtual void set_state(uint8_t state, float rpm); virtual uint8_t get_state(); virtual void stop(); virtual void config_message(); virtual bool isRateAdjusted(); virtual void spindle_sync(uint8_t state, float rpm); - - }; // This is a dummy spindle that has no I/O. @@ -68,7 +65,7 @@ class PWMSpindle : public Spindle { void set_state(uint8_t state, float rpm); uint8_t get_state(); void stop(); - void config_message(); + void config_message(); private: int8_t _spindle_pwm_chan_num; @@ -113,7 +110,7 @@ class Laser : public PWMSpindle { class DacSpindle : public PWMSpindle { public: void init(); - void config_message(); + void config_message(); float set_rpm(float rpm); private: bool _gpio_ok; // DAC is on a valid pin