Commit e1907d84 authored by Michael Zehrer's avatar Michael Zehrer
Browse files

expansion of the serial communication interface

parent a29f1591
......@@ -13,7 +13,7 @@
// TODO This is a temporary implementation -> delete ASAP
// IMPORTANT: 'RPI_DEBUG_SERIAL_IDX' isn't save to use here, because it is first defined in bare-metal!
#define PRINTF (modules::serial::get(modules::serial::RPI_DEBUG_SERIAL_IDX)->printf)
#define PANIC (modules::serial::get(modules::serial::RPI_DEBUG_SERIAL_IDX)->printf)
#define PRINTF (modules::serial::get(RPI_DEBUG_SERIAL_IDX)->printf)
#define PANIC (modules::serial::get(RPI_DEBUG_SERIAL_IDX)->printf)
#endif /* ARCTOS_DEBUG_HPP */
......@@ -33,7 +33,7 @@ public:
extern TimeModel sys_time;
// TODO overthink
#define NOW() sys_time.now()
#define NOW() ::arctos::sys_time.now()
} /* namespace arctos */
#endif /* ARCTOS_TIME_HPP */
......@@ -12,79 +12,101 @@
#include <stdint.h>
#include <arctos/printable.hpp>
#include <arctos/time.hpp>
namespace modules {
namespace serial {
typedef enum {
SERIAL_IDX0 = 0,
SERIAL_IDX1,
SERIAL_IDX2,
SERIAL_IDX3,
SERIAL_IDX4,
SERIAL_IDX5,
SERIAL_IDX6,
SERIAL_IDX7,
SERIAL_IDX8,
SERIAL_IDX9
} SERIAL_IDX;
class Serial : public arctos::Printable {
protected:
Serial() {}
Serial() : timeout_(1000 * MILLISECONDS) {}
int64_t timeout_;
public:
/**
* @brief Get the number of bytes (characters) already available for reading
* @return Number of bytes available
* @brief Get the number of bytes already available for reading
* @return Number of bytes available
*/
virtual size_t availableForRead(void) const = 0;
/**
* @brief Get the number of bytes (characters) available for buffered writing without blocking
* @return Number of bytes available
* @brief Get the number of bytes available for buffered writing
* @return Number of bytes available
*/
virtual size_t availableForWrite(void) const = 0;
/**
* @brief Initializes the serial communication at a given rate
* @param speed Baudrate (in bits per second)
* @return Actual baudrate (on some platforms this may be lower than the given one)
* @brief Initializes the serial communication at a given rate
* @param[in] speed Baudrate (in bits per second)
* @return Actual baudrate (might be lower than the given one)
*/
virtual uint32_t begin(const uint32_t speed = 115200) = 0;
virtual uint32_t begin(uint32_t speed = 115200) = 0;
/**
* @brief Disables serial communication,
* allowing the RX and TX pins to be used for general input and output
* @brief Disables serial communication
*
* Allowing the RX and TX pins to be used for general input and output
*/
virtual void end(void) = 0;
/**
* @brief Reads one byte (character)
* @return Character or -1 if no data is available
* @brief Reads one byte
*
* This function doesn't block, if availableForRead() is greater than zero.
* Otherwise it will block until one byte is received, or a timeout occurred.
* @see setTimeout()
*
* @return Byte or -1 if unable to read (timeout)
*/
virtual int read(void) = 0;
/**
* @brief Reads several bytes (characters)
* @param buffer Buffer to store the bytes
* @param length Number of bytes to be read
* @return Number of bytes placed in the buffer
* @brief Reads several bytes
*
* This function doesn't block, if availableForRead() is greater than or
* equal to the given length. Otherwise it will block until the requested
* amount of bytes are received, or a timeout occurred.
* @see setTimeout()
*
* @param[out] buffer Buffer to store the bytes
* @param[in] length Number of bytes to be read
* @return Number of bytes placed in the buffer
*/
virtual size_t read(char *buffer, size_t length) = 0;
virtual size_t read(uint8_t *buffer, size_t length) = 0;
/**
* @brief Writes one byte (character)
* @return Number of bytes written
* @brief Sets the maximum time to wait for serial data
* @param[in] timeout Time in nanoseconds
*/
virtual size_t write(const char c) = 0;
void setTimeout(int64_t timeout) { this->timeout_ = timeout; }
/**
* @brief Writes several bytes (characters)
* @param buffer Buffer containing the bytes
* @param length Number of bytes to be sent
* @return Number of bytes written
* @brief Writes one byte
*
* The transmission is asynchronous. This function doesn't block, if
* availableForWrite() is greater than zero. Otherwise it will block
* until the given byte could be inserted into the transmit buffer.
* @see flush()
*
* @param[in] b Byte to be sent
* @return Number of bytes written
*/
virtual size_t write(uint8_t b) = 0;
/**
* @brief Writes several bytes
*
* The transmission is asynchronous. This function doesn't block, if there
* is enough empty space in the transmit buffer. But if the available buffer
* size is insufficient, it will block until all given data is transferred
* to the buffer.
* @see availableForWrite()
* @see flush()
*
* @param[in] buffer Buffer containing the bytes
* @param[in] length Number of bytes to be sent
* @return Number of bytes written
*/
virtual size_t write(const char *buffer, size_t length) = 0;
virtual size_t write(const uint8_t *buffer, size_t length) = 0;
/**
* @brief Indicates if the serial communication is initialized
* @brief Indicates if the serial communication is initialized
*/
operator bool() const { return false; }
......@@ -99,7 +121,9 @@ public:
* @param[in] c Character to print.
* @return The value one
*/
virtual size_t print(char c) { return this->write(c); }
virtual size_t print(char c) {
return this->write(static_cast<uint8_t>(c));
}
/**
* @brief Print a string.
......@@ -108,10 +132,12 @@ public:
* @param[in] len Length of the input string
* @return Number of chars really printed
*/
virtual size_t print(const char *str, size_t len) { return this->write(str, len); }
virtual size_t print(const char *str, size_t len) {
return this->write(reinterpret_cast<const uint8_t *>(str), len);
}
};
Serial *get(SERIAL_IDX idx);
Serial *get(size_t index);
} /* namespace serial */
} /* namespace modules */
......
......@@ -16,29 +16,18 @@
namespace modules {
namespace serial {
/**
* @brief Get the number of bytes (characters) already available for reading
* @return Number of bytes available
*/
size_t MiniUART::availableForRead() const {
return this->receive_buffer.getElementCount();
return this->receive_buffer_.getElementCount() +
((AUXILIARY->MU_STAT & AUX_MUSTAT_RX_FIFO_LEVEL_MASK) >> AUX_MUSTAT_RX_FIFO_LEVEL_POS);
}
/**
* @brief Get the number of bytes (characters) available for buffered writing without blocking
* @return Number of bytes available
*/
size_t MiniUART::availableForWrite() const {
return this->transmit_buffer.getFreeSpaceCount();
return this->transmit_buffer_.getFreeSpaceCount() +
((AUXILIARY->MU_STAT & AUX_MUSTAT_TX_FIFO_LEVEL_MASK) >> AUX_MUSTAT_TX_FIFO_LEVEL_POS);
}
/**
* @brief Initializes the serial communication at a given rate
* @param speed Baudrate (in bits per second)
* @return Actual baudrate (on some platforms this may be lower than the given one)
*/
uint32_t MiniUART::begin(const uint32_t speed) {
if (!this->config.attr.is_initialized) {
uint32_t MiniUART::begin(uint32_t speed) {
if (!this->config_.attr.is_initialized) {
// activate alternative pin functions
RPI_GPIO_SetMultiplePinFunction(14, 15, FS_ALT5);
// disable the pull up / pull down resistors
......@@ -70,24 +59,22 @@ uint32_t MiniUART::begin(const uint32_t speed) {
// enable transmitter and receiver
AUXILIARY->MU_CNTL = AUX_MUCNTL_RX_ENABLE | AUX_MUCNTL_TX_ENABLE;
this->config.attr.is_initialized = 1;
this->config_.attr.is_initialized = 1;
}
return BSC_CLOCK_FREQ / (8 * (AUXILIARY->MU_BAUD + 1));
}
/**
* @brief Disables serial communication,
* allowing the RX and TX pins to be used for general input and output
*/
void MiniUART::end() {
if (!this->config.attr.is_initialized)
if (!this->config_.attr.is_initialized)
return;
// disable transmit and receive interrupts
AUXILIARY->MU_IER = 0;
// disable transmitter, receiver, hw-flow-ctrl., ...
AUXILIARY->MU_CNTL = 0;
// clear transmit and receive FIFO's
AUXILIARY->MU_IIR = AUX_MUIIR_CLEAR_RX_FIFO | AUX_MUIIR_CLEAR_TX_FIFO;
// disable the miniUART
uint32_t ra = AUXILIARY->ENABLES;
......@@ -97,126 +84,120 @@ void MiniUART::end() {
// reset alternative pin functions
RPI_GPIO_SetMultiplePinFunction(14, 15, FS_INPUT);
this->config.value = 0;
this->receive_buffer.clear();
this->transmit_buffer.clear();
this->config_.value = 0;
this->receive_buffer_.clear();
this->transmit_buffer_.clear();
}
/**
* @brief Waits for the transmission of outgoing serial data to complete
*/
void MiniUART::flush() {
while (!this->transmit_buffer.isEmpty()) {
while (!this->transmit_buffer_.isEmpty()) {
if (!ARM_IsIRQEnabled())
this->write_finish();
this->write_transfer();
}
while (!(AUXILIARY->MU_STAT & AUX_MUSTAT_TX_DONE)) { }
}
/**
* @brief Reads one byte (character)
* @return Character or -1 if no data is available
*/
void MiniUART::read_transfer() {
AUXILIARY->MU_IER &= ~AUX_MUIER_ENABLE_RXIRQ;
while (!this->receive_buffer_.isFull() && (AUXILIARY->MU_STAT & AUX_MUSTAT_SYMBOL_AVAILABLE))
this->receive_buffer_.put(static_cast<uint8_t>(AUXILIARY->MU_IO & 0xFF));
AUXILIARY->MU_IER |= AUX_MUIER_ENABLE_RXIRQ;
}
int MiniUART::read() {
if (!this->config.attr.is_initialized)
if (!this->config_.attr.is_initialized)
return -1;
int64_t time_start = NOW();
uint8_t c;
if (this->receive_buffer.get(c))
return c;
else
return -1;
do {
this->read_transfer();
if (this->receive_buffer_.get(c))
return c;
} while((NOW() - time_start) < this->timeout_);
return -1;
}
/**
* @brief Reads several bytes (characters)
* @param buffer Buffer to store the bytes
* @param length Number of bytes to be read
* @return Number of bytes placed in the buffer
*/
size_t MiniUART::read(char *buffer, size_t length) {
if (length == 0 || !this->config.attr.is_initialized)
size_t MiniUART::read(uint8_t *buffer, size_t length) {
if (length == 0 || !this->config_.attr.is_initialized)
return 0;
size_t read_cnt = 0;
uint8_t *p = this->receive_buffer.getBufferToRead(read_cnt);
if (p != nullptr && read_cnt > 0) {
if (read_cnt > length)
read_cnt = length;
memcpy(buffer, p, read_cnt);
this->receive_buffer.readConcluded(read_cnt);
read_cnt += this->read(buffer + read_cnt, length - read_cnt);
return read_cnt;
} else
return 0;
int64_t time_start = NOW();
size_t bytes_readable;
size_t bytes_read = 0;
do {
this->read_transfer();
uint8_t *p = this->receive_buffer_.getBufferToRead(bytes_readable);
if (bytes_readable > 0) {
time_start = NOW();
if (bytes_readable > length)
bytes_readable = length;
memcpy(buffer, p, bytes_readable);
this->receive_buffer_.readConcluded(bytes_readable);
buffer += bytes_readable;
length -= bytes_readable;
bytes_read += bytes_readable;
}
} while (length > 0 && (NOW() - time_start) < this->timeout_);
return bytes_read;
}
/**
* @brief Finishes an write operation
*
* Both write-functions are writing only to the transmission buffer!
* This helper function is called afterwards to transfer the data to the miniUART
*/
void MiniUART::write_finish() {
void MiniUART::write_transfer() {
uint8_t ch;
AUXILIARY->MU_IER &= ~AUX_MUIER_ENABLE_TXIRQ;
while (AUXILIARY->MU_STAT & AUX_MUSTAT_SPACE_AVAILABLE) {
if (this->transmit_buffer.get(ch))
if (this->transmit_buffer_.get(ch))
AUXILIARY->MU_IO = ch;
else
break;
}
if (!this->transmit_buffer.isEmpty() && ARM_IsIRQEnabled())
if (!this->transmit_buffer_.isEmpty() && ARM_IsIRQEnabled())
AUXILIARY->MU_IER |= AUX_MUIER_ENABLE_TXIRQ;
}
/**
* @brief Writes one byte (character)
* @return Number of bytes written
*/
size_t MiniUART::write(const char c) {
if (!this->config.attr.is_initialized || !this->transmit_buffer.put(c))
size_t MiniUART::write(uint8_t b) {
if (!this->config_.attr.is_initialized)
return 0;
this->write_finish();
bool inserted;
do {
inserted = this->transmit_buffer_.put(b);
this->write_transfer();
} while (!inserted);
return 1;
}
/**
* @brief Writes several bytes (characters)
* @param buffer Buffer containing the bytes
* @param length Number of bytes to be sent
* @return Number of bytes written
*/
size_t MiniUART::write(const char *buffer, size_t length) {
if (length == 0 || !this->config.attr.is_initialized)
size_t MiniUART::write(const uint8_t *buffer, size_t length) {
if (length == 0 || !this->config_.attr.is_initialized)
return 0;
size_t write_cnt;
uint8_t* p = this->transmit_buffer.getBufferToWrite(write_cnt);
size_t bytes_writable;
size_t bytes_written = 0;
do {
uint8_t *p = this->transmit_buffer_.getBufferToWrite(bytes_writable);
if (bytes_writable > 0) {
if (bytes_writable > length)
bytes_writable = length;
if (p != nullptr && write_cnt > 0) {
if (write_cnt > length)
write_cnt = length;
memcpy(p, buffer, bytes_writable);
this->transmit_buffer_.writeConcluded(bytes_writable);
memcpy(p, buffer, write_cnt);
this->transmit_buffer.writeConcluded(write_cnt);
write_cnt += this->write(buffer + write_cnt, length - write_cnt);
this->write_finish();
return write_cnt;
} else { return 0; }
buffer += bytes_writable;
length -= bytes_writable;
bytes_written += bytes_writable;
}
this->write_transfer();
} while (length > 0);
return bytes_written;
}
/**
* @brief Indicates if the serial communication is initialized
*/
MiniUART::operator bool() {
return this->config.attr.is_initialized;
return this->config_.attr.is_initialized;
}
MiniUART *MiniUART::instance() {
......@@ -235,15 +216,15 @@ void UART1_IRQHandler() {
while (!(iir & AUX_MUIIR_IRQ_PENDING)) {
if ((iir & AUX_MUIIR_IRQID_MASK) == AUX_MUIIR_IRQID_RX_READY) {
c = (uint8_t)(AUXILIARY->MU_IO & 0xFF); // read byte from rx fifo
if (!mini->receive_buffer.put(c))
mini->config.attr.rx_error++; // BlockFifo Overflow
if (!mini->receive_buffer_.put(c))
mini->config_.attr.rx_error++; // BlockFifo Overflow
// TODO
// ...->upCallDataReady();
}
if ((iir & AUX_MUIIR_IRQID_MASK) == AUX_MUIIR_IRQID_TX_EMPTY) {
uint8_t sent = 0;
do {
if (mini->transmit_buffer.get(c)) {
if (mini->transmit_buffer_.get(c)) {
AUXILIARY->MU_IO = c;
sent++;
}
......
......@@ -40,83 +40,118 @@ class MiniUART : public Serial {
friend void UART1_IRQHandler();
protected:
MiniUARTConfig config;
MiniUART() : config_({ .value = 0 }) {}
arctos::BlockFifo<uint8_t, SERIAL_BUFFER_SIZE> receive_buffer;
arctos::BlockFifo<uint8_t, SERIAL_BUFFER_SIZE> transmit_buffer;
MiniUART() : config({ .value = 0 }) {}
/**
* @brief Starts the transfer from the hardware (RX)
*
* Both read(...)-functions are reading only from the receive buffer!
* This helper function is called before, to transfer the data
*/
void read_transfer(void);
/**
* @brief Finishes a write operation
* @brief Starts the transfer to the hardware (TX)
*
* Both write(...)-functions are writing only to the transmission buffer!
* This helper function is called afterwards to transfer the data
* Both write(...)-functions are writing only to the transmit buffer!
* This helper function is called afterwards, to transfer the data
*/
void write_finish(void);
void write_transfer(void);
MiniUARTConfig config_;
arctos::BlockFifo<uint8_t, SERIAL_BUFFER_SIZE> receive_buffer_;
arctos::BlockFifo<uint8_t, SERIAL_BUFFER_SIZE> transmit_buffer_;
public:
/**
* @brief Get the number of bytes (characters) already available for reading
* @return Number of bytes available
* @brief Get the number of bytes already available for reading
* @return Number of bytes available
*/
size_t availableForRead(void) const;
/**
* @brief Get the number of bytes (characters) available for buffered writing without blocking
* @return Number of bytes available
* @brief Get the number of bytes available for buffered writing
* @return Number of bytes available
*/
size_t availableForWrite(void) const;
/**
* @brief Initializes the serial communication at a given rate
* @param speed Baudrate (in bits per second)
* @return Actual baudrate (on some platforms this may be lower than the given one)
* @brief Initializes the serial communication at a given rate
* @param[in] speed Baudrate (in bits per second)
* @return Actual baudrate (might be lower than the given one)
*/
uint32_t begin(const uint32_t speed = 115200);
uint32_t begin(uint32_t speed = 115200);
/**
* @brief Disables serial communication,
* allowing the RX and TX pins to be used for general input and output
* @brief Disables serial communication
*
* Allowing the RX and TX pins to be used for general input and output
*/
void end(void);
/**
* @brief Waits for the transmission of outgoing serial data to complete
* @brief Waits for the transmission of outgoing serial data to complete
*/
void flush(void);
/**
* @brief Reads one byte (character)
* @return Character or -1 if no data is available
* @brief Reads one byte
*
* This function doesn't block, if availableForRead() is greater than zero.
* Otherwise it will block until one byte is received, or a timeout occurred.
* @see setTimeout()
*
* @return Byte or -1 if unable to read (timeout)
*/
int read(void);
/**
* @brief Reads several bytes (characters)
* @param buffer Buffer to store the bytes
* @param length Number of bytes to be read
* @return Number of bytes placed in the buffer
* @brief Reads several bytes
*
* This function doesn't block, if availableForRead() is greater than or
* equal to the given length. Otherwise it will block until the requested
* amount of bytes are received, or a timeout occurred.
* @see setTimeout()
*
* @param[out] buffer Buffer to store the bytes
* @param[in] length Number of bytes to be read
* @return Number of bytes placed in the buffer
*/
size_t read(char *buffer, size_t length);
size_t read(uint8_t *buffer, size_t length);
/**
* @brief Writes one byte (character)
* @return Number of bytes written
* @brief Writes one byte
*
* The transmission is asynchronous. This function doesn't block, if
* availableForWrite() is greater than zero. Otherwise it will block
* until the given byte could be inserted into the transmit buffer.
* @see flush()
*
* @param[in] b Byte to be sent
* @return Number of bytes written
*/
size_t write(const char c);
size_t write(uint8_t b);
/**
* @brief Writes several bytes (characters)
* @param buffer Buffer containing the bytes
* @param length Number of bytes to be sent
* @return Number of bytes written
* @brief Writes several bytes
*
* The transmission is asynchronous. This function doesn't block, if there
* is enough empty space in the transmit buffer. But if the available buffer
* size is insufficient, it will block until all given data is transferred