Compare commits

...

18 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
8 changed files with 288 additions and 386 deletions

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

@@ -2,17 +2,13 @@
#include "../clock.hpp"
#include <stdint.h>
#include "utils.hpp"
#define FORCE_INLINE __attribute__((always_inline))
#include <cmath>
#include <cstdint>
namespace uart {
enum class Mode {
ASYNCHRONOUS,
ASYNCHRONOUS_2X,
SYNCHRONOUS_MASTER,
SYNCHRONOUS_SLAVE,
SPI,
@@ -25,18 +21,18 @@ enum class Driven {
namespace detail {
using reg_ptr_t = volatile uint8_t *;
using reg_ptr_t = volatile std::uint8_t *;
template <uintptr_t Address>
template <std::uintptr_t Address>
static inline reg_ptr_t getRegPtr()
{
return reinterpret_cast<reg_ptr_t>(Address);
}
template <typename data_t, uint8_t Size>
template <typename data_t, std::uint8_t Size>
struct RingBuffer {
uint8_t head;
uint8_t tail;
std::uint8_t head;
std::uint8_t tail;
data_t buf[Size];
};
@@ -44,29 +40,41 @@ template <class Registers, typename CtrlFlagsA, typename CtrlFlagsB, typename Ct
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");
*getRegPtr<Registers::BAUD_REG_H_ADDR>() = static_cast<uint8_t>(baudVal >> 8);
*getRegPtr<Registers::BAUD_REG_L_ADDR>() = 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();
*getRegPtr<Registers::CTRL_STAT_REG_B_ADDR>() = controlRegB;
*getRegPtr<Registers::CTRL_STAT_REG_C_ADDR>() = 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 (*getRegPtr<Registers::CTRL_STAT_REG_A_ADDR>() & (1 << CtrlFlagsA::RECEIVE_COMPLETE)) {
byte = *getRegPtr<Registers::IO_REG_ADDR>();
@@ -76,27 +84,27 @@ class Hardware {
return false;
}
static typename cfg::data_t rxByteInterrupt() FORCE_INLINE
[[gnu::always_inline]] static typename cfg::data_t rxByteInterrupt()
{
return *getRegPtr<Registers::IO_REG_ADDR>();
}
static bool txEmpty() FORCE_INLINE
[[gnu::always_inline]] static bool txEmpty()
{
return *getRegPtr<Registers::CTRL_STAT_REG_A_ADDR>() & (1 << CtrlFlagsA::DATA_REG_EMPTY);
}
static bool txComplete() FORCE_INLINE
[[gnu::always_inline]] static bool txComplete()
{
return *getRegPtr<Registers::CTRL_STAT_REG_A_ADDR>() & (1 << CtrlFlagsA::TRANSMIT_COMPLETE);
}
static void clearTxComplete() FORCE_INLINE
[[gnu::always_inline]] static void clearTxComplete()
{
*getRegPtr<Registers::CTRL_STAT_REG_A_ADDR>() |= (1 << CtrlFlagsA::TRANSMIT_COMPLETE);
}
static void txByteBlocking(const typename cfg::data_t &byte) FORCE_INLINE
[[gnu::always_inline]] static void txByteBlocking(const typename cfg::data_t &byte)
{
while (!txEmpty())
;
@@ -104,12 +112,12 @@ class Hardware {
*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)
{
*getRegPtr<Registers::IO_REG_ADDR>() = byte;
}
static bool peekBlocking() FORCE_INLINE
[[gnu::always_inline]] static bool peekBlocking()
{
if (*getRegPtr<Registers::CTRL_STAT_REG_A_ADDR>() & (1 << CtrlFlagsA::RECEIVE_COMPLETE)) {
return true;
@@ -118,27 +126,55 @@ class Hardware {
return false;
}
static void enableDataRegEmptyInt() FORCE_INLINE
[[gnu::always_inline]] static void enableDataRegEmptyInt()
{
*getRegPtr<Registers::CTRL_STAT_REG_B_ADDR>() |= (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()
{
*getRegPtr<Registers::CTRL_STAT_REG_B_ADDR>() &= ~(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()
@@ -169,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);
@@ -181,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);
@@ -193,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);
@@ -202,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;
@@ -226,7 +262,7 @@ class Hardware {
static constexpr auto calcInterrupt()
{
uint8_t interruptVal = 0;
std::uint8_t interruptVal = 0;
if (driven == Driven::INTERRUPT)
interruptVal = (1 << CtrlFlagsB::RX_INT_ENABLE);
@@ -241,33 +277,33 @@ class BlockingHardware {
using data_t = typename cfg::data_t;
static constexpr auto DATA_BITS = cfg::DATA_BITS;
static void init() FORCE_INLINE
[[gnu::always_inline]] static void init()
{
HardwareImpl::init();
}
static void txByte(const data_t &byte) FORCE_INLINE
[[gnu::always_inline]] static void txByte(const data_t &byte)
{
HardwareImpl::txByteBlocking(byte);
}
static bool rxByte(data_t &byte) FORCE_INLINE
[[gnu::always_inline]] static bool rxByte(data_t &byte)
{
return HardwareImpl::rxByteBlocking(byte);
}
static bool peek(data_t &) FORCE_INLINE
[[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;
}
static bool peek() FORCE_INLINE
[[gnu::always_inline]] static bool peek()
{
return HardwareImpl::peekBlocking();
}
static void flushTx() FORCE_INLINE
[[gnu::always_inline]] static void flushTx()
{
while (!HardwareImpl::txEmpty())
;
@@ -286,9 +322,9 @@ class InterruptHardware {
using data_t = typename cfg::data_t;
static constexpr auto DATA_BITS = cfg::DATA_BITS;
static void txByte(const data_t &byte) FORCE_INLINE
[[gnu::always_inline]] static void txByte(const data_t &byte)
{
uint8_t tmpHead = (sm_txBuf.head + 1) % TX_BUFFER_SIZE;
std::uint8_t tmpHead = (sm_txBuf.head + 1) % TX_BUFFER_SIZE;
while (tmpHead == sm_txBuf.tail)
;
@@ -298,35 +334,35 @@ class InterruptHardware {
HardwareImpl::enableDataRegEmptyInt();
}
static bool rxByte(data_t &byte) FORCE_INLINE
[[gnu::always_inline]] static bool rxByte(data_t &byte)
{
if (sm_rxBuf.head == sm_rxBuf.tail)
return false;
uint8_t tmpTail = (sm_rxBuf.tail + 1) % RX_BUFFER_SIZE;
std::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
[[gnu::always_inline]] static bool peek(data_t &byte)
{
if (sm_rxBuf.head == sm_rxBuf.tail)
return false;
uint8_t tmpTail = (sm_rxBuf.tail + 1) % RX_BUFFER_SIZE;
std::uint8_t tmpTail = (sm_rxBuf.tail + 1) % RX_BUFFER_SIZE;
byte = sm_rxBuf.buf[tmpTail];
return true;
}
static bool peek() FORCE_INLINE
[[gnu::always_inline]] static bool peek()
{
return (sm_rxBuf.head != sm_rxBuf.tail);
}
static void flushTx() FORCE_INLINE
[[gnu::always_inline]] static void flushTx()
{
while (sm_txBuf.head != sm_txBuf.tail)
;
@@ -338,11 +374,11 @@ class InterruptHardware {
}
protected:
static void rxIntHandler() FORCE_INLINE
[[gnu::always_inline]] static void rxIntHandler()
{
auto data = HardwareImpl::rxByteInterrupt();
const auto data = HardwareImpl::rxByteInterrupt();
uint8_t tmpHead = (sm_rxBuf.head + 1) % RX_BUFFER_SIZE;
const std::uint8_t tmpHead = (sm_rxBuf.head + 1) % RX_BUFFER_SIZE;
if (tmpHead != sm_rxBuf.tail) {
sm_rxBuf.head = tmpHead;
@@ -352,10 +388,10 @@ class InterruptHardware {
}
}
static void dataRegEmptyIntHandler() FORCE_INLINE
[[gnu::always_inline]] static void dataRegEmptyIntHandler()
{
if (sm_txBuf.head != sm_txBuf.tail) {
uint8_t tmpTail = (sm_txBuf.tail + 1) % TX_BUFFER_SIZE;
const std::uint8_t tmpTail = (sm_txBuf.tail + 1) % TX_BUFFER_SIZE;
sm_txBuf.tail = tmpTail;
HardwareImpl::txByteInterrupt(sm_txBuf.buf[tmpTail]);
} else
@@ -385,5 +421,3 @@ volatile RingBuffer<typename InterruptHardware<Registers, CtrlFlagsA, CtrlFlagsB
} // namespace detail
} // namespace uart
#undef FORCE_INLINE

View File

@@ -1,7 +1,6 @@
#ifndef UART_HARDWARE_0_HPP
#define UART_HARDWARE_0_HPP
#pragma once
#include <stdint.h>
#include <cstdint>
#include <avr/interrupt.h>
#include <avr/io.h>
@@ -10,8 +9,6 @@
#include "config.hpp"
#include "hardware.hpp"
#define FORCE_INLINE __attribute__((always_inline))
namespace uart {
namespace detail {
@@ -33,12 +30,12 @@ The workaround therefore is to disable the pointer cast and dereference macro _M
#define _MMIO_BYTE
struct Registers0 {
static constexpr uintptr_t IO_REG_ADDR = UDR0;
static constexpr uintptr_t CTRL_STAT_REG_A_ADDR = UCSR0A;
static constexpr uintptr_t CTRL_STAT_REG_B_ADDR = UCSR0B;
static constexpr uintptr_t CTRL_STAT_REG_C_ADDR = UCSR0C;
static constexpr uintptr_t BAUD_REG_L_ADDR = UBRR0L;
static constexpr uintptr_t BAUD_REG_H_ADDR = 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")
@@ -82,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"
@@ -100,70 +99,54 @@ template <class cfg, Mode mode>
class Hardware0<cfg, Driven::INTERRUPT, mode>
: public detail::InterruptHardware<detail::Registers0, detail::ControlFlagsA0, detail::ControlFlagsB0,
detail::ControlFlagsC0, cfg, mode> {
using detail::InterruptHardware<detail::Registers0, detail::ControlFlagsA0, detail::ControlFlagsB0,
detail::ControlFlagsC0, cfg, mode>::rxIntHandler;
using detail::InterruptHardware<detail::Registers0, detail::ControlFlagsA0, detail::ControlFlagsB0,
detail::ControlFlagsC0, cfg, mode>::dataRegEmptyIntHandler;
public:
[[gnu::always_inline]] static void init()
{
HardwareImpl::init();
sei();
}
private:
using HardwareImpl = detail::Hardware<detail::Registers0, detail::ControlFlagsA0, detail::ControlFlagsB0,
detail::ControlFlagsC0, cfg, Driven::INTERRUPT, mode>;
public:
static void init() FORCE_INLINE
{
detail::fnRx0IntHandler = rxIntHandler;
detail::fnDataReg0EmptyIntHandler = dataRegEmptyIntHandler;
using InterruptHardwareImpl = detail::InterruptHardware<detail::Registers0, detail::ControlFlagsA0,
detail::ControlFlagsB0, detail::ControlFlagsC0, cfg, mode>;
HardwareImpl::init();
sei();
// Must be friends with Uart interface to call these private handlers
template <class Driver>
friend class Uart;
[[gnu::always_inline]] static void rxIntHandler()
{
InterruptHardwareImpl::rxIntHandler();
}
[[gnu::always_inline]] static void dataRegEmptyIntHandler()
{
InterruptHardwareImpl::dataRegEmptyIntHandler();
}
};
} // namespace uart
#undef FORCE_INLINE
#endif
//////////////////////////////////////////////////////////////////////////
#ifdef UART0_INT_VECTORS
#include <avr/interrupt.h>
namespace uart {
namespace detail {
#if defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega328P__)
#if defined(__AVR_ATmega328P__)
#define USART0_RX_vect USART_RX_vect
#define USART0_UDRE_vect USART_UDRE_vect
#endif
void (*fnRx0IntHandler)() = nullptr;
void (*fnDataReg0EmptyIntHandler)() = nullptr;
ISR(USART0_RX_vect)
{
if (fnRx0IntHandler)
fnRx0IntHandler();
// Forward declare interrupt functions to allow adding them as friends
extern "C" {
void USART0_RX_vect() __attribute__((signal));
void USART0_UDRE_vect() __attribute__((signal));
}
ISR(USART0_UDRE_vect)
{
if (fnDataReg0EmptyIntHandler)
fnDataReg0EmptyIntHandler();
}
#else
#error "This chip is not supported"
#endif
} // namespace detail
} // namespace uart
#undef UART0_INT_VECTORS
#endif
// 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,7 +1,6 @@
#ifndef UART_HARDWARE_1_HPP
#define UART_HARDWARE_1_HPP
#pragma once
#include <stdint.h>
#include <cstdint>
#include <avr/interrupt.h>
#include <avr/io.h>
@@ -10,8 +9,6 @@
#include "config.hpp"
#include "hardware.hpp"
#define FORCE_INLINE __attribute__((always_inline))
namespace uart {
namespace detail {
@@ -33,12 +30,12 @@ The workaround therefore is to disable the pointer cast and dereference macro _M
#define _MMIO_BYTE
struct Registers1 {
static constexpr uintptr_t IO_REG_ADDR = UDR1;
static constexpr uintptr_t CTRL_STAT_REG_A_ADDR = UCSR1A;
static constexpr uintptr_t CTRL_STAT_REG_B_ADDR = UCSR1B;
static constexpr uintptr_t CTRL_STAT_REG_C_ADDR = UCSR1C;
static constexpr uintptr_t BAUD_REG_L_ADDR = UBRR1L;
static constexpr uintptr_t BAUD_REG_H_ADDR = 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")
@@ -82,9 +79,6 @@ 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
#endif
@@ -102,23 +96,32 @@ template <class cfg, Mode mode>
class Hardware1<cfg, Driven::INTERRUPT, mode>
: public detail::InterruptHardware<detail::Registers1, detail::ControlFlagsA1, detail::ControlFlagsB1,
detail::ControlFlagsC1, cfg, mode> {
using detail::InterruptHardware<detail::Registers1, detail::ControlFlagsA1, detail::ControlFlagsB1,
detail::ControlFlagsC1, cfg, mode>::rxIntHandler;
using detail::InterruptHardware<detail::Registers1, detail::ControlFlagsA1, detail::ControlFlagsB1,
detail::ControlFlagsC1, cfg, mode>::dataRegEmptyIntHandler;
public:
[[gnu::always_inline]] static void init()
{
HardwareImpl::init();
sei();
}
private:
using HardwareImpl = detail::Hardware<detail::Registers1, detail::ControlFlagsA1, detail::ControlFlagsB1,
detail::ControlFlagsC1, cfg, Driven::INTERRUPT, mode>;
public:
static void init() FORCE_INLINE
{
detail::fnRx1IntHandler = rxIntHandler;
detail::fnDataReg1EmptyIntHandler = dataRegEmptyIntHandler;
using InterruptHardwareImpl = detail::InterruptHardware<detail::Registers1, detail::ControlFlagsA1,
detail::ControlFlagsB1, detail::ControlFlagsC1, cfg, mode>;
HardwareImpl::init();
sei();
// Must be friends with Uart interface to call these private handlers
template <class Driver>
friend class Uart;
[[gnu::always_inline]] static void rxIntHandler()
{
InterruptHardwareImpl::rxIntHandler();
}
[[gnu::always_inline]] static void dataRegEmptyIntHandler()
{
InterruptHardwareImpl::dataRegEmptyIntHandler();
}
};
@@ -126,41 +129,29 @@ class Hardware1<cfg, Driven::INTERRUPT, mode>
} // namespace uart
#undef FORCE_INLINE
#endif
//////////////////////////////////////////////////////////////////////////
#ifdef UART1_INT_VECTORS
#ifdef HAS_UART1
#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();
// Forward declare interrupt functions to allow adding them as friends
extern "C" {
void USART1_RX_vect() __attribute__((signal));
void USART1_UDRE_vect() __attribute__((signal));
}
ISR(USART1_UDRE_vect)
{
if (fnDataReg1EmptyIntHandler)
fnDataReg1EmptyIntHandler();
// 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
} // namespace detail
} // namespace uart
#undef UART1_INT_VECTORS
#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 {

View File

@@ -1,29 +1,27 @@
#pragma once
#include <stdint.h>
#include <limits>
#include <cstdint>
#include "config.hpp"
#include "software.hpp"
#include "utils.hpp"
#undef UART0_INT_VECTORS
#include "hardware0.hpp"
#undef UART1_INT_VECTORS
#include "hardware1.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;
T num = Limit;
std::size_t cnt = 0;
do {
num /= Base;
@@ -33,16 +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()
{
constexpr T minVal = util::NumericLimits<T>::min();
constexpr T maxVal = util::NumericLimits<T>::max();
constexpr T MinVal = std::numeric_limits<T>::min();
constexpr T MaxVal = std::numeric_limits<T>::max();
T minDigits = cntDigits<T, minVal, Base>();
T maxDigits = cntDigits<T, maxVal, Base>();
constexpr T MinDigits = cntDigits<T, MinVal, Base>();
constexpr T MaxDigits = cntDigits<T, MaxVal, Base>();
return (minDigits < maxDigits) ? maxDigits : minDigits;
return (MinDigits < MaxDigits) ? MaxDigits : MinDigits;
}
} // namespace detail
@@ -112,19 +110,16 @@ class Uart {
txByte(ch);
}
template <typename T, size_t Base = 10, size_t Padding = 0, char PadChar = '0', bool LowerCase = true>
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(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 char alphaChar = (LowerCase) ? 'a' : 'A';
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;
@@ -133,23 +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) : (alphaChar + lastDigit - 10);
const data_t lastDigit = digits % Base;
*bufEnd-- = (lastDigit < 10) ? ('0' + lastDigit) : (AlphaChar + lastDigit - 10);
digits /= Base;
} while (digits > 0);
if (Padding > 0) {
size_t strLen = buffer + numDigits - (bufEnd + 1);
std::size_t strLen = buffer + NumDigits - (bufEnd + 1);
if (Padding > strLen) {
for (size_t i = Padding; i > strLen && bufEnd >= buffer; --i) {
for (std::size_t i = Padding; i > strLen && bufEnd >= buffer; --i) {
*bufEnd-- = PadChar;
}
}
}
for (data_t *buf = bufEnd + 1; buf < buffer + numDigits; ++buf)
for (data_t *buf = bufEnd + 1; buf < buffer + NumDigits; ++buf)
txByte(*buf);
}
@@ -192,7 +190,7 @@ class Uart {
return *this;
}
Uart &operator<<(unsigned short &val)
Uart &operator<<(const unsigned short &val)
{
txNumber(val);
return *this;
@@ -216,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;
@@ -237,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)
@@ -261,7 +259,7 @@ class Uart {
Uart &operator<<(const void *val)
{
txString(F("0x"));
txNumber<uint16_t, 16, 4, '0', false>(reinterpret_cast<uint16_t>(val));
txNumber<std::uint16_t, 16, 4, '0', false>(reinterpret_cast<std::uint16_t>(val));
return *this;
}
@@ -331,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>
@@ -357,6 +355,25 @@ 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<>>
@@ -369,5 +386,4 @@ using Uart1 = Uart<Hardware1<cfg, Driven::INTERRUPT, Mode::ASYNCHRONOUS>>;
} // namespace uart
#undef FORCE_INLINE
#undef HAS_UART1

143
utils.hpp
View File

@@ -1,143 +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 <float.h>
#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() { return FLT_MIN; }
template <typename... Ts> static constexpr float max() { return FLT_MAX; }
};
template <>
struct NumericLimits<double> {
template <typename... Ts> static constexpr double min() { return DBL_MIN; }
template <typename... Ts> static constexpr double max() { return DBL_MAX; }
};
template <>
struct NumericLimits<long double> {
template <typename... Ts> static constexpr long double min() { return LDBL_MIN; }
template <typename... Ts> static constexpr long double max() { return LDBL_MAX; }
};
// clang-format on
} // namespace util
} // namespace uart