24#include "boards/board.h"
39#define REGISTER_UARX_PCI_ISR(RX, PCI_NUM) \
40 ISR(CAT3(PCINT, PCI_NUM, _vect)) \
42 serial::soft::isr_handler::check_uarx_pci<PCI_NUM, RX>(); \
51#define REGISTER_UARX_INT_ISR(RX, INT_NUM) \
52 ISR(CAT3(INT, INT_NUM, _vect)) \
54 serial::soft::isr_handler::check_uarx_int<INT_NUM, RX>(); \
64#define REGISTER_UART_PCI_ISR(RX, TX, PCI_NUM) \
65 ISR(CAT3(PCINT, PCI_NUM, _vect)) \
67 serial::soft::isr_handler::check_uart_pci<PCI_NUM, RX, TX>(); \
77#define REGISTER_UART_INT_ISR(RX, TX, INT_NUM) \
78 ISR(CAT3(INT, INT_NUM, _vect)) \
80 serial::soft::isr_handler::check_uart_int<INT_NUM, RX, TX>(); \
116 AbstractUATX(
const AbstractUATX&) =
delete;
117 AbstractUATX& operator=(
const AbstractUATX&) =
delete;
119 template<u
int8_t SIZE_TX>
120 explicit AbstractUATX(
char (&output)[SIZE_TX]) : obuf_{output} {}
128 void compute_times(uint32_t rate, StopBits stop_bits)
131 uint16_t bit_time = uint16_t(F_CPU / rate);
137 start_bit_tx_time_ = (bit_time - 5) / 4;
146 interbit_tx_time_ = (bit_time - 9) / 4;
149 stop_bit_tx_time_ = (bit_time / 4);
150 if (stop_bits == StopBits::TWO) stop_bit_tx_time_ *= 2;
152 stop_bit_tx_time_ = (bit_time / 4) * 5 / 4;
155 static Parity calculate_parity(Parity parity, uint8_t value)
157 if (parity == Parity::NONE)
return Parity::NONE;
161 if (value & 0x01) odd = !odd;
164 return (odd ? Parity::ODD : Parity::EVEN);
172 void check_overflow(Errors&
errors)
174 errors.queue_overflow = obuf_.overflow();
177 template<board::DigitalPin DPIN>
void write(Parity parity, uint8_t value)
179 synchronized write_<DPIN>(parity, value);
181 template<board::DigitalPin DPIN>
void write_(Parity parity, uint8_t value);
188 uint16_t interbit_tx_time_;
189 uint16_t start_bit_tx_time_;
190 uint16_t stop_bit_tx_time_;
193 template<board::DigitalPin DPIN>
void AbstractUATX::write_(
Parity parity, uint8_t value)
197 Parity parity_bit = calculate_parity(parity, value);
202 _delay_loop_2(start_bit_tx_time_);
203 for (uint8_t bit = 0; bit < 8; ++bit)
210 _delay_loop_2(interbit_tx_time_);
215 if (parity_bit == parity)
219 _delay_loop_2(interbit_tx_time_);
223 _delay_loop_2(stop_bit_tx_time_);
238 template<board::DigitalPin TX_>
class UATX :
public AbstractUATX,
public UARTErrors
250 template<u
int8_t SIZE_TX>
explicit UATX(
char (&output)[SIZE_TX]) : AbstractUATX{output}
268 compute_times(rate, stop_bits);
269 out_().queue().unlock();
282 out_().queue().lock();
288 if (&obuf != &out_())
return false;
291 while (out_().queue().pull(value)) write<TX>(parity_, uint8_t(value));
315 AbstractUARX(
const AbstractUARX&) =
delete;
316 AbstractUARX& operator=(
const AbstractUARX&) =
delete;
318 template<u
int8_t SIZE_RX>
explicit AbstractUARX(
char (&input)[SIZE_RX]) : ibuf_{input} {}
325 void compute_times(uint32_t rate,
UNUSED bool has_parity,
UNUSED StopBits stop_bits)
328 uint16_t bit_time = uint16_t(F_CPU / rate);
346 start_bit_rx_time_ = compute_delay((3 * bit_time) / 2, 3 + 2 + 4 + 48 + 2 + 4 + 8);
353 interbit_rx_time_ = compute_delay(bit_time, 10);
360 template<board::DigitalPin DPIN>
void pin_change(Parity parity, Errors&
errors);
363 static constexpr uint16_t compute_delay(uint16_t total_cycles, uint16_t less_cycles)
366 return (total_cycles > less_cycles) ? ((total_cycles - less_cycles + 3) / 4) : 1;
373 uint16_t interbit_rx_time_;
374 uint16_t start_bit_rx_time_;
377 template<board::DigitalPin DPIN>
void AbstractUARX::pin_change(
Parity parity, Errors&
errors)
381 if (RX::value())
return;
386 _delay_loop_2(start_bit_rx_time_);
388 for (uint8_t i = 0; i < 8; ++i)
397 _delay_loop_2(interbit_rx_time_);
403 bool parity_bit = (parity ==
Parity::ODD ? !odd : odd);
405 errors.parity_error = (RX::value() != parity_bit);
406 _delay_loop_2(interbit_rx_time_);
411 errors.frame_error =
true;
413 if (
errors.has_errors == 0)
414 errors.queue_overflow = !in_().queue().push_(
char(value));
419 template<
typename T, T IRQ>
class UARX {};
423 template<board::ExternalInterruptPin RX_>
452 template<u
int8_t SIZE_RX>
453 explicit UARX(
char (&input)[SIZE_RX],
INT_TYPE& enabler) : AbstractUARX{input}, int_{enabler}
471 AbstractUARX::compute_times(rate, parity != Parity::NONE, stop_bits);
485 if (buffer_handling == BufferHandling::CLEAR)
486 in_().queue().clear();
492 this->pin_change<RX>(parity_,
errors());
500 friend struct isr_handler;
504 template<
typename T, T IRQ, board::DigitalPin TX>
class UART {};
508 template<board::ExternalInterruptPin RX_, board::DigitalPin TX_>
509 class UART<
board::ExternalInterruptPin, RX_, TX_> :
public AbstractUARX,
public AbstractUATX,
public UARTErrors
538 template<u
int8_t SIZE_RX, u
int8_t SIZE_TX>
539 explicit UART(
char (&input)[SIZE_RX],
char (&output)[SIZE_TX],
INT_TYPE& enabler)
540 : AbstractUARX{input}, AbstractUATX{output}, int_{enabler}
557 out_().queue().unlock();
559 AbstractUARX::compute_times(rate, parity != Parity::NONE, stop_bits);
560 AbstractUATX::compute_times(rate, stop_bits);
574 if (buffer_handling == BufferHandling::CLEAR)
575 in_().queue().clear();
576 out_().queue().lock();
582 if (&obuf != &out_())
return false;
585 while (out_().queue().pull(value)) write<TX>(parity_, uint8_t(value));
591 this->pin_change<RX>(parity_,
errors());
601 friend struct isr_handler;
606 template<board::InterruptPin RX_>
class UARX<
board::InterruptPin, RX_> :
public AbstractUARX,
public UARTErrors
634 template<u
int8_t SIZE_RX>
636 : AbstractUARX{input}, pci_{enabler}
654 AbstractUARX::compute_times(rate, parity != Parity::NONE, stop_bits);
655 pci_.template enable_pin<RX_>();
667 pci_.template disable_pin<RX_>();
668 if (buffer_handling == BufferHandling::CLEAR)
669 in_().queue().clear();
675 this->pin_change<RX>(parity_,
errors());
683 friend struct isr_handler;
687 template<board::InterruptPin RX_, board::DigitalPin TX_>
688 class UART<
board::InterruptPin, RX_, TX_> :
public AbstractUARX,
public AbstractUATX,
public UARTErrors
717 template<u
int8_t SIZE_RX, u
int8_t SIZE_TX>
718 explicit UART(
char (&input)[SIZE_RX],
char (&output)[SIZE_TX],
PCI_TYPE& enabler)
719 : AbstractUARX{input}, AbstractUATX{output}, pci_{enabler}
736 out_().queue().unlock();
738 AbstractUARX::compute_times(rate, parity != Parity::NONE, stop_bits);
739 AbstractUATX::compute_times(rate, stop_bits);
740 pci_.template enable_pin<RX_>();
753 pci_.template disable_pin<RX_>();
754 if (buffer_handling == BufferHandling::CLEAR)
755 in_().queue().clear();
756 out_().queue().lock();
762 if (&obuf != &out_())
return false;
765 while (out_().queue().pull(value)) write<TX>(parity_, uint8_t(value));
771 this->pin_change<RX>(parity_,
errors());
781 friend struct isr_handler;
800 template<board::ExternalInterruptPin RX_>
using UARX_EXT = UARX<board::ExternalInterruptPin, RX_>;
814 template<board::InterruptPin RX_>
using UARX_PCI = UARX<board::InterruptPin, RX_>;
832 template<board::ExternalInterruptPin RX_, board::DigitalPin TX_>
833 using UART_EXT = UART<board::ExternalInterruptPin, RX_, TX_>;
851 template<board::InterruptPin RX_, board::DigitalPin TX_>
852 using UART_PCI = UART<board::InterruptPin, RX_, TX_>;
857 template<u
int8_t PCI_NUM_, board::InterruptPin RX_>
static void check_uarx_pci()
859 interrupt::isr_handler_pci::check_pci_pins<PCI_NUM_, RX_>();
861 interrupt::HandlerHolder<UARX>::handler()->on_pin_change();
864 template<u
int8_t INT_NUM_, board::ExternalInterruptPin RX_>
static void check_uarx_int()
866 interrupt::isr_handler_int::check_int_pin<INT_NUM_, RX_>();
868 interrupt::HandlerHolder<UARX>::handler()->on_pin_change();
871 template<u
int8_t PCI_NUM_, board::InterruptPin RX_, board::DigitalPin TX_>
static void check_uart_pci()
873 interrupt::isr_handler_pci::check_pci_pins<PCI_NUM_, RX_>();
875 interrupt::HandlerHolder<UART>::handler()->on_pin_change();
878 template<u
int8_t INT_NUM_, board::ExternalInterruptPin RX_, board::DigitalPin TX_>
static void check_uart_int()
880 interrupt::isr_handler_int::check_int_pin<INT_NUM_, RX_>();
882 interrupt::HandlerHolder<UART>::handler()->on_pin_change();
892 template<board::DigitalPin TX>
struct UART_trait<soft::UATX<TX>>
894 static constexpr bool IS_UART =
true;
895 static constexpr bool IS_HW_UART =
false;
896 static constexpr bool IS_SW_UART =
true;
897 static constexpr bool HAS_TX =
true;
898 static constexpr bool HAS_RX =
false;
900 template<board::InterruptPin RX>
struct UART_trait<soft::
UARX_PCI<RX>>
902 static constexpr bool IS_UART =
true;
903 static constexpr bool IS_HW_UART =
false;
904 static constexpr bool IS_SW_UART =
true;
905 static constexpr bool HAS_TX =
false;
906 static constexpr bool HAS_RX =
true;
908 template<board::ExternalInterruptPin RX>
struct UART_trait<soft::
UARX_EXT<RX>>
910 static constexpr bool IS_UART =
true;
911 static constexpr bool IS_HW_UART =
false;
912 static constexpr bool IS_SW_UART =
true;
913 static constexpr bool HAS_TX =
false;
914 static constexpr bool HAS_RX =
true;
916 template<board::DigitalPin TX, board::InterruptPin RX>
struct UART_trait<soft::
UART_PCI<RX, TX>>
918 static constexpr bool IS_UART =
true;
919 static constexpr bool IS_HW_UART =
false;
920 static constexpr bool IS_SW_UART =
true;
921 static constexpr bool HAS_TX =
true;
922 static constexpr bool HAS_RX =
true;
924 template<board::DigitalPin TX, board::ExternalInterruptPin RX>
struct UART_trait<soft::
UART_EXT<RX, TX>>
926 static constexpr bool IS_UART =
true;
927 static constexpr bool IS_HW_UART =
false;
928 static constexpr bool IS_SW_UART =
true;
929 static constexpr bool HAS_TX =
true;
930 static constexpr bool HAS_RX =
true;
API that manipulates a given digital IO pin of a the target MCU.
Handler of an External Interrupt.
Holder of latest UART errors.
UART(char(&input)[SIZE_RX], char(&output)[SIZE_TX], INT_TYPE &enabler)
Construct a new software serial receiver/transceiver and provide it with 2 buffers,...
void begin(uint32_t rate, Parity parity=Parity::NONE, StopBits stop_bits=StopBits::ONE)
Enable the receiver/transceiver.
typename interrupt::INTSignal< RX_ > INT_TYPE
The interrupt::INTSignal type for RX_ pin, if it is a External Interrupt pin.
void end(BufferHandling buffer_handling=BufferHandling::KEEP)
Stop all transmissions and receptions.
void begin(uint32_t rate, Parity parity=Parity::NONE, StopBits stop_bits=StopBits::ONE)
Enable the receiver/transceiver.
void end(BufferHandling buffer_handling=BufferHandling::KEEP)
Stop all transmissions and receptions.
UART(char(&input)[SIZE_RX], char(&output)[SIZE_TX], PCI_TYPE &enabler)
Construct a new software serial receiver/transceiver and provide it with 2 buffers,...
interrupt::PCI_SIGNAL< RX_ > PCI_TYPE
The interrupt::PCISignal type for RX_ pin, if it is a PinChangeInterrupt pin.
UARX(char(&input)[SIZE_RX], INT_TYPE &enabler)
Construct a new software serial receiver and provide it with a buffer for interrupt-based reception.
void end(BufferHandling buffer_handling=BufferHandling::KEEP)
Stop reception.
void begin(uint32_t rate, Parity parity=Parity::NONE, StopBits stop_bits=StopBits::ONE)
Enable the receiver.
typename interrupt::INTSignal< RX_ > INT_TYPE
The interrupt::INTSignal type for RX_ pin, if it is a External Interrupt pin.
void end(BufferHandling buffer_handling=BufferHandling::KEEP)
Stop reception.
interrupt::PCI_SIGNAL< RX_ > PCI_TYPE
The interrupt::PCISignal type for RX_ pin, if it is a PinChangeInterrupt pin.
void begin(uint32_t rate, Parity parity=Parity::NONE, StopBits stop_bits=StopBits::ONE)
Enable the receiver.
UARX(char(&input)[SIZE_RX], PCI_TYPE &enabler)
Construct a new software serial receiver and provide it with a buffer for interrupt-based reception.
Software-emulated serial transmitter API.
void end(UNUSED BufferHandling buffer_handling=BufferHandling::KEEP)
Stop all transmissions.
UATX(char(&output)[SIZE_TX])
Construct a new software serial transmitter and provide it with a buffer for payload transmission.
static constexpr const board::DigitalPin TX
The board::DigitalPin to which transmitted signal is sent.
void begin(uint32_t rate, Parity parity=Parity::NONE, StopBits stop_bits=StopBits::ONE)
Enable the transmitter.
Input stream wrapper to provide formatted input API, a la C++.
Input API based on a ring buffer.
Output stream wrapper to provide formatted output API, a la C++.
Output API based on a ring buffer.
#define UNUSED
Specific GCC attribute to declare an argument or variable unused, so that the compiler does not emit ...
General Purpose (digital) Input Output API.
General API for handling External Interrupt pins.
General API for handling AVR interrupt vectors.
Defines all types and constants specific to support a specific MCU target.
DigitalPin
Defines all available digital input/output pins of the target MCU.
This namespace defines common errors that can be returned by some FastArduino API methods,...
typename FastPinType< DPIN_ >::TYPE FAST_PIN
Useful alias type to the FastPin type matching a given board::DigitalPin.
@ OUTPUT
Digital pin is configured as output.
@ INPUT
Digital pin is configured as high-impedance (open drain) input.
typename PCIType< PIN >::TYPE PCI_SIGNAL
Useful alias type to the PCISignal type matching a given board::InterruptPin.
void register_handler(Handler &handler)
Register a class instance containing methods that shall be called back by an ISR.
Defines API types used by software UART features.
UARX< board::InterruptPin, RX_ > UARX_PCI
Software-emulated serial receiver API.
UART< board::ExternalInterruptPin, RX_, TX_ > UART_EXT
Software-emulated serial receiver/transceiver API.
UARX< board::ExternalInterruptPin, RX_ > UARX_EXT
Software-emulated serial receiver API.
UART< board::InterruptPin, RX_, TX_ > UART_PCI
Software-emulated serial receiver/transceiver API.
Defines all API for UART features.
BufferHandling
How the TX/RX buffer should be handled when ending transmission (see end() methods) on UATX/UARX.
@ KEEP
Stop transmission immediately, keep buffer as-is.
StopBits
Number of stop bits used for serial transmission.
Parity
Parity used for serial transmission.
General API for handling Pin Change Interrupts.
#define DECL_OSTREAMBUF_LISTENERS_FRIEND
This macro shall be used in a class containing a private callback method bool on_put(streams::ostream...
C++-like std::iostream facilities.
Common definitions for serial API.
General utilities API that have broad application in programs.