Compare commits

..

37 Commits

Author SHA1 Message Date
419b86999d Make use of C++ standard library 2022-05-29 16:15:11 +02:00
a5f8e8e3d7 Fix volatile compound assignments being deprecated in C++20 2022-05-29 14:46:32 +02:00
119de32445 Adapt to moved type submodule 2020-05-16 17:42:52 +02:00
8f88cdccea Fix misaligned ifdef-endif pair 2020-04-13 17:17:17 +02:00
dfb076cda8 Change interrupt handler technique to not rely on function pointer and instead use user-facing macros 2020-04-13 16:43:04 +02:00
6d9ef6e4be Make variables const 2020-04-13 16:41:16 +02:00
bcd18db494 Fix double speed flag not being cleared if not used 2020-04-13 13:06:11 +02:00
04b6782ec4 Add automatic selection of double speed based on error 2020-04-12 23:57:16 +02:00
ae03c8d43e Adapt to new std compliant naming of numeric_limits 2020-04-07 19:15:05 +02:00
41b9ef74f9 Moved uart utils to separate type submodule 2020-04-07 03:50:29 +02:00
6f592dd098 Fixed missing const qualifier 2020-04-06 21:45:20 +02:00
fa0a65a94c Capitalized local constexpr variables to be more consistent 2020-04-05 03:36:05 +02:00
0e128bcb7d Made variable const 2020-04-05 03:30:22 +02:00
0a52110d47 Moved buffer closer to where needed 2020-04-05 03:28:45 +02:00
f751833a88 Made variables constexpr 2020-04-05 03:25:25 +02:00
1bd49f65fa Fixed template variable capitalization 2020-04-05 03:23:12 +02:00
cb436b11a8 Changed static assert to indicate impossible usage 2020-02-21 17:46:28 +01:00
2a2b9b8817 Added MIT license file 2020-02-01 15:36:20 +01:00
0532bf48b0 Changed library so that user has to explicitly include hardware header with int vector define set to get ISR 2019-08-15 18:58:25 +02:00
f6df6a6a18 Undid accidental regression 2019-08-15 18:49:29 +02:00
1bdc06a325 Switched to include interrupt vectors by default 2019-08-15 18:12:37 +02:00
ddf105a175 Made library header only again and provided way to disable interrupt vectors 2019-08-15 18:07:11 +02:00
2fd05483ee Replaced workaround with provided macro 2019-08-15 17:48:41 +02:00
16c9015f43 Replaced maybe_unused with unnamed parameter 2019-08-14 19:55:06 +02:00
7c21664fe4 Refactored code to get rid of code duplication 2019-08-14 19:49:42 +02:00
e326e40b38 Fixed blocking on full rx buffer and implemented support for ATmega328P 2019-08-14 18:58:21 +02:00
8d028aa635 Changed git ignore to ignore make output 2019-08-11 10:04:15 +02:00
c4700ed824 Fixed non-compliant use of constexpr for pointers 2019-08-10 14:12:10 +02:00
1ee9bc8ca4 Removed sign from maxNumDigits, because sign is handled separately anyway 2019-08-07 19:59:48 +02:00
1c026e8eb3 Updated git ignore file 2019-08-05 21:18:36 +02:00
c4f38cbcdf Changed template parameter order 2019-08-05 20:05:59 +02:00
87e6936051 Added enabling of interrupts for interrupt driven uart 2019-08-05 17:59:33 +02:00
1d633c538e Implemented numeric limits for floating point 2019-08-05 17:56:10 +02:00
231fc0de48 Changed clock header to cpp file extension 2019-08-03 20:20:20 +02:00
6438aa81c1 Added padding and case selection to txNumber 2019-08-03 19:49:01 +02:00
8153696309 Fixed flushing not blocking correctly 2019-08-03 18:45:51 +02:00
9f9f7a8de5 Added flushing of transmit buffer 2019-08-03 17:52:28 +02:00
11 changed files with 524 additions and 575 deletions

8
.gitignore vendored
View File

@@ -2,4 +2,10 @@
Release
Debug
*.componentinfo.xml
avrdude.bat
*.elf
*.o
*.hex
*.srec
*.eeprom
*.lss
*.map

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 BlackMark
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,6 +1,6 @@
#pragma once
#include <stdint.h>
#include <cstdint>
namespace uart {
@@ -27,17 +27,17 @@ namespace detail {
template <DataBits dataBits>
struct choose_data_type {
using type = uint8_t;
using type = std::uint8_t;
};
template <>
struct choose_data_type<DataBits::NINE> {
using type = uint16_t;
using type = std::uint16_t;
};
} // namespace detail
template <uint32_t baudRate = 9600, DataBits dataBits = DataBits::EIGHT, Parity parity = Parity::NONE,
template <std::uint32_t baudRate = 9600, DataBits dataBits = DataBits::EIGHT, Parity parity = Parity::NONE,
StopBits stopBits = StopBits::ONE>
struct Config {
static constexpr auto BAUD_RATE = baudRate;

View File

@@ -1,16 +1,14 @@
#pragma once
#include "../clock.h"
#include "../clock.hpp"
#include <stdint.h>
#define FORCE_INLINE __attribute__((always_inline))
#include <cmath>
#include <cstdint>
namespace uart {
enum class Mode {
ASYNCHRONOUS,
ASYNCHRONOUS_2X,
SYNCHRONOUS_MASTER,
SYNCHRONOUS_SLAVE,
SPI,
@@ -23,90 +21,160 @@ enum class Driven {
namespace detail {
template <class Registers, typename CtrlFlagsA, typename CtrlFlagsB, typename CtrlFlagsC, class cfg, Mode mode,
Driven driven>
using reg_ptr_t = volatile std::uint8_t *;
template <std::uintptr_t Address>
static inline reg_ptr_t getRegPtr()
{
return reinterpret_cast<reg_ptr_t>(Address);
}
template <typename data_t, std::uint8_t Size>
struct RingBuffer {
std::uint8_t head;
std::uint8_t tail;
data_t buf[Size];
};
template <class Registers, typename CtrlFlagsA, typename CtrlFlagsB, typename CtrlFlagsC, class cfg, Driven driven,
Mode mode>
class Hardware {
public:
static void init() FORCE_INLINE
[[gnu::always_inline]] static void init()
{
constexpr auto baudVal = calcBaud();
constexpr auto AbsDoubleError = std::fabs(calcBaudError<true>());
constexpr auto AbsNormalError = std::fabs(calcBaudError<false>());
static_assert(AbsDoubleError <= 3.0 || AbsNormalError <= 3.0, "Baud rate error over 3%, probably unusable");
*Registers::BAUD_REG_H = static_cast<uint8_t>(baudVal >> 8);
*Registers::BAUD_REG_L = static_cast<uint8_t>(baudVal);
constexpr auto UseDoubleSpeed = (AbsDoubleError < AbsNormalError);
constexpr auto BaudVal = calcBaudVal<UseDoubleSpeed>();
constexpr auto dataBitsVal = calcDataBits();
constexpr auto parityVal = calcParity();
constexpr auto stopBitsVal = calcStopBits();
constexpr auto modeVal = calcMode();
constexpr auto enableRx = calcRxState<true>();
constexpr auto enableTx = calcTxState<true>();
constexpr auto interruptVal = calcInterrupt();
*getRegPtr<Registers::BAUD_REG_H_ADDR>() = static_cast<std::uint8_t>(BaudVal >> 8);
*getRegPtr<Registers::BAUD_REG_L_ADDR>() = static_cast<std::uint8_t>(BaudVal);
constexpr uint8_t controlRegB = dataBitsVal.regBVal | enableRx | enableTx | interruptVal;
constexpr uint8_t controlRegC = dataBitsVal.regCVal | parityVal | stopBitsVal | modeVal;
constexpr auto DataBitsValues = calcDataBits();
constexpr auto ParityVal = calcParity();
constexpr auto StopBitsVal = calcStopBits();
constexpr auto ModeVal = calcMode();
constexpr auto EnableRx = calcRxState<true>();
constexpr auto EnableTx = calcTxState<true>();
constexpr auto InterruptVal = calcInterrupt();
*Registers::CTRL_STAT_REG_B = controlRegB;
*Registers::CTRL_STAT_REG_C = controlRegC;
constexpr std::uint8_t ControlRegB = DataBitsValues.regBVal | EnableRx | EnableTx | InterruptVal;
constexpr std::uint8_t ControlRegC = DataBitsValues.regCVal | ParityVal | StopBitsVal | ModeVal;
auto ctrlStatRegA = getRegPtr<Registers::CTRL_STAT_REG_A_ADDR>();
if constexpr (UseDoubleSpeed)
*ctrlStatRegA = *ctrlStatRegA | (1 << CtrlFlagsA::SPEED_2X);
else
*ctrlStatRegA = *ctrlStatRegA & ~(1 << CtrlFlagsA::SPEED_2X);
*getRegPtr<Registers::CTRL_STAT_REG_B_ADDR>() = ControlRegB;
*getRegPtr<Registers::CTRL_STAT_REG_C_ADDR>() = ControlRegC;
}
static bool rxByteBlocking(typename cfg::data_t &byte) FORCE_INLINE
[[gnu::always_inline]] static bool rxByteBlocking(typename cfg::data_t &byte)
{
if (*Registers::CTRL_STAT_REG_A & (1 << CtrlFlagsA::RECEIVE_COMPLETE)) {
byte = *Registers::IO_REG;
if (*getRegPtr<Registers::CTRL_STAT_REG_A_ADDR>() & (1 << CtrlFlagsA::RECEIVE_COMPLETE)) {
byte = *getRegPtr<Registers::IO_REG_ADDR>();
return true;
}
return false;
}
static typename cfg::data_t rxByteInterrupt() FORCE_INLINE
[[gnu::always_inline]] static typename cfg::data_t rxByteInterrupt()
{
return *Registers::IO_REG;
return *getRegPtr<Registers::IO_REG_ADDR>();
}
static void txByteBlocking(const typename cfg::data_t &byte) FORCE_INLINE
[[gnu::always_inline]] static bool txEmpty()
{
while (!(*Registers::CTRL_STAT_REG_A & (1 << CtrlFlagsA::DATA_REG_EMPTY)))
return *getRegPtr<Registers::CTRL_STAT_REG_A_ADDR>() & (1 << CtrlFlagsA::DATA_REG_EMPTY);
}
[[gnu::always_inline]] static bool txComplete()
{
return *getRegPtr<Registers::CTRL_STAT_REG_A_ADDR>() & (1 << CtrlFlagsA::TRANSMIT_COMPLETE);
}
[[gnu::always_inline]] static void clearTxComplete()
{
*getRegPtr<Registers::CTRL_STAT_REG_A_ADDR>() |= (1 << CtrlFlagsA::TRANSMIT_COMPLETE);
}
[[gnu::always_inline]] static void txByteBlocking(const typename cfg::data_t &byte)
{
while (!txEmpty())
;
*Registers::IO_REG = byte;
*getRegPtr<Registers::IO_REG_ADDR>() = byte;
}
static void txByteInterrupt(volatile const typename cfg::data_t &byte) FORCE_INLINE
[[gnu::always_inline]] static void txByteInterrupt(volatile const typename cfg::data_t &byte)
{
*Registers::IO_REG = byte;
*getRegPtr<Registers::IO_REG_ADDR>() = byte;
}
static bool peekBlocking() FORCE_INLINE
[[gnu::always_inline]] static bool peekBlocking()
{
if (*Registers::CTRL_STAT_REG_A & (1 << CtrlFlagsA::RECEIVE_COMPLETE)) {
if (*getRegPtr<Registers::CTRL_STAT_REG_A_ADDR>() & (1 << CtrlFlagsA::RECEIVE_COMPLETE)) {
return true;
}
return false;
}
static void enableDataRegEmptyInt() FORCE_INLINE
[[gnu::always_inline]] static void enableDataRegEmptyInt()
{
*Registers::CTRL_STAT_REG_B |= (1 << CtrlFlagsB::DATA_REG_EMPTY_INT_ENABLE);
auto ctrlStatRegB = getRegPtr<Registers::CTRL_STAT_REG_B_ADDR>();
*ctrlStatRegB = *ctrlStatRegB | (1 << CtrlFlagsB::DATA_REG_EMPTY_INT_ENABLE);
}
static void disableDataRegEmptyInt() FORCE_INLINE
[[gnu::always_inline]] static void disableDataRegEmptyInt()
{
*Registers::CTRL_STAT_REG_B &= ~(1 << CtrlFlagsB::DATA_REG_EMPTY_INT_ENABLE);
auto ctrlStatRegB = getRegPtr<Registers::CTRL_STAT_REG_B_ADDR>();
*ctrlStatRegB = *ctrlStatRegB & ~(1 << CtrlFlagsB::DATA_REG_EMPTY_INT_ENABLE);
}
private:
struct DataBitsVal {
uint8_t regCVal = 0;
uint8_t regBVal = 0;
std::uint8_t regCVal = 0;
std::uint8_t regBVal = 0;
};
static constexpr auto calcBaud()
template <bool DoubleSpeed = true>
static constexpr auto calcBaudVal()
{
// The actual formula is (F_CPU / (16 * baudRate)) - 1, but this one has the advantage of rounding correctly
constexpr auto baudVal = (F_CPU + 8 * cfg::BAUD_RATE) / (16 * cfg::BAUD_RATE) - 1;
return baudVal;
if constexpr (DoubleSpeed) {
constexpr auto BaudVal = static_cast<std::uint16_t>(round(F_CPU / (8.0 * cfg::BAUD_RATE) - 1));
return BaudVal;
}
constexpr auto BaudVal = static_cast<std::uint16_t>(round(F_CPU / (16.0 * cfg::BAUD_RATE) - 1));
return BaudVal;
}
template <std::uint16_t BaudVal, bool DoubleSpeed = true>
static constexpr auto calcBaudRate()
{
if constexpr (DoubleSpeed) {
constexpr auto BaudRate = static_cast<std::uint32_t>(round(F_CPU / (8.0 * (BaudVal + 1))));
return BaudRate;
}
constexpr auto BaudRate = static_cast<std::uint32_t>(round(F_CPU / (16.0 * (BaudVal + 1))));
return BaudRate;
}
template <bool DoubleSpeed = true>
static constexpr auto calcBaudError()
{
constexpr auto BaudVal = calcBaudVal<DoubleSpeed>();
constexpr auto ClosestBaudRate = calcBaudRate<BaudVal, DoubleSpeed>();
constexpr auto BaudError = (static_cast<double>(ClosestBaudRate) / cfg::BAUD_RATE - 1) * 100;
return BaudError;
}
static constexpr auto calcDataBits()
@@ -137,7 +205,7 @@ class Hardware {
static constexpr auto calcParity()
{
uint8_t parityVal = 0;
std::uint8_t parityVal = 0;
if (cfg::PARITY == Parity::EVEN)
parityVal = (1 << CtrlFlagsC::PARITY_MODE_1);
@@ -149,7 +217,7 @@ class Hardware {
static constexpr auto calcStopBits()
{
uint8_t stopBitsVal = 0;
std::uint8_t stopBitsVal = 0;
if (cfg::STOP_BITS == StopBits::TWO)
stopBitsVal = (1 << CtrlFlagsC::STOP_BIT_SEL);
@@ -161,7 +229,7 @@ class Hardware {
{
static_assert(mode != Mode::SPI, "SPI mode can not be used with uart");
uint8_t modeVal = 0;
std::uint8_t modeVal = 0;
if (mode == Mode::SYNCHRONOUS_MASTER || mode == Mode::SYNCHRONOUS_SLAVE) {
modeVal = (1 << CtrlFlagsC::MODE_SEL_0);
@@ -170,23 +238,23 @@ class Hardware {
return modeVal;
}
template <bool enable>
template <bool Enable>
static constexpr auto calcRxState()
{
uint8_t enableVal = 0;
std::uint8_t enableVal = 0;
if (enable)
if (Enable)
enableVal = (1 << CtrlFlagsB::RX_ENABLE);
return enableVal;
}
template <bool enable>
template <bool Enable>
static constexpr auto calcTxState()
{
uint8_t enableVal = 0;
std::uint8_t enableVal = 0;
if (enable)
if (Enable)
enableVal = (1 << CtrlFlagsB::TX_ENABLE);
return enableVal;
@@ -194,24 +262,162 @@ class Hardware {
static constexpr auto calcInterrupt()
{
uint8_t interruptVal = 0;
std::uint8_t interruptVal = 0;
if (driven == Driven::INTERRUPT)
interruptVal |= (1 << CtrlFlagsB::DATA_REG_EMPTY_INT_ENABLE) | (1 << CtrlFlagsB::RX_INT_ENABLE);
interruptVal = (1 << CtrlFlagsB::RX_INT_ENABLE);
return interruptVal;
}
};
template <typename data_t, uint8_t Size>
struct RingBuffer {
uint8_t head;
uint8_t tail;
data_t buf[Size];
template <class Registers, typename CtrlFlagsA, typename CtrlFlagsB, typename CtrlFlagsC, class cfg, Mode mode>
class BlockingHardware {
public:
using data_t = typename cfg::data_t;
static constexpr auto DATA_BITS = cfg::DATA_BITS;
[[gnu::always_inline]] static void init()
{
HardwareImpl::init();
}
[[gnu::always_inline]] static void txByte(const data_t &byte)
{
HardwareImpl::txByteBlocking(byte);
}
[[gnu::always_inline]] static bool rxByte(data_t &byte)
{
return HardwareImpl::rxByteBlocking(byte);
}
[[gnu::always_inline]] static bool peek(data_t &)
{
static_assert(util::always_false_v<data_t>, "Peek with data is not supported in blocking mode");
return false;
}
[[gnu::always_inline]] static bool peek()
{
return HardwareImpl::peekBlocking();
}
[[gnu::always_inline]] static void flushTx()
{
while (!HardwareImpl::txEmpty())
;
while (!HardwareImpl::txComplete())
;
HardwareImpl::clearTxComplete();
}
private:
using HardwareImpl = Hardware<Registers, CtrlFlagsA, CtrlFlagsB, CtrlFlagsC, cfg, Driven::BLOCKING, mode>;
};
template <class Registers, typename CtrlFlagsA, typename CtrlFlagsB, typename CtrlFlagsC, class cfg, Mode mode>
class InterruptHardware {
public:
using data_t = typename cfg::data_t;
static constexpr auto DATA_BITS = cfg::DATA_BITS;
[[gnu::always_inline]] static void txByte(const data_t &byte)
{
std::uint8_t tmpHead = (sm_txBuf.head + 1) % TX_BUFFER_SIZE;
while (tmpHead == sm_txBuf.tail)
;
sm_txBuf.buf[tmpHead] = byte;
sm_txBuf.head = tmpHead;
HardwareImpl::enableDataRegEmptyInt();
}
[[gnu::always_inline]] static bool rxByte(data_t &byte)
{
if (sm_rxBuf.head == sm_rxBuf.tail)
return false;
std::uint8_t tmpTail = (sm_rxBuf.tail + 1) % RX_BUFFER_SIZE;
byte = sm_rxBuf.buf[tmpTail];
sm_rxBuf.tail = tmpTail;
return true;
}
[[gnu::always_inline]] static bool peek(data_t &byte)
{
if (sm_rxBuf.head == sm_rxBuf.tail)
return false;
std::uint8_t tmpTail = (sm_rxBuf.tail + 1) % RX_BUFFER_SIZE;
byte = sm_rxBuf.buf[tmpTail];
return true;
}
[[gnu::always_inline]] static bool peek()
{
return (sm_rxBuf.head != sm_rxBuf.tail);
}
[[gnu::always_inline]] static void flushTx()
{
while (sm_txBuf.head != sm_txBuf.tail)
;
while (!HardwareImpl::txEmpty())
;
while (!HardwareImpl::txComplete())
;
HardwareImpl::clearTxComplete();
}
protected:
[[gnu::always_inline]] static void rxIntHandler()
{
const auto data = HardwareImpl::rxByteInterrupt();
const std::uint8_t tmpHead = (sm_rxBuf.head + 1) % RX_BUFFER_SIZE;
if (tmpHead != sm_rxBuf.tail) {
sm_rxBuf.head = tmpHead;
sm_rxBuf.buf[tmpHead] = data;
} else {
// TODO: Handle overflow
}
}
[[gnu::always_inline]] static void dataRegEmptyIntHandler()
{
if (sm_txBuf.head != sm_txBuf.tail) {
const std::uint8_t tmpTail = (sm_txBuf.tail + 1) % TX_BUFFER_SIZE;
sm_txBuf.tail = tmpTail;
HardwareImpl::txByteInterrupt(sm_txBuf.buf[tmpTail]);
} else
HardwareImpl::disableDataRegEmptyInt();
}
private:
using HardwareImpl = Hardware<Registers, CtrlFlagsA, CtrlFlagsB, CtrlFlagsC, cfg, Driven::INTERRUPT, mode>;
static constexpr auto TX_BUFFER_SIZE = 16;
static constexpr auto RX_BUFFER_SIZE = 16;
static volatile RingBuffer<data_t, TX_BUFFER_SIZE> sm_txBuf;
static volatile RingBuffer<data_t, RX_BUFFER_SIZE> sm_rxBuf;
};
template <class Registers, typename CtrlFlagsA, typename CtrlFlagsB, typename CtrlFlagsC, class cfg, Mode mode>
volatile RingBuffer<typename InterruptHardware<Registers, CtrlFlagsA, CtrlFlagsB, CtrlFlagsC, cfg, mode>::data_t,
InterruptHardware<Registers, CtrlFlagsA, CtrlFlagsB, CtrlFlagsC, cfg, mode>::TX_BUFFER_SIZE>
InterruptHardware<Registers, CtrlFlagsA, CtrlFlagsB, CtrlFlagsC, cfg, mode>::sm_txBuf = {0, 0, {0}};
template <class Registers, typename CtrlFlagsA, typename CtrlFlagsB, typename CtrlFlagsC, class cfg, Mode mode>
volatile RingBuffer<typename InterruptHardware<Registers, CtrlFlagsA, CtrlFlagsB, CtrlFlagsC, cfg, mode>::data_t,
InterruptHardware<Registers, CtrlFlagsA, CtrlFlagsB, CtrlFlagsC, cfg, mode>::RX_BUFFER_SIZE>
InterruptHardware<Registers, CtrlFlagsA, CtrlFlagsB, CtrlFlagsC, cfg, mode>::sm_rxBuf = {0, 0, {0}};
} // namespace detail
} // namespace uart
#undef FORCE_INLINE

View File

@@ -1,30 +0,0 @@
#include "hardware0.hpp"
#include <avr/interrupt.h>
namespace uart {
namespace detail {
#if defined(__AVR_ATmega1284P__)
void (*fnRx0IntHandler)() = nullptr;
void (*fnDataReg0EmptyIntHandler)() = nullptr;
ISR(USART0_RX_vect)
{
if (fnRx0IntHandler)
fnRx0IntHandler();
}
ISR(USART0_UDRE_vect)
{
if (fnDataReg0EmptyIntHandler)
fnDataReg0EmptyIntHandler();
}
#else
#error "This chip is not supported"
#endif
} // namespace detail
} // namespace uart

View File

@@ -1,29 +1,45 @@
#pragma once
#include <stdint.h>
#include <cstdint>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/sfr_defs.h>
#include "config.hpp"
#include "hardware.hpp"
#define FORCE_INLINE __attribute__((always_inline))
namespace uart {
namespace detail {
#if defined(__AVR_ATmega1284P__)
#if defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega328P__)
/*
The following works in avr-gcc 5.4.0, but is not legal C++, because ptr's are not legal constexpr's:
constexpr auto *foo = ptr;
Workaround is to store the the address of the ptr in a uintptr_t and reinterpret_cast it at call site.
The _SFR_ADDR macro in sfr_defs.h would give the address, but it does that by taking the address of the dereferenced
pointer and casts it to uint16_t, which is still not a legal constexpr.
The workaround therefore is to disable the pointer cast and dereference macro _MMIO_BYTE temporarily.
*/
#pragma push_macro("_MMIO_BYTE")
#undef _MMIO_BYTE
#define _MMIO_BYTE
struct Registers0 {
static constexpr volatile auto *IO_REG = &UDR0;
static constexpr volatile auto *CTRL_STAT_REG_A = &UCSR0A;
static constexpr volatile auto *CTRL_STAT_REG_B = &UCSR0B;
static constexpr volatile auto *CTRL_STAT_REG_C = &UCSR0C;
static constexpr volatile auto *BAUD_REG_L = &UBRR0L;
static constexpr volatile auto *BAUD_REG_H = &UBRR0H;
static constexpr std::uintptr_t IO_REG_ADDR = UDR0;
static constexpr std::uintptr_t CTRL_STAT_REG_A_ADDR = UCSR0A;
static constexpr std::uintptr_t CTRL_STAT_REG_B_ADDR = UCSR0B;
static constexpr std::uintptr_t CTRL_STAT_REG_C_ADDR = UCSR0C;
static constexpr std::uintptr_t BAUD_REG_L_ADDR = UBRR0L;
static constexpr std::uintptr_t BAUD_REG_H_ADDR = UBRR0H;
};
#pragma pop_macro("_MMIO_BYTE")
enum class ControlFlagsA0 {
MULTI_PROC_COMM_MODE = MPCM0,
SPEED_2X = U2X0,
@@ -63,8 +79,10 @@ constexpr int operator<<(const int &lhs, const ControlFlagsB0 &rhs) { return lhs
constexpr int operator<<(const int &lhs, const ControlFlagsC0 &rhs) { return lhs << static_cast<int>(rhs); }
// clang-format on
extern void (*fnRx0IntHandler)();
extern void (*fnDataReg0EmptyIntHandler)();
#if defined(__AVR_ATmega328P__)
#define USART0_RX_vect USART_RX_vect
#define USART0_UDRE_vect USART_UDRE_vect
#endif
#else
#error "This chip is not supported"
@@ -72,139 +90,63 @@ extern void (*fnDataReg0EmptyIntHandler)();
} // namespace detail
template <Mode mode = Mode::ASYNCHRONOUS, class cfg = Config<>, Driven driven = Driven::INTERRUPT>
class Hardware0 {
public:
using data_t = typename cfg::data_t;
static constexpr auto DATA_BITS = cfg::DATA_BITS;
template <class cfg = Config<>, Driven driven = Driven::INTERRUPT, Mode mode = Mode::ASYNCHRONOUS>
class Hardware0 : public detail::BlockingHardware<detail::Registers0, detail::ControlFlagsA0, detail::ControlFlagsB0,
detail::ControlFlagsC0, cfg, mode> {
};
static void init() FORCE_INLINE
template <class cfg, Mode mode>
class Hardware0<cfg, Driven::INTERRUPT, mode>
: public detail::InterruptHardware<detail::Registers0, detail::ControlFlagsA0, detail::ControlFlagsB0,
detail::ControlFlagsC0, cfg, mode> {
public:
[[gnu::always_inline]] static void init()
{
HardwareImpl::init();
}
static void txByte(data_t byte) FORCE_INLINE
{
HardwareImpl::txByteBlocking(byte);
}
static bool rxByte(data_t &byte) FORCE_INLINE
{
return HardwareImpl::rxByteBlocking(byte);
}
static bool peek(data_t &byte) FORCE_INLINE
{
static_cast<void>(byte);
static_assert(driven == Driven::BLOCKING, "Peek with data is not supported in blocking mode");
return false;
}
static bool peek() FORCE_INLINE
{
return HardwareImpl::peekBlocking();
sei();
}
private:
using HardwareImpl = detail::Hardware<detail::Registers0, detail::ControlFlagsA0, detail::ControlFlagsB0,
detail::ControlFlagsC0, cfg, mode, driven>;
};
detail::ControlFlagsC0, cfg, Driven::INTERRUPT, mode>;
template <Mode mode, class cfg>
class Hardware0<mode, cfg, Driven::INTERRUPT> {
public:
using data_t = typename cfg::data_t;
static constexpr auto DATA_BITS = cfg::DATA_BITS;
using InterruptHardwareImpl = detail::InterruptHardware<detail::Registers0, detail::ControlFlagsA0,
detail::ControlFlagsB0, detail::ControlFlagsC0, cfg, mode>;
static void init() FORCE_INLINE
// Must be friends with Uart interface to call these private handlers
template <class Driver>
friend class Uart;
[[gnu::always_inline]] static void rxIntHandler()
{
detail::fnRx0IntHandler = rxIntHandler;
detail::fnDataReg0EmptyIntHandler = dataRegEmptyIntHandler;
HardwareImpl::init();
InterruptHardwareImpl::rxIntHandler();
}
static void txByte(const data_t &byte) FORCE_INLINE
[[gnu::always_inline]] static void dataRegEmptyIntHandler()
{
uint8_t tmpHead = (sm_txBuf.head + 1) % TX_BUFFER_SIZE;
while (tmpHead == sm_txBuf.tail)
;
sm_txBuf.buf[tmpHead] = byte;
sm_txBuf.head = tmpHead;
HardwareImpl::enableDataRegEmptyInt();
}
static bool rxByte(data_t &byte) FORCE_INLINE
{
if (sm_rxBuf.head == sm_rxBuf.tail)
return false;
uint8_t tmpTail = (sm_rxBuf.tail + 1) % RX_BUFFER_SIZE;
byte = sm_rxBuf.buf[tmpTail];
sm_rxBuf.tail = tmpTail;
return true;
}
static bool peek(data_t &byte) FORCE_INLINE
{
if (sm_rxBuf.head == sm_rxBuf.tail)
return false;
uint8_t tmpTail = (sm_rxBuf.tail + 1) % RX_BUFFER_SIZE;
byte = sm_rxBuf.buf[tmpTail];
return true;
}
static bool peek() FORCE_INLINE
{
return (sm_rxBuf.head != sm_rxBuf.tail);
}
private:
using HardwareImpl = detail::Hardware<detail::Registers0, detail::ControlFlagsA0, detail::ControlFlagsB0,
detail::ControlFlagsC0, cfg, mode, Driven::INTERRUPT>;
static constexpr auto TX_BUFFER_SIZE = 16;
static constexpr auto RX_BUFFER_SIZE = 16;
static volatile detail::RingBuffer<data_t, TX_BUFFER_SIZE> sm_txBuf;
static volatile detail::RingBuffer<data_t, RX_BUFFER_SIZE> sm_rxBuf;
static void rxIntHandler()
{
uint8_t tmpHead = (sm_rxBuf.head + 1) % RX_BUFFER_SIZE;
if (tmpHead != sm_rxBuf.tail) {
sm_rxBuf.head = tmpHead;
sm_rxBuf.buf[tmpHead] = HardwareImpl::rxByteInterrupt();
}
}
static void dataRegEmptyIntHandler() FORCE_INLINE
{
if (sm_txBuf.head != sm_txBuf.tail) {
uint8_t tmpTail = (sm_txBuf.tail + 1) % TX_BUFFER_SIZE;
sm_txBuf.tail = tmpTail;
HardwareImpl::txByteInterrupt(sm_txBuf.buf[tmpTail]);
} else
HardwareImpl::disableDataRegEmptyInt();
InterruptHardwareImpl::dataRegEmptyIntHandler();
}
};
template <Mode mode, class cfg>
volatile detail::RingBuffer<typename Hardware0<mode, cfg, Driven::INTERRUPT>::data_t,
Hardware0<mode, cfg, Driven::INTERRUPT>::TX_BUFFER_SIZE>
Hardware0<mode, cfg, Driven::INTERRUPT>::sm_txBuf = {0, 0, {0}};
template <Mode mode, class cfg>
volatile detail::RingBuffer<typename Hardware0<mode, cfg, Driven::INTERRUPT>::data_t,
Hardware0<mode, cfg, Driven::INTERRUPT>::RX_BUFFER_SIZE>
Hardware0<mode, cfg, Driven::INTERRUPT>::sm_rxBuf = {0, 0, {0}};
} // namespace uart
#undef FORCE_INLINE
//////////////////////////////////////////////////////////////////////////
// Forward declare interrupt functions to allow adding them as friends
extern "C" {
void USART0_RX_vect() __attribute__((signal));
void USART0_UDRE_vect() __attribute__((signal));
}
// clang-format off
#define REGISTER_UART0_INT_VECTORS(uart_type) \
ISR(USART0_RX_vect) \
{ \
uart_type::rxIntHandler(); \
} \
ISR(USART0_UDRE_vect) \
{ \
uart_type::dataRegEmptyIntHandler(); \
} \
struct _##uart_type {}
// clang-format on

View File

@@ -1,30 +0,0 @@
#include "hardware1.hpp"
#include <avr/interrupt.h>
namespace uart {
namespace detail {
#if defined(__AVR_ATmega1284P__)
void (*fnRx1IntHandler)() = nullptr;
void (*fnDataReg1EmptyIntHandler)() = nullptr;
ISR(USART1_RX_vect)
{
if (fnRx1IntHandler)
fnRx1IntHandler();
}
ISR(USART1_UDRE_vect)
{
if (fnDataReg1EmptyIntHandler)
fnDataReg1EmptyIntHandler();
}
#else
#error "This chip is not supported"
#endif
} // namespace detail
} // namespace uart

View File

@@ -1,29 +1,45 @@
#pragma once
#include <stdint.h>
#include <cstdint>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/sfr_defs.h>
#include "config.hpp"
#include "hardware.hpp"
#define FORCE_INLINE __attribute__((always_inline))
namespace uart {
namespace detail {
#if defined(__AVR_ATmega1284P__)
/*
The following works in avr-gcc 5.4.0, but is not legal C++, because ptr's are not legal constexpr's:
constexpr auto *foo = ptr;
Workaround is to store the the address of the ptr in a uintptr_t and reinterpret_cast it at call site.
The _SFR_ADDR macro in sfr_defs.h would give the address, but it does that by taking the address of the dereferenced
pointer and casts it to uint16_t, which is still not a legal constexpr.
The workaround therefore is to disable the pointer cast and dereference macro _MMIO_BYTE temporarily.
*/
#pragma push_macro("_MMIO_BYTE")
#undef _MMIO_BYTE
#define _MMIO_BYTE
struct Registers1 {
static constexpr volatile auto *IO_REG = &UDR1;
static constexpr volatile auto *CTRL_STAT_REG_A = &UCSR1A;
static constexpr volatile auto *CTRL_STAT_REG_B = &UCSR1B;
static constexpr volatile auto *CTRL_STAT_REG_C = &UCSR1C;
static constexpr volatile auto *BAUD_REG_L = &UBRR1L;
static constexpr volatile auto *BAUD_REG_H = &UBRR1H;
static constexpr std::uintptr_t IO_REG_ADDR = UDR1;
static constexpr std::uintptr_t CTRL_STAT_REG_A_ADDR = UCSR1A;
static constexpr std::uintptr_t CTRL_STAT_REG_B_ADDR = UCSR1B;
static constexpr std::uintptr_t CTRL_STAT_REG_C_ADDR = UCSR1C;
static constexpr std::uintptr_t BAUD_REG_L_ADDR = UBRR1L;
static constexpr std::uintptr_t BAUD_REG_H_ADDR = UBRR1H;
};
#pragma pop_macro("_MMIO_BYTE")
enum class ControlFlagsA1 {
MULTI_PROC_COMM_MODE = MPCM1,
SPEED_2X = U2X1,
@@ -63,154 +79,79 @@ constexpr int operator<<(const int &lhs, const ControlFlagsB1 &rhs) { return lhs
constexpr int operator<<(const int &lhs, const ControlFlagsC1 &rhs) { return lhs << static_cast<int>(rhs); }
// clang-format on
extern void (*fnRx1IntHandler)();
extern void (*fnDataReg1EmptyIntHandler)();
#define HAS_UART1
#else
#error "This chip is not supported"
#endif
} // namespace detail
#ifdef HAS_UART1
template <Mode mode = Mode::ASYNCHRONOUS, class cfg = Config<>, Driven driven = Driven::INTERRUPT>
class Hardware1 {
public:
using data_t = typename cfg::data_t;
static constexpr auto DATA_BITS = cfg::DATA_BITS;
template <class cfg = Config<>, Driven driven = Driven::INTERRUPT, Mode mode = Mode::ASYNCHRONOUS>
class Hardware1 : public detail::BlockingHardware<detail::Registers1, detail::ControlFlagsA1, detail::ControlFlagsB1,
detail::ControlFlagsC1, cfg, mode> {
};
static void init() FORCE_INLINE
template <class cfg, Mode mode>
class Hardware1<cfg, Driven::INTERRUPT, mode>
: public detail::InterruptHardware<detail::Registers1, detail::ControlFlagsA1, detail::ControlFlagsB1,
detail::ControlFlagsC1, cfg, mode> {
public:
[[gnu::always_inline]] static void init()
{
HardwareImpl::init();
}
static void txByte(const data_t &byte) FORCE_INLINE
{
HardwareImpl::txByteBlocking(byte);
}
static bool rxByte(data_t &byte) FORCE_INLINE
{
return HardwareImpl::rxByteBlocking(byte);
}
static bool peek(data_t &byte) FORCE_INLINE
{
static_cast<void>(byte);
static_assert(driven != Driven::BLOCKING, "Peek with data is not supported in blocking mode");
return false;
}
static bool peek() FORCE_INLINE
{
return HardwareImpl::peekBlocking();
sei();
}
private:
using HardwareImpl = detail::Hardware<detail::Registers1, detail::ControlFlagsA1, detail::ControlFlagsB1,
detail::ControlFlagsC1, cfg, mode, driven>;
};
detail::ControlFlagsC1, cfg, Driven::INTERRUPT, mode>;
template <Mode mode, class cfg>
class Hardware1<mode, cfg, Driven::INTERRUPT> {
public:
using data_t = typename cfg::data_t;
static constexpr auto DATA_BITS = cfg::DATA_BITS;
using InterruptHardwareImpl = detail::InterruptHardware<detail::Registers1, detail::ControlFlagsA1,
detail::ControlFlagsB1, detail::ControlFlagsC1, cfg, mode>;
static void init() FORCE_INLINE
// Must be friends with Uart interface to call these private handlers
template <class Driver>
friend class Uart;
[[gnu::always_inline]] static void rxIntHandler()
{
detail::fnRx1IntHandler = rxIntHandler;
detail::fnDataReg1EmptyIntHandler = dataRegEmptyIntHandler;
HardwareImpl::init();
InterruptHardwareImpl::rxIntHandler();
}
static void txByte(const data_t &byte) FORCE_INLINE
[[gnu::always_inline]] static void dataRegEmptyIntHandler()
{
uint8_t tmpHead = (sm_txBuf.head + 1) % TX_BUFFER_SIZE;
while (tmpHead == sm_txBuf.tail)
;
sm_txBuf.buf[tmpHead] = byte;
sm_txBuf.head = tmpHead;
HardwareImpl::enableDataRegEmptyInt();
}
static bool rxByte(data_t &byte) FORCE_INLINE
{
if (sm_rxBuf.head == sm_rxBuf.tail)
return false;
uint8_t tmpTail = (sm_rxBuf.tail + 1) % RX_BUFFER_SIZE;
byte = sm_rxBuf.buf[tmpTail];
sm_rxBuf.tail = tmpTail;
return true;
}
static bool peek(data_t &byte) FORCE_INLINE
{
if (sm_rxBuf.head == sm_rxBuf.tail)
return false;
uint8_t tmpTail = (sm_rxBuf.tail + 1) % RX_BUFFER_SIZE;
byte = sm_rxBuf.buf[tmpTail];
return true;
}
static bool peek() FORCE_INLINE
{
return (sm_rxBuf.head != sm_rxBuf.tail);
}
private:
using HardwareImpl = detail::Hardware<detail::Registers1, detail::ControlFlagsA1, detail::ControlFlagsB1,
detail::ControlFlagsC1, cfg, mode, Driven::INTERRUPT>;
static constexpr auto TX_BUFFER_SIZE = 16;
static constexpr auto RX_BUFFER_SIZE = 16;
static volatile detail::RingBuffer<data_t, TX_BUFFER_SIZE> sm_txBuf;
static volatile detail::RingBuffer<data_t, RX_BUFFER_SIZE> sm_rxBuf;
static void rxIntHandler()
{
uint8_t tmpHead = (sm_rxBuf.head + 1) % RX_BUFFER_SIZE;
if (tmpHead != sm_rxBuf.tail) {
sm_rxBuf.head = tmpHead;
sm_rxBuf.buf[tmpHead] = HardwareImpl::rxByteInterrupt();
}
}
static void dataRegEmptyIntHandler() FORCE_INLINE
{
if (sm_txBuf.head != sm_txBuf.tail) {
uint8_t tmpTail = (sm_txBuf.tail + 1) % TX_BUFFER_SIZE;
sm_txBuf.tail = tmpTail;
HardwareImpl::txByteInterrupt(sm_txBuf.buf[tmpTail]);
} else
HardwareImpl::disableDataRegEmptyInt();
InterruptHardwareImpl::dataRegEmptyIntHandler();
}
};
template <Mode mode, class cfg>
volatile detail::RingBuffer<typename Hardware1<mode, cfg, Driven::INTERRUPT>::data_t,
Hardware1<mode, cfg, Driven::INTERRUPT>::TX_BUFFER_SIZE>
Hardware1<mode, cfg, Driven::INTERRUPT>::sm_txBuf = {0, 0, {0}};
template <Mode mode, class cfg>
volatile detail::RingBuffer<typename Hardware1<mode, cfg, Driven::INTERRUPT>::data_t,
Hardware1<mode, cfg, Driven::INTERRUPT>::RX_BUFFER_SIZE>
Hardware1<mode, cfg, Driven::INTERRUPT>::sm_rxBuf = {0, 0, {0}};
#endif
} // namespace uart
#undef FORCE_INLINE
//////////////////////////////////////////////////////////////////////////
#ifdef HAS_UART1
// Forward declare interrupt functions to allow adding them as friends
extern "C" {
void USART1_RX_vect() __attribute__((signal));
void USART1_UDRE_vect() __attribute__((signal));
}
// clang-format off
#define REGISTER_UART1_INT_VECTORS(uart_type) \
ISR(USART1_RX_vect) \
{ \
uart_type::rxIntHandler(); \
} \
ISR(USART1_UDRE_vect) \
{ \
uart_type::dataRegEmptyIntHandler(); \
} \
struct _##uart_type { \
}
// clang-format off
#endif

View File

@@ -1,9 +1,9 @@
#pragma once
#include "config.hpp"
#include "utils.hpp"
#include "../io/io.hpp"
#include "../util/util.hpp"
namespace uart {

121
uart.hpp
View File

@@ -1,31 +1,27 @@
#pragma once
#include <stdint.h>
#include <limits>
#include <cstdint>
#include "config.hpp"
#include "software.hpp"
#include "hardware0.hpp"
#include "hardware1.hpp"
#include "software.hpp"
#include "utils.hpp"
#include "../flash/flash.hpp"
#define FORCE_INLINE __attribute__((always_inline))
#include "../util/util.hpp"
namespace uart {
namespace detail {
template <typename T, T limit, size_t Base>
static constexpr size_t cntDigits()
template <typename T, T Limit, std::size_t Base>
static constexpr std::size_t cntDigits()
{
T num = limit;
size_t cnt = 0;
if (num < 0) {
num = -num;
++cnt;
}
T num = Limit;
std::size_t cnt = 0;
do {
num /= Base;
@@ -35,13 +31,16 @@ static constexpr size_t cntDigits()
return cnt;
}
template <typename T, size_t Base>
static constexpr size_t maxNumDigits()
template <typename T, std::size_t Base>
static constexpr std::size_t maxNumDigits()
{
T minDigits = cntDigits<T, util::NumericLimits<T>::min(), Base>();
T maxDigits = cntDigits<T, util::NumericLimits<T>::max(), Base>();
constexpr T MinVal = std::numeric_limits<T>::min();
constexpr T MaxVal = std::numeric_limits<T>::max();
return (minDigits < maxDigits) ? maxDigits : minDigits;
constexpr T MinDigits = cntDigits<T, MinVal, Base>();
constexpr T MaxDigits = cntDigits<T, MaxVal, Base>();
return (MinDigits < MaxDigits) ? MaxDigits : MinDigits;
}
} // namespace detail
@@ -88,6 +87,11 @@ class Uart {
return Driver::peek();
}
static void flushTx()
{
Driver::flushTx();
}
static void txString(const char *str)
{
static_assert(Driver::DATA_BITS == DataBits::EIGHT, "Strings are only supported with 8 data bits");
@@ -106,16 +110,16 @@ class Uart {
txByte(ch);
}
template <typename T, size_t Base = 10>
template <typename T, std::size_t Base = 10, std::size_t Padding = 0, char PadChar = '0', bool LowerCase = true>
static void txNumber(const T &val)
{
static_assert(util::is_integral_v<T>, "Only supported on integral types");
static_assert(Base >= 2, "Numbers with bases less than 2 make no sense");
static_assert(Base <= 16, "Numbers with bases higher than 16 are not supported");
static_assert(std::is_integral_v<T>, "Only supported on integral types");
static_assert(Base >= 2, "Numbers with base less than 2 make no sense");
static_assert(Base <= 16, "Numbers with base higher than 16 are not supported");
static_assert(Padding <= detail::maxNumDigits<T, Base>(), "Cannot pad more than maximum length of number");
constexpr size_t numDigits = detail::maxNumDigits<T, Base>();
data_t buffer[numDigits];
data_t *bufEnd = buffer + numDigits - 1;
constexpr char AlphaChar = (LowerCase) ? 'a' : 'A';
constexpr std::size_t NumDigits = detail::maxNumDigits<T, Base>();
T digits = val;
@@ -124,13 +128,26 @@ class Uart {
txByte('-');
}
data_t buffer[NumDigits];
data_t *bufEnd = buffer + NumDigits - 1;
do {
data_t lastDigit = digits % Base;
*bufEnd-- = (lastDigit < 10) ? ('0' + lastDigit) : ('a' + lastDigit - 10);
const data_t lastDigit = digits % Base;
*bufEnd-- = (lastDigit < 10) ? ('0' + lastDigit) : (AlphaChar + lastDigit - 10);
digits /= Base;
} while (digits > 0);
for (data_t *buf = bufEnd + 1; buf < buffer + numDigits; ++buf)
if (Padding > 0) {
std::size_t strLen = buffer + NumDigits - (bufEnd + 1);
if (Padding > strLen) {
for (std::size_t i = Padding; i > strLen && bufEnd >= buffer; --i) {
*bufEnd-- = PadChar;
}
}
}
for (data_t *buf = bufEnd + 1; buf < buffer + NumDigits; ++buf)
txByte(*buf);
}
@@ -173,7 +190,7 @@ class Uart {
return *this;
}
Uart &operator<<(unsigned short &val)
Uart &operator<<(const unsigned short &val)
{
txNumber(val);
return *this;
@@ -197,19 +214,19 @@ class Uart {
return *this;
}
Uart &operator<<(unsigned long &val)
Uart &operator<<(const unsigned long &val)
{
txNumber(val);
return *this;
}
Uart &operator<<(long long &val)
Uart &operator<<(const long long &val)
{
txNumber(val);
return *this;
}
Uart &operator<<(unsigned long long &val)
Uart &operator<<(const unsigned long long &val)
{
txNumber(val);
return *this;
@@ -218,19 +235,19 @@ class Uart {
template <typename... Ts>
Uart &operator<<(float) const
{
static_assert(util::always_false_v<Ts...>, "Not implemented");
static_assert(util::always_false_v<Ts...>, "Not supported by hardware");
}
template <typename... Ts>
Uart &operator<<(double) const
{
static_assert(util::always_false_v<Ts...>, "Not implemented");
static_assert(util::always_false_v<Ts...>, "Not supported by hardware");
}
template <typename... Ts>
Uart &operator<<(long double) const
{
static_assert(util::always_false_v<Ts...>, "Not implemented");
static_assert(util::always_false_v<Ts...>, "Not supported by hardware");
}
Uart &operator<<(const bool &val)
@@ -242,7 +259,7 @@ class Uart {
Uart &operator<<(const void *val)
{
txString(F("0x"));
txNumber<uint16_t, 16>(reinterpret_cast<uint16_t>(val));
txNumber<std::uint16_t, 16, 4, '0', false>(reinterpret_cast<std::uint16_t>(val));
return *this;
}
@@ -312,19 +329,19 @@ class Uart {
template <typename... Ts>
Uart &operator>>(float &) const
{
static_assert(util::always_false_v<Ts...>, "Not implemented");
static_assert(util::always_false_v<Ts...>, "Not supported by hardware");
}
template <typename... Ts>
Uart &operator>>(double &) const
{
static_assert(util::always_false_v<Ts...>, "Not implemented");
static_assert(util::always_false_v<Ts...>, "Not supported by hardware");
}
template <typename... Ts>
Uart &operator>>(long double &) const
{
static_assert(util::always_false_v<Ts...>, "Not implemented");
static_assert(util::always_false_v<Ts...>, "Not supported by hardware");
}
template <typename... Ts>
@@ -338,17 +355,35 @@ class Uart {
{
static_assert(util::always_false_v<Ts...>, "Not implemented");
}
private:
friend void ::USART0_RX_vect();
friend void ::USART0_UDRE_vect();
#ifdef HAS_UART1
friend void ::USART1_RX_vect();
friend void ::USART1_UDRE_vect();
#endif
[[gnu::always_inline]] static void rxIntHandler()
{
Driver::rxIntHandler();
}
[[gnu::always_inline]] static void dataRegEmptyIntHandler()
{
Driver::dataRegEmptyIntHandler();
}
};
template <typename cfg = Config<>>
using Uart0 = Uart<Hardware0<Mode::ASYNCHRONOUS, cfg>>;
using Uart0 = Uart<Hardware0<cfg, Driven::INTERRUPT, Mode::ASYNCHRONOUS>>;
#ifdef HAS_UART1
template <typename cfg = Config<>>
using Uart1 = Uart<Hardware1<Mode::ASYNCHRONOUS, cfg>>;
using Uart1 = Uart<Hardware1<cfg, Driven::INTERRUPT, Mode::ASYNCHRONOUS>>;
#endif
} // namespace uart
#undef FORCE_INLINE
#undef HAS_UART1

142
utils.hpp
View File

@@ -1,142 +0,0 @@
#pragma once
// Fix for limits.h not exposing LLONG_MIN, LLONG_MIN, and ULLONG_MAX to C++ context
#ifdef __cplusplus
#define __STDC_VERSION__ 201112L
#endif
#include <limits.h>
namespace uart {
namespace util {
// clang-format off
template <bool Val> struct set_bool { static constexpr auto value = Val; };
struct true_type : set_bool<true> {};
struct false_type : set_bool<false> {};
template <typename...> struct always_false : false_type {};
template <typename... Ts> static constexpr auto always_false_v = always_false<Ts...>::value;
template <typename T> struct is_integral : false_type {};
template <> struct is_integral<bool> : true_type {};
template <> struct is_integral<char> : true_type {};
template <> struct is_integral<signed char> : true_type {};
template <> struct is_integral<unsigned char> : true_type {};
template <> struct is_integral<short> : true_type {};
template <> struct is_integral<int> : true_type {};
template <> struct is_integral<long int> : true_type {};
template <> struct is_integral<long long int> : true_type {};
template <> struct is_integral<unsigned short> : true_type {};
template <> struct is_integral<unsigned int> : true_type {};
template <> struct is_integral<unsigned long int> : true_type {};
template <> struct is_integral<unsigned long long int> : true_type {};
template <typename T> static constexpr auto is_integral_v = is_integral<T>::value;
template <typename T, typename U> struct is_same : false_type {};
template <typename T> struct is_same<T, T> : true_type {};
template <typename T, typename U> static constexpr auto is_same_v = is_same<T, U>::value;
template <typename T>
struct NumericLimits {
static constexpr T min() { return T(); }
static constexpr T max() { return T(); }
};
template <>
struct NumericLimits<bool> {
static constexpr bool min() { return false; }
static constexpr bool max() { return true; }
};
template <>
struct NumericLimits<char> {
static constexpr char min() { return CHAR_MIN; }
static constexpr char max() { return CHAR_MAX; }
};
template <>
struct NumericLimits<signed char> {
static constexpr signed char min() { return SCHAR_MIN; }
static constexpr signed char max() { return SCHAR_MAX; }
};
template <>
struct NumericLimits<unsigned char> {
static constexpr unsigned char min() { return 0; }
static constexpr unsigned char max() { return UCHAR_MAX; }
};
template <>
struct NumericLimits<short> {
static constexpr short min() { return SHRT_MIN; }
static constexpr short max() { return SHRT_MAX; }
};
template <>
struct NumericLimits<int> {
static constexpr int min() { return INT_MIN; }
static constexpr int max() { return INT_MAX; }
};
template <>
struct NumericLimits<long> {
static constexpr long int min() { return LONG_MIN; }
static constexpr long int max() { return LONG_MAX; }
};
template <>
struct NumericLimits<long long int> {
static constexpr long long int min() { return LLONG_MIN; }
static constexpr long long int max() { return LLONG_MAX; }
};
template <>
struct NumericLimits<unsigned short> {
static constexpr unsigned short min() { return 0; }
static constexpr unsigned short max() { return USHRT_MAX; }
};
template <>
struct NumericLimits<unsigned int> {
static constexpr unsigned int min() { return 0; }
static constexpr unsigned int max() { return UINT_MAX; }
};
template <>
struct NumericLimits<unsigned long int> {
static constexpr unsigned long int min() { return 0; }
static constexpr unsigned long int max() { return ULONG_MAX; }
};
template <>
struct NumericLimits<unsigned long long int> {
static constexpr unsigned long long int min() { return 0; }
static constexpr unsigned long long int max() { return ULLONG_MAX; }
};
template <>
struct NumericLimits<float> {
template <typename... Ts> static constexpr float min() { static_assert(always_false_v<Ts...>, "Not implemented"); return 0; }
template <typename... Ts> static constexpr float max() { static_assert(always_false_v<Ts...>, "Not implemented"); return 0; }
};
template <>
struct NumericLimits<double> {
template <typename... Ts> static constexpr double min() { static_assert(always_false_v<Ts...>, "Not implemented"); return 0; }
template <typename... Ts> static constexpr double max() { static_assert(always_false_v<Ts...>, "Not implemented"); return 0; }
};
template <>
struct NumericLimits<long double> {
template <typename... Ts> static constexpr long double min() { static_assert(always_false_v<Ts...>, "Not implemented"); return 0; }
template <typename... Ts> static constexpr long double max() { static_assert(always_false_v<Ts...>, "Not implemented"); return 0; }
};
// clang-format on
} // namespace util
} // namespace uart