mirror of
https://github.com/bdring/Grbl_Esp32.git
synced 2025-09-02 19:02:35 +02:00
Split SpindleClass into invididual class files.
This commit is contained in:
@@ -24,4 +24,4 @@
|
|||||||
// This saves me from touching the grbl_esp32 code as much right now.
|
// This saves me from touching the grbl_esp32 code as much right now.
|
||||||
|
|
||||||
// define a spindle type
|
// define a spindle type
|
||||||
DacSpindle my_spindle;
|
NullSpindle my_spindle;
|
||||||
|
@@ -24,6 +24,6 @@
|
|||||||
#include "grbl.h"
|
#include "grbl.h"
|
||||||
#include "tools/SpindleClass.h"
|
#include "tools/SpindleClass.h"
|
||||||
|
|
||||||
extern DacSpindle my_spindle;
|
extern NullSpindle my_spindle;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
107
Grbl_Esp32/tools/DacSpindle.cpp
Normal file
107
Grbl_Esp32/tools/DacSpindle.cpp
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
42
Grbl_Esp32/tools/Laser.cpp
Normal file
42
Grbl_Esp32/tools/Laser.cpp
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
||||||
|
}
|
40
Grbl_Esp32/tools/NullSpindle.cpp
Normal file
40
Grbl_Esp32/tools/NullSpindle.cpp
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
|
#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");
|
||||||
|
}
|
215
Grbl_Esp32/tools/PWMSpindle.cpp
Normal file
215
Grbl_Esp32/tools/PWMSpindle.cpp
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
|
#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);
|
||||||
|
}
|
55
Grbl_Esp32/tools/RelaySpindle.cpp
Normal file
55
Grbl_Esp32/tools/RelaySpindle.cpp
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
|
#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
|
||||||
|
}
|
@@ -35,6 +35,11 @@
|
|||||||
*/
|
*/
|
||||||
#include "grbl.h"
|
#include "grbl.h"
|
||||||
#include "SpindleClass.h"
|
#include "SpindleClass.h"
|
||||||
|
#include "NullSpindle.cpp"
|
||||||
|
#include "PWMSpindle.cpp"
|
||||||
|
#include "DacSpindle.cpp"
|
||||||
|
#include "RelaySpindle.cpp"
|
||||||
|
#include "Laser.cpp"
|
||||||
|
|
||||||
bool Spindle::isRateAdjusted() {
|
bool Spindle::isRateAdjusted() {
|
||||||
return false; // default for basic spindles is false
|
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);
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
@@ -37,15 +37,12 @@ class Spindle {
|
|||||||
public:
|
public:
|
||||||
virtual void init(); // not in constructor because this also gets called when $$ settings change
|
virtual void init(); // not in constructor because this also gets called when $$ settings change
|
||||||
virtual float set_rpm(float rpm);
|
virtual float set_rpm(float rpm);
|
||||||
//virtual void set_pwm(uint32_t duty);
|
|
||||||
virtual void set_state(uint8_t state, float rpm);
|
virtual void set_state(uint8_t state, float rpm);
|
||||||
virtual uint8_t get_state();
|
virtual uint8_t get_state();
|
||||||
virtual void stop();
|
virtual void stop();
|
||||||
virtual void config_message();
|
virtual void config_message();
|
||||||
virtual bool isRateAdjusted();
|
virtual bool isRateAdjusted();
|
||||||
virtual void spindle_sync(uint8_t state, float rpm);
|
virtual void spindle_sync(uint8_t state, float rpm);
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// This is a dummy spindle that has no I/O.
|
// This is a dummy spindle that has no I/O.
|
||||||
|
Reference in New Issue
Block a user