diff --git a/FluidNC/src/Pin.cpp b/FluidNC/src/Pin.cpp index 5e22afacf..829493dcc 100644 --- a/FluidNC/src/Pin.cpp +++ b/FluidNC/src/Pin.cpp @@ -10,8 +10,11 @@ #include "Pins/GPIOPinDetail.h" #include "Pins/VoidPinDetail.h" #include "Pins/I2SOPinDetail.h" +#include "Pins/UARTPinDetail.h" #include "Pins/ErrorPinDetail.h" #include "string_util.h" +#include "Machine/MachineConfig.h" // config +#include Pins::PinDetail* Pin::undefinedPin = new Pins::VoidPinDetail(); Pins::PinDetail* Pin::errorPin = new Pins::ErrorPinDetail("unknown"); @@ -73,8 +76,28 @@ const char* Pin::parse(std::string_view pin_str, Pins::PinDetail*& pinImplementa pinImplementation = new Pins::GPIOPinDetail(static_cast(pin_number), parser); } if (string_util::equal_ignore_case(prefix, "i2so")) { + if (config->_i2so == nullptr) { + return "i2so: section must be defined before using i2so pins"; + } pinImplementation = new Pins::I2SOPinDetail(static_cast(pin_number), parser); } + + if (prefix.length() == 13) { + if (prefix.substr(0, 12) == "uart_channel") { + log_info("More:" << prefix[12]); + if (prefix[12] >= '0' && prefix[12] <= '2') { // TODO use MAX_N_UARTS + // check to see if the channel exits + auto deviceId = prefix[12] - '0'; + if (config->_uart_channels[deviceId] == nullptr) { + return " uart channel number not found"; + } + pinImplementation = new Pins::UARTIODetail(deviceId, pinnum_t(pin_number), parser); + } else { + return "Incorrect pin extender specification. Expected 'pinext[0-2].[port number]'."; + } + } + } + if (string_util::equal_ignore_case(prefix, "no_pin")) { pinImplementation = undefinedPin; } diff --git a/FluidNC/src/Pins/PinCapabilities.cpp b/FluidNC/src/Pins/PinCapabilities.cpp index 1b8c6d0cf..3e54bb00a 100644 --- a/FluidNC/src/Pins/PinCapabilities.cpp +++ b/FluidNC/src/Pins/PinCapabilities.cpp @@ -29,6 +29,7 @@ namespace Pins { PinCapabilities PinCapabilities::Native(1 << (__LINE__ - START_LINE)); PinCapabilities PinCapabilities::I2S(1 << (__LINE__ - START_LINE)); + PinCapabilities PinCapabilities::UARTIO(1 << (__LINE__ - START_LINE)); PinCapabilities PinCapabilities::Error(1 << (__LINE__ - START_LINE)); PinCapabilities PinCapabilities::Void(1 << (__LINE__ - START_LINE)); } diff --git a/FluidNC/src/Pins/PinCapabilities.h b/FluidNC/src/Pins/PinCapabilities.h index ea9849d2c..f457115f1 100644 --- a/FluidNC/src/Pins/PinCapabilities.h +++ b/FluidNC/src/Pins/PinCapabilities.h @@ -21,7 +21,7 @@ namespace Pins { friend class PinAttributes; // Wants access to _value for validation public: - PinCapabilities(const PinCapabilities&) = default; + PinCapabilities(const PinCapabilities&) = default; PinCapabilities& operator=(const PinCapabilities&) = default; // All the capabilities we use and test: @@ -44,6 +44,7 @@ namespace Pins { // can compare classes of pins along with their properties by just looking at the capabilities. static PinCapabilities Native; static PinCapabilities I2S; + static PinCapabilities UARTIO; static PinCapabilities Error; static PinCapabilities Void; diff --git a/FluidNC/src/Pins/UARTPinDetail.cpp b/FluidNC/src/Pins/UARTPinDetail.cpp new file mode 100644 index 000000000..34ae4b280 --- /dev/null +++ b/FluidNC/src/Pins/UARTPinDetail.cpp @@ -0,0 +1,107 @@ +// Copyright (c) 2023 Bart Dring +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#ifdef ESP32 +# include "UARTPinDetail.h" +# include "PinDetail.h" + +# include "../I2SOut.h" +# include "../Assert.h" + +namespace Pins { + std::vector UARTIODetail::_claimed(nPins, false); + + UARTIODetail::UARTIODetail(int deviceId, pinnum_t index, const PinOptionsParser& options) : + PinDetail(index), _capabilities(PinCapabilities::Output | PinCapabilities::UARTIO), _attributes(Pins::PinAttributes::Undefined), + _readWriteMask(0) { + Assert(index < nPins, "Pin number is greater than max %d", nPins - 1); + Assert(!_claimed[index], "Pin is already used."); + + // User defined pin capabilities + for (auto opt : options) { + if (opt.is("pu")) { + } else if (opt.is("pd")) { + } else if (opt.is("low")) { + _attributes = _attributes | PinAttributes::ActiveLow; + } else if (opt.is("high")) { + // Default: Active HIGH. + } else { + Assert(false, "Bad UARTPin option passed to pin %d: %s", int(index), opt()); + } + } + + _device_id = deviceId; + + //log_info("created uart pin:" << index); + _claimed[index] = true; + + // readWriteMask is xor'ed with the value to invert it if active low + _readWriteMask = int(_attributes.has(PinAttributes::ActiveLow)); + } + // The write will not happen immediately; the data is queued for + // delivery to the serial shift register chain via DMA and a FIFO + + PinCapabilities UARTIODetail::capabilities() const { + return PinCapabilities::Output | PinCapabilities::UARTIO; + } + + void IRAM_ATTR UARTIODetail::write(int high) { + if (high != _lastWrittenValue) { + _lastWrittenValue = high; + if (!_attributes.has(PinAttributes::Output)) { + log_error(toString()); + } + Assert(_attributes.has(PinAttributes::Output), "Pin %s cannot be written", toString().c_str()); + int value = _readWriteMask ^ high; + log_info("Set uart pin:" << high); + } + } + + int UARTIODetail::read() { + return 0; + } + + void UARTIODetail::setAttr(PinAttributes value) { + // These two assertions will fail if we do them for index 1/3 (Serial uart). This is because + // they are initialized by HardwareSerial well before we start our main operations. Best to + // just ignore them for now, and figure this out later. TODO FIXME! + + // Check the attributes first: + Assert(value.validateWith(this->_capabilities) || _index == 1 || _index == 3, + "The requested attributes don't match the capabilities for %s", + toString().c_str()); + Assert(!_attributes.conflictsWith(value) || _index == 1 || _index == 3, + "The requested attributes on %s conflict with previous settings", + toString().c_str()); + + _attributes = _attributes | value; + + // If the pin is ActiveLow, we should take that into account here: + if (value.has(PinAttributes::Output)) { + //gpio_write(_index, int(value.has(PinAttributes::InitialOn)) ^ _readWriteMask); + } + } + + PinAttributes UARTIODetail::getAttr() const { + return _attributes; + } + + std::string UARTIODetail::toString() { + std::string s("uart_channel"); + s += std::to_string(_device_id); + s += "."; + s += std::to_string(_index); + if (_attributes.has(PinAttributes::ActiveLow)) { + s += ":low"; + } + if (_attributes.has(PinAttributes::PullUp)) { + s += ":pu"; + } + if (_attributes.has(PinAttributes::PullDown)) { + s += ":pd"; + } + return s; + } +} + +#endif diff --git a/FluidNC/src/Pins/UARTPinDetail.h b/FluidNC/src/Pins/UARTPinDetail.h new file mode 100644 index 000000000..4137f295a --- /dev/null +++ b/FluidNC/src/Pins/UARTPinDetail.h @@ -0,0 +1,38 @@ +// Copyright (c) 2023 B. Dring +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#pragma once +#ifdef ESP32 + +# include "PinDetail.h" + +namespace Pins { + class UARTIODetail : public PinDetail { + PinCapabilities _capabilities; + PinAttributes _attributes; + int _readWriteMask; + + int _device_id; + + static const int nPins = 255; + static std::vector _claimed; + + bool _lastWrittenValue = false; + + public: + UARTIODetail(int deviceId, pinnum_t index, const PinOptionsParser& options); + + // I/O: + PinCapabilities capabilities() const override; + void write(int high) override; + int read() override; + void setAttr(PinAttributes value) override; + PinAttributes getAttr() const override; + + std::string toString() override; + + ~UARTIODetail() override { _claimed[_index] = false; } + }; +} + +#endif diff --git a/FluidNC/src/UartChannel.cpp b/FluidNC/src/UartChannel.cpp index f304f8802..8cd56f158 100644 --- a/FluidNC/src/UartChannel.cpp +++ b/FluidNC/src/UartChannel.cpp @@ -20,6 +20,7 @@ void UartChannel::init() { void UartChannel::init(Uart* uart) { _uart = uart; allChannels.registration(this); + log_info("uart_channel" << _uart_num << " created"); } size_t UartChannel::write(uint8_t c) { diff --git a/FluidNC/src/UartChannel.h b/FluidNC/src/UartChannel.h index 94f9aec5f..dbbca4e4c 100644 --- a/FluidNC/src/UartChannel.h +++ b/FluidNC/src/UartChannel.h @@ -12,7 +12,8 @@ class UartChannel : public Channel, public Configuration::Configurable { Lineedit* _lineedit; Uart* _uart; - int _uart_num = 0; + int _uart_num = 0; + bool _has_pins = false; // the device is a pin extender public: UartChannel(bool addCR = false); @@ -39,7 +40,10 @@ class UartChannel : public Channel, public Configuration::Configurable { Channel* pollLine(char* line) override; // Configuration methods - void group(Configuration::HandlerBase& handler) override { handler.item("uart_num", _uart_num); } + void group(Configuration::HandlerBase& handler) override { + handler.item("uart_num", _uart_num); + handler.item("has_pins", _has_pins); + } }; extern UartChannel Uart0;