1
0
mirror of https://github.com/bdring/Grbl_Esp32.git synced 2025-08-19 12:51:47 +02:00

Split motor files into separate files and introduced namespace Motors (#522)

* Renamed some files to match the class name.

* Split out motor class into different files.

* Fixed last newline in cpp/h files

Co-authored-by: Stefan de Bruijn <stefan@nubilosoft.com>
This commit is contained in:
Stefan de Bruijn
2020-08-08 23:01:19 +02:00
committed by GitHub
parent 7b6b4b2087
commit 06fae66507
24 changed files with 1059 additions and 954 deletions

4
.editorconfig Normal file
View File

@@ -0,0 +1,4 @@
root = true
[*]
insert_final_newline = true

View File

@@ -0,0 +1,53 @@
/*
MotorClass.cpp
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/>.
TODO
Make sure public/private/protected is cleaned up.
Only a few Unipolar axes have been setup in init()
Get rid of Z_SERVO, just reply on Z_SERVO_PIN
Deal with custom machine ... machine_trinamic_setup();
Class is ready to deal with non SPI pins, but they have not been needed yet.
It would be nice in the config message though
Testing
Done (success)
3 Axis (3 Standard Steppers)
MPCNC (ganged with shared direction pin)
TMC2130 Pen Laser (trinamics, stallguard tuning)
Unipolar
TODO
4 Axis SPI (Daisy Chain, Ganged with unique direction pins)
Reference
TMC2130 Datasheet https://www.trinamic.com/fileadmin/assets/Products/ICs_Documents/TMC2130_datasheet.pdf
*/
#include "Motor.h"
namespace Motors {
Motor::Motor() { type_id = MOTOR; }
void Motor::init() { _homing_mask = 0; }
void Motor::config_message() {}
void Motor::debug_message() {}
void Motor::read_settings() {}
void Motor::set_disable(bool disable) {}
void Motor::set_direction_pins(uint8_t onMask) {}
void Motor::step(uint8_t step_mask, uint8_t dir_mask) {}
bool Motor::test() { return true; }; // true = OK
void Motor::update() {}
void Motor::set_axis_name() { sprintf(_axis_name, "%c%s", report_get_axis_letter(axis_index), dual_axis_index ? "2" : ""); }
void Motor::set_homing_mode(uint8_t homing_mask, bool isHoming) { _homing_mask = homing_mask; }
}

View File

@@ -0,0 +1,65 @@
#pragma once
/*
Motor.h
Header file for Motor Classes
Here is the hierarchy
Motor
Nullmotor
StandardStepper
TrinamicDriver
Unipolar
RC Servo
These are for motors coordinated by Grbl_ESP32
See motorClass.cpp for more details
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 "Motors.h"
#include <cstdint>
namespace Motors {
class Motor {
public:
Motor();
virtual void init(); // not in constructor because this also gets called when $$ settings change
virtual void config_message();
virtual void debug_message();
virtual void read_settings();
virtual void set_homing_mode(uint8_t homing_mask, bool isHoming);
virtual void set_disable(bool disable);
virtual void set_direction_pins(uint8_t onMask);
virtual void step(uint8_t step_mask, uint8_t dir_mask); // only used on Unipolar right now
virtual bool test();
virtual void set_axis_name();
virtual void update();
motor_class_id_t type_id;
uint8_t is_active = false;
protected:
uint8_t axis_index; // X_AXIS, etc
uint8_t dual_axis_index; // 0 = primary 1=ganged
bool _showError;
bool _use_mpos = true;
uint8_t _homing_mask;
char _axis_name[10]; // this the name to use when reporting like "X" or "X2"
};
}

View File

@@ -1,205 +0,0 @@
#pragma once
/*
MotorClass.h
Header file for Motor Classes
Here is the hierarchy
Motor
Nullmotor
StandardStepper
TrinamicDriver
Unipolar
RC Servo
These are for motors coordinated by Grbl_ESP32
See motorClass.cpp for more details
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 <TMCStepper.h> // https://github.com/teemuatlut/TMCStepper
#include "TrinamicDriverClass.h"
#include "RcServoClass.h"
//#include "SolenoidClass.h"
extern uint8_t rmt_chan_num[MAX_AXES][2];
extern rmt_item32_t rmtItem[2];
extern rmt_config_t rmtConfig;
typedef enum { MOTOR, NULL_MOTOR, STANDARD_MOTOR, TRINAMIC_SPI_MOTOR, UNIPOLAR_MOTOR, RC_SERVO_MOTOR, SOLENOID } motor_class_id_t;
// These are used for setup and to talk to the motors as a group.
void init_motors();
uint8_t get_next_trinamic_driver_index();
bool motors_have_type_id(motor_class_id_t id);
void readSgTask(void* pvParameters);
void motors_read_settings();
void motors_set_homing_mode(uint8_t homing_mask, bool isHoming);
void motors_set_disable(bool disable);
void motors_set_direction_pins(uint8_t onMask);
void motors_step(uint8_t step_mask, uint8_t dir_mask);
void servoUpdateTask(void* pvParameters);
extern bool motor_class_steps; // true if at least one motor class is handling steps
// ==================== Motor Classes ====================
class Motor {
public:
Motor();
virtual void init(); // not in constructor because this also gets called when $$ settings change
virtual void config_message();
virtual void debug_message();
virtual void read_settings();
virtual void set_homing_mode(uint8_t homing_mask, bool isHoming);
virtual void set_disable(bool disable);
virtual void set_direction_pins(uint8_t onMask);
virtual void step(uint8_t step_mask, uint8_t dir_mask); // only used on Unipolar right now
virtual bool test();
virtual void set_axis_name();
virtual void update();
motor_class_id_t type_id;
uint8_t is_active = false;
protected:
uint8_t axis_index; // X_AXIS, etc
uint8_t dual_axis_index; // 0 = primary 1=ganged
bool _showError;
bool _use_mpos = true;
uint8_t _homing_mask;
char _axis_name[10]; // this the name to use when reporting like "X" or "X2"
};
class Nullmotor : public Motor {};
class StandardStepper : public Motor {
public:
StandardStepper();
StandardStepper(uint8_t axis_index, uint8_t step_pin, uint8_t dir_pin, uint8_t disable_pin);
virtual void config_message();
virtual void init();
virtual void set_direction_pins(uint8_t onMask);
void init_step_dir_pins();
virtual void set_disable(bool disable);
uint8_t step_pin;
protected:
bool _invert_step_pin;
uint8_t dir_pin;
uint8_t disable_pin;
};
class TrinamicDriver : public StandardStepper {
public:
TrinamicDriver(uint8_t axis_index,
uint8_t step_pin,
uint8_t dir_pin,
uint8_t disable_pin,
uint8_t cs_pin,
uint16_t driver_part_number,
float r_sense,
int8_t spi_index);
void config_message();
void init();
void set_mode(bool isHoming);
void read_settings();
void trinamic_test_response();
void trinamic_stepper_enable(bool enable);
void debug_message();
void set_homing_mode(uint8_t homing_mask, bool ishoming);
void set_disable(bool disable);
bool test();
private:
uint32_t calc_tstep(float speed, float percent);
TMC2130Stepper* tmcstepper; // all other driver types are subclasses of this one
uint8_t _homing_mode;
uint8_t cs_pin = UNDEFINED_PIN; // The chip select pin (can be the same for daisy chain)
uint16_t _driver_part_number; // example: use 2130 for TMC2130
float _r_sense;
int8_t spi_index;
protected:
uint8_t _mode;
uint8_t _lastMode = 255;
};
class UnipolarMotor : public Motor {
public:
UnipolarMotor();
UnipolarMotor(uint8_t axis_index, uint8_t pin_phase0, uint8_t pin_phase1, uint8_t pin_phase2, uint8_t pin_phase3);
void init();
void config_message();
void set_disable(bool disable);
void step(uint8_t step_mask, uint8_t dir_mask); // only used on Unipolar right now
private:
uint8_t _pin_phase0;
uint8_t _pin_phase1;
uint8_t _pin_phase2;
uint8_t _pin_phase3;
uint8_t _current_phase;
bool _half_step;
bool _enabled;
};
class RcServo : public Motor {
public:
RcServo();
RcServo(uint8_t axis_index, uint8_t pwm_pin, float min, float max);
virtual void config_message();
virtual void init();
void _write_pwm(uint32_t duty);
virtual void set_disable(bool disable);
virtual void update();
void read_settings();
void set_homing_mode(bool is_homing, bool isHoming);
protected:
void set_location();
void _get_calibration();
uint8_t _pwm_pin;
uint8_t _channel_num;
uint32_t _current_pwm_duty;
bool _disabled;
float _position_min;
float _position_max; // position in millimeters
float _homing_position;
float _pwm_pulse_min;
float _pwm_pulse_max;
};
class Solenoid : public RcServo {
public:
Solenoid();
Solenoid(uint8_t axis_index, gpio_num_t pwm_pin, float transition_poiont);
void config_message();
void set_location();
void update();
void init();
void set_disable(bool disable);
float _transition_poiont;
};

View File

@@ -1,40 +1,48 @@
/*
MotorClass.cpp
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/>.
TODO
Make sure public/private/protected is cleaned up.
Only a few Unipolar axes have been setup in init()
Get rid of Z_SERVO, just reply on Z_SERVO_PIN
Deal with custom machine ... machine_trinamic_setup();
Class is ready to deal with non SPI pins, but they have not been needed yet.
It would be nice in the config message though
Testing
Done (success)
3 Axis (3 Standard Steppers)
MPCNC (ganged with shared direction pin)
TMC2130 Pen Laser (trinamics, stallguard tuning)
Unipolar
TODO
4 Axis SPI (Daisy Chain, Ganged with unique direction pins)
Reference
TMC2130 Datasheet https://www.trinamic.com/fileadmin/assets/Products/ICs_Documents/TMC2130_datasheet.pdf
MotorClass.cpp
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/>.
TODO
Make sure public/private/protected is cleaned up.
Only a few Unipolar axes have been setup in init()
Get rid of Z_SERVO, just reply on Z_SERVO_PIN
Deal with custom machine ... machine_trinamic_setup();
Class is ready to deal with non SPI pins, but they have not been needed yet.
It would be nice in the config message though
Testing
Done (success)
3 Axis (3 Standard Steppers)
MPCNC (ganged with shared direction pin)
TMC2130 Pen Laser (trinamics, stallguard tuning)
Unipolar
TODO
4 Axis SPI (Daisy Chain, Ganged with unique direction pins)
Reference
TMC2130 Datasheet https://www.trinamic.com/fileadmin/assets/Products/ICs_Documents/TMC2130_datasheet.pdf
*/
#include "../grbl.h"
#include "MotorClass.h"
#include "Motors.h"
Motor* myMotor[MAX_AXES][MAX_GANGED]; // number of axes (normal and ganged)
#include "Motor.h"
#include "../grbl.h"
#include "NullMotor.h"
#include "StandardStepper.h"
#include "UnipolarMotor.h"
#include "RcServo.h"
#include "TrinamicDriver.h"
Motors::Motor* myMotor[MAX_AXES][MAX_GANGED]; // number of axes (normal and ganged)
static TaskHandle_t readSgTaskHandle = 0; // for realtime stallguard data diaplay
static TaskHandle_t servoUpdateTaskHandle = 0;
@@ -45,6 +53,8 @@ rmt_config_t rmtConfig;
bool motor_class_steps; // true if at least one motor class is handling steps
void init_motors() {
using namespace Motors;
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Init Motors");
#ifdef X_TRINAMIC_DRIVER
@@ -402,7 +412,7 @@ void motors_step(uint8_t step_mask, uint8_t dir_mask) {
}
/*
This will print StallGuard data that is useful for tuning.
This will print StallGuard data that is useful for tuning.
*/
void readSgTask(void* pvParameters) {
TickType_t xLastWakeTime;
@@ -439,32 +449,3 @@ void TMC2130Stepper::switchCSpin(bool state) {
i2s_out_delay();
}
#endif
// ============================== Class Methods ================================================
Motor ::Motor() {
type_id = MOTOR;
}
void Motor ::init() {
_homing_mask = 0;
}
void Motor ::config_message() {}
void Motor ::debug_message() {}
void Motor ::read_settings() {}
void Motor ::set_disable(bool disable) {}
void Motor ::set_direction_pins(uint8_t onMask) {}
void Motor ::step(uint8_t step_mask, uint8_t dir_mask) {}
bool Motor ::test() {
return true;
}; // true = OK
void Motor ::update() {}
void Motor ::set_axis_name() {
sprintf(_axis_name, "%c%s", report_get_axis_letter(axis_index), dual_axis_index ? "2" : "");
}
void Motor ::set_homing_mode(uint8_t homing_mask, bool isHoming) {
_homing_mask = homing_mask;
}

View File

@@ -0,0 +1,53 @@
#pragma once
/*
Motor.h
Header file for Motor Classes
Here is the hierarchy
Motor
Nullmotor
StandardStepper
TrinamicDriver
Unipolar
RC Servo
These are for motors coordinated by Grbl_ESP32
See motorClass.cpp for more details
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 <TMCStepper.h> // https://github.com/teemuatlut/TMCStepper
extern uint8_t rmt_chan_num[MAX_AXES][2];
extern rmt_item32_t rmtItem[2];
extern rmt_config_t rmtConfig;
typedef enum { MOTOR, NULL_MOTOR, STANDARD_MOTOR, TRINAMIC_SPI_MOTOR, UNIPOLAR_MOTOR, RC_SERVO_MOTOR, SOLENOID } motor_class_id_t;
// These are used for setup and to talk to the motors as a group.
void init_motors();
uint8_t get_next_trinamic_driver_index();
bool motors_have_type_id(motor_class_id_t id);
void readSgTask(void* pvParameters);
void motors_read_settings();
void motors_set_homing_mode(uint8_t homing_mask, bool isHoming);
void motors_set_disable(bool disable);
void motors_set_direction_pins(uint8_t onMask);
void motors_step(uint8_t step_mask, uint8_t dir_mask);
void servoUpdateTask(void* pvParameters);
extern bool motor_class_steps; // true if at least one motor class is handling steps

View File

@@ -0,0 +1,7 @@
#pragma once
#include "Motor.h"
namespace Motors {
class Nullmotor : public Motor {};
}

View File

@@ -0,0 +1,189 @@
/*
RcServoServoClass.cpp
This allows an RcServo to be used like any other motor. Serrvos
do have limitation in travel and speed, so you do need to respect that.
Part of Grbl_ESP32
2020 - Bart Dring
Servos have a limited travel, so they map the travel across a range in
the current work coordinatee system. The servo can only travel as far
as the range, but the internal axis value can keep going.
Range: The range is specified in the machine definition file with...
#define X_SERVO_RANGE_MIN 0.0
#define X_SERVO_RANGE_MAX 5.0
Direction: The direction can be changed using the $3 setting for the axis
Homing: During homing, the servo will move to one of the endpoints. The
endpoint is determined by the $23 or $HomingDirInvertMask setting for the axis.
Do not define a homing cycle for the axis with the servo.
You do need at least 1 homing cycle. TODO: Fix this
Calibration. You can tweak the endpoints using the $10n or nStepsPerMm and
$13n or $xMaxTravel setting, where n is the axis.
The value is a percent. If you secify a percent outside the
the range specified by the values below, it will be reset to 100.0 (100% ... no change)
The calibration adjusts in direction of positive momement, so a value above 100% moves
towards the higher axis value.
#define SERVO_CAL_MIN
#define SERVO_CAL_MAX
Grbl_ESP32 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 "RcServo.h"
namespace Motors {
RcServo::RcServo() {}
RcServo::RcServo(uint8_t axis_index, uint8_t pwm_pin, float min, float max) {
type_id = RC_SERVO_MOTOR;
this->axis_index = axis_index % MAX_AXES;
this->dual_axis_index = axis_index < MAX_AXES ? 0 : 1; // 0 = primary 1 = ganged
this->_pwm_pin = pwm_pin;
_position_min = min;
_position_max = max;
init();
}
void RcServo::init() {
read_settings();
_channel_num = sys_get_next_PWM_chan_num();
ledcSetup(_channel_num, SERVO_PULSE_FREQ, SERVO_PULSE_RES_BITS);
ledcAttachPin(_pwm_pin, _channel_num);
_current_pwm_duty = 0;
is_active = true; // as opposed to NullMotors, this is a real motor
set_axis_name();
config_message();
}
void RcServo::config_message() {
grbl_msg_sendf(CLIENT_SERIAL,
MSG_LEVEL_INFO,
"%s Axis RC Servo motor Output:%d Min:%5.3fmm Max:%5.3fmm",
_axis_name,
_pwm_pin,
_position_min,
_position_max);
}
void RcServo::_write_pwm(uint32_t duty) {
// to prevent excessive calls to ledcWrite, make sure duty hass changed
if (duty == _current_pwm_duty)
return;
_current_pwm_duty = duty;
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "%s Servo Pwm %d", _axis_name, duty);
ledcWrite(_channel_num, duty);
}
// sets the PWM to zero. This allows most servos to be manually moved
void RcServo::set_disable(bool disable) {
return;
_disabled = disable;
if (_disabled)
_write_pwm(0);
}
void RcServo::set_homing_mode(bool is_homing, bool isHoming) {
float home_pos = 0.0;
if (!is_homing)
return;
if (bit_istrue(homing_dir_mask->get(), bit(axis_index)))
home_pos = _position_min;
else
home_pos = _position_max;
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Servo set home %d %3.2f", is_homing, home_pos);
sys_position[axis_index] = home_pos * axis_settings[axis_index]->steps_per_mm->get(); // convert to steps
}
void RcServo::update() { set_location(); }
void RcServo::set_location() {
uint32_t servo_pulse_len;
float servo_pos, mpos, offset;
// skip location if we are in alarm mode
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "locate");
_get_calibration();
if (sys.state == STATE_ALARM) {
set_disable(true);
return;
}
mpos = system_convert_axis_steps_to_mpos(sys_position, axis_index); // get the axis machine position in mm
offset = gc_state.coord_system[axis_index] + gc_state.coord_offset[axis_index]; // get the current axis work offset
servo_pos = mpos - offset; // determine the current work position
// determine the pulse length
servo_pulse_len = (uint32_t)mapConstrain(servo_pos, _position_min, _position_max, _pwm_pulse_min, _pwm_pulse_max);
_write_pwm(servo_pulse_len);
}
void RcServo::read_settings() { _get_calibration(); }
// this should change to use its own settings.
void RcServo::_get_calibration() {
float _cal_min = 1.0;
float _cal_max = 1.0;
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Read settings");
// make sure the min is in range
if ((axis_settings[axis_index]->steps_per_mm->get() < SERVO_CAL_MIN) ||
(axis_settings[axis_index]->steps_per_mm->get() > SERVO_CAL_MAX)) {
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Servo calibration ($10%d) value error. Reset to 100", axis_index);
char reset_val[] = "100";
axis_settings[axis_index]->steps_per_mm->setStringValue(reset_val);
}
// make sure the max is in range
// Note: Max travel is set positive via $$, but stored as a negative number
if ((axis_settings[axis_index]->max_travel->get() < SERVO_CAL_MIN) || (axis_settings[axis_index]->max_travel->get() > SERVO_CAL_MAX)) {
grbl_msg_sendf(CLIENT_SERIAL,
MSG_LEVEL_INFO,
"Servo calibration ($13%d) value error. %3.2f Reset to 100",
axis_index,
axis_settings[axis_index]->max_travel->get());
char reset_val[] = "100";
axis_settings[axis_index]->max_travel->setStringValue(reset_val);
}
_pwm_pulse_min = SERVO_MIN_PULSE;
_pwm_pulse_max = SERVO_MAX_PULSE;
if (bit_istrue(dir_invert_mask->get(), bit(axis_index))) { // normal direction
_cal_min = 2.0 - (axis_settings[axis_index]->steps_per_mm->get() / 100.0);
_cal_max = 2.0 - (axis_settings[axis_index]->max_travel->get() / 100.0);
swap(_pwm_pulse_min, _pwm_pulse_max);
} else { // inverted direction
_cal_min = (axis_settings[axis_index]->steps_per_mm->get() / 100.0);
_cal_max = (axis_settings[axis_index]->max_travel->get() / 100.0);
}
_pwm_pulse_min *= _cal_min;
_pwm_pulse_max *= _cal_max;
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Servo calibration min:%1.2f max %1.2f", _pwm_pulse_min, _pwm_pulse_max);
}
}

View File

@@ -0,0 +1,55 @@
#pragma once
/*
RcServo.h
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 "Motor.h"
#include "RcServoSettings.h"
namespace Motors {
class RcServo : public Motor {
public:
RcServo();
RcServo(uint8_t axis_index, uint8_t pwm_pin, float min, float max);
virtual void config_message();
virtual void init();
void _write_pwm(uint32_t duty);
virtual void set_disable(bool disable);
virtual void update();
void read_settings();
void set_homing_mode(bool is_homing, bool isHoming);
protected:
void set_location();
void _get_calibration();
uint8_t _pwm_pin;
uint8_t _channel_num;
uint32_t _current_pwm_duty;
bool _disabled;
float _position_min;
float _position_max; // position in millimeters
float _homing_position;
float _pwm_pulse_min;
float _pwm_pulse_max;
};
}

View File

@@ -1,190 +0,0 @@
/*
RcServoServoClass.cpp
This allows an RcServo to be used like any other motor. Serrvos
do have limitation in travel and speed, so you do need to respect that.
Part of Grbl_ESP32
2020 - Bart Dring
Servos have a limited travel, so they map the travel across a range in
the current work coordinatee system. The servo can only travel as far
as the range, but the internal axis value can keep going.
Range: The range is specified in the machine definition file with...
#define X_SERVO_RANGE_MIN 0.0
#define X_SERVO_RANGE_MAX 5.0
Direction: The direction can be changed using the $3 setting for the axis
Homing: During homing, the servo will move to one of the endpoints. The
endpoint is determined by the $23 or $HomingDirInvertMask setting for the axis.
Do not define a homing cycle for the axis with the servo.
You do need at least 1 homing cycle. TODO: Fix this
Calibration. You can tweak the endpoints using the $10n or nStepsPerMm and
$13n or $xMaxTravel setting, where n is the axis.
The value is a percent. If you secify a percent outside the
the range specified by the values below, it will be reset to 100.0 (100% ... no change)
The calibration adjusts in direction of positive momement, so a value above 100% moves
towards the higher axis value.
#define SERVO_CAL_MIN
#define SERVO_CAL_MAX
Grbl_ESP32 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 "MotorClass.h"
RcServo ::RcServo() {}
RcServo ::RcServo(uint8_t axis_index, uint8_t pwm_pin, float min, float max) {
type_id = RC_SERVO_MOTOR;
this->axis_index = axis_index % MAX_AXES;
this->dual_axis_index = axis_index < MAX_AXES ? 0 : 1; // 0 = primary 1 = ganged
this->_pwm_pin = pwm_pin;
_position_min = min;
_position_max = max;
init();
}
void RcServo ::init() {
read_settings();
_channel_num = sys_get_next_PWM_chan_num();
ledcSetup(_channel_num, SERVO_PULSE_FREQ, SERVO_PULSE_RES_BITS);
ledcAttachPin(_pwm_pin, _channel_num);
_current_pwm_duty = 0;
is_active = true; // as opposed to NullMotors, this is a real motor
set_axis_name();
config_message();
}
void RcServo ::config_message() {
grbl_msg_sendf(CLIENT_SERIAL,
MSG_LEVEL_INFO,
"%s Axis RC Servo motor Output:%d Min:%5.3fmm Max:%5.3fmm",
_axis_name,
_pwm_pin,
_position_min,
_position_max);
}
void RcServo::_write_pwm(uint32_t duty) {
// to prevent excessive calls to ledcWrite, make sure duty hass changed
if (duty == _current_pwm_duty)
return;
_current_pwm_duty = duty;
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "%s Servo Pwm %d", _axis_name, duty);
ledcWrite(_channel_num, duty);
}
// sets the PWM to zero. This allows most servos to be manually moved
void RcServo::set_disable(bool disable) {
return;
_disabled = disable;
if (_disabled)
_write_pwm(0);
}
void RcServo::set_homing_mode(bool is_homing, bool isHoming) {
float home_pos = 0.0;
if (!is_homing)
return;
if (bit_istrue(homing_dir_mask->get(), bit(axis_index)))
home_pos = _position_min;
else
home_pos = _position_max;
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Servo set home %d %3.2f", is_homing, home_pos);
sys_position[axis_index] = home_pos * axis_settings[axis_index]->steps_per_mm->get(); // convert to steps
}
void RcServo::update() {
set_location();
}
void RcServo::set_location() {
uint32_t servo_pulse_len;
float servo_pos, mpos, offset;
// skip location if we are in alarm mode
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "locate");
_get_calibration();
if (sys.state == STATE_ALARM) {
set_disable(true);
return;
}
mpos = system_convert_axis_steps_to_mpos(sys_position, axis_index); // get the axis machine position in mm
offset = gc_state.coord_system[axis_index] + gc_state.coord_offset[axis_index]; // get the current axis work offset
servo_pos = mpos - offset; // determine the current work position
// determine the pulse length
servo_pulse_len = (uint32_t)mapConstrain(servo_pos, _position_min, _position_max, _pwm_pulse_min, _pwm_pulse_max);
_write_pwm(servo_pulse_len);
}
void RcServo::read_settings() {
_get_calibration();
}
// this should change to use its own settings.
void RcServo::_get_calibration() {
float _cal_min = 1.0;
float _cal_max = 1.0;
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Read settings");
// make sure the min is in range
if ((axis_settings[axis_index]->steps_per_mm->get() < SERVO_CAL_MIN) || (axis_settings[axis_index]->steps_per_mm->get() > SERVO_CAL_MAX)) {
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Servo calibration ($10%d) value error. Reset to 100", axis_index);
char reset_val[] = "100";
axis_settings[axis_index]->steps_per_mm->setStringValue(reset_val);
}
// make sure the max is in range
// Note: Max travel is set positive via $$, but stored as a negative number
if ((axis_settings[axis_index]->max_travel->get() < SERVO_CAL_MIN) || (axis_settings[axis_index]->max_travel->get() > SERVO_CAL_MAX)) {
grbl_msg_sendf(CLIENT_SERIAL,
MSG_LEVEL_INFO,
"Servo calibration ($13%d) value error. %3.2f Reset to 100",
axis_index,
axis_settings[axis_index]->max_travel->get());
char reset_val[] = "100";
axis_settings[axis_index]->max_travel->setStringValue(reset_val);
}
_pwm_pulse_min = SERVO_MIN_PULSE;
_pwm_pulse_max = SERVO_MAX_PULSE;
if (bit_istrue(dir_invert_mask->get(), bit(axis_index))) { // normal direction
_cal_min = 2.0 - (axis_settings[axis_index]->steps_per_mm->get() / 100.0);
_cal_max = 2.0 - (axis_settings[axis_index]->max_travel->get() / 100.0);
swap(_pwm_pulse_min, _pwm_pulse_max);
} else { // inverted direction
_cal_min = (axis_settings[axis_index]->steps_per_mm->get() / 100.0);
_cal_max = (axis_settings[axis_index]->max_travel->get() / 100.0);
}
_pwm_pulse_min *= _cal_min;
_pwm_pulse_max *= _cal_max;
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Servo calibration min:%1.2f max %1.2f", _pwm_pulse_min, _pwm_pulse_max);
}

View File

@@ -1,24 +1,5 @@
#pragma once
/*
RcServoClass.h
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/>.
*/
// this is the pulse range of a the servo. Typical servos are 0.001 to 0.002 seconds
// some servos have a wider range. You can adjust this here or in the calibration feature
#define SERVO_MIN_PULSE_SEC 0.001 // min pulse in seconds

View File

@@ -0,0 +1,18 @@
#pragma once
#include "RcServo.h"
namespace Motors {
class Solenoid : public RcServo {
public:
Solenoid();
Solenoid(uint8_t axis_index, gpio_num_t pwm_pin, float transition_poiont);
void config_message();
void set_location();
void update();
void init();
void set_disable(bool disable);
float _transition_poiont;
};
}

View File

@@ -0,0 +1,103 @@
/*
StandardStepperClass.cpp
This is used for a stepper motor that just requires step and direction
pins.
TODO: Add an enable pin
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 "StandardStepper.h"
namespace Motors {
StandardStepper::StandardStepper() {}
StandardStepper::StandardStepper(uint8_t axis_index, uint8_t step_pin, uint8_t dir_pin, uint8_t disable_pin) {
type_id = STANDARD_MOTOR;
this->axis_index = axis_index % MAX_AXES;
this->dual_axis_index = axis_index < MAX_AXES ? 0 : 1; // 0 = primary 1 = ganged
this->step_pin = step_pin;
this->dir_pin = dir_pin;
this->disable_pin = disable_pin;
init();
}
void StandardStepper::init() {
_homing_mask = 0;
is_active = true; // as opposed to NullMotors, this is a real motor
set_axis_name();
init_step_dir_pins();
config_message();
}
void StandardStepper::init_step_dir_pins() {
// TODO Step pin, but RMT complicates things
_invert_step_pin = bit_istrue(step_invert_mask->get(), bit(axis_index));
pinMode(dir_pin, OUTPUT);
#ifdef USE_RMT_STEPS
rmtConfig.rmt_mode = RMT_MODE_TX;
rmtConfig.clk_div = 20;
rmtConfig.mem_block_num = 2;
rmtConfig.tx_config.loop_en = false;
rmtConfig.tx_config.carrier_en = false;
rmtConfig.tx_config.carrier_freq_hz = 0;
rmtConfig.tx_config.carrier_duty_percent = 50;
rmtConfig.tx_config.carrier_level = RMT_CARRIER_LEVEL_LOW;
rmtConfig.tx_config.idle_output_en = true;
# ifdef STEP_PULSE_DELAY
rmtItem[0].duration0 = STEP_PULSE_DELAY * 4;
# else
rmtItem[0].duration0 = 1;
# endif
rmtItem[0].duration1 = 4 * pulse_microseconds->get();
rmtItem[1].duration0 = 0;
rmtItem[1].duration1 = 0;
rmt_chan_num[axis_index][dual_axis_index] = sys_get_next_RMT_chan_num();
rmt_set_source_clk((rmt_channel_t)rmt_chan_num[axis_index][dual_axis_index], RMT_BASECLK_APB);
rmtConfig.channel = (rmt_channel_t)rmt_chan_num[axis_index][dual_axis_index];
rmtConfig.tx_config.idle_level = _invert_step_pin ? RMT_IDLE_LEVEL_HIGH : RMT_IDLE_LEVEL_LOW;
rmtConfig.gpio_num = gpio_num_t(step_pin); // c is a wacky lang
rmtItem[0].level0 = rmtConfig.tx_config.idle_level;
rmtItem[0].level1 = !rmtConfig.tx_config.idle_level;
rmt_config(&rmtConfig);
rmt_fill_tx_items(rmtConfig.channel, &rmtItem[0], rmtConfig.mem_block_num, 0);
#else
pinMode(step_pin, OUTPUT);
#endif // USE_RMT_STEPS
pinMode(disable_pin, OUTPUT);
}
void StandardStepper::config_message() {
grbl_msg_sendf(CLIENT_SERIAL,
MSG_LEVEL_INFO,
"%s Axis standard stepper motor Step:%s Dir:%s Disable:%s",
_axis_name,
pinName(step_pin).c_str(),
pinName(dir_pin).c_str(),
pinName(disable_pin).c_str());
}
void StandardStepper::set_direction_pins(uint8_t onMask) { digitalWrite(dir_pin, (onMask & bit(axis_index))); }
void StandardStepper::set_disable(bool disable) { digitalWrite(disable_pin, disable); }
}

View File

@@ -0,0 +1,23 @@
#pragma once
#include "Motor.h"
namespace Motors {
class StandardStepper : public Motor {
public:
StandardStepper();
StandardStepper(uint8_t axis_index, uint8_t step_pin, uint8_t dir_pin, uint8_t disable_pin);
virtual void config_message();
virtual void init();
virtual void set_direction_pins(uint8_t onMask);
void init_step_dir_pins();
virtual void set_disable(bool disable);
uint8_t step_pin;
protected:
bool _invert_step_pin;
uint8_t dir_pin;
uint8_t disable_pin;
};
}

View File

@@ -1,105 +0,0 @@
/*
StandardStepperClass.cpp
This is used for a stepper motor that just requires step and direction
pins.
TODO: Add an enable pin
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 "MotorClass.h"
StandardStepper ::StandardStepper() {}
StandardStepper ::StandardStepper(uint8_t axis_index, uint8_t step_pin, uint8_t dir_pin, uint8_t disable_pin) {
type_id = STANDARD_MOTOR;
this->axis_index = axis_index % MAX_AXES;
this->dual_axis_index = axis_index < MAX_AXES ? 0 : 1; // 0 = primary 1 = ganged
this->step_pin = step_pin;
this->dir_pin = dir_pin;
this->disable_pin = disable_pin;
init();
}
void StandardStepper ::init() {
_homing_mask = 0;
is_active = true; // as opposed to NullMotors, this is a real motor
set_axis_name();
init_step_dir_pins();
config_message();
}
void StandardStepper ::init_step_dir_pins() {
// TODO Step pin, but RMT complicates things
_invert_step_pin = bit_istrue(step_invert_mask->get(), bit(axis_index));
pinMode(dir_pin, OUTPUT);
#ifdef USE_RMT_STEPS
rmtConfig.rmt_mode = RMT_MODE_TX;
rmtConfig.clk_div = 20;
rmtConfig.mem_block_num = 2;
rmtConfig.tx_config.loop_en = false;
rmtConfig.tx_config.carrier_en = false;
rmtConfig.tx_config.carrier_freq_hz = 0;
rmtConfig.tx_config.carrier_duty_percent = 50;
rmtConfig.tx_config.carrier_level = RMT_CARRIER_LEVEL_LOW;
rmtConfig.tx_config.idle_output_en = true;
# ifdef STEP_PULSE_DELAY
rmtItem[0].duration0 = STEP_PULSE_DELAY * 4;
# else
rmtItem[0].duration0 = 1;
# endif
rmtItem[0].duration1 = 4 * pulse_microseconds->get();
rmtItem[1].duration0 = 0;
rmtItem[1].duration1 = 0;
rmt_chan_num[axis_index][dual_axis_index] = sys_get_next_RMT_chan_num();
rmt_set_source_clk((rmt_channel_t)rmt_chan_num[axis_index][dual_axis_index], RMT_BASECLK_APB);
rmtConfig.channel = (rmt_channel_t)rmt_chan_num[axis_index][dual_axis_index];
rmtConfig.tx_config.idle_level = _invert_step_pin ? RMT_IDLE_LEVEL_HIGH : RMT_IDLE_LEVEL_LOW;
rmtConfig.gpio_num = gpio_num_t(step_pin); // c is a wacky lang
rmtItem[0].level0 = rmtConfig.tx_config.idle_level;
rmtItem[0].level1 = !rmtConfig.tx_config.idle_level;
rmt_config(&rmtConfig);
rmt_fill_tx_items(rmtConfig.channel, &rmtItem[0], rmtConfig.mem_block_num, 0);
#else
pinMode(step_pin, OUTPUT);
#endif // USE_RMT_STEPS
pinMode(disable_pin, OUTPUT);
}
void StandardStepper ::config_message() {
grbl_msg_sendf(CLIENT_SERIAL,
MSG_LEVEL_INFO,
"%s Axis standard stepper motor Step:%s Dir:%s Disable:%s",
_axis_name,
pinName(step_pin).c_str(),
pinName(dir_pin).c_str(),
pinName(disable_pin).c_str());
}
void StandardStepper ::set_direction_pins(uint8_t onMask) {
digitalWrite(dir_pin, (onMask & bit(axis_index)));
}
void StandardStepper ::set_disable(bool disable) {
digitalWrite(disable_pin, disable);
}

View File

@@ -0,0 +1,235 @@
/*
TrinamicDriverClass.cpp
This is used for Trinamic SPI controlled stepper motor drivers.
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 "TrinamicDriver.h"
#include <TMCStepper.h>
namespace Motors {
TrinamicDriver::TrinamicDriver(uint8_t axis_index,
uint8_t step_pin,
uint8_t dir_pin,
uint8_t disable_pin,
uint8_t cs_pin,
uint16_t driver_part_number,
float r_sense,
int8_t spi_index) {
type_id = TRINAMIC_SPI_MOTOR;
this->axis_index = axis_index % MAX_AXES;
this->dual_axis_index = axis_index < 6 ? 0 : 1; // 0 = primary 1 = ganged
_driver_part_number = driver_part_number;
_r_sense = r_sense;
this->step_pin = step_pin;
this->dir_pin = dir_pin;
this->disable_pin = disable_pin;
this->cs_pin = cs_pin;
this->spi_index = spi_index;
_homing_mode = TRINAMIC_HOMING_MODE;
_homing_mask = 0; // no axes homing
if (_driver_part_number == 2130)
tmcstepper = new TMC2130Stepper(cs_pin, _r_sense, spi_index);
else if (_driver_part_number == 5160)
tmcstepper = new TMC5160Stepper(cs_pin, _r_sense, spi_index);
else {
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Trinamic unsupported p/n:%d", _driver_part_number);
return;
}
set_axis_name();
init_step_dir_pins(); // from StandardStepper
digitalWrite(cs_pin, HIGH);
pinMode(cs_pin, OUTPUT);
// use slower speed if I2S
if (cs_pin >= I2S_OUT_PIN_BASE)
tmcstepper->setSPISpeed(TRINAMIC_SPI_FREQ);
config_message();
// init() must be called later, after all TMC drivers have CS pins setup.
}
void TrinamicDriver::init() {
SPI.begin(); // this will get called for each motor, but does not seem to hurt anything
tmcstepper->begin();
test(); // Try communicating with motor. Prints an error if there is a problem.
read_settings(); // pull info from settings
set_mode(false);
_homing_mask = 0;
is_active = true; // as opposed to NullMotors, this is a real motor
}
/*
This is the startup message showing the basic definition
*/
void TrinamicDriver::config_message() {
grbl_msg_sendf(CLIENT_SERIAL,
MSG_LEVEL_INFO,
"%s Axis Trinamic TMC%d Step:%s Dir:%s CS:%s Disable:%s Index:%d",
_axis_name,
_driver_part_number,
pinName(step_pin).c_str(),
pinName(dir_pin).c_str(),
pinName(cs_pin).c_str(),
pinName(disable_pin).c_str(),
spi_index);
}
bool TrinamicDriver::test() {
switch (tmcstepper->test_connection()) {
case 1:
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "%s Trinamic driver test failed. Check connection", _axis_name);
return false;
case 2:
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "%s Trinamic driver test failed. Check motor power", _axis_name);
return false;
default: grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "%s Trinamic driver test passed", _axis_name); return true;
}
}
/*
Read setting and send them to the driver. Called at init() and whenever related settings change
both are stored as float Amps, but TMCStepper library expects...
uint16_t run (mA)
float hold (as a percentage of run)
*/
void TrinamicDriver::read_settings() {
uint16_t run_i_ma = (uint16_t)(axis_settings[axis_index]->run_current->get() * 1000.0);
float hold_i_percent;
if (axis_settings[axis_index]->run_current->get() == 0)
hold_i_percent = 0;
else {
hold_i_percent = axis_settings[axis_index]->hold_current->get() / axis_settings[axis_index]->run_current->get();
if (hold_i_percent > 1.0)
hold_i_percent = 1.0;
}
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "%s Current run %d hold %f", _axis_name, run_i_ma, hold_i_percent);
tmcstepper->microsteps(axis_settings[axis_index]->microsteps->get());
tmcstepper->rms_current(run_i_ma, hold_i_percent);
}
void TrinamicDriver::set_homing_mode(uint8_t homing_mask, bool isHoming) {
_homing_mask = homing_mask;
set_mode(isHoming);
}
/*
There are ton of settings. I'll start by grouping then into modes for now.
Many people will want quiet and stallgaurd homing. Stallguard only run in
Coolstep mode, so it will need to switch to Coolstep when homing
*/
void TrinamicDriver::set_mode(bool isHoming) {
if (isHoming)
_mode = TRINAMIC_HOMING_MODE;
else
_mode = TRINAMIC_RUN_MODE;
if (_lastMode == _mode)
return;
_lastMode = _mode;
switch (_mode) {
case TRINAMIC_MODE_STEALTHCHOP:
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "TRINAMIC_MODE_STEALTHCHOP");
tmcstepper->en_pwm_mode(true);
tmcstepper->pwm_autoscale(true);
tmcstepper->diag1_stall(false);
break;
case TRINAMIC_MODE_COOLSTEP:
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "TRINAMIC_MODE_COOLSTEP");
tmcstepper->en_pwm_mode(false);
tmcstepper->pwm_autoscale(false);
tmcstepper->TCOOLTHRS(NORMAL_TCOOLTHRS); // when to turn on coolstep
tmcstepper->THIGH(NORMAL_THIGH);
break;
case TRINAMIC_MODE_STALLGUARD:
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "TRINAMIC_MODE_STALLGUARD");
tmcstepper->en_pwm_mode(false);
tmcstepper->pwm_autoscale(false);
tmcstepper->TCOOLTHRS(calc_tstep(homing_feed_rate->get(), 150.0));
tmcstepper->THIGH(calc_tstep(homing_feed_rate->get(), 60.0));
tmcstepper->sfilt(1);
tmcstepper->diag1_stall(true); // stallguard i/o is on diag1
tmcstepper->sgt(axis_settings[axis_index]->stallguard->get());
break;
default: grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "TRINAMIC_MODE_UNDEFINED");
}
}
/*
This is the stallguard tuning info. It is call debug, so it could be generic across all classes.
*/
void TrinamicDriver::debug_message() {
uint32_t tstep = tmcstepper->TSTEP();
if (tstep == 0xFFFFF || tstep < 1) // if axis is not moving return
return;
float feedrate = st_get_realtime_rate(); //* settings.microsteps[axis_index] / 60.0 ; // convert mm/min to Hz
grbl_msg_sendf(CLIENT_SERIAL,
MSG_LEVEL_INFO,
"%s Stallguard %d SG_Val: %04d Rate: %05.0f mm/min SG_Setting:%d",
_axis_name,
tmcstepper->stallguard(),
tmcstepper->sg_result(),
feedrate,
axis_settings[axis_index]->stallguard->get());
}
// calculate a tstep from a rate
// tstep = TRINAMIC_FCLK / (time between 1/256 steps)
// This is used to set the stallguard window from the homing speed.
// The percent is the offset on the window
uint32_t TrinamicDriver::calc_tstep(float speed, float percent) {
float tstep =
speed / 60.0 * axis_settings[axis_index]->steps_per_mm->get() * (float)(256 / axis_settings[axis_index]->microsteps->get());
tstep = TRINAMIC_FCLK / tstep * percent / 100.0;
return (uint32_t)tstep;
}
// this can use the enable feature over SPI. The dedicated pin must be in the enable mode,
// but that can be hardwired that way.
void TrinamicDriver::set_disable(bool disable) {
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "%s Axis disable %d", _axis_name, disable);
digitalWrite(disable_pin, disable);
#ifdef USE_TRINAMIC_ENABLE
if (disable)
tmcstepper->toff(TRINAMIC_TOFF_DISABLE);
else {
if (_mode == TRINAMIC_MODE_STEALTHCHOP)
tmcstepper->toff(TRINAMIC_TOFF_STEALTHCHOP);
else
tmcstepper->toff(TRINAMIC_TOFF_COOLSTEP);
}
#endif
// the pin based enable could be added here.
// This would be for individual motors, not the single pin for all motors.
}
}

View File

@@ -1,7 +1,7 @@
#pragma once
/*
TrinamicDriverClass.h
TrinamicDriver.h
Part of Grbl_ESP32
@@ -19,6 +19,10 @@
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Motor.h"
#include "StandardStepper.h"
#include <TMCStepper.h> // https://github.com/teemuatlut/TMCStepper
#define TRINAMIC_MODE_STEALTHCHOP 0 // very quiet
#define TRINAMIC_MODE_COOLSTEP 1 // everything runs cooler so higher current possible
#define TRINAMIC_MODE_STALLGUARD 2 // coolstep plus generates stall indication
@@ -54,5 +58,41 @@
# define TRINAMIC_TOFF_COOLSTEP 3
#endif
#include "MotorClass.h"
#include <TMCStepper.h> // https://github.com/teemuatlut/TMCStepper
namespace Motors {
class TrinamicDriver : public StandardStepper {
public:
TrinamicDriver(uint8_t axis_index,
uint8_t step_pin,
uint8_t dir_pin,
uint8_t disable_pin,
uint8_t cs_pin,
uint16_t driver_part_number,
float r_sense,
int8_t spi_index);
void config_message();
void init();
void set_mode(bool isHoming);
void read_settings();
void trinamic_test_response();
void trinamic_stepper_enable(bool enable);
void debug_message();
void set_homing_mode(uint8_t homing_mask, bool ishoming);
void set_disable(bool disable);
bool test();
private:
uint32_t calc_tstep(float speed, float percent);
TMC2130Stepper* tmcstepper; // all other driver types are subclasses of this one
uint8_t _homing_mode;
uint8_t cs_pin = UNDEFINED_PIN; // The chip select pin (can be the same for daisy chain)
uint16_t _driver_part_number; // example: use 2130 for TMC2130
float _r_sense;
int8_t spi_index;
protected:
uint8_t _mode;
uint8_t _lastMode = 255;
};
}

View File

@@ -1,230 +0,0 @@
/*
TrinamicDriverClass.cpp
This is used for Trinamic SPI controlled stepper motor drivers.
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 "MotorClass.h"
#include <TMCStepper.h>
#include "TrinamicDriverClass.h"
TrinamicDriver ::TrinamicDriver(uint8_t axis_index,
uint8_t step_pin,
uint8_t dir_pin,
uint8_t disable_pin,
uint8_t cs_pin,
uint16_t driver_part_number,
float r_sense,
int8_t spi_index) {
type_id = TRINAMIC_SPI_MOTOR;
this->axis_index = axis_index % MAX_AXES;
this->dual_axis_index = axis_index < 6 ? 0 : 1; // 0 = primary 1 = ganged
_driver_part_number = driver_part_number;
_r_sense = r_sense;
this->step_pin = step_pin;
this->dir_pin = dir_pin;
this->disable_pin = disable_pin;
this->cs_pin = cs_pin;
this->spi_index = spi_index;
_homing_mode = TRINAMIC_HOMING_MODE;
_homing_mask = 0; // no axes homing
if (_driver_part_number == 2130)
tmcstepper = new TMC2130Stepper(cs_pin, _r_sense, spi_index);
else if (_driver_part_number == 5160)
tmcstepper = new TMC5160Stepper(cs_pin, _r_sense, spi_index);
else {
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Trinamic unsupported p/n:%d", _driver_part_number);
return;
}
set_axis_name();
init_step_dir_pins(); // from StandardStepper
digitalWrite(cs_pin, HIGH);
pinMode(cs_pin, OUTPUT);
// use slower speed if I2S
if (cs_pin >= I2S_OUT_PIN_BASE)
tmcstepper->setSPISpeed(TRINAMIC_SPI_FREQ);
config_message();
// init() must be called later, after all TMC drivers have CS pins setup.
}
void TrinamicDriver ::init() {
SPI.begin(); // this will get called for each motor, but does not seem to hurt anything
tmcstepper->begin();
test(); // Try communicating with motor. Prints an error if there is a problem.
read_settings(); // pull info from settings
set_mode(false);
_homing_mask = 0;
is_active = true; // as opposed to NullMotors, this is a real motor
}
/*
This is the startup message showing the basic definition
*/
void TrinamicDriver ::config_message() {
grbl_msg_sendf(CLIENT_SERIAL,
MSG_LEVEL_INFO,
"%s Axis Trinamic TMC%d Step:%s Dir:%s CS:%s Disable:%s Index:%d",
_axis_name,
_driver_part_number,
pinName(step_pin).c_str(),
pinName(dir_pin).c_str(),
pinName(cs_pin).c_str(),
pinName(disable_pin).c_str(),
spi_index);
}
bool TrinamicDriver ::test() {
switch (tmcstepper->test_connection()) {
case 1: grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "%s Trinamic driver test failed. Check connection", _axis_name); return false;
case 2:
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "%s Trinamic driver test failed. Check motor power", _axis_name);
return false;
default: grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "%s Trinamic driver test passed", _axis_name); return true;
}
}
/*
Read setting and send them to the driver. Called at init() and whenever related settings change
both are stored as float Amps, but TMCStepper library expects...
uint16_t run (mA)
float hold (as a percentage of run)
*/
void TrinamicDriver ::read_settings() {
uint16_t run_i_ma = (uint16_t)(axis_settings[axis_index]->run_current->get() * 1000.0);
float hold_i_percent;
if (axis_settings[axis_index]->run_current->get() == 0)
hold_i_percent = 0;
else {
hold_i_percent = axis_settings[axis_index]->hold_current->get() / axis_settings[axis_index]->run_current->get();
if (hold_i_percent > 1.0)
hold_i_percent = 1.0;
}
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "%s Current run %d hold %f", _axis_name, run_i_ma, hold_i_percent);
tmcstepper->microsteps(axis_settings[axis_index]->microsteps->get());
tmcstepper->rms_current(run_i_ma, hold_i_percent);
}
void TrinamicDriver ::set_homing_mode(uint8_t homing_mask, bool isHoming) {
_homing_mask = homing_mask;
set_mode(isHoming);
}
/*
There are ton of settings. I'll start by grouping then into modes for now.
Many people will want quiet and stallgaurd homing. Stallguard only run in
Coolstep mode, so it will need to switch to Coolstep when homing
*/
void TrinamicDriver ::set_mode(bool isHoming) {
if (isHoming)
_mode = TRINAMIC_HOMING_MODE;
else
_mode = TRINAMIC_RUN_MODE;
if (_lastMode == _mode)
return;
_lastMode = _mode;
switch (_mode) {
case TRINAMIC_MODE_STEALTHCHOP:
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "TRINAMIC_MODE_STEALTHCHOP");
tmcstepper->en_pwm_mode(true);
tmcstepper->pwm_autoscale(true);
tmcstepper->diag1_stall(false);
break;
case TRINAMIC_MODE_COOLSTEP:
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "TRINAMIC_MODE_COOLSTEP");
tmcstepper->en_pwm_mode(false);
tmcstepper->pwm_autoscale(false);
tmcstepper->TCOOLTHRS(NORMAL_TCOOLTHRS); // when to turn on coolstep
tmcstepper->THIGH(NORMAL_THIGH);
break;
case TRINAMIC_MODE_STALLGUARD:
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "TRINAMIC_MODE_STALLGUARD");
tmcstepper->en_pwm_mode(false);
tmcstepper->pwm_autoscale(false);
tmcstepper->TCOOLTHRS(calc_tstep(homing_feed_rate->get(), 150.0));
tmcstepper->THIGH(calc_tstep(homing_feed_rate->get(), 60.0));
tmcstepper->sfilt(1);
tmcstepper->diag1_stall(true); // stallguard i/o is on diag1
tmcstepper->sgt(axis_settings[axis_index]->stallguard->get());
break;
default: grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "TRINAMIC_MODE_UNDEFINED");
}
}
/*
This is the stallguard tuning info. It is call debug, so it could be generic across all classes.
*/
void TrinamicDriver ::debug_message() {
uint32_t tstep = tmcstepper->TSTEP();
if (tstep == 0xFFFFF || tstep < 1) // if axis is not moving return
return;
float feedrate = st_get_realtime_rate(); //* settings.microsteps[axis_index] / 60.0 ; // convert mm/min to Hz
grbl_msg_sendf(CLIENT_SERIAL,
MSG_LEVEL_INFO,
"%s Stallguard %d SG_Val: %04d Rate: %05.0f mm/min SG_Setting:%d",
_axis_name,
tmcstepper->stallguard(),
tmcstepper->sg_result(),
feedrate,
axis_settings[axis_index]->stallguard->get());
}
// calculate a tstep from a rate
// tstep = TRINAMIC_FCLK / (time between 1/256 steps)
// This is used to set the stallguard window from the homing speed.
// The percent is the offset on the window
uint32_t TrinamicDriver ::calc_tstep(float speed, float percent) {
float tstep = speed / 60.0 * axis_settings[axis_index]->steps_per_mm->get() * (float)(256 / axis_settings[axis_index]->microsteps->get());
tstep = TRINAMIC_FCLK / tstep * percent / 100.0;
return (uint32_t)tstep;
}
// this can use the enable feature over SPI. The dedicated pin must be in the enable mode,
// but that can be hardwired that way.
void TrinamicDriver ::set_disable(bool disable) {
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "%s Axis disable %d", _axis_name, disable);
digitalWrite(disable_pin, disable);
#ifdef USE_TRINAMIC_ENABLE
if (disable)
tmcstepper->toff(TRINAMIC_TOFF_DISABLE);
else {
if (_mode == TRINAMIC_MODE_STEALTHCHOP)
tmcstepper->toff(TRINAMIC_TOFF_STEALTHCHOP);
else
tmcstepper->toff(TRINAMIC_TOFF_COOLSTEP);
}
#endif
// the pin based enable could be added here.
// This would be for individual motors, not the single pin for all motors.
}

View File

@@ -0,0 +1,139 @@
#include "UnipolarMotor.h"
namespace Motors {
UnipolarMotor::UnipolarMotor() {}
UnipolarMotor::UnipolarMotor(uint8_t axis_index, uint8_t pin_phase0, uint8_t pin_phase1, uint8_t pin_phase2, uint8_t pin_phase3) {
type_id = UNIPOLAR_MOTOR;
this->axis_index = axis_index % MAX_AXES;
this->dual_axis_index = axis_index < MAX_AXES ? 0 : 1; // 0 = primary 1 = ganged
_pin_phase0 = pin_phase0;
_pin_phase1 = pin_phase1;
_pin_phase2 = pin_phase2;
_pin_phase3 = pin_phase3;
_half_step = true; // TODO read from settings ... microstep > 1 = half step
set_axis_name();
init();
config_message();
}
void UnipolarMotor::init() {
pinMode(_pin_phase0, OUTPUT);
pinMode(_pin_phase1, OUTPUT);
pinMode(_pin_phase2, OUTPUT);
pinMode(_pin_phase3, OUTPUT);
_current_phase = 0;
}
void UnipolarMotor::config_message() {
grbl_msg_sendf(CLIENT_SERIAL,
MSG_LEVEL_INFO,
"%s Axis unipolar stepper motor Ph0:%s Ph1:%s Ph2:%s Ph3:%s",
_axis_name,
pinName(_pin_phase0).c_str(),
pinName(_pin_phase1).c_str(),
pinName(_pin_phase2).c_str(),
pinName(_pin_phase3).c_str());
}
void UnipolarMotor::set_disable(bool disable) {
if (disable) {
digitalWrite(_pin_phase0, 0);
digitalWrite(_pin_phase1, 0);
digitalWrite(_pin_phase2, 0);
digitalWrite(_pin_phase3, 0);
}
_enabled = !disable;
}
void UnipolarMotor::step(uint8_t step_mask, uint8_t dir_mask) {
uint8_t _phase[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; // temporary phase values...all start as off
uint8_t phase_max;
if (!(step_mask & bit(axis_index)))
return; // a step is not required on this interrupt
if (!_enabled)
return; // don't do anything, phase is not changed or lost
if (_half_step)
phase_max = 7;
else
phase_max = 3;
if (dir_mask & bit(axis_index)) { // count up
if (_current_phase == phase_max)
_current_phase = 0;
else
_current_phase++;
} else { // count down
if (_current_phase == 0)
_current_phase = phase_max;
else
_current_phase--;
}
/*
8 Step : A AB B BC C CD D DA
4 Step : AB BC CD DA
Step IN4 IN3 IN2 IN1
A 0 0 0 1
AB 0 0 1 1
B 0 0 1 0
BC 0 1 1 0
C 0 1 0 0
CD 1 1 0 0
D 1 0 0 0
DA 1 0 0 1
*/
if (_half_step) {
switch (_current_phase) {
case 0: _phase[0] = 1; break;
case 1:
_phase[0] = 1;
_phase[1] = 1;
break;
case 2: _phase[1] = 1; break;
case 3:
_phase[1] = 1;
_phase[2] = 1;
break;
case 4: _phase[2] = 1; break;
case 5:
_phase[2] = 1;
_phase[3] = 1;
break;
case 6: _phase[3] = 1; break;
case 7:
_phase[3] = 1;
_phase[0] = 1;
break;
}
} else {
switch (_current_phase) {
case 0:
_phase[0] = 1;
_phase[1] = 1;
break;
case 1:
_phase[1] = 1;
_phase[2] = 1;
break;
case 2:
_phase[2] = 1;
_phase[3] = 1;
break;
case 3:
_phase[3] = 1;
_phase[0] = 1;
break;
}
}
digitalWrite(_pin_phase0, _phase[0]);
digitalWrite(_pin_phase1, _phase[1]);
digitalWrite(_pin_phase2, _phase[2]);
digitalWrite(_pin_phase3, _phase[3]);
}
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include "Motor.h"
namespace Motors {
class UnipolarMotor : public Motor {
public:
UnipolarMotor();
UnipolarMotor(uint8_t axis_index, uint8_t pin_phase0, uint8_t pin_phase1, uint8_t pin_phase2, uint8_t pin_phase3);
void init();
void config_message();
void set_disable(bool disable);
void step(uint8_t step_mask, uint8_t dir_mask); // only used on Unipolar right now
private:
uint8_t _pin_phase0;
uint8_t _pin_phase1;
uint8_t _pin_phase2;
uint8_t _pin_phase3;
uint8_t _current_phase;
bool _half_step;
bool _enabled;
};
}

View File

@@ -1,137 +0,0 @@
#include "MotorClass.h"
UnipolarMotor ::UnipolarMotor() {}
UnipolarMotor ::UnipolarMotor(uint8_t axis_index, uint8_t pin_phase0, uint8_t pin_phase1, uint8_t pin_phase2, uint8_t pin_phase3) {
type_id = UNIPOLAR_MOTOR;
this->axis_index = axis_index % MAX_AXES;
this->dual_axis_index = axis_index < MAX_AXES ? 0 : 1; // 0 = primary 1 = ganged
_pin_phase0 = pin_phase0;
_pin_phase1 = pin_phase1;
_pin_phase2 = pin_phase2;
_pin_phase3 = pin_phase3;
_half_step = true; // TODO read from settings ... microstep > 1 = half step
set_axis_name();
init();
config_message();
}
void UnipolarMotor ::init() {
pinMode(_pin_phase0, OUTPUT);
pinMode(_pin_phase1, OUTPUT);
pinMode(_pin_phase2, OUTPUT);
pinMode(_pin_phase3, OUTPUT);
_current_phase = 0;
}
void UnipolarMotor ::config_message() {
grbl_msg_sendf(CLIENT_SERIAL,
MSG_LEVEL_INFO,
"%s Axis unipolar stepper motor Ph0:%s Ph1:%s Ph2:%s Ph3:%s",
_axis_name,
pinName(_pin_phase0).c_str(),
pinName(_pin_phase1).c_str(),
pinName(_pin_phase2).c_str(),
pinName(_pin_phase3).c_str());
}
void UnipolarMotor ::set_disable(bool disable) {
if (disable) {
digitalWrite(_pin_phase0, 0);
digitalWrite(_pin_phase1, 0);
digitalWrite(_pin_phase2, 0);
digitalWrite(_pin_phase3, 0);
}
_enabled = !disable;
}
void UnipolarMotor::step(uint8_t step_mask, uint8_t dir_mask) {
uint8_t _phase[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; // temporary phase values...all start as off
uint8_t phase_max;
if (!(step_mask & bit(axis_index)))
return; // a step is not required on this interrupt
if (!_enabled)
return; // don't do anything, phase is not changed or lost
if (_half_step)
phase_max = 7;
else
phase_max = 3;
if (dir_mask & bit(axis_index)) { // count up
if (_current_phase == phase_max)
_current_phase = 0;
else
_current_phase++;
} else { // count down
if (_current_phase == 0)
_current_phase = phase_max;
else
_current_phase--;
}
/*
8 Step : A AB B BC C CD D DA
4 Step : AB BC CD DA
Step IN4 IN3 IN2 IN1
A 0 0 0 1
AB 0 0 1 1
B 0 0 1 0
BC 0 1 1 0
C 0 1 0 0
CD 1 1 0 0
D 1 0 0 0
DA 1 0 0 1
*/
if (_half_step) {
switch (_current_phase) {
case 0: _phase[0] = 1; break;
case 1:
_phase[0] = 1;
_phase[1] = 1;
break;
case 2: _phase[1] = 1; break;
case 3:
_phase[1] = 1;
_phase[2] = 1;
break;
case 4: _phase[2] = 1; break;
case 5:
_phase[2] = 1;
_phase[3] = 1;
break;
case 6: _phase[3] = 1; break;
case 7:
_phase[3] = 1;
_phase[0] = 1;
break;
}
} else {
switch (_current_phase) {
case 0:
_phase[0] = 1;
_phase[1] = 1;
break;
case 1:
_phase[1] = 1;
_phase[2] = 1;
break;
case 2:
_phase[2] = 1;
_phase[3] = 1;
break;
case 3:
_phase[3] = 1;
_phase[0] = 1;
break;
}
}
digitalWrite(_pin_phase0, _phase[0]);
digitalWrite(_pin_phase1, _phase[1]);
digitalWrite(_pin_phase2, _phase[2]);
digitalWrite(_pin_phase3, _phase[3]);
}

View File

@@ -57,7 +57,7 @@
#include "serial.h"
#include "Pins.h"
#include "Spindles/SpindleClass.h"
#include "Motors/MotorClass.h"
#include "Motors/Motors.h"
#include "stepper.h"
#include "jog.h"
#include "inputbuffer.h"

View File

@@ -24,6 +24,8 @@
#include "grbl.h"
#include "servo_axis.h"
#ifdef USE_SERVO_AXES
static TaskHandle_t servosSyncTaskHandle = 0;

View File

@@ -53,7 +53,7 @@
*/
#include "Motors/RcServoClass.h"
#include "Motors/RcServoSettings.h"
#define SERVO_HOMING_OFF 0 // servo is off during homing
#define SERVO_HOMING_TARGET 1 // servo is send to a location during homing