1
0
mirror of https://github.com/bdring/Grbl_Esp32.git synced 2025-09-01 02:21:46 +02:00

Implemented Pin. Figured out a way to work around the TrinamicDriver pins.

This commit is contained in:
Stefan de Bruijn
2021-06-02 09:41:59 +02:00
parent ab962bf8b1
commit b04d824837
3 changed files with 69 additions and 33 deletions

View File

@@ -24,12 +24,42 @@
#include <atomic>
// This is global so it can be accessed from the override
Pin* csPinTMC;
namespace Motors {
namespace Details {
// How this hack works... In short, we just derive from the TMCStepper classes, and add the pin as a field. This is
// fine of course, as long as we know what CS pin we have to switch. Unfortunately, this is a bit tougher, because
// the TMC5160Stepper class derives from TMC2130Stepper. Normally, this is solved through multiple inheritance, where
// we put the pin in the second class. Unfortunately, that requires dynamic_cast down the road, which is not available.
// So instead, we use the old _pinCS in the TMC2130Stepper class to figure out which class we actually have, and cast
// away with static_cast.
class MyTMC2130Stepper : public TMC2130Stepper {
public:
MyTMC2130Stepper(Pin& cspin, float rsense, int spiIndex) : TMC2130Stepper(2130, rsense, spiIndex), _cspin(cspin) {}
Pin& _cspin;
};
class MyTMC5160Stepper : public TMC5160Stepper {
public:
MyTMC5160Stepper(Pin& cspin, float rsense, int spiIndex) : TMC5160Stepper(5160, rsense, spiIndex), _cspin(cspin) {}
Pin& _cspin;
};
}
}
// Override default functions to use our pin framework
void TMC2130Stepper::switchCSpin(bool state) {
csPinTMC->write(state);
// We use the _pinCS to figure out which derived class we have
switch (this->_pinCS) {
case 2130:
static_cast<Motors::Details::MyTMC2130Stepper*>(this)->_cspin.write(state);
break;
case 5160:
static_cast<Motors::Details::MyTMC5160Stepper*>(this)->_cspin.write(state);
break;
}
}
void IRAM_ATTR pinMode(uint8_t pin, uint8_t mode) {}
namespace Motors {
@@ -49,18 +79,10 @@ namespace Motors {
void TrinamicDriver::init() {
_has_errors = false;
// This pin is "fake" because it is not used. The TMCStepper
// library calls switchCSpin() which uses the pin number that
// is passed to the constructor, but we override the weak
// definition of switchCSPin() to using our own pin framework.
// Similarly, the constructors call pinMode(), but we override
// that too. Thus the "fake" pin number is never used.
uint16_t fake_spi_cs_pin = 0;
if (_driver_part_number == 2130) {
tmcstepper = new TMC2130Stepper(fake_spi_cs_pin, _r_sense, _spi_index);
tmcstepper = new Details::MyTMC2130Stepper(_cs_pin, _r_sense, _spi_index);
} else if (_driver_part_number == 5160) {
tmcstepper = new TMC5160Stepper(fake_spi_cs_pin, _r_sense, _spi_index);
tmcstepper = new Details::MyTMC5160Stepper(_cs_pin, _r_sense, _spi_index);
} else {
grbl_msg_sendf(CLIENT_SERIAL,
MsgLevel::Info,
@@ -75,8 +97,6 @@ namespace Motors {
_cs_pin.setAttr(Pin::Attr::Output | Pin::Attr::InitialOn);
csPinTMC = &_cs_pin; // Prepare for switchCSpin callback
// use slower speed if I2S
if (_cs_pin.capabilities().has(Pin::Capabilities::I2S)) {
tmcstepper->setSPISpeed(TRINAMIC_SPI_FREQ);
@@ -107,8 +127,6 @@ namespace Motors {
SPI.begin(sckPin, misoPin, mosiPin, ssPin); // this will get called for each motor, but does not seem to hurt anything
csPinTMC = &_cs_pin; // Prepare for switchCSpin callback
tmcstepper->begin();
_has_errors = !test(); // Try communicating with motor. Prints an error if there is a problem.
@@ -157,8 +175,6 @@ namespace Motors {
return false;
}
csPinTMC = &_cs_pin; // Prepare for switchCSpin callback
switch (tmcstepper->test_connection()) {
case 1:
grbl_msg_sendf(CLIENT_SERIAL,
@@ -226,8 +242,6 @@ o Read setting and send them to the driver. Called at init() and whenever rel
hold_i_percent = 1.0;
}
csPinTMC = &_cs_pin; // Prepare for switchCSpin callback
tmcstepper->microsteps(_microsteps);
tmcstepper->rms_current(run_i_ma, hold_i_percent);
@@ -249,8 +263,6 @@ o Read setting and send them to the driver. Called at init() and whenever rel
return;
}
csPinTMC = &_cs_pin; // Prepare for switchCSpin callback
TrinamicMode newMode = isHoming ? TRINAMIC_HOMING_MODE : TRINAMIC_RUN_MODE;
if (newMode == _mode) {
@@ -299,8 +311,6 @@ o Read setting and send them to the driver. Called at init() and whenever rel
return;
}
csPinTMC = &_cs_pin; // Prepare for switchCSpin callback
uint32_t tstep = tmcstepper->TSTEP();
if (tstep == 0xFFFFF || tstep < 1) { // if axis is not moving return
@@ -356,8 +366,6 @@ o Read setting and send them to the driver. Called at init() and whenever rel
return;
}
csPinTMC = &_cs_pin; // Prepare for switchCSpin callback
_disabled = disable;
_disable_pin.write(_disabled);

View File

@@ -41,7 +41,7 @@
#endif
Pins::PinDetail* Pin::undefinedPin = new Pins::VoidPinDetail();
Pins::PinDetail* Pin::errorPin = new Pins::ErrorPinDetail();
Pins::PinDetail* Pin::errorPin = new Pins::ErrorPinDetail();
bool Pin::parse(StringRange tmp, Pins::PinDetail*& pinImplementation) {
String str = tmp.str();
@@ -168,14 +168,14 @@ bool Pin::validate(const String& str) {
void Pin::report(const char* legend) {
if (defined()) {
#if ESP32
#if ESP32
grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "%s on %s", legend, name().c_str());
#endif
#endif
}
}
Pin::~Pin() {
if (_detail != undefinedPin) {
if (_detail != undefinedPin && _detail != errorPin) {
delete _detail;
}
}

View File

@@ -36,6 +36,23 @@
// Forward declarations:
class String;
// Pin class. A pin is basically a thing that can 'output', 'input' or do both. GPIO on an ESP32 comes to mind,
// but there are way more possible pins. Think about I2S/I2C/SPI extenders, RS485 driven pin devices and even
// WiFi wall sockets.
//
// Normally people define a pin using the yaml configuration, and specify there how a pin should behave. For
// example, ':low' is normally supported, which means 'default low'. For output pins this is relevant, because
// it basically means 'off is a high (3.3V) signal, on is a low (GND) signal'. By doing it like this, there is
// no longer a need for invert flags, and things like that.
//
// Pins are supposed to be fields during the lifetime of the MachineConfig. In normal operations, the pin is
// created by the parser (using the 'create' methods), then kept in fields from the configurable, and eventually
// cleaned up by the destructor. Pins cannot be copied, there always has to be 1 owner of a pin, and they shouldn't
// be thrown around in the application.
//
// Pins internally use PinDetail classes. PinDetail's are just implementation details for a certain type of pin.
// PinDetail is not exposed to developers, because they should never be used directly. Pin class is your
// one-stop-go-to-shop for an pin.
class Pin {
// Helper for handling callbacks and mapping them to the proper class:
template <typename ThisType, void (ThisType::*Callback)()>
@@ -50,10 +67,15 @@ class Pin {
static void IRAM_ATTR callback(void* /*ptr*/) { Callback(); }
};
// The undefined pin and error pin are two special pins. Error pins always throw an error when they are used.
// These are useful for unit testing, and for initializing pins that _always_ have to be defined by a user
// (or else). Undefined pins are basically pins with no functionality. They don't have to be defined, but also
// have no functionality when they are used.
static Pins::PinDetail* undefinedPin;
static Pins::PinDetail* errorPin;
Pins::PinDetail* _detail;
// Implementation details of this pin.
Pins::PinDetail* _detail;
static bool parse(StringRange str, Pins::PinDetail*& detail);
@@ -63,6 +85,7 @@ public:
using Capabilities = Pins::PinCapabilities;
using Attr = Pins::PinAttributes;
// A default pin is an undefined pin.
inline Pin() : _detail(undefinedPin) {}
static const bool On = true;
@@ -70,11 +93,14 @@ public:
// inline static Pins::PinDetail* create(const char* str) { return create(StringRange(str)); };
static Pin create(const char* str) { return create(StringRange(str)); } // ensure it's not ambiguous
static Pin create(const char* str) { return create(StringRange(str)); } // ensure it's not ambiguous
static Pin create(const StringRange& str);
static Pin create(const String& str);
static bool validate(const String& str);
// We delete the copy constructor, and implement the move constructor. The move constructor is required to support
// the correct execution of 'return' in f.ex. `create` calls. It basically transfers ownership from the callee to the
// caller of the Pin.
inline Pin(const Pin& o) = delete;
inline Pin(Pin&& o) : _detail(nullptr) { std::swap(_detail, o._detail); }
@@ -84,13 +110,15 @@ public:
return *this;
}
// Some convenience operators:
// Some convenience operators and functions:
inline bool operator==(const Pin& o) const { return _detail == o._detail; }
inline bool operator!=(const Pin& o) const { return _detail != o._detail; }
inline bool undefined() const { return _detail == undefinedPin; }
inline bool defined() const { return !undefined(); }
// External libraries normally use digitalWrite, digitalRead and setMode. Since we cannot handle that behavior, we
// just give back the uint8_t for getNative.
inline uint8_t getNative(Capabilities expectedBehavior) const {
Assert(_detail->capabilities().has(expectedBehavior), "Requested pin does not have the expected behavior.");
return _detail->_index;