mirror of
https://github.com/bdring/Grbl_Esp32.git
synced 2025-09-02 10:53:01 +02:00
Implemented most of the Uart functionality as a proof-of-principle. Not tested or being used; this is mainly for checking if important parts are missing in the api.
This commit is contained in:
@@ -116,7 +116,7 @@ void run_once() {
|
|||||||
// This can exit on a system abort condition, in which case run_once()
|
// This can exit on a system abort condition, in which case run_once()
|
||||||
// is re-executed by an enclosing loop.
|
// is re-executed by an enclosing loop.
|
||||||
protocol_main_loop();
|
protocol_main_loop();
|
||||||
} catch (AssertionFailed ex) {
|
} catch (const AssertionFailed& ex) {
|
||||||
// This means something is terribly broken:
|
// This means something is terribly broken:
|
||||||
grbl_sendf(CLIENT_ALL, "Critical error: %s", ex.stackTrace.c_str());
|
grbl_sendf(CLIENT_ALL, "Critical error: %s", ex.stackTrace.c_str());
|
||||||
}
|
}
|
||||||
|
@@ -8,6 +8,9 @@
|
|||||||
|
|
||||||
#if defined PIN_DEBUG && defined ESP32
|
#if defined PIN_DEBUG && defined ESP32
|
||||||
# include "Pins/DebugPinDetail.h"
|
# include "Pins/DebugPinDetail.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ESP32
|
||||||
# include "Grbl.h" // grbl_sendf
|
# include "Grbl.h" // grbl_sendf
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -125,7 +128,7 @@ Pin Pin::create(const String& str) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (AssertionFailed& ex) { // We shouldn't get here under normal circumstances.
|
} catch (const AssertionFailed& ex) { // We shouldn't get here under normal circumstances.
|
||||||
#ifdef PIN_DEBUG
|
#ifdef PIN_DEBUG
|
||||||
# ifdef ESP32
|
# ifdef ESP32
|
||||||
grbl_sendf(CLIENT_ALL, "Failed. Details: %s\r\n", ex.stackTrace.c_str());
|
grbl_sendf(CLIENT_ALL, "Failed. Details: %s\r\n", ex.stackTrace.c_str());
|
||||||
|
33
Grbl_Esp32/src/PinUsers/LimitedResource.h
Normal file
33
Grbl_Esp32/src/PinUsers/LimitedResource.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace PinUsers {
|
||||||
|
/// <summary>
|
||||||
|
/// Helper class that to manage 'Count' resources. Resources can be claimed and released at
|
||||||
|
/// any time. This helper class keeps track of a bitmap that holds the resources.
|
||||||
|
/// </summary>
|
||||||
|
template <int Count>
|
||||||
|
class LimitedResource {
|
||||||
|
static_assert(Count > 0, "Resource count cannot be 0 or negative.");
|
||||||
|
|
||||||
|
// We do +32 instead of +31 because 0 should round to 1. Worse case we loose 1 uint.
|
||||||
|
uint32_t _claimed[(Count + 31) / 32];
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline void forceClaim(int index) { _claimed[index / 32] |= (1 << (index % 32)); }
|
||||||
|
|
||||||
|
inline int tryClaim() {
|
||||||
|
for (int i = 0; i < Count; ++i) {
|
||||||
|
auto bit = _claimed[i / 32] & (uint32_t(1) << (i % 32));
|
||||||
|
if (bit == 0) {
|
||||||
|
_claimed[i / 32] |= (uint32_t(1) << (i % 32));
|
||||||
|
return bit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void release(int index) { _claimed[index / 32] &= ~(uint32_t(1) << (index % 32)); }
|
||||||
|
};
|
||||||
|
}
|
107
Grbl_Esp32/src/PinUsers/Uart.cpp
Normal file
107
Grbl_Esp32/src/PinUsers/Uart.cpp
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
#include "Uart.h"
|
||||||
|
|
||||||
|
#include "../Pin.h"
|
||||||
|
#include "../Pins/PinOptionsParser.h"
|
||||||
|
#include "../Assert.h"
|
||||||
|
#include "LimitedResource.h"
|
||||||
|
#include <driver/uart.h>
|
||||||
|
|
||||||
|
namespace PinUsers {
|
||||||
|
|
||||||
|
// Native UART in the ESP32
|
||||||
|
class NativeUart : public UartDetail {
|
||||||
|
static LimitedResource<2>& UartResources() {
|
||||||
|
// The ESP32 chip has three UART controllers (UART0, UART1, and UART2), but
|
||||||
|
// UART0 is used for Serial0. In other words, we can only make 1 and 2 available.
|
||||||
|
|
||||||
|
static LimitedResource<2> instances_;
|
||||||
|
return instances_;
|
||||||
|
}
|
||||||
|
|
||||||
|
Pin tx_;
|
||||||
|
Pin rx_;
|
||||||
|
Pin rts_;
|
||||||
|
|
||||||
|
uart_port_t uartPort_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
NativeUart(Pin tx, Pin rx, Pin rts, Pins::PinOptionsParser& options, Pins::PinOptionsParser& userOptions) :
|
||||||
|
tx_(tx), rx_(rx), rts_(rts), uartPort_(UART_NUM_MAX) {
|
||||||
|
// Validate if claiming the resources will err:
|
||||||
|
Assert(tx.capabilities().has(Pin::Capabilities::Native | Pin::Capabilities::UART | Pin::Capabilities::Output));
|
||||||
|
Assert(rx.capabilities().has(Pin::Capabilities::Native | Pin::Capabilities::UART | Pin::Capabilities::Input));
|
||||||
|
Assert(rts.capabilities().has(Pin::Capabilities::Native | Pin::Capabilities::UART | Pin::Capabilities::Output));
|
||||||
|
|
||||||
|
// Iterate options:
|
||||||
|
uart_config_t uart_config = { 0 };
|
||||||
|
int bufferSize = 128;
|
||||||
|
for (auto opt : options) {
|
||||||
|
// if (opt.is("baud")) { baudRate = opt.valueInt(); }
|
||||||
|
// if (opt.is("mode")) { mode = opt.valueString(); /* 8N1 etc */ }
|
||||||
|
// if (opt.is("bufsize")) { mode = opt.valueString(); /* 128 etc */ }
|
||||||
|
// if (opt.is("protocol")) { protocol= opt.valueString(); /* RS485 etc */ }
|
||||||
|
// etc...
|
||||||
|
}
|
||||||
|
for (auto opt : userOptions) {
|
||||||
|
// if (opt.is("baud")) { baudRate = opt.valueInt(); }
|
||||||
|
// if (opt.is("mode")) { mode = opt.valueString(); /* 8N1 etc */ }
|
||||||
|
// etc...
|
||||||
|
}
|
||||||
|
|
||||||
|
int uartIndex = UartResources().tryClaim();
|
||||||
|
Assert(uartIndex >= 0);
|
||||||
|
uartPort_ = static_cast<uart_port_t>(uartIndex + 1);
|
||||||
|
|
||||||
|
// We have everything we need. Set it up:
|
||||||
|
|
||||||
|
auto txn = tx_.getNative(Pin::Capabilities::UART | Pin::Capabilities::Output);
|
||||||
|
auto rxn = rx_.getNative(Pin::Capabilities::UART | Pin::Capabilities::Output);
|
||||||
|
auto rtsn = rts_.getNative(Pin::Capabilities::UART | Pin::Capabilities::Output);
|
||||||
|
|
||||||
|
Assert(uart_param_config(uartPort_, &uart_config) == ESP_OK, "Uart parameter set failed.");
|
||||||
|
Assert(uart_set_pin(uartPort_, txn, rxn, rtsn, UART_PIN_NO_CHANGE) == ESP_OK, "Uart parameter set failed.");
|
||||||
|
Assert(uart_driver_install(uartPort_, bufferSize * 2, 0, 0, NULL, 0) == ESP_OK, "Uart driver install failed.");
|
||||||
|
|
||||||
|
// TODO FIXME: We should set the UART mode somewhere better suited than here:
|
||||||
|
if (uart_set_mode(uartPort_, UART_MODE_RS485_HALF_DUPLEX) != ESP_OK) {
|
||||||
|
uart_driver_delete(uartPort_);
|
||||||
|
uartPort_ = UART_NUM_MAX;
|
||||||
|
|
||||||
|
Assert(false, "UART set mode failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int write(const uint8_t* ptr, int bytes) override {
|
||||||
|
// Flush the UART and write the data:
|
||||||
|
uart_flush(uartPort_);
|
||||||
|
return uart_write_bytes(uartPort_, reinterpret_cast<const char*>(ptr), bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
int read(uint8_t* ptr, int bytes, int ticksToWait) override {
|
||||||
|
// Read the response
|
||||||
|
return uart_read_bytes(uartPort_, ptr, bytes, ticksToWait);
|
||||||
|
}
|
||||||
|
|
||||||
|
~NativeUart() override {
|
||||||
|
// Tear down the uart, give back all resources.
|
||||||
|
if (uartPort_ != UART_NUM_MAX) {
|
||||||
|
uart_driver_delete(uartPort_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Uart::Uart(Pin tx, Pin rx, Pin rts, String config, String userConfig) {
|
||||||
|
Pins::PinOptionsParser configParser(config.begin(), config.end());
|
||||||
|
Pins::PinOptionsParser userConfigParser(userConfig.begin(), userConfig.end());
|
||||||
|
|
||||||
|
// Decide on the RX pin what to do:
|
||||||
|
if (rx.capabilities().has(Pin::Capabilities::Native))
|
||||||
|
{
|
||||||
|
this->_detail = new NativeUart(tx, rx, rts, configParser, userConfigParser);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Assert(false, "Pin is not supported for UART.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
125
Grbl_Esp32/src/PinUsers/Uart.h
Normal file
125
Grbl_Esp32/src/PinUsers/Uart.h
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../Pin.h"
|
||||||
|
#include <WString.h>
|
||||||
|
#include "../Assert.h"
|
||||||
|
|
||||||
|
namespace PinUsers {
|
||||||
|
class UartDetail {
|
||||||
|
public:
|
||||||
|
virtual int write(const uint8_t* ptr, int bytes) = 0;
|
||||||
|
virtual int read(uint8_t* ptr, int bytes, int ticksToWait) = 0;
|
||||||
|
|
||||||
|
virtual ~UartDetail() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Uart {
|
||||||
|
Pin _tx;
|
||||||
|
Pin _rx;
|
||||||
|
Pin _rts;
|
||||||
|
|
||||||
|
UartDetail* _detail;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Uart() : _tx(Pin::UNDEFINED), _rx(Pin::UNDEFINED), _rts(Pin::UNDEFINED), _detail(nullptr) {}
|
||||||
|
Uart(Pin tx, Pin rx, Pin rts, String config, String userConfig);
|
||||||
|
|
||||||
|
// TODO FIXME: If _detail is null is currently asserted. We can also just 'do nothing', which might
|
||||||
|
// be easier in the application.
|
||||||
|
|
||||||
|
// Writes a buffer to the uart. Returns the number of _bytes_ written
|
||||||
|
template <typename T>
|
||||||
|
int writePartial(const T& buffer, int byteOffset = 0) {
|
||||||
|
writePartial(&buffer, 1, byteOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writes a buffer to the uart. Returns the number of _bytes_ written
|
||||||
|
template <typename T>
|
||||||
|
int writePartial(const T* bufferArray, int numberElements, int byteOffset = 0) {
|
||||||
|
Assert(_detail != nullptr, "Uart is not initialized; cannot write to it.");
|
||||||
|
auto byteBuf = reinterpret_cast<const uint8_t*>(bufferArray);
|
||||||
|
return _detail->write(byteBuf + byteOffset, sizeof(T) * numberElements - byteOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writes a buffer to the uart. This continues while it can write. If writing succeeded, true is
|
||||||
|
// returned, otherwise false.
|
||||||
|
template <typename T>
|
||||||
|
bool write(const T& buffer) {
|
||||||
|
return write(&buffer, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writes a buffer to the uart. This continues while it can write. If writing succeeded, true is
|
||||||
|
// returned, otherwise false.
|
||||||
|
template <typename T>
|
||||||
|
bool write(const T* buffer, int numberElements) {
|
||||||
|
auto index = 0;
|
||||||
|
auto limit = sizeof(T) * numberElements;
|
||||||
|
while (index < limit) {
|
||||||
|
int size = writePartial(buffer, numberElements, index);
|
||||||
|
index += size;
|
||||||
|
|
||||||
|
if (size == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads a buffer from the uart. If ticks elapsed, stops reading. Returns the number of bytes
|
||||||
|
// that was read.
|
||||||
|
template <typename T>
|
||||||
|
int readPartial(T* bufferArray, int numberElements, int offset = 0, int ticksToWait = 0) {
|
||||||
|
Assert(_detail != nullptr, "Uart is not initialized; cannot write to it.");
|
||||||
|
|
||||||
|
auto byteArray = reinterpret_cast<uint8_t*>(bufferArray) + offset;
|
||||||
|
auto bytes = sizeof(T) * numberElements - offset;
|
||||||
|
return _detail->read(byteArray, bytes, ticksToWait);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads a buffer from the uart. If ticks elapsed, stops reading. Returns the number of bytes
|
||||||
|
// that was read.
|
||||||
|
template <typename T>
|
||||||
|
int readPartial(T& buffer, int offset = 0, int ticksToWait = 0) {
|
||||||
|
return readPartial(&buffer, 1, offset, ticksToWait);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads a buffer from the uart within the given time. The return value is 'true' if this succeeded
|
||||||
|
// and 'false' if it failed or gave a partial result.
|
||||||
|
template <typename T>
|
||||||
|
bool readOnce(T* bufferArray, int numberElements, int ticksToWait = 0) {
|
||||||
|
auto bytesRead = readPartial(bufferArray, numberElements, 0, ticksToWait);
|
||||||
|
return bytesRead == (numberElements * sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads a buffer from the uart within the given time. The return value is 'true' if this succeeded
|
||||||
|
// and 'false' if it failed or gave a partial result.
|
||||||
|
template <typename T>
|
||||||
|
bool readOnce(T& buffer, int ticksToWait = 0) {
|
||||||
|
return readOnce(&buffer, 1, ticksToWait);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads a buffer from the uart. While data is returned, this keeps on reading until the buffer is
|
||||||
|
// full. If no data is returned anymore, or if the buffer is full, this returns 'true' if successul,
|
||||||
|
// or 'false' otherwise
|
||||||
|
template <typename T>
|
||||||
|
bool readBlocking(T* bufferArray, int numberElements, int ticksToWait = 0) {
|
||||||
|
int offset = 0;
|
||||||
|
int limit = sizeof(T) * numberElements;
|
||||||
|
while (offset < limit) {
|
||||||
|
auto bytesRead = readPartial(bufferArray, numberElements, offset, ticksToWait);
|
||||||
|
if (bytesRead == 0) { break; }
|
||||||
|
offset += bytesRead;
|
||||||
|
}
|
||||||
|
return offset == limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads a buffer from the uart within the given time. The return value is 'true' if this succeeded
|
||||||
|
// and 'false' if it failed or gave a partial result.
|
||||||
|
template <typename T>
|
||||||
|
bool readBlocking(T& buffer, int offset = 0, int ticksToWait = 0) {
|
||||||
|
return readBlocking(&buffer, 1, ticksToWait);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ~Uart() { delete _detail; }
|
||||||
|
};
|
||||||
|
}
|
@@ -80,7 +80,7 @@ void TestFactory::runAll() {
|
|||||||
|
|
||||||
current->run();
|
current->run();
|
||||||
printf("Passed.\r\n");
|
printf("Passed.\r\n");
|
||||||
} catch (AssertionFailed& ex) {
|
} catch (const AssertionFailed& ex) {
|
||||||
setColor(12);
|
setColor(12);
|
||||||
printf("FAILED!\r\n");
|
printf("FAILED!\r\n");
|
||||||
printf(ex.stackTrace.c_str());
|
printf(ex.stackTrace.c_str());
|
||||||
|
Reference in New Issue
Block a user