Add kernel drivers for SPI and UART and make locking APIs more consistent (#489)

- Add kernel support for SPI driver
- Add kernel support for UART driver
- Implemented ESP32 UART kernel driver
- Update existing UART-related code in Tactility to use new kernel driver
- Remove UART from tt::hal::Configuration
- Remove tt_hal_uart functionality but keep functions for now
- Update devicetrees for UART changes
- Kernel mutex and recursive mutex: improved locking API design
- Other kernel improvements
- Added device_exists_of_type() and device_find_by_name()
This commit is contained in:
Ken Van Hoeylandt
2026-02-07 21:28:11 +01:00
committed by GitHub
parent 7e24105d0c
commit 74127a5f6c
119 changed files with 1679 additions and 1792 deletions
-3
View File
@@ -3,9 +3,7 @@
#include <tactility/check.h>
#include <Tactility/hal/Configuration.h>
#include <tactility/hal/Device.h>
#include <Tactility/hal/power/PowerDevice.h>
#include <Tactility/hal/spi/SpiInit.h>
#include <Tactility/hal/uart/UartInit.h>
#include <Tactility/hal/display/DisplayDevice.h>
#include <Tactility/hal/sdcard/SdCardMounting.h>
@@ -67,7 +65,6 @@ void init(const Configuration& configuration) {
kernel::publishSystemEvent(kernel::SystemEvent::BootInitHalBegin);
check(spi::init(configuration.spi), "SPI init failed");
check(uart::init(configuration.uart), "UART init failed");
if (configuration.initBoot != nullptr) {
check(configuration.initBoot(), "Init boot failed");
+23 -11
View File
@@ -1,9 +1,11 @@
#include <Tactility/hal/gps/GpsDevice.h>
#include <Tactility/hal/gps/GpsInit.h>
#include <Tactility/hal/gps/Probe.h>
#include <Tactility/hal/uart/Uart.h>
#include <Tactility/Logger.h>
#include <tactility/device.h>
#include <tactility/drivers/uart_controller.h>
#include <cstring>
#include <minmea.h>
@@ -16,25 +18,34 @@ static const auto LOGGER = Logger("GpsDevice");
int32_t GpsDevice::threadMain() {
uint8_t buffer[GPS_UART_BUFFER_SIZE];
auto uart = uart::open(configuration.uartName);
auto* uart = device_find_by_name(configuration.uartName);
if (uart == nullptr) {
LOGGER.error("Failed to open UART {}", configuration.uartName);
LOGGER.error("Failed to find UART {}", configuration.uartName);
return -1;
}
if (!uart->start()) {
LOGGER.error("Failed to start UART {}", configuration.uartName);
struct UartConfig uartConfig = {
.baud_rate = configuration.baudRate,
.data_bits = UART_CONTROLLER_DATA_8_BITS,
.parity = UART_CONTROLLER_PARITY_DISABLE,
.stop_bits = UART_CONTROLLER_STOP_BITS_1
};
error_t error = uart_controller_set_config(uart, &uartConfig);
if (error != ERROR_NONE) {
LOGGER.error("Failed to configure UART {}: {}", configuration.uartName, error_to_string(error));
return -1;
}
if (!uart->setBaudRate(static_cast<int>(configuration.baudRate))) {
LOGGER.error("Failed to set baud rate to {} for UART {}", configuration.baudRate, configuration.uartName);
error = uart_controller_open(uart);
if (error != ERROR_NONE) {
LOGGER.error("Failed to open UART {}: {}", configuration.uartName, error_to_string(error));
return -1;
}
GpsModel model = configuration.model;
if (model == GpsModel::Unknown) {
model = probe(*uart);
model = probe(uart);
if (model == GpsModel::Unknown) {
LOGGER.error("Probe failed");
setState(State::Error);
@@ -45,7 +56,7 @@ int32_t GpsDevice::threadMain() {
this->model = model;
mutex.unlock();
if (!init(*uart, model)) {
if (!init(uart, model)) {
LOGGER.error("Init failed");
setState(State::Error);
return -1;
@@ -55,7 +66,8 @@ int32_t GpsDevice::threadMain() {
// Reference: https://gpsd.gitlab.io/gpsd/NMEA.html
while (!isThreadInterrupted()) {
size_t bytes_read = uart->readUntil(reinterpret_cast<std::byte*>(buffer), GPS_UART_BUFFER_SIZE, '\n', 100 / portTICK_PERIOD_MS);
size_t bytes_read = 0;
uart_controller_read_until(uart, buffer, GPS_UART_BUFFER_SIZE, '\n', true, &bytes_read, 100 / portTICK_PERIOD_MS);
// Thread might've been interrupted in the meanwhile
if (isThreadInterrupted()) {
@@ -103,7 +115,7 @@ int32_t GpsDevice::threadMain() {
}
}
if (uart->isStarted() && !uart->stop()) {
if (uart_controller_close(uart) != ERROR_NONE) {
LOGGER.warn("Failed to stop UART {}", configuration.uartName);
}
+51 -45
View File
@@ -5,18 +5,21 @@
#include <Tactility/hal/gps/Ublox.h>
#include <Tactility/kernel/Kernel.h>
#include <tactility/device.h>
#include <tactility/drivers/uart_controller.h>
#include <cstring>
namespace tt::hal::gps {
static const auto LOGGER = Logger("Gps");
bool initMtk(uart::Uart& uart);
bool initMtkL76b(uart::Uart& uart);
bool initMtkPa1616s(uart::Uart& uart);
bool initAtgm336h(uart::Uart& uart);
bool initUc6580(uart::Uart& uart);
bool initAg33xx(uart::Uart& uart);
bool initMtk(::Device* uart);
bool initMtkL76b(::Device* uart);
bool initMtkPa1616s(::Device* uart);
bool initAtgm336h(::Device* uart);
bool initUc6580(::Device* uart);
bool initAg33xx(::Device* uart);
// region CAS
@@ -73,11 +76,12 @@ static uint8_t makeCASPacket(uint8_t* buffer, uint8_t class_id, uint8_t msg_id,
return (payload_size + 10);
}
GpsResponse getACKCas(uart::Uart& uart, uint8_t class_id, uint8_t msg_id, uint32_t waitMillis)
GpsResponse getACKCas(::Device* uart, uint8_t class_id, uint8_t msg_id, uint32_t waitMillis)
{
uint32_t startTime = kernel::getMillis();
uint8_t buffer[CAS_ACK_NACK_MSG_SIZE] = {0};
uint8_t bufferPos = 0;
TickType_t waitTicks = pdMS_TO_TICKS(waitMillis);
// CAS-ACK-(N)ACK structure
// | H1 | H2 | Payload Len | cls | msg | Payload | Checksum (4) |
@@ -86,9 +90,11 @@ GpsResponse getACKCas(uart::Uart& uart, uint8_t class_id, uint8_t msg_id, uint32
// ACK-NACK| 0xBA | 0xCE | 0x04 | 0x00 | 0x05 | 0x00 | 0xXX | 0xXX | 0x00 | 0x00 | 0xXX | 0xXX | 0xXX | 0xXX |
// ACK-ACK | 0xBA | 0xCE | 0x04 | 0x00 | 0x05 | 0x01 | 0xXX | 0xXX | 0x00 | 0x00 | 0xXX | 0xXX | 0xXX | 0xXX |
while (kernel::getTicks() - startTime < waitMillis) {
if (uart.available()) {
uart.readByte(&buffer[bufferPos++]);
while (kernel::getTicks() - startTime < waitTicks) {
size_t available = 0;
uart_controller_get_available(uart, &available);
if (available > 0) {
uart_controller_read_byte(uart, &buffer[bufferPos++], 1);
// keep looking at the first two bytes of buffer until
// we have found the CAS frame header (0xBA, 0xCE), if not
@@ -136,7 +142,7 @@ GpsResponse getACKCas(uart::Uart& uart, uint8_t class_id, uint8_t msg_id, uint32
// endregion
bool init(uart::Uart& uart, GpsModel type) {
bool init(::Device* uart, GpsModel type) {
switch (type) {
case GpsModel::Unknown:
check(false);
@@ -167,58 +173,58 @@ bool init(uart::Uart& uart, GpsModel type) {
return false;
}
bool initAg33xx(uart::Uart& uart) {
uart.writeString("$PAIR066,1,0,1,0,0,1*3B\r\n"); // Enable GPS+GALILEO+NAVIC
bool initAg33xx(::Device* uart) {
uart_controller_write_bytes(uart, (const uint8_t*)"$PAIR066,1,0,1,0,0,1*3B\r\n", 25, 250); // Enable GPS+GALILEO+NAVIC
// Configure NMEA (sentences will output once per fix)
uart.writeString("$PAIR062,0,1*3F\r\n"); // GGA ON
uart.writeString("$PAIR062,1,0*3F\r\n"); // GLL OFF
uart.writeString("$PAIR062,2,0*3C\r\n"); // GSA OFF
uart.writeString("$PAIR062,3,0*3D\r\n"); // GSV OFF
uart.writeString("$PAIR062,4,1*3B\r\n"); // RMC ON
uart.writeString("$PAIR062,5,0*3B\r\n"); // VTG OFF
uart.writeString("$PAIR062,6,0*38\r\n"); // ZDA ON
uart_controller_write_bytes(uart, (const uint8_t*)"$PAIR062,0,1*3F\r\n", 17, 250); // GGA ON
uart_controller_write_bytes(uart, (const uint8_t*)"$PAIR062,1,0*3F\r\n", 17, 250); // GLL OFF
uart_controller_write_bytes(uart, (const uint8_t*)"$PAIR062,2,0*3C\r\n", 17, 250); // GSA OFF
uart_controller_write_bytes(uart, (const uint8_t*)"$PAIR062,3,0*3D\r\n", 17, 250); // GSV OFF
uart_controller_write_bytes(uart, (const uint8_t*)"$PAIR062,4,1*3B\r\n", 17, 250); // RMC ON
uart_controller_write_bytes(uart, (const uint8_t*)"$PAIR062,5,0*3B\r\n", 17, 250); // VTG OFF
uart_controller_write_bytes(uart, (const uint8_t*)"$PAIR062,6,0*38\r\n", 17, 250); // ZDA ON
kernel::delayMillis(250);
uart.writeString("$PAIR513*3D\r\n"); // save configuration
uart_controller_write_bytes(uart, (const uint8_t*)"$PAIR513*3D\r\n", 13, 250); // save configuration
return true;
}
bool initUc6580(uart::Uart& uart) {
bool initUc6580(::Device* uart) {
// The Unicore UC6580 can use a lot of sat systems, enable it to
// use GPS L1 & L5 + BDS B1I & B2a + GLONASS L1 + GALILEO E1 & E5a + SBAS + QZSS
// This will reset the receiver, so wait a bit afterwards
// The paranoid will wait for the OK*04 confirmation response after each command.
uart.writeString("$CFGSYS,h35155\r\n");
uart_controller_write_bytes(uart, (const uint8_t*)"$CFGSYS,h35155\r\n", 16, 250);
kernel::delayMillis(750);
// Must be done after the CFGSYS command
// Turn off GSV messages, we don't really care about which and where the sats are, maybe someday.
uart.writeString("$CFGMSG,0,3,0\r\n");
uart_controller_write_bytes(uart, (const uint8_t*)"$CFGMSG,0,3,0\r\n", 15, 250);
kernel::delayMillis(250);
// Turn off GSA messages, TinyGPS++ doesn't use this message.
uart.writeString("$CFGMSG,0,2,0\r\n");
uart_controller_write_bytes(uart, (const uint8_t*)"$CFGMSG,0,2,0\r\n", 15, 250);
kernel::delayMillis(250);
// Turn off NOTICE __TXT messages, these may provide Unicore some info but we don't care.
uart.writeString("$CFGMSG,6,0,0\r\n");
uart_controller_write_bytes(uart, (const uint8_t*)"$CFGMSG,6,0,0\r\n", 15, 250);
kernel::delayMillis(250);
uart.writeString("$CFGMSG,6,1,0\r\n");
uart_controller_write_bytes(uart, (const uint8_t*)"$CFGMSG,6,1,0\r\n", 15, 250);
kernel::delayMillis(250);
return true;
}
bool initAtgm336h(uart::Uart& uart) {
bool initAtgm336h(::Device* uart) {
uint8_t buffer[256];
// Set the intial configuration of the device - these _should_ work for most AT6558 devices
int msglen = makeCASPacket(buffer, 0x06, 0x07, sizeof(_message_CAS_CFG_NAVX_CONF), _message_CAS_CFG_NAVX_CONF);
uart.writeBytes(buffer, msglen);
uart_controller_write_bytes(uart, buffer, msglen, 250);
if (getACKCas(uart, 0x06, 0x07, 250) != GpsResponse::Ok) {
LOGGER.warn("ATGM336H: Could not set Config");
}
// Set the update frequence to 1Hz
msglen = makeCASPacket(buffer, 0x06, 0x04, sizeof(_message_CAS_CFG_RATE_1HZ), _message_CAS_CFG_RATE_1HZ);
uart.writeBytes(buffer, msglen);
uart_controller_write_bytes(uart, buffer, msglen, 250);
if (getACKCas(uart, 0x06, 0x04, 250) != GpsResponse::Ok) {
LOGGER.warn("ATGM336H: Could not set Update Frequency");
}
@@ -230,7 +236,7 @@ bool initAtgm336h(uart::Uart& uart) {
// Construct a CAS-CFG-MSG packet
uint8_t cas_cfg_msg_packet[] = {0x4e, fields[i], 0x01, 0x00};
msglen = makeCASPacket(buffer, 0x06, 0x01, sizeof(cas_cfg_msg_packet), cas_cfg_msg_packet);
uart.writeBytes(buffer, msglen);
uart_controller_write_bytes(uart, buffer, msglen, 250);
if (getACKCas(uart, 0x06, 0x01, 250) != GpsResponse::Ok) {
LOGGER.warn("ATGM336H: Could not enable NMEA MSG: {}", fields[i]);
}
@@ -238,53 +244,53 @@ bool initAtgm336h(uart::Uart& uart) {
return true;
}
bool initMtkPa1616s(uart::Uart& uart) {
bool initMtkPa1616s(::Device* uart) {
// PA1616S is used in some GPS breakout boards from Adafruit
// PA1616S does not have GLONASS capability. PA1616D does, but is not implemented here.
uart.writeString("$PMTK353,1,0,0,0,0*2A\r\n");
uart_controller_write_bytes(uart, (const uint8_t*)"$PMTK353,1,0,0,0,0*2A\r\n", 23, 250);
// Above command will reset the GPS and takes longer before it will accept new commands
kernel::delayMillis(1000);
// Only ask for RMC and GGA (GNRMC and GNGGA)
uart.writeString("$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28\r\n");
uart_controller_write_bytes(uart, (const uint8_t*)"$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28\r\n", 51, 250);
kernel::delayMillis(250);
// Enable SBAS / WAAS
uart.writeString("$PMTK301,2*2E\r\n");
uart_controller_write_bytes(uart, (const uint8_t*)"$PMTK301,2*2E\r\n", 15, 250);
kernel::delayMillis(250);
return true;
}
bool initMtkL76b(uart::Uart& uart) {
bool initMtkL76b(::Device* uart) {
// Waveshare Pico-GPS hat uses the L76B with 9600 baud
// Initialize the L76B Chip, use GPS + GLONASS
// See note in L76_Series_GNSS_Protocol_Specification, chapter 3.29
uart.writeString("$PMTK353,1,1,0,0,0*2B\r\n");
uart_controller_write_bytes(uart, (const uint8_t*)"$PMTK353,1,1,0,0,0*2B\r\n", 23, 250);
// Above command will reset the GPS and takes longer before it will accept new commands
kernel::delayMillis(1000);
// only ask for RMC and GGA (GNRMC and GNGGA)
// See note in L76_Series_GNSS_Protocol_Specification, chapter 2.1
uart.writeString("$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28\r\n");
uart_controller_write_bytes(uart, (const uint8_t*)"$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28\r\n", 51, 250);
kernel::delayMillis(250);
// Enable SBAS
uart.writeString("$PMTK301,2*2E\r\n");
uart_controller_write_bytes(uart, (const uint8_t*)"$PMTK301,2*2E\r\n", 15, 250);
kernel::delayMillis(250);
// Enable PPS for 2D/3D fix only
uart.writeString("$PMTK285,3,100*3F\r\n");
uart_controller_write_bytes(uart, (const uint8_t*)"$PMTK285,3,100*3F\r\n", 19, 250);
kernel::delayMillis(250);
// Switch to Fitness Mode, for running and walking purpose with low speed (<5 m/s)
uart.writeString("$PMTK886,1*29\r\n");
uart_controller_write_bytes(uart, (const uint8_t*)"$PMTK886,1*29\r\n", 15, 250);
kernel::delayMillis(250);
return true;
}
bool initMtk(uart::Uart& uart) {
bool initMtk(::Device* uart) {
// Initialize the L76K Chip, use GPS + GLONASS + BEIDOU
uart.writeString("$PCAS04,7*1E\r\n");
uart_controller_write_bytes(uart, (const uint8_t*)"$PCAS04,7*1E\r\n", 14, 250);
kernel::delayMillis(250);
// only ask for RMC and GGA
uart.writeString("$PCAS03,1,0,0,0,1,0,0,0,0,0,,,0,0*02\r\n");
uart_controller_write_bytes(uart, (const uint8_t*)"$PCAS03,1,0,0,0,1,0,0,0,0,0,,,0,0*02\r\n", 38, 250);
kernel::delayMillis(250);
// Switch to Vehicle Mode, since SoftRF enables Aviation < 2g
uart.writeString("$PCAS11,3*1E\r\n");
uart_controller_write_bytes(uart, (const uint8_t*)"$PCAS11,3*1E\r\n", 14, 250);
kernel::delayMillis(250);
return true;
}
+20 -16
View File
@@ -1,9 +1,11 @@
#include "Tactility/hal/gps/GpsDevice.h"
#include "Tactility/hal/gps/Ublox.h"
#include <Tactility/Logger.h>
#include <Tactility/hal/uart/Uart.h>
#include <Tactility/kernel/Kernel.h>
#include <tactility/device.h>
#include <tactility/drivers/uart_controller.h>
#include <cstring>
static const auto LOGGER = tt::Logger("Gps");
@@ -41,7 +43,7 @@ char* strnstr(const char* s, const char* find, size_t slen) {
/**
* From: https://github.com/meshtastic/firmware/blob/f81d3b045dd1b7e3ca7870af3da915ff4399ea98/src/gps/GPS.cpp
*/
GpsResponse getAck(uart::Uart& uart, const char* message, uint32_t waitMillis) {
GpsResponse getAck(::Device* uart, const char* message, uint32_t waitMillis) {
uint8_t buffer[768] = {0};
uint8_t b;
int bytesRead = 0;
@@ -50,8 +52,10 @@ GpsResponse getAck(uart::Uart& uart, const char* message, uint32_t waitMillis) {
std::string debugmsg = "";
#endif
while (kernel::getMillis() < startTimeout) {
if (uart.available()) {
uart.readByte(&b);
size_t available = 0;
uart_controller_get_available(uart, &available);
if (available > 0) {
uart_controller_read_byte(uart, &b, 1);
#ifdef GPS_DEBUG
debugmsg += vformat("%c", (b >= 32 && b <= 126) ? b : '.');
@@ -82,8 +86,8 @@ GpsResponse getAck(uart::Uart& uart, const char* message, uint32_t waitMillis) {
#define PROBE_SIMPLE(UART, CHIP, TOWRITE, RESPONSE, DRIVER, TIMEOUT, ...) \
do { \
LOGGER.info("Probing for {} ({})", CHIP, TOWRITE); \
UART.flushInput(); \
UART.writeString(TOWRITE "\r\n", TIMEOUT); \
uart_controller_flush_input(UART); \
uart_controller_write_bytes(UART, (const uint8_t*)(TOWRITE "\r\n"), strlen(TOWRITE "\r\n"), TIMEOUT); \
if (getAck(UART, RESPONSE, TIMEOUT) == GpsResponse::Ok) { \
LOGGER.info("Probe detected {} {}", CHIP, #DRIVER); \
return DRIVER; \
@@ -93,15 +97,15 @@ GpsResponse getAck(uart::Uart& uart, const char* message, uint32_t waitMillis) {
/**
* From: https://github.com/meshtastic/firmware/blob/f81d3b045dd1b7e3ca7870af3da915ff4399ea98/src/gps/GPS.cpp
*/
GpsModel probe(uart::Uart& uart) {
GpsModel probe(::Device* uart) {
// Close all NMEA sentences, valid for L76K, ATGM336H (and likely other AT6558 devices)
uart.writeString("$PCAS03,0,0,0,0,0,0,0,0,0,0,,,0,0*02\r\n");
uart_controller_write_bytes(uart, (const uint8_t*)"$PCAS03,0,0,0,0,0,0,0,0,0,0,,,0,0*02\r\n", 40, 500);
kernel::delayMillis(20);
// Close NMEA sequences on Ublox
uart.writeString("$PUBX,40,GLL,0,0,0,0,0,0*5C\r\n");
uart.writeString("$PUBX,40,GSV,0,0,0,0,0,0*59\r\n");
uart.writeString("$PUBX,40,VTG,0,0,0,0,0,0*5E\r\n");
uart_controller_write_bytes(uart, (const uint8_t*)"$PUBX,40,GLL,0,0,0,0,0,0*5C\r\n", 29, 500);
uart_controller_write_bytes(uart, (const uint8_t*)"$PUBX,40,GSV,0,0,0,0,0,0*59\r\n", 29, 500);
uart_controller_write_bytes(uart, (const uint8_t*)"$PUBX,40,VTG,0,0,0,0,0,0*5E\r\n", 29, 500);
kernel::delayMillis(20);
// Unicore UFirebirdII Series: UC6580, UM620, UM621, UM670A, UM680A, or UM681A
@@ -114,9 +118,9 @@ GpsModel probe(uart::Uart& uart) {
PROBE_SIMPLE(uart, "ATGM332D", "$PCAS06,1*1A", "$GPTXT,01,01,02,HW=ATGM332D", GpsModel::ATGM336H, 500);
/* Airoha (Mediatek) AG3335A/M/S, A3352Q, Quectel L89 2.0, SimCom SIM65M */
uart.writeString("$PAIR062,2,0*3C\r\n"); // GSA OFF to reduce volume
uart.writeString("$PAIR062,3,0*3D\r\n"); // GSV OFF to reduce volume
uart.writeString("$PAIR513*3D\r\n"); // save configuration
uart_controller_write_bytes(uart, (const uint8_t*)"$PAIR062,2,0*3C\r\n", 17, 500); // GSA OFF to reduce volume
uart_controller_write_bytes(uart, (const uint8_t*)"$PAIR062,3,0*3D\r\n", 17, 500); // GSV OFF to reduce volume
uart_controller_write_bytes(uart, (const uint8_t*)"$PAIR513*3D\r\n", 13, 500); // save configuration
PROBE_SIMPLE(uart, "AG3335", "$PAIR021*39", "$PAIR021,AG3335", GpsModel::AG3335, 500);
PROBE_SIMPLE(uart, "AG3352", "$PAIR021*39", "$PAIR021,AG3352", GpsModel::AG3352, 500);
PROBE_SIMPLE(uart, "LC86", "$PQTMVERNO*58", "$PQTMVERNO,LC86", GpsModel::AG3352, 500);
@@ -124,7 +128,7 @@ GpsModel probe(uart::Uart& uart) {
PROBE_SIMPLE(uart, "L76K", "$PCAS06,0*1B", "$GPTXT,01,01,02,SW=", GpsModel::MTK, 500);
// Close all NMEA sentences, valid for L76B MTK platform (Waveshare Pico GPS)
uart.writeString("$PMTK514,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*2E\r\n");
uart_controller_write_bytes(uart, (const uint8_t*)"$PMTK514,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*2E\r\n", 51, 500);
kernel::delayMillis(20);
PROBE_SIMPLE(uart, "L76B", "$PMTK605*31", "Quectel-L76B", GpsModel::MTK_L76B, 500);
@@ -134,7 +138,7 @@ GpsModel probe(uart::Uart& uart) {
if (ublox_result != GpsModel::Unknown) {
return ublox_result;
} else {
LOGGER.warn("No GNSS Module (baud rate {})", uart.getBaudRate());
LOGGER.warn("No GNSS Module");
return GpsModel::Unknown;
}
}
+55 -45
View File
@@ -1,24 +1,26 @@
#include <Tactility/hal/gps/Ublox.h>
#include <Tactility/hal/gps/UbloxMessages.h>
#include <Tactility/hal/uart/Uart.h>
#include <Tactility/kernel/Kernel.h>
#include <Tactility/Logger.h>
#include <tactility/device.h>
#include <tactility/drivers/uart_controller.h>
#include <cstring>
namespace tt::hal::gps::ublox {
static const auto LOGGER = Logger("Ublox");
bool initUblox6(uart::Uart& uart);
bool initUblox789(uart::Uart& uart, GpsModel model);
bool initUblox10(uart::Uart& uart);
bool initUblox6(::Device* uart);
bool initUblox789(::Device* uart, GpsModel model);
bool initUblox10(::Device* uart);
#define SEND_UBX_PACKET(UART, BUFFER, TYPE, ID, DATA, ERRMSG, TIMEOUT) \
#define SEND_UBX_PACKET(UART, BUFFER, TYPE, ID, DATA, ERRMSG, TIMEOUT_MILLIS) \
do { \
auto msglen = makePacket(TYPE, ID, DATA, sizeof(DATA), BUFFER); \
UART.writeBytes(BUFFER, sizeof(BUFFER)); \
if (getAck(UART, TYPE, ID, TIMEOUT) != GpsResponse::Ok) { \
uart_controller_write_bytes(UART, BUFFER, msglen, TIMEOUT_MILLIS / portTICK_PERIOD_MS); \
if (getAck(UART, TYPE, ID, TIMEOUT_MILLIS) != GpsResponse::Ok) { \
LOGGER.info("Sending packet failed: {}", #ERRMSG); \
} \
} while (0)
@@ -56,12 +58,13 @@ uint8_t makePacket(uint8_t classId, uint8_t messageId, const uint8_t* payload, u
return (payloadSize + 8U);
}
GpsResponse getAck(uart::Uart& uart, uint8_t class_id, uint8_t msg_id, uint32_t waitMillis) {
GpsResponse getAck(::Device* uart, uint8_t class_id, uint8_t msg_id, uint32_t waitMillis) {
uint8_t b;
uint8_t ack = 0;
const uint8_t ackP[2] = {class_id, msg_id};
uint8_t buf[10] = {0xB5, 0x62, 0x05, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00};
uint32_t startTime = kernel::getMillis();
uint32_t startTime = kernel::getTicks();
TickType_t waitTicks = pdMS_TO_TICKS(waitMillis);
const char frame_errors[] = "More than 100 frame errors";
int sCounter = 0;
#ifdef GPS_DEBUG
@@ -79,15 +82,17 @@ GpsResponse getAck(uart::Uart& uart, uint8_t class_id, uint8_t msg_id, uint32_t
buf[9] += buf[8];
}
while (kernel::getTicks() - startTime < waitMillis) {
while (kernel::getTicks() - startTime < waitTicks) {
if (ack > 9) {
#ifdef GPS_DEBUG
LOGGER.info("Got ACK for class {:02X} message {:02X} in {}ms", class_id, msg_id, kernel::getMillis() - startTime);
#endif
return GpsResponse::Ok; // ACK received
}
if (uart.available()) {
uart.readByte(&b);
size_t available = 0;
uart_controller_get_available(uart, &available);
if (available > 0) {
uart_controller_read_byte(uart, &b, 1);
if (b == frame_errors[sCounter]) {
sCounter++;
if (sCounter == 26) {
@@ -124,15 +129,19 @@ GpsResponse getAck(uart::Uart& uart, uint8_t class_id, uint8_t msg_id, uint32_t
return GpsResponse::None; // No response received within timeout
}
static int getAck(uart::Uart& uart, uint8_t* buffer, uint16_t size, uint8_t requestedClass, uint8_t requestedId, TickType_t timeout) {
static int getAck(::Device* uart, uint8_t* buffer, uint16_t size, uint8_t requestedClass, uint8_t requestedId, uint32_t timeoutMillis) {
uint16_t ubxFrameCounter = 0;
uint32_t startTime = kernel::getTicks();
TickType_t startTime = kernel::getTicks();
TickType_t timeoutTicks = pdMS_TO_TICKS(timeoutMillis);
uint16_t needRead = 0;
while (kernel::getTicks() - startTime < timeout) {
while (uart.available()) {
while ((kernel::getTicks() - startTime) < timeoutTicks) {
size_t available = 0;
uart_controller_get_available(uart, &available);
while (available > 0) {
uint8_t c;
uart.readByte(&c);
uart_controller_read_byte(uart, &c, 1);
available--;
switch (ubxFrameCounter) {
case 0:
@@ -174,7 +183,8 @@ static int getAck(uart::Uart& uart, uint8_t* buffer, uint16_t size, uint8_t requ
ubxFrameCounter = 0;
break;
}
auto read_bytes = uart.readBytes(buffer, needRead, 250 / portTICK_PERIOD_MS);
auto read_bytes = 0U;
uart_controller_read_bytes(uart, buffer, needRead, 250 / portTICK_PERIOD_MS);
if (read_bytes != needRead) {
ubxFrameCounter = 0;
} else {
@@ -203,21 +213,21 @@ static struct uBloxGnssModelInfo {
uint8_t protocol_version;
} ublox_info;
GpsModel probe(uart::Uart& uart) {
GpsModel probe(::Device* uart) {
LOGGER.info("Probing for U-blox");
constexpr auto DETECTED_MESSAGE = "{} detected, using {} Module";
uint8_t cfg_rate[] = {0xB5, 0x62, 0x06, 0x08, 0x00, 0x00, 0x00, 0x00};
checksum(cfg_rate, sizeof(cfg_rate));
uart.flushInput();
uart.writeBytes(cfg_rate, sizeof(cfg_rate));
uart_controller_flush_input(uart);
uart_controller_write_bytes(uart, cfg_rate, sizeof(cfg_rate), 500 / portTICK_PERIOD_MS);
// Check that the returned response class and message ID are correct
GpsResponse response = getAck(uart, 0x06, 0x08, 750);
if (response == GpsResponse::None) {
LOGGER.warn("No GNSS Module (baudrate {})", uart.getBaudRate());
LOGGER.warn("No GNSS Module");
return GpsModel::Unknown;
} else if (response == GpsResponse::FrameErrors) {
LOGGER.warn("UBlox Frame Errors (baudrate {})", uart.getBaudRate());
LOGGER.warn("UBlox Frame Errors");
}
uint8_t buffer[256];
@@ -230,8 +240,8 @@ GpsModel probe(uart::Uart& uart) {
};
// Get Ublox gnss module hardware and software info
checksum(_message_MONVER, sizeof(_message_MONVER));
uart.flushInput();
uart.writeBytes(_message_MONVER, sizeof(_message_MONVER));
uart_controller_flush_input(uart);
uart_controller_write_bytes(uart, _message_MONVER, sizeof(_message_MONVER), 500);
uint16_t ack_response_len = getAck(uart, buffer, sizeof(buffer), 0x0A, 0x04, 1200);
if (ack_response_len) {
@@ -303,7 +313,7 @@ GpsModel probe(uart::Uart& uart) {
return GpsModel::Unknown;
}
bool init(uart::Uart& uart, GpsModel model) {
bool init(::Device* uart, GpsModel model) {
LOGGER.info("U-blox init");
switch (model) {
case GpsModel::UBLOX6:
@@ -320,19 +330,19 @@ bool init(uart::Uart& uart, GpsModel model) {
}
}
bool initUblox10(uart::Uart& uart) {
bool initUblox10(::Device* uart) {
uint8_t buffer[256];
kernel::delayMillis(1000);
uart.flushInput();
uart_controller_flush_input(uart);
SEND_UBX_PACKET(uart, buffer, 0x06, 0x8A, _message_VALSET_DISABLE_NMEA_RAM, "disable NMEA messages in M10 RAM", 300);
kernel::delayMillis(750);
uart.flushInput();
uart_controller_flush_input(uart);
SEND_UBX_PACKET(uart, buffer, 0x06, 0x8A, _message_VALSET_DISABLE_NMEA_BBR, "disable NMEA messages in M10 BBR", 300);
kernel::delayMillis(750);
uart.flushInput();
uart_controller_flush_input(uart);
SEND_UBX_PACKET(uart, buffer, 0x06, 0x8A, _message_VALSET_DISABLE_TXT_INFO_RAM, "disable Info messages for M10 GPS RAM", 300);
kernel::delayMillis(750);
uart.flushInput();
uart_controller_flush_input(uart);
SEND_UBX_PACKET(uart, buffer, 0x06, 0x8A, _message_VALSET_DISABLE_TXT_INFO_BBR, "disable Info messages for M10 GPS BBR", 300);
kernel::delayMillis(750);
SEND_UBX_PACKET(uart, buffer, 0x06, 0x8A, _message_VALSET_PM_RAM, "enable powersave for M10 GPS RAM", 300);
@@ -362,7 +372,7 @@ bool initUblox10(uart::Uart& uart) {
// BBR will survive a restart, and power off for a while, but modules with small backup
// batteries or super caps will not retain the config for a long power off time.
auto packet_size = makePacket(0x06, 0x09, _message_SAVE_10, sizeof(_message_SAVE_10), buffer);
uart.writeBytes(buffer, packet_size);
uart_controller_write_bytes(uart, buffer, packet_size, 2000 / portTICK_PERIOD_MS);
if (getAck(uart, 0x06, 0x09, 2000) != GpsResponse::Ok) {
LOGGER.warn("Unable to save GNSS module config");
} else {
@@ -371,15 +381,15 @@ bool initUblox10(uart::Uart& uart) {
return true;
}
bool initUblox789(uart::Uart& uart, GpsModel model) {
bool initUblox789(::Device* uart, GpsModel model) {
uint8_t buffer[256];
if (model == GpsModel::UBLOX7) {
LOGGER.debug("Set GPS+SBAS");
auto msglen = makePacket(0x06, 0x3e, _message_GNSS_7, sizeof(_message_GNSS_7), buffer);
uart.writeBytes(buffer, msglen);
uart_controller_write_bytes(uart, buffer, msglen, 800 / portTICK_PERIOD_MS);
} else { // 8,9
auto msglen = makePacket(0x06, 0x3e, _message_GNSS_8, sizeof(_message_GNSS_8), buffer);
uart.writeBytes(buffer, msglen);
uart_controller_write_bytes(uart, buffer, msglen, 800 / portTICK_PERIOD_MS);
}
if (getAck(uart, 0x06, 0x3e, 800) == GpsResponse::NotAck) {
@@ -396,15 +406,15 @@ bool initUblox789(uart::Uart& uart, GpsModel model) {
kernel::delayMillis(1000);
}
uart.flushInput();
uart_controller_flush_input(uart);
SEND_UBX_PACKET(uart, buffer, 0x06, 0x02, _message_DISABLE_TXT_INFO, "disable text info messages", 500);
if (model == GpsModel::UBLOX8) { // 8
uart.flushInput();
uart_controller_flush_input(uart);
SEND_UBX_PACKET(uart, buffer, 0x06, 0x39, _message_JAM_8, "enable interference resistance", 500);
uart.flushInput();
uart_controller_flush_input(uart);
SEND_UBX_PACKET(uart, buffer, 0x06, 0x23, _message_NAVX5_8, "configure NAVX5_8 settings", 500);
} else { // 6,7,9
SEND_UBX_PACKET(uart, buffer, 0x06, 0x39, _message_JAM_6_7, "enable interference resistance", 500);
@@ -421,13 +431,13 @@ bool initUblox789(uart::Uart& uart, GpsModel model) {
SEND_UBX_PACKET(uart, buffer, 0x06, 0x01, _message_GGA, "enable NMEA GGA", 500);
if (ublox_info.protocol_version >= 18) {
uart.flushInput();
uart_controller_flush_input(uart);
SEND_UBX_PACKET(uart, buffer, 0x06, 0x86, _message_PMS, "enable powersave for GPS", 500);
SEND_UBX_PACKET(uart, buffer, 0x06, 0x3B, _message_CFG_PM2, "enable powersave details for GPS", 500);
// For M8 we want to enable NMEA version 4.10 so we can see the additional satellites.
if (model == GpsModel::UBLOX8) {
uart.flushInput();
uart_controller_flush_input(uart);
SEND_UBX_PACKET(uart, buffer, 0x06, 0x17, _message_NMEA, "enable NMEA 4.10", 500);
}
} else {
@@ -436,7 +446,7 @@ bool initUblox789(uart::Uart& uart, GpsModel model) {
}
auto packet_size = makePacket(0x06, 0x09, _message_SAVE, sizeof(_message_SAVE), buffer);
uart.writeBytes(buffer, packet_size);
uart_controller_write_bytes(uart, buffer, packet_size, 2000 / portTICK_PERIOD_MS);
if (getAck(uart, 0x06, 0x09, 2000) != GpsResponse::Ok) {
LOGGER.warn("Unable to save GNSS module config");
} else {
@@ -445,10 +455,10 @@ bool initUblox789(uart::Uart& uart, GpsModel model) {
return true;
}
bool initUblox6(uart::Uart& uart) {
bool initUblox6(::Device* uart) {
uint8_t buffer[256];
uart.flushInput();
uart_controller_flush_input(uart);
SEND_UBX_PACKET(uart, buffer, 0x06, 0x02, _message_DISABLE_TXT_INFO, "disable text info messages", 500);
SEND_UBX_PACKET(uart, buffer, 0x06, 0x39, _message_JAM_6_7, "enable interference resistance", 500);
@@ -463,14 +473,14 @@ bool initUblox6(uart::Uart& uart) {
SEND_UBX_PACKET(uart, buffer, 0x06, 0x01, _message_RMC, "enable NMEA RMC", 500);
SEND_UBX_PACKET(uart, buffer, 0x06, 0x01, _message_GGA, "enable NMEA GGA", 500);
uart.flushInput();
uart_controller_flush_input(uart);
SEND_UBX_PACKET(uart, buffer, 0x06, 0x11, _message_CFG_RXM_ECO, "enable powersave ECO mode for Neo-6", 500);
SEND_UBX_PACKET(uart, buffer, 0x06, 0x3B, _message_CFG_PM2, "enable powersave details for GPS", 500);
SEND_UBX_PACKET(uart, buffer, 0x06, 0x01, _message_AID, "disable UBX-AID", 500);
auto packet_size = makePacket(0x06, 0x09, _message_SAVE, sizeof(_message_SAVE), buffer);
uart.writeBytes(buffer, packet_size);
uart_controller_write_bytes(uart, buffer, packet_size, 2000);
if (getAck(uart, 0x06, 0x09, 2000) != GpsResponse::Ok) {
LOGGER.warn("Unable to save GNSS module config");
} else {
+2 -3
View File
@@ -37,9 +37,8 @@ Device* findDevice(i2c_port_t port) {
auto* driver = device_get_driver(device);
if (driver == nullptr) return true;
if (!driver_is_compatible(driver, "espressif,esp32-i2c")) return true;
i2c_port_t port;
if (esp32_i2c_get_port(device, &port) != ERROR_NONE) return true;
if (port != params_ptr->port) return true;
auto* config = static_cast<const Esp32I2cConfig*>(device->config);
if (config->port != params_ptr->port) return true;
// Found it, stop iterating
params_ptr->device = device;
return false;
-192
View File
@@ -1,192 +0,0 @@
#include "Tactility/hal/uart/Uart.h"
#include <Tactility/Logger.h>
#include <Tactility/Mutex.h>
#include <ranges>
#include <cstring>
#include <Tactility/Tactility.h>
#ifdef ESP_PLATFORM
#include <Tactility/hal/uart/UartEsp.h>
#include <esp_check.h>
#else
#include <Tactility/hal/uart/UartPosix.h>
#include <dirent.h>
#endif
namespace tt::hal::uart {
static const auto LOGGER = Logger("UART");
constexpr uint32_t uartIdNotInUse = 0;
struct UartEntry {
uint32_t usageId = uartIdNotInUse;
Configuration configuration;
};
static std::vector<UartEntry> uartEntries = {};
static uint32_t lastUartId = uartIdNotInUse;
bool init(const std::vector<Configuration>& configurations) {
LOGGER.info("Init");
for (const auto& configuration: configurations) {
uartEntries.push_back({
.usageId = uartIdNotInUse,
.configuration = configuration
});
}
return true;
}
bool Uart::writeString(const char* buffer, TickType_t timeout) {
auto size = strlen(buffer);
writeBytes((std::byte*)buffer, size, timeout);
return true;
}
size_t Uart::readUntil(std::byte* buffer, size_t bufferSize, uint8_t untilByte, TickType_t timeout, bool addNullTerminator) {
TickType_t start_time = kernel::getTicks();
auto* buffer_write_ptr = reinterpret_cast<uint8_t*>(buffer);
uint8_t* buffer_limit = buffer_write_ptr + bufferSize - 1; // Keep 1 extra char as mull terminator
TickType_t timeout_left = timeout;
while (readByte(reinterpret_cast<std::byte*>(buffer_write_ptr), timeout_left) && buffer_write_ptr < buffer_limit) {
#ifdef DEBUG_READ_UNTIL
// If first successful read and we're not receiving an empty response
if (buffer_write_ptr == buffer && *buffer_write_ptr != 0x00U && *buffer_write_ptr != untilByte) {
printf(">>");
}
#endif
if (*buffer_write_ptr == untilByte) {
// TODO: Fix when untilByte is null terminator char already
if (addNullTerminator) {
buffer_write_ptr++;
*buffer_write_ptr = 0x00U;
}
break;
}
#ifdef DEBUG_READ_UNTIL
printf("%c", *buffer_write_ptr);
#endif
buffer_write_ptr++;
TickType_t now = kernel::getTicks();
if (now > (start_time + timeout)) {
#ifdef DEBUG_READ_UNTIL
LOGGER.warn("readUntil() timeout");
#endif
break;
} else {
timeout_left = timeout - (now - start_time);
}
}
#ifdef DEBUG_READ_UNTIL
// If we read data and it's not an empty response
if (buffer_write_ptr != buffer && *buffer != 0x00U && *buffer != untilByte) {
printf("\n");
}
#endif
if (addNullTerminator && (buffer_write_ptr > reinterpret_cast<uint8_t*>(buffer))) {
return reinterpret_cast<size_t>(buffer_write_ptr) - reinterpret_cast<size_t>(buffer) - 1UL;
} else {
return reinterpret_cast<size_t>(buffer_write_ptr) - reinterpret_cast<size_t>(buffer);
}
}
static std::unique_ptr<Uart> open(UartEntry& entry) {
if (entry.usageId != uartIdNotInUse) {
LOGGER.error("UART in use: {}", entry.configuration.name);
return nullptr;
}
auto uart = create(entry.configuration);
assert(uart != nullptr);
entry.usageId = uart->getId();
LOGGER.info("Opened {}", entry.usageId);
return uart;
}
std::unique_ptr<Uart> open(uart_port_t port) {
LOGGER.info("Open {}", static_cast<int>(port));
auto result = std::views::filter(uartEntries, [port](auto& entry) {
return entry.configuration.port == port;
});
if (result.empty()) {
LOGGER.error("UART not found: {}", static_cast<int>(port));
return nullptr;
}
return open(*result.begin());
}
std::unique_ptr<Uart> open(std::string name) {
LOGGER.info("Open {}", name);
auto result = std::views::filter(uartEntries, [&name](auto& entry) {
return entry.configuration.name == name;
});
if (result.empty()) {
LOGGER.error("UART not found: {}", name);
return nullptr;
}
return open(*result.begin());
}
void close(uint32_t uartId) {
LOGGER.info("Close {}", uartId);
auto result = std::views::filter(uartEntries, [&uartId](auto& entry) {
return entry.usageId == uartId;
});
if (!result.empty()) {
auto& entry = *result.begin();
entry.usageId = uartIdNotInUse;
} else {
LOGGER.warn("Auto-closing UART, but can't find it");
}
}
std::vector<std::string> getNames() {
std::vector<std::string> names;
#ifdef ESP_PLATFORM
for (auto& config : getConfiguration()->uart) {
names.push_back(config.name);
}
#else
DIR* dir = opendir("/dev");
if (dir == nullptr) {
LOGGER.error("Failed to read /dev");
return names;
}
struct dirent* current_entry;
while ((current_entry = readdir(dir)) != nullptr) {
auto name = std::string(current_entry->d_name);
if (name.starts_with("tty")) {
auto path = std::string("/dev/") + name;
names.push_back(path);
}
}
closedir(dir);
#endif
return names;
}
Uart::Uart() : id(++lastUartId) {}
Uart::~Uart() {
close(getId());
}
} // namespace tt::hal::uart
-157
View File
@@ -1,157 +0,0 @@
#ifdef ESP_PLATFORM
#include <Tactility/hal/uart/UartEsp.h>
#include <Tactility/Logger.h>
#include <Tactility/kernel/Kernel.h>
#include <Tactility/Mutex.h>
#include <esp_check.h>
#include <sstream>
namespace tt::hal::uart {
static const auto LOGGER = Logger("UART");
bool UartEsp::start() {
LOGGER.info("[{}] Starting", configuration.name);
auto lock = mutex.asScopedLock();
lock.lock();
if (started) {
LOGGER.error("[{}] Starting: Already started", configuration.name);
return false;
}
int intr_alloc_flags;
#if CONFIG_UART_ISR_IN_IRAM
intr_alloc_flags = ESP_INTR_FLAG_IRAM;
#else
intr_alloc_flags = 0;
#endif
esp_err_t result = uart_param_config(configuration.port, &configuration.config);
if (result != ESP_OK) {
LOGGER.error("[{}] Starting: Failed to configure: {}", configuration.name, esp_err_to_name(result));
return false;
}
if (uart_is_driver_installed(configuration.port)) {
LOGGER.error("[{}] Driver was still installed. You probably forgot to stop, or another system uses/used the driver.", configuration.name);
uart_driver_delete(configuration.port);
}
result = uart_set_pin(configuration.port, configuration.txPin, configuration.rxPin, configuration.rtsPin, configuration.ctsPin);
if (result != ESP_OK) {
LOGGER.error("[{}] Starting: Failed set pins: {}", configuration.name, esp_err_to_name(result));
return false;
}
result = uart_driver_install(configuration.port, (int)configuration.rxBufferSize, (int)configuration.txBufferSize, 0, nullptr, intr_alloc_flags);
if (result != ESP_OK) {
LOGGER.error("[{}] Starting: Failed to install driver: {}", configuration.name, esp_err_to_name(result));
return false;
}
started = true;
LOGGER.info("[{}] Started", configuration.name);
return true;
}
bool UartEsp::stop() {
LOGGER.info("[{}] Stopping", configuration.name);
auto lock = mutex.asScopedLock();
lock.lock();
if (!started) {
LOGGER.error("[{}] Stopping: Not started", configuration.name);
return false;
}
esp_err_t result = uart_driver_delete(configuration.port);
if (result != ESP_OK) {
LOGGER.error("[{}] Stopping: Failed to delete driver: {}", configuration.name, esp_err_to_name(result));
return false;
}
started = false;
LOGGER.info("[{}] Stopped", configuration.name);
return true;
}
bool UartEsp::isStarted() const {
auto lock = mutex.asScopedLock();
lock.lock();
return started;
}
size_t UartEsp::readBytes(std::byte* buffer, size_t bufferSize, TickType_t timeout) {
auto lock = mutex.asScopedLock();
if (!lock.lock(timeout)) {
return false;
}
auto start_time = kernel::getTicks();
auto lock_time = kernel::getTicks() - start_time;
auto remaining_timeout = std::max(timeout - lock_time, 0UL);
auto result = uart_read_bytes(configuration.port, buffer, bufferSize, remaining_timeout);
return result;
}
bool UartEsp::readByte(std::byte* output, TickType_t timeout) {
return readBytes(output, 1, timeout) == 1;
}
size_t UartEsp::writeBytes(const std::byte* buffer, size_t bufferSize, TickType_t timeout) {
auto lock = mutex.asScopedLock();
if (!lock.lock(timeout)) {
return false;
}
return uart_write_bytes(configuration.port, buffer, bufferSize);
}
size_t UartEsp::available(TickType_t timeout) {
auto lock = mutex.asScopedLock();
if (!lock.lock(timeout)) {
return false;
}
size_t size = 0;
uart_get_buffered_data_len(configuration.port, &size);
return size;
}
void UartEsp::flushInput() {
uart_flush_input(configuration.port);
}
uint32_t UartEsp::getBaudRate() {
uint32_t baud_rate = 0;
auto result = uart_get_baudrate(configuration.port, &baud_rate);
ESP_ERROR_CHECK_WITHOUT_ABORT(result);
return baud_rate;
}
bool UartEsp::setBaudRate(uint32_t baudRate, TickType_t timeout) {
auto lock = mutex.asScopedLock();
if (!lock.lock(timeout)) {
return false;
}
auto result = uart_set_baudrate(configuration.port, baudRate);
ESP_ERROR_CHECK_WITHOUT_ABORT(result);
return result == ESP_OK;
}
std::unique_ptr<Uart> create(const Configuration& configuration) {
return std::make_unique<UartEsp>(configuration);
}
} // namespace tt::hal::uart
#endif
-191
View File
@@ -1,191 +0,0 @@
#ifndef ESP_PLATFORM
#include <Tactility/hal/uart/UartPosix.h>
#include <Tactility/hal/uart/Uart.h>
#include <Tactility/kernel/Kernel.h>
#include <Tactility/Logger.h>
#include <cstring>
#include <sstream>
#include <sys/ioctl.h>
#include <unistd.h>
namespace tt::hal::uart {
static const auto LOGGER = Logger("UART");
bool UartPosix::start() {
auto lock = mutex.asScopedLock();
lock.lock();
if (device != nullptr) {
LOGGER.error("[{}] Starting: Already started", configuration.name);
return false;
}
auto file = fopen(configuration.name.c_str(), "w");
if (file == nullptr) {
LOGGER.error("[{}] Open device failed", configuration.name);
return false;
}
auto new_device = std::unique_ptr<FILE, AutoCloseFileDeleter>(file);
struct termios tty;
if (tcgetattr(fileno(file), &tty) < 0) {
LOGGER.error("[{}] tcgetattr failed: {}", configuration.name, strerror(errno));
return false;
}
if (cfsetospeed(&tty, (speed_t)configuration.baudRate) == -1) {
LOGGER.error("[{}] Setting output speed failed", configuration.name);
}
if (cfsetispeed(&tty, (speed_t)configuration.baudRate) == -1) {
LOGGER.error("[{}] Setting input speed failed", configuration.name);
}
tty.c_cflag |= (CLOCAL | CREAD); /* ignore modem controls */
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8; /* 8-bit characters */
tty.c_cflag &= ~PARENB; /* no parity bit */
tty.c_cflag &= ~CSTOPB; /* only need 1 stop bit */
tty.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */
tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
tty.c_oflag &= ~OPOST;
/* fetch bytes as they become available */
tty.c_cc[VMIN] = 1;
tty.c_cc[VTIME] = 1;
if (tcsetattr(fileno(file), TCSANOW, &tty) != 0) {
LOGGER.error("[{}] tcsetattr failed: {}", configuration.name, strerror(errno));
return false;
}
device = std::move(new_device);
LOGGER.info("[{}] Started", configuration.name);
return true;
}
bool UartPosix::stop() {
auto lock = mutex.asScopedLock();
lock.lock();
if (device == nullptr) {
LOGGER.error("[{}] Stopping: Not started", configuration.name);
return false;
}
device = nullptr;
LOGGER.info("[{}] Stopped", configuration.name);
return true;
}
bool UartPosix::isStarted() const {
auto lock = mutex.asScopedLock();
lock.lock();
return device != nullptr;
}
size_t UartPosix::readBytes(std::byte* buffer, size_t bufferSize, TickType_t timeout) {
auto lock = mutex.asScopedLock();
if (!lock.lock(timeout)) {
return false;
}
if (awaitAvailable(timeout)) {
return read(fileno(device.get()), buffer, bufferSize);
} else {
return 0;
}
}
bool UartPosix::readByte(std::byte* output, TickType_t timeout) {
if (awaitAvailable(timeout)) {
return read(fileno(device.get()), output, 1) == 1;
} else {
return false;
}
}
size_t UartPosix::writeBytes(const std::byte* buffer, size_t bufferSize, TickType_t timeout) {
if (!mutex.lock(timeout)) {
return false;
}
return write(fileno(device.get()), buffer, bufferSize);
}
size_t UartPosix::available(TickType_t timeout) {
auto lock = mutex.asScopedLock();
if (!lock.lock(timeout)) {
return false;
}
uint32_t bytes_available = 0;
ioctl(fileno(device.get()), FIONREAD, bytes_available);
return bytes_available;
}
void UartPosix::flushInput() {
// TODO
}
uint32_t UartPosix::getBaudRate() {
struct termios tty;
if (tcgetattr(fileno(device.get()), &tty) < 0) {
LOGGER.error("[{}] tcgetattr failed: {}", configuration.name, strerror(errno));
return false;
} else {
return (uint32_t)cfgetispeed(&tty);
}
}
bool UartPosix::setBaudRate(uint32_t baudRate, TickType_t timeout) {
auto lock = mutex.asScopedLock();
if (!lock.lock(timeout)) {
return false;
}
struct termios tty;
if (tcgetattr(fileno(device.get()), &tty) < 0) {
LOGGER.error("[{}] tcgetattr failed: {}", configuration.name, strerror(errno));
return false;
}
if (cfsetospeed(&tty, (speed_t)configuration.baudRate) == -1) {
LOGGER.error("[{}] Failed to set output speed", configuration.name);
return false;
}
if (cfsetispeed(&tty, (speed_t)configuration.baudRate) == -1) {
LOGGER.error("[{}] Failed to set input speed", configuration.name);
return false;
}
return true;
}
bool UartPosix::awaitAvailable(TickType_t timeout) {
auto start_time = kernel::getTicks();
do {
if (available(timeout) > 0) {
return true;
}
kernel::delayTicks(timeout / 10);
} while ((kernel::getTicks() - start()) < timeout);
return false;
}
std::unique_ptr<Uart> create(const Configuration& configuration) {
return std::make_unique<UartPosix>(configuration);
}
} // namespace tt::hal::uart
#endif