1
0
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:
Stefan de Bruijn
2020-10-24 11:37:25 +02:00
parent 2822cc5268
commit 10151863d7
6 changed files with 271 additions and 3 deletions

View File

@@ -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());
} }

View File

@@ -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());

View 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)); }
};
}

View 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.");
}
}
}

View 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; }
};
}

View File

@@ -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());