USART
module reference#include <usart.h>Makefile :
MODULES+=usart
An USART (Universal Synchronous/Asynchronous Receiver/Transmitter) is a peripheral used to send and receive data to/from another device using a standard and very simple serial communication scheme. This interface is sometimes abusively called "Serial" (even though SPI, I2C and USB are also serial protocols) or RS-232 (which is the older computer standard which used the same encoding scheme but with different voltage levels). In this protocol, there is no "master" or "slave" : the communication is symetrical, each device has a transmitter (Tx) and a receiver (Rx), and one device's transmitter sends data to the other device's receiver. If the communication is only required in one direction, the other line can be left unconnected. For more information and see how this interface compares to other protocols such as I2C and SPI, take a look at the Communication buses tutorial.
The most common use cases are :
USB
.A communication line is called "synchronous" when a clock signal is transfered along the data, or "asynchronous" otherwise. Synchronous protocols are somewhat easier to use and more reliable because the sender and the receiver are, as the name implies, synchronized; however, this requires another dedicated wire to carry the clock signal. With asynchronous protocols, the two devices must be correctly configured with the same clock setting in order to understand each other.
The USART protocol in general can be used either in synchronous or asynchronous mode, but in fact, the vast majority of devices do not have a clock line and therefore only work in asynchronous mode. Currently, this is the only mode supported by the driver. This is why the S is sometimes dropped from the name, and the peripheral (or, by extension, the protocol) is simply called UART.
#ifndef _USART_H_ #define _USART_H_ #include <stdint.h> #include "gpio.h" // Universal Synchronous Asynchronous Receiver Transmitter // This module allows the chip to communicate on an RS232 link // (also sometimes called Serial port) namespace USART { // Peripheral memory space base addresses const uint32_t USART_BASE = 0x40024000; const uint32_t USART_REG_SIZE = 0x4000; // Register offsets const uint32_t OFFSET_CR = 0x00; const uint32_t OFFSET_MR = 0x04; const uint32_t OFFSET_IER = 0x08; const uint32_t OFFSET_IDR = 0x0C; const uint32_t OFFSET_IMR = 0x10; const uint32_t OFFSET_CSR = 0x14; const uint32_t OFFSET_RHR = 0x18; const uint32_t OFFSET_THR = 0x1C; const uint32_t OFFSET_BRGR = 0x20; const uint32_t OFFSET_RTOR = 0x24; const uint32_t OFFSET_TTGR = 0x28; const uint32_t OFFSET_FIDI = 0x40; const uint32_t OFFSET_NER = 0x44; const uint32_t OFFSET_IFR = 0x4C; const uint32_t OFFSET_MAN = 0x50; const uint32_t OFFSET_LINMR = 0x54; const uint32_t OFFSET_LINIR = 0x58; const uint32_t OFFSET_LINBR = 0x5C; const uint32_t OFFSET_WPMR = 0xE4; const uint32_t OFFSET_WPSR = 0xE8; const uint32_t OFFSET_VERSION = 0xFC; // Subregisters const uint32_t CR_RSTRX = 2; const uint32_t CR_RSTTX = 3; const uint32_t CR_RXEN = 4; const uint32_t CR_RXDIS = 5; const uint32_t CR_TXEN = 6; const uint32_t CR_TXDIS = 7; const uint32_t CR_RSTSTA = 8; const uint32_t MR_MODE = 0; const uint32_t MR_CHRL = 6; const uint32_t MR_PAR = 9; const uint32_t MR_NBSTOP = 12; const uint32_t MR_MODE9 = 17; const uint32_t BRGR_CD = 0; const uint32_t BRGR_FP = 16; const uint32_t CSR_RXRDY = 0; const uint32_t CSR_TXRDY = 1; const uint32_t CSR_RXBRK = 2; const uint32_t CSR_OVRE = 5; const uint32_t CSR_PARE = 7; const uint32_t CSR_TXEMPTY = 9; const uint32_t IER_RXRDY = 0; // Constants const uint32_t WPMR_KEY = 0x555341 << 8; const uint32_t WPMR_ENABLE = 1; const uint32_t WPMR_DISABLE = 0; const uint32_t MODE_NORMAL = 0b0000; const uint32_t MODE_HARDWARE_HANDSHAKE = 0b0010; const int N_PORTS = 4; enum class Port { USART0, USART1, USART2, USART3 }; const uint8_t BIN = 2; const uint8_t DEC = 10; const uint8_t HEX = 16; // Port configuration : character length (default 8) // Value refers to MR.CHRL and MR.MODE9 enum class CharLength { CHAR5 = 0b000, CHAR6 = 0b001, CHAR7 = 0b010, CHAR8 = 0b011, CHAR9 = 0b100 }; // Port configuration : parity type (default NONE) // Value refers to MR.PAR enum class Parity { EVEN = 0b000, ODD = 0b001, SPACE = 0b010, // Parity forced to 0 MARK = 0b011, // Parity forced to 1 NONE = 0b100, }; // Port configuration : length of stop bit (default 1) // Value refers to MR.NBSTOP enum class StopBit { STOP1 = 0b00, STOP15 = 0b01, // length 1.5 bit STOP2 = 0b10 }; const int N_INTERRUPTS = 4; enum class Interrupt { BYTE_RECEIVED, TRANSMIT_READY, OVERFLOW, PARITY_ERROR }; enum class PinFunction { RX, TX, RTS, CTS }; const int BUFFER_SIZE = 2048; // Error codes const Error::Code ERR_BAUDRATE_OUT_OF_RANGE = 0x0001; // Module API void enable(Port port, unsigned long baudrate, bool hardwareFlowControl=false, CharLength charLength=CharLength::CHAR8, Parity parity=Parity::NONE, StopBit stopBit=StopBit::STOP1); void disable(Port port); void enableInterrupt(Port port, void (*handler)(), Interrupt interrupt); int available(Port port); bool contains(Port port, char byte); char peek(Port port); bool peek(Port port, const char* test, int size); char read(Port port); int read(Port port, char* buffer, int size, bool readUntil=false, char end=0x00); int readUntil(Port port, char* buffer, int size, char end); unsigned long readInt(Port port, int nBytes, bool wait=true); int write(Port port, const char* buffer, int size=-1, bool async=false); int write(Port port, char byte, bool async=false); int write(Port port, int number, uint8_t base, bool async=false); int write(Port port, bool boolean, bool async=false); int writeLine(Port port, const char* buffer, int size=-1, bool async=false); int writeLine(Port port, char byte, bool async=false); int writeLine(Port port, int number, uint8_t base, bool async=false); int writeLine(Port port, bool boolean, bool async=false); bool isWriteFinished(Port port); void waitWriteFinished(Port port); void waitReadFinished(Port port, unsigned long timeout=100); void flush(Port port); void setPin(Port port, PinFunction function, GPIO::Pin pin); } #endif
#include "usart.h" #include "core.h" #include "gpio.h" #include "pm.h" #include "dma.h" namespace USART { // Internal functions void rxBufferFullHandler(); // Clocks const int PM_CLK[] = {PM::CLK_USART0, PM::CLK_USART1, PM::CLK_USART2, PM::CLK_USART3}; // Interrupt handlers extern uint8_t INTERRUPT_PRIORITY; uint32_t _interruptHandlers[N_PORTS][N_INTERRUPTS]; const int _interruptBits[N_INTERRUPTS] = {CSR_RXRDY, CSR_TXRDY, CSR_OVRE, CSR_PARE}; void interruptHandlerWrapper(); // Package-dependant, defined in pins_sam4l_XX.cpp // Can be modified using setPin() extern struct GPIO::Pin PINS_RX[]; extern struct GPIO::Pin PINS_TX[]; extern struct GPIO::Pin PINS_CTS[]; extern struct GPIO::Pin PINS_RTS[]; // Ports struct USART { unsigned long baudrate; bool hardwareFlowControl; CharLength charLength; Parity parity; StopBit stopBit; uint8_t rxBuffer[BUFFER_SIZE]; uint8_t txBuffer[BUFFER_SIZE]; int rxBufferCursorR; int rxBufferCursorW; int txBufferCursor; int rxDMAChannel = -1; int txDMAChannel = -1; }; struct USART _ports[N_PORTS]; int _rxDMAChannelsToPorts[DMA::N_CHANNELS_MAX]; bool _portsEnabled[N_PORTS] = {false, false, false, false}; void enable(Port port, unsigned long baudrate, bool hardwareFlowControl, CharLength charLength, Parity parity, StopBit stopBit) { const uint32_t REG_BASE = USART_BASE + static_cast<int>(port) * USART_REG_SIZE; struct USART* p = &(_ports[static_cast<int>(port)]); // Port configuration p->hardwareFlowControl = hardwareFlowControl; p->charLength = charLength; p->parity = parity; p->stopBit = stopBit; // Initialize the buffers for (int i = 0; i < BUFFER_SIZE; i++) { p->rxBuffer[i] = 0; p->txBuffer[i] = 0; } p->rxBufferCursorR = 0; p->rxBufferCursorW = 0; p->txBufferCursor = 0; // Set the pins in peripheral mode GPIO::enablePeripheral(PINS_RX[static_cast<int>(port)]); GPIO::enablePeripheral(PINS_TX[static_cast<int>(port)]); if (hardwareFlowControl) { GPIO::enablePeripheral(PINS_RTS[static_cast<int>(port)]); GPIO::enablePeripheral(PINS_CTS[static_cast<int>(port)]); } // Enable the clock PM::enablePeripheralClock(PM_CLK[static_cast<int>(port)]); // WPMR (Write Protect Mode Register) : disable the Write Protect (*(volatile uint32_t*)(REG_BASE + OFFSET_WPMR)) = WPMR_KEY | WPMR_DISABLE; // MR (Mode Register) : set the USART configuration (*(volatile uint32_t*)(REG_BASE + OFFSET_MR)) = (hardwareFlowControl ? MODE_HARDWARE_HANDSHAKE : MODE_NORMAL) << MR_MODE // Hardware flow control | (static_cast<int>(charLength) & 0b11) << MR_CHRL // Character length <= 8 | (static_cast<int>(charLength) >> 2) << MR_MODE9 // Character length == 9 | static_cast<int>(parity) << MR_PAR // Parity | static_cast<int>(stopBit) << MR_NBSTOP; // Length of stop bit // BRGR (Baud Rate Generator Register) : configure the baudrate generator // Cf datasheet 24.6.4 : baudrate = mainclock / (16 * CD), with FP to fine-tune by steps of 1/8 p->baudrate = baudrate; const unsigned long clk = PM::getModuleClockFrequency(PM_CLK[static_cast<int>(port)]); const uint64_t cd100 = 100 * clk / 16 / baudrate; // cd100 = cd * 100 const uint32_t cd = cd100 / 100; const uint64_t fp100 = (cd100 % 100) * 8; uint8_t fp = fp100 / 100; if (fp100 - fp * 100 >= 50) { fp++; // Round } if (cd == 0 || cd > 0xFFFF) { Error::happened(Error::Module::USART, ERR_BAUDRATE_OUT_OF_RANGE, Error::Severity::CRITICAL); return; } (*(volatile uint32_t*)(REG_BASE + OFFSET_BRGR)) = cd << BRGR_CD | fp << BRGR_FP; // CR (Control Register) : enable RX and TX (*(volatile uint32_t*)(REG_BASE + OFFSET_CR)) = 1 << CR_RXEN | 1 << CR_TXEN; // WPMR (Write Protect Mode Register) : re-enable the Write Protect (*(volatile uint32_t*)(REG_BASE + OFFSET_WPMR)) = WPMR_KEY | WPMR_ENABLE; // Set up the DMA channels and related interrupts p->rxDMAChannel = DMA::setupChannel(p->rxDMAChannel, static_cast<DMA::Device>(static_cast<int>(DMA::Device::USART0_RX) + static_cast<int>(port)), DMA::Size::BYTE); p->txDMAChannel = DMA::setupChannel(p->txDMAChannel, static_cast<DMA::Device>(static_cast<int>(DMA::Device::USART0_TX) + static_cast<int>(port)), DMA::Size::BYTE); _rxDMAChannelsToPorts[p->rxDMAChannel] = static_cast<int>(port); DMA::startChannel(p->rxDMAChannel, (uint32_t)(p->rxBuffer), BUFFER_SIZE); //DMA::reloadChannel(p->rxDMAChannel, (uint32_t)(p->rxBuffer), BUFFER_SIZE); DMA::enableInterrupt(p->rxDMAChannel, &rxBufferFullHandler, DMA::Interrupt::TRANSFER_FINISHED); _portsEnabled[static_cast<int>(port)] = true; } void disable(Port port) { // Don't do anything if this port is not enabled if (!_portsEnabled[static_cast<int>(port)]) { return; } _portsEnabled[static_cast<int>(port)] = false; const uint32_t REG_BASE = USART_BASE + static_cast<int>(port) * USART_REG_SIZE; struct USART* p = &(_ports[static_cast<int>(port)]); // Disable the DMA channels DMA::stopChannel(p->rxDMAChannel); DMA::stopChannel(p->txDMAChannel); // WPMR (Write Protect Mode Register) : disable the Write Protect (*(volatile uint32_t*)(REG_BASE + OFFSET_WPMR)) = WPMR_KEY | WPMR_DISABLE; // CR (Control Register) : disable RX and TX (*(volatile uint32_t*)(REG_BASE + OFFSET_CR)) = 1 << CR_RXDIS | 1 << CR_TXDIS; // WPMR (Write Protect Mode Register) : re-enable the Write Protect (*(volatile uint32_t*)(REG_BASE + OFFSET_WPMR)) = WPMR_KEY | WPMR_ENABLE; // Disable the clock PM::disablePeripheralClock(PM_CLK[static_cast<int>(port)]); // Free the pins GPIO::disablePeripheral(PINS_RX[static_cast<int>(port)]); GPIO::disablePeripheral(PINS_TX[static_cast<int>(port)]); if (p->hardwareFlowControl) { GPIO::disablePeripheral(PINS_RTS[static_cast<int>(port)]); GPIO::disablePeripheral(PINS_CTS[static_cast<int>(port)]); } } void enableInterrupt(Port port, void (*handler)(), Interrupt interrupt) { const uint32_t REG_BASE = USART_BASE + static_cast<int>(port) * USART_REG_SIZE; // Save the user handler _interruptHandlers[static_cast<int>(port)][static_cast<int>(interrupt)] = (uint32_t)handler; // IER (Interrupt Enable Register) : enable the requested interrupt (*(volatile uint32_t*)(REG_BASE + OFFSET_IER)) = 1 << IER_RXRDY; // Enable the interrupt in the NVIC Core::Interrupt interruptChannel = static_cast<Core::Interrupt>(static_cast<int>(Core::Interrupt::USART0) + static_cast<int>(port)); Core::setInterruptHandler(interruptChannel, interruptHandlerWrapper); Core::enableInterrupt(interruptChannel, INTERRUPT_PRIORITY); } void interruptHandlerWrapper() { // Get the port number through the current interrupt number int port = static_cast<int>(Core::currentInterrupt()) - static_cast<int>(Core::Interrupt::USART0); const uint32_t REG_BASE = USART_BASE + port * USART_REG_SIZE; // Call the user handler of every interrupt that is enabled and pending for (int i = 0; i < N_INTERRUPTS; i++) { if ((*(volatile uint32_t*)(REG_BASE + OFFSET_IMR)) & (1 << _interruptBits[i]) // Interrupt is enabled && (*(volatile uint32_t*)(REG_BASE + OFFSET_CSR)) & (1 << _interruptBits[i])) { // Interrupt is pending void (*handler)() = (void (*)())_interruptHandlers[port][i]; if (handler != nullptr) { handler(); } } } // Clear the interrupts (*(volatile uint32_t*)(REG_BASE + OFFSET_CR)) = 1 << CR_RSTSTA; // Reset status bits } void rxBufferFullHandler() { // Get the port that provoqued this interrupt int channel = static_cast<int>(Core::currentInterrupt()) - static_cast<int>(Core::Interrupt::DMA0); int portNumber = _rxDMAChannelsToPorts[channel]; struct USART* p = &(_ports[portNumber]); // Reload the DMA channel int length = 0; int cur = p->rxBufferCursorW; if (p->rxBufferCursorR == p->rxBufferCursorW) { // Buffer is full, the DMA channel will be disabled // If hardware flow control is enabled this will pause the transfer by raising RTS, // otherwise if more data arrives it will trigger an overflow length = 0; } else if (p->rxBufferCursorR > p->rxBufferCursorW) { // Reload until the cursor length = p->rxBufferCursorR - p->rxBufferCursorW; p->rxBufferCursorW = p->rxBufferCursorR; } else { // p->rxBufferCursorR < p->rxBufferCursorW // Reload until the end of the linear buffer length = BUFFER_SIZE - p->rxBufferCursorW; p->rxBufferCursorW = 0; } if (length == 0) { DMA::stopChannel(p->rxDMAChannel); } else { DMA::reloadChannel(p->rxDMAChannel, (uint32_t)(p->rxBuffer + cur), length); } } int available(Port port) { struct USART* p = &(_ports[static_cast<int>(port)]); // Get the position of the second cursors from the DMA int cursor2 = p->rxBufferCursorW - DMA::getCounter(p->rxDMAChannel); // Get the position of the last char inserted into the buffer by DMA int length = cursor2 - p->rxBufferCursorR; if (length == 0) { // The buffer is either complety empty or complety full if (DMA::getCounter(p->rxDMAChannel) == 0) { // Complety full return BUFFER_SIZE; } else { // Complety empty return 0; } } else if (length > 0) { return length; } else { return BUFFER_SIZE + length; // length is negative here } } bool contains(Port port, char byte) { struct USART* p = &(_ports[static_cast<int>(port)]); // Check if the received buffer contains the specified byte int avail = available(port); for (int i = 0; i < avail; i++) { if (p->rxBuffer[(p->rxBufferCursorR + i) % BUFFER_SIZE] == byte) { return true; } } return false; } char peek(Port port) { struct USART* p = &(_ports[static_cast<int>(port)]); if (available(port) > 0) { // Return the next char in the port's RX buffer return p->rxBuffer[p->rxBufferCursorR]; } else { return 0; } } bool peek(Port port, const char* test, int size) { struct USART* p = &(_ports[static_cast<int>(port)]); if (available(port) >= size) { // Check whether the /size/ next chars in the buffer are equal to test for (int i = 0; i < size; i++) { if (p->rxBuffer[(p->rxBufferCursorR + i) % BUFFER_SIZE] != test[i]) { return false; } } return true; } else { return false; } } // Read one byte char read(Port port) { struct USART* p = &(_ports[static_cast<int>(port)]); if (available(port) > 0) { // Read the next char in the port's Rx buffer char c = p->rxBuffer[p->rxBufferCursorR]; // Increment the cursor p->rxBufferCursorR++; if (p->rxBufferCursorR == BUFFER_SIZE) { p->rxBufferCursorR = 0; } // If the Rx DMA channel was stopped because the buffer was full, // there is now room available, so it can be restarted if (!DMA::isEnabled(p->rxDMAChannel)) { int cur = p->rxBufferCursorW; p->rxBufferCursorW++; if (p->rxBufferCursorW == BUFFER_SIZE) { p->rxBufferCursorW = 0; } DMA::startChannel(p->rxDMAChannel, (uint32_t)(p->rxBuffer + cur), 1); } // Return the character that was read return c; } else { return 0; } } // Read up to /size/ bytes // If buffer is nullptr, the bytes are simply poped from the buffer int read(Port port, char* buffer, int size, bool readUntil, char end) { struct USART* p = &(_ports[static_cast<int>(port)]); int avail = available(port); int n = 0; for (int i = 0; i < size && i < avail; i++) { // Copy the next char from the port's Rx buffer to the user buffer char c = p->rxBuffer[p->rxBufferCursorR]; if (buffer != nullptr) { buffer[i] = c; } // Keep track of the number of bytes written n++; // Increment the cursor p->rxBufferCursorR++; if (p->rxBufferCursorR == BUFFER_SIZE) { p->rxBufferCursorR = 0; } // If the "read until" mode is selected, exit the loop if the selected byte is found if (readUntil && c == end) { break; } } // If the Rx DMA channel was stopped because the buffer was full, // there is now room available, so it can be restarted if (!DMA::isEnabled(p->rxDMAChannel)) { int cur = p->rxBufferCursorW; int length = 0; if (p->rxBufferCursorR > p->rxBufferCursorW) { length = p->rxBufferCursorR - p->rxBufferCursorW; } else { length = BUFFER_SIZE - p->rxBufferCursorW; } p->rxBufferCursorW += length; if (p->rxBufferCursorW >= BUFFER_SIZE) { p->rxBufferCursorW -= BUFFER_SIZE; } DMA::startChannel(p->rxDMAChannel, (uint32_t)(p->rxBuffer + cur), length); } return n; } // Read up to n bytes until the specified byte is found int readUntil(Port port, char* buffer, int size, char end) { return read(port, buffer, size, true, end); } // Read an int on n bytes (LSByte first, max n = 8 bytes) // and return it as an unsigned long unsigned long readInt(Port port, int nBytes, bool wait) { if (nBytes > 8) { nBytes = 8; } if (wait) { while (available(port) < nBytes); } unsigned long result = 0; for (int i = 0; i < nBytes; i++) { uint8_t c = read(port); result |= c << (i * 8); } return result; } int write(Port port, const char* buffer, int size, bool async) { struct USART* p = &(_ports[static_cast<int>(port)]); // Wait until any previous write is finished waitWriteFinished(port); // If size is not specified, write at most BUFFER_SIZE characters int n = size; if (n < 0 || n > BUFFER_SIZE) { n = BUFFER_SIZE; } // Copy the user buffer into the Tx buffer for (int i = 0; i < n; i++) { // If size is not specified, stops at the end of the string if (size < 0 && buffer[i] == 0) { n = i; break; } p->txBuffer[i] = buffer[i]; } // Start the DMA DMA::startChannel(p->txDMAChannel, (uint32_t)(p->txBuffer), n); // In synchronous mode, wait until this transfer is finished if (!async) { waitWriteFinished(port); } // Return the number of bytes written return n; } int write(Port port, char byte, bool async) { struct USART* p = &(_ports[static_cast<int>(port)]); // Wait until any previous write is finished waitWriteFinished(port); // Write a single byte p->txBuffer[0] = byte; DMA::startChannel(p->txDMAChannel, (uint32_t)(p->txBuffer), 1); // In synchronous mode, wait until this transfer is finished if (!async) { waitWriteFinished(port); } return 1; } int write(Port port, int number, uint8_t base, bool async) { // Write a human-readable number in the given base const int bufferSize = 32; // Enough to write a 32-bits word in binary char buffer[bufferSize]; int cursor = 0; if (base < 2) { return 0; } // Special case : number = 0 if (number == 0) { return write(port, '0'); } // Minus sign if (number < 0) { buffer[cursor] = '-'; cursor++; number = -number; } // Compute the number in reverse int start = cursor; for (; cursor < bufferSize && number > 0; cursor++) { char c = number % base; if (c < 10) { c += '0'; } else { c += 'A' - 10; } buffer[cursor] = c; number = number / base; } // Reverse the result for (int i = 0; i < (cursor - start) / 2; i++) { char c = buffer[start + i]; buffer[start + i] = buffer[cursor - i - 1]; buffer[cursor - i - 1] = c; } buffer[cursor] = 0; cursor++; return write(port, buffer, cursor, async); } int write(Port port, bool boolean, bool async) { // Write a boolean value if (boolean) { return write(port, "true", 4, async); } else { return write(port, "false", 5, async); } } int writeLine(Port port, const char* buffer, int size, bool async) { int written = write(port, buffer, size, async); written += write(port, "\r\n", 2, async); return written; } int writeLine(Port port, char byte, bool async) { int written = write(port, byte, async); written += write(port, "\r\n", 2, async); return written; } int writeLine(Port port, int number, uint8_t base, bool async) { int written = write(port, number, base, async); written += write(port, "\r\n", 2, async); return written; } int writeLine(Port port, bool boolean, bool async) { int written = write(port, boolean, async); written += write(port, "\r\n", 2, async); return written; } void flush(Port port) { struct USART* p = &(_ports[static_cast<int>(port)]); // Stop the Rx channel DMA::stopChannel(p->rxDMAChannel); // Empty the reception buffer p->rxBufferCursorR = 0; p->rxBufferCursorW = 0; // Start the channel again DMA::startChannel(p->rxDMAChannel, (uint32_t)(p->rxBuffer), BUFFER_SIZE); } bool isWriteFinished(Port port) { const uint32_t REG_BASE = USART_BASE + static_cast<int>(port) * USART_REG_SIZE; struct USART* p = &(_ports[static_cast<int>(port)]); // Check if the DMA has finished the transfer and if the Tx buffer is empty return DMA::isFinished(p->txDMAChannel) && (*(volatile uint32_t*)(REG_BASE + OFFSET_CSR)) & (1 << CSR_TXEMPTY); } void waitWriteFinished(Port port) { // Wait for the transfer to finish while (!isWriteFinished(port)); } void waitReadFinished(Port port, unsigned long timeout) { // Wait until no more bytes is received struct USART* p = &(_ports[static_cast<int>(port)]); unsigned long tStart = Core::time(); int n = available(port); while (n == 0) { n = available(port); if (timeout > 0 && Core::time() - tStart > timeout) { return; } } unsigned long byteDuration = 1000000UL / p->baudrate * 8; // in us while (1) { Core::waitMicroseconds(5 * byteDuration); // Wait for 5 times the duration of a byte int n2 = available(port); if (n2 == n) { // No byte received during the delay, the transfer looks finished return; } n = n2; } } void setPin(Port port, PinFunction function, GPIO::Pin pin) { switch (function) { case PinFunction::RX: PINS_RX[static_cast<int>(port)] = pin; break; case PinFunction::TX: PINS_TX[static_cast<int>(port)] = pin; break; case PinFunction::RTS: PINS_RTS[static_cast<int>(port)] = pin; break; case PinFunction::CTS: PINS_CTS[static_cast<int>(port)] = pin; break; } } }