Skip to content

File Serial.cpp

File List > api > Serial > Serial.cpp

Go to the documentation of this file.

/* Copyright (c) Kuba Szczodrzyński 2023-05-23. */

#if LT_ARD_HAS_SERIAL || DOXYGEN

#include <SerialPrivate.h>

#if LT_AUTO_DOWNLOAD_REBOOT && defined(LT_UART_ADR_PATTERN)
static uint8_t adrState = 0;
static uint8_t adrCmd[] = {LT_UART_ADR_PATTERN};

void SerialClass::adrParse(uint8_t c) {
    adrState = (adrState + 1) * (c == adrCmd[adrState]);
    if (adrState == sizeof(adrCmd) / sizeof(uint8_t)) {
        LT_I("Auto download mode: rebooting");
        LT.restartDownloadMode();
    }
}
#endif

bool SerialClass::validatePins(pin_size_t rx, pin_size_t tx) {
    // for each role (RX/TX) in each UART, fail validation if:
    // - a pin is specified and no such role exists on the board, or
    // - a pin is specified and is not among the supported pins for that role
    switch (this->port) {
#if LT_HW_UART0
        case 0:
            if (rx != PIN_INVALID
#ifdef PINS_SERIAL0_RX
                && !ltArrayContains((pin_size_t[])PINS_SERIAL0_RX, rx)
#endif
            )
                return false;
            if (tx != PIN_INVALID
#ifdef PINS_SERIAL0_TX
                && !ltArrayContains((pin_size_t[])PINS_SERIAL0_TX, tx)
#endif
            )
                return false;
            break;
#endif

#if LT_HW_UART1
        case 1:
            if (rx != PIN_INVALID
#ifdef PINS_SERIAL1_RX
                && !ltArrayContains((pin_size_t[])PINS_SERIAL1_RX, rx)
#endif
            )
                return false;
            if (tx != PIN_INVALID
#ifdef PINS_SERIAL1_TX
                && !ltArrayContains((pin_size_t[])PINS_SERIAL1_TX, tx)
#endif
            )
                return false;
            break;
#endif

#if LT_HW_UART2
        case 2:
            if (rx != PIN_INVALID
#ifdef PINS_SERIAL2_RX
                && !ltArrayContains((pin_size_t[])PINS_SERIAL2_RX, rx)
#endif
            )
                return false;
            if (tx != PIN_INVALID
#ifdef PINS_SERIAL2_TX
                && !ltArrayContains((pin_size_t[])PINS_SERIAL2_TX, tx)
#endif
            )
                return false;
            break;
#endif

        default:
            return false;
    }
    return true;
}

void SerialClass::begin(unsigned long baudrate, uint16_t config, pin_size_t rx, pin_size_t tx) {
    // let family code cleanup any resources
    this->end();

    // apply pin configuration
    if (!this->setPins(rx, tx))
        return;

    // allocate data structure and ring buffer
    if (!this->data) {
        this->data  = new SerialData();
        this->rxBuf = new SerialRingBuffer();
    }

    this->beginPrivate(baudrate, config);
    // if (this->baudrate != baudrate || this->config != config)
    this->configure(baudrate, config);
}

bool SerialClass::setPins(pin_size_t rx, pin_size_t tx) {
    // return early if configured pins are the same
    if (rx == this->rx && tx == this->tx)
        return true;
    // use provided pins or those used previously
    if (rx == PIN_INVALID)
        rx = this->rx;
    if (tx == PIN_INVALID)
        tx = this->tx;
    // nothing to configure if RX and TX are invalid
    if (rx == PIN_INVALID && tx == PIN_INVALID)
        return false;
    // validate if provided pins are valid for this UART port
    if (!this->validatePins(rx, tx)) {
        LT_E("Serial pin numbers RX=%d,TX=%d invalid for port %u", rx, tx, this->port);
        return false;
    }
    // store in SerialClass for family code to use
    this->rx = rx;
    this->tx = tx;

    // reinitialize Serial with changed pins if already started
    if (this->data) {
        this->endPrivate();
        this->beginPrivate(this->baudrate, this->config);
        this->configure(this->baudrate, this->config);
    }

    return true;
}

void SerialClass::end() {
    this->endPrivate();

    delete this->data;
    delete this->rxBuf;
    this->data     = nullptr;
    this->rxBuf    = nullptr;
    this->baudrate = 0;
}

int SerialClass::available() {
    return this->rxBuf ? this->rxBuf->available() : 0;
}

int SerialClass::peek() {
    return this->rxBuf ? this->rxBuf->peek() : -1;
}

int SerialClass::read() {
    return this->rxBuf ? this->rxBuf->read_char() : -1;
}

#if LT_HW_UART0
SerialClass Serial0(0, PIN_SERIAL0_RX, PIN_SERIAL0_TX);
#endif
#if LT_HW_UART1
SerialClass Serial1(1, PIN_SERIAL1_RX, PIN_SERIAL1_TX);
#endif
#if LT_HW_UART2
SerialClass Serial2(2, PIN_SERIAL2_RX, PIN_SERIAL2_TX);
#endif

#endif