Compare commits

..

94 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
b6c1c3b51b Fixed conversion for bases other than 10 2019-08-03 17:32:38 +02:00
8d07e2d4db Implemented overloads for printing numbers 2019-08-03 16:52:57 +02:00
fe9e67036e Implemented stream operator for some basic types 2019-08-03 16:17:07 +02:00
9f7dc0da55 Added alias for driver dependent data size 2019-08-03 16:13:52 +02:00
2a07744575 Added static_assert for transmitting numbers 2019-08-03 16:11:21 +02:00
ecdefe40e7 Implemented integral type checking 2019-08-03 16:10:19 +02:00
53b791cb05 Implemented printing of numbers 2019-08-03 15:00:41 +02:00
50e01c480d Implemented templated numeric limits 2019-08-03 14:54:23 +02:00
7d4eddbd8b Changed interface to not initialize uart on construction 2019-08-03 11:14:01 +02:00
778f5f9754 Moved always_false template to utils 2019-08-03 10:58:32 +02:00
1ca8ea2061 Removed Peter Fleury's c uart library 2019-08-03 10:47:32 +02:00
80633998c7 Removed legacy usart library 2019-08-03 10:23:53 +02:00
2cd4069654 Moved interrupt vectors to their own translation unit to solve redefinition error 2019-08-02 20:29:04 +02:00
33c3cedb1e Fixed missing headers 2019-08-02 20:23:54 +02:00
925bb56f98 Added clang format file 2019-08-02 19:44:34 +02:00
a3d76a138d Implemented peeking with and without data for interrupt and blocking mode 2019-08-02 18:20:06 +02:00
95963295e2 Implemented blocking rx 2019-08-02 17:54:34 +02:00
0bfd303a26 Removed unneeded tx complete interrupt 2019-08-02 17:41:14 +02:00
16f4be8c6c Implemented interrupt driven rx for uart0 2019-08-02 17:38:00 +02:00
efe1446907 Implemented interrupt driven rx for uart1 2019-08-02 17:36:07 +02:00
b90da9bd9c Moved ring buffer to it's own struct 2019-08-02 17:13:53 +02:00
51a9d30c0a Implemented interrupt driven tx 2019-08-02 16:41:53 +02:00
496bb3d4d1 Removed force inlining functions that would be called often 2019-08-02 16:41:34 +02:00
5d6bc4761c Changed interrupt order to match vector numbers 2019-08-02 15:46:07 +02:00
3aeb43ee1e Added basic structure to support interrupt driven operation 2019-08-02 12:18:35 +02:00
a13a1ca9ab Fixed header includes 2019-08-02 12:18:35 +02:00
bdf4bd169e Moved hardware abstraction to separate header 2019-08-02 12:18:35 +02:00
6e45340993 Removed trailing whitespaces 2019-08-01 21:51:46 +02:00
f0a3cd6da8 Improved formatting 2019-07-30 21:51:13 +02:00
6861b8f5d1 Added force inline to reduce code size 2019-07-30 21:48:00 +02:00
aac73447b3 Implemented hardware abstraction that both hardware0 and hardware1 can use 2019-07-30 21:43:52 +02:00
fbd087808a Added enums for register bit positions 2019-07-30 20:56:16 +02:00
65bbf5e96a Refactored hardware abstraction 2019-07-30 20:29:38 +02:00
c03196493a Ran clang-format 2019-07-30 20:29:15 +02:00
470eb06345 Removed unused struct 2019-07-30 18:35:45 +02:00
099be106a7 Added C uart library by Peter Fleury as comparison 2019-07-30 18:32:00 +02:00
6ded0e1c8d Fixed rounding error in baud rate calculation 2019-07-28 19:20:03 +02:00
00cb9ad13c Added alias for first hardware uart 2019-07-28 17:58:23 +02:00
4a9cee922a Refactored class names to start with capital letter 2019-07-28 17:57:49 +02:00
8e0ba5a463 Implemented basic hardware support for transmitting 2019-07-28 17:32:51 +02:00
a71035d4b7 Specified default for hardware mode 2019-07-28 14:10:48 +02:00
fd7e8e7238 Added driving parameter 2019-07-28 14:09:09 +02:00
d9a05d0273 Fixed refactoring problem 2019-07-28 14:08:41 +02:00
2bbba0fcbd Removed unnecessary const qualifiers in template 2019-07-28 14:00:46 +02:00
29b4d85ce3 Renamed settings to config 2019-07-28 12:15:19 +02:00
ae90fdae3f Renamed choosing function 2019-07-28 10:54:47 +02:00
2364bff11f Implemented automatic type deducation depending on data bits 2019-07-28 10:49:14 +02:00
f08f607265 Fixed not implemented compiler error 2019-07-28 10:35:11 +02:00
f9c34b09ba Added basic layout for new library implementation 2019-07-27 18:55:17 +02:00
e07ba7ecd8 Fixed handling for ATmega1284P 2019-07-27 18:54:51 +02:00
81e6a9fdc0 Added support for atmega8a 2018-04-12 10:44:03 +02:00
b2a3b03867 Added git ignore and attribute files 2016-08-16 16:46:33 +02:00
88eb11cc02 Added blocking receive function and peek function 2016-05-26 20:15:21 +02:00
7fd6d8ddec Added separate receive line function with timeout and removed terminating string from received line 2016-05-25 23:38:02 +02:00
c261d7d309 Added getter functions for usart settings and fixed receive flush to work for all usart settings 2016-05-25 21:22:16 +02:00
e008cc8da1 Fixed flushing receive buffer to work independent of baud rate and with and without interrupts 2016-05-25 01:40:53 +02:00
99d09b0f0f Converted to submodule 2016-05-24 21:11:44 +02:00
15 changed files with 1192 additions and 579 deletions

12
.gitmodules vendored
View File

@ -1,12 +0,0 @@
[submodule "uart/io"]
path = uart/io
url = git@git.blackmark.me:avr/io.git
[submodule "uart/uart"]
path = uart/uart
url = git@git.blackmark.me:avr/uart.git
[submodule "uart/flash"]
path = uart/flash
url = git@git.blackmark.me:avr/flash.git
[submodule "uart/util"]
path = uart/util
url = git@git.blackmark.me:avr/util.git

50
config.hpp Normal file
View File

@ -0,0 +1,50 @@
#pragma once
#include <cstdint>
namespace uart {
enum class DataBits {
FIVE,
SIX,
SEVEN,
EIGHT,
NINE,
};
enum class StopBits {
ONE,
TWO,
};
enum class Parity {
NONE,
ODD,
EVEN,
};
namespace detail {
template <DataBits dataBits>
struct choose_data_type {
using type = std::uint8_t;
};
template <>
struct choose_data_type<DataBits::NINE> {
using type = std::uint16_t;
};
} // namespace detail
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;
static constexpr auto DATA_BITS = dataBits;
static constexpr auto PARITY = parity;
static constexpr auto STOP_BITS = stopBits;
using data_t = typename detail::choose_data_type<DATA_BITS>::type;
};
} // namespace uart

423
hardware.hpp Normal file
View File

@ -0,0 +1,423 @@
#pragma once
#include "../clock.hpp"
#include <cmath>
#include <cstdint>
namespace uart {
enum class Mode {
ASYNCHRONOUS,
SYNCHRONOUS_MASTER,
SYNCHRONOUS_SLAVE,
SPI,
};
enum class Driven {
INTERRUPT,
BLOCKING,
};
namespace detail {
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:
[[gnu::always_inline]] static void init()
{
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");
constexpr auto UseDoubleSpeed = (AbsDoubleError < AbsNormalError);
constexpr auto BaudVal = calcBaudVal<UseDoubleSpeed>();
*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 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();
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;
}
[[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>();
return true;
}
return false;
}
[[gnu::always_inline]] static typename cfg::data_t rxByteInterrupt()
{
return *getRegPtr<Registers::IO_REG_ADDR>();
}
[[gnu::always_inline]] static bool txEmpty()
{
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())
;
*getRegPtr<Registers::IO_REG_ADDR>() = byte;
}
[[gnu::always_inline]] static void txByteInterrupt(volatile const typename cfg::data_t &byte)
{
*getRegPtr<Registers::IO_REG_ADDR>() = byte;
}
[[gnu::always_inline]] static bool peekBlocking()
{
if (*getRegPtr<Registers::CTRL_STAT_REG_A_ADDR>() & (1 << CtrlFlagsA::RECEIVE_COMPLETE)) {
return true;
}
return false;
}
[[gnu::always_inline]] static void enableDataRegEmptyInt()
{
auto ctrlStatRegB = getRegPtr<Registers::CTRL_STAT_REG_B_ADDR>();
*ctrlStatRegB = *ctrlStatRegB | (1 << CtrlFlagsB::DATA_REG_EMPTY_INT_ENABLE);
}
[[gnu::always_inline]] static void disableDataRegEmptyInt()
{
auto ctrlStatRegB = getRegPtr<Registers::CTRL_STAT_REG_B_ADDR>();
*ctrlStatRegB = *ctrlStatRegB & ~(1 << CtrlFlagsB::DATA_REG_EMPTY_INT_ENABLE);
}
private:
struct DataBitsVal {
std::uint8_t regCVal = 0;
std::uint8_t regBVal = 0;
};
template <bool DoubleSpeed = true>
static constexpr auto calcBaudVal()
{
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()
{
DataBitsVal dataBitsVal;
switch (cfg::DATA_BITS) {
case DataBits::FIVE:
dataBitsVal.regCVal = 0;
break;
case DataBits::SIX:
dataBitsVal.regCVal = (1 << CtrlFlagsC::CHAR_SIZE_0);
break;
case DataBits::SEVEN:
dataBitsVal.regCVal = (1 << CtrlFlagsC::CHAR_SIZE_1);
break;
case DataBits::EIGHT:
dataBitsVal.regCVal = (1 << CtrlFlagsC::CHAR_SIZE_1) | (1 << CtrlFlagsC::CHAR_SIZE_0);
break;
case DataBits::NINE:
dataBitsVal.regCVal = (1 << CtrlFlagsC::CHAR_SIZE_1) | (1 << CtrlFlagsC::CHAR_SIZE_0);
dataBitsVal.regBVal = (1 << CtrlFlagsB::CHAR_SIZE_2);
break;
}
return dataBitsVal;
}
static constexpr auto calcParity()
{
std::uint8_t parityVal = 0;
if (cfg::PARITY == Parity::EVEN)
parityVal = (1 << CtrlFlagsC::PARITY_MODE_1);
else if (cfg::PARITY == Parity::ODD)
parityVal = (1 << CtrlFlagsC::PARITY_MODE_1) | (1 << CtrlFlagsC::PARITY_MODE_0);
return parityVal;
}
static constexpr auto calcStopBits()
{
std::uint8_t stopBitsVal = 0;
if (cfg::STOP_BITS == StopBits::TWO)
stopBitsVal = (1 << CtrlFlagsC::STOP_BIT_SEL);
return stopBitsVal;
}
static constexpr auto calcMode()
{
static_assert(mode != Mode::SPI, "SPI mode can not be used with uart");
std::uint8_t modeVal = 0;
if (mode == Mode::SYNCHRONOUS_MASTER || mode == Mode::SYNCHRONOUS_SLAVE) {
modeVal = (1 << CtrlFlagsC::MODE_SEL_0);
}
return modeVal;
}
template <bool Enable>
static constexpr auto calcRxState()
{
std::uint8_t enableVal = 0;
if (Enable)
enableVal = (1 << CtrlFlagsB::RX_ENABLE);
return enableVal;
}
template <bool Enable>
static constexpr auto calcTxState()
{
std::uint8_t enableVal = 0;
if (Enable)
enableVal = (1 << CtrlFlagsB::TX_ENABLE);
return enableVal;
}
static constexpr auto calcInterrupt()
{
std::uint8_t interruptVal = 0;
if (driven == Driven::INTERRUPT)
interruptVal = (1 << CtrlFlagsB::RX_INT_ENABLE);
return interruptVal;
}
};
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

152
hardware0.hpp Normal file
View File

@ -0,0 +1,152 @@
#pragma once
#include <cstdint>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/sfr_defs.h>
#include "config.hpp"
#include "hardware.hpp"
namespace uart {
namespace detail {
#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 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,
PARITY_ERROR = UPE0,
DATA_OVER_RUN = DOR0,
FRAME_ERROR = FE0,
DATA_REG_EMPTY = UDRE0,
TRANSMIT_COMPLETE = TXC0,
RECEIVE_COMPLETE = RXC0,
};
enum class ControlFlagsB0 {
TX_DATA_BIT_8 = TXB80,
RX_DATA_BIT_8 = RXB80,
CHAR_SIZE_2 = UCSZ02,
TX_ENABLE = TXEN0,
RX_ENABLE = RXEN0,
DATA_REG_EMPTY_INT_ENABLE = UDRIE0,
TX_INT_ENABLE = TXCIE0,
RX_INT_ENABLE = RXCIE0,
};
enum class ControlFlagsC0 {
CLK_POLARITY = UCPOL0,
CHAR_SIZE_0 = UCSZ00,
CHAR_SIZE_1 = UCSZ01,
STOP_BIT_SEL = USBS0,
PARITY_MODE_0 = UPM00,
PARITY_MODE_1 = UPM01,
MODE_SEL_0 = UMSEL00,
MODE_SEL_1 = UMSEL01,
};
// clang-format off
constexpr int operator<<(const int &lhs, const ControlFlagsA0 &rhs) { return lhs << static_cast<int>(rhs); }
constexpr int operator<<(const int &lhs, const ControlFlagsB0 &rhs) { return lhs << static_cast<int>(rhs); }
constexpr int operator<<(const int &lhs, const ControlFlagsC0 &rhs) { return lhs << static_cast<int>(rhs); }
// clang-format on
#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"
#endif
} // namespace detail
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> {
};
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();
sei();
}
private:
using HardwareImpl = detail::Hardware<detail::Registers0, detail::ControlFlagsA0, detail::ControlFlagsB0,
detail::ControlFlagsC0, cfg, Driven::INTERRUPT, mode>;
using InterruptHardwareImpl = detail::InterruptHardware<detail::Registers0, detail::ControlFlagsA0,
detail::ControlFlagsB0, detail::ControlFlagsC0, cfg, mode>;
// 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
//////////////////////////////////////////////////////////////////////////
// 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

157
hardware1.hpp Normal file
View File

@ -0,0 +1,157 @@
#pragma once
#include <cstdint>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/sfr_defs.h>
#include "config.hpp"
#include "hardware.hpp"
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 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,
PARITY_ERROR = UPE1,
DATA_OVER_RUN = DOR1,
FRAME_ERROR = FE1,
DATA_REG_EMPTY = UDRE1,
TRANSMIT_COMPLETE = TXC1,
RECEIVE_COMPLETE = RXC1,
};
enum class ControlFlagsB1 {
TX_DATA_BIT_8 = TXB81,
RX_DATA_BIT_8 = RXB81,
CHAR_SIZE_2 = UCSZ12,
TX_ENABLE = TXEN1,
RX_ENABLE = RXEN1,
DATA_REG_EMPTY_INT_ENABLE = UDRIE1,
TX_INT_ENABLE = TXCIE1,
RX_INT_ENABLE = RXCIE1,
};
enum class ControlFlagsC1 {
CLK_POLARITY = UCPOL1,
CHAR_SIZE_0 = UCSZ10,
CHAR_SIZE_1 = UCSZ11,
STOP_BIT_SEL = USBS1,
PARITY_MODE_0 = UPM10,
PARITY_MODE_1 = UPM11,
MODE_SEL_0 = UMSEL10,
MODE_SEL_1 = UMSEL11,
};
// clang-format off
constexpr int operator<<(const int &lhs, const ControlFlagsA1 &rhs) { return lhs << static_cast<int>(rhs); }
constexpr int operator<<(const int &lhs, const ControlFlagsB1 &rhs) { return lhs << static_cast<int>(rhs); }
constexpr int operator<<(const int &lhs, const ControlFlagsC1 &rhs) { return lhs << static_cast<int>(rhs); }
// clang-format on
#define HAS_UART1
#endif
} // namespace detail
#ifdef HAS_UART1
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> {
};
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();
sei();
}
private:
using HardwareImpl = detail::Hardware<detail::Registers1, detail::ControlFlagsA1, detail::ControlFlagsB1,
detail::ControlFlagsC1, cfg, Driven::INTERRUPT, mode>;
using InterruptHardwareImpl = detail::InterruptHardware<detail::Registers1, detail::ControlFlagsA1,
detail::ControlFlagsB1, detail::ControlFlagsC1, cfg, mode>;
// 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();
}
};
#endif
} // namespace uart
//////////////////////////////////////////////////////////////////////////
#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

21
software.hpp Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include "config.hpp"
#include "../io/io.hpp"
#include "../util/util.hpp"
namespace uart {
template <io::P rxPin, io::P txPin, class cfg = Config<>>
class Software {
static_assert(util::always_false_v<cfg>, "Not implemented");
public:
using data_t = typename cfg::data_t;
static constexpr auto DATA_BITS = cfg::DATA_BITS;
static void init() {}
};
} // namespace uart

View File

@ -1,22 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Atmel Studio Solution File, Format Version 11.00
VisualStudioVersion = 14.0.23107.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{E66E83B9-2572-4076-B26E-6BE79FF3018A}") = "uart", "uart\uart.cppproj", "{DCE6C7E3-EE26-4D79-826B-08594B9AD897}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|AVR = Debug|AVR
Release|AVR = Release|AVR
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{DCE6C7E3-EE26-4D79-826B-08594B9AD897}.Debug|AVR.ActiveCfg = Debug|AVR
{DCE6C7E3-EE26-4D79-826B-08594B9AD897}.Debug|AVR.Build.0 = Debug|AVR
{DCE6C7E3-EE26-4D79-826B-08594B9AD897}.Release|AVR.ActiveCfg = Release|AVR
{DCE6C7E3-EE26-4D79-826B-08594B9AD897}.Release|AVR.Build.0 = Release|AVR
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

389
uart.hpp Normal file
View File

@ -0,0 +1,389 @@
#pragma once
#include <limits>
#include <cstdint>
#include "config.hpp"
#include "software.hpp"
#include "hardware0.hpp"
#include "hardware1.hpp"
#include "../flash/flash.hpp"
#include "../util/util.hpp"
namespace uart {
namespace detail {
template <typename T, T Limit, std::size_t Base>
static constexpr std::size_t cntDigits()
{
T num = Limit;
std::size_t cnt = 0;
do {
num /= Base;
++cnt;
} while (num > 0);
return cnt;
}
template <typename T, std::size_t Base>
static constexpr std::size_t maxNumDigits()
{
constexpr T MinVal = std::numeric_limits<T>::min();
constexpr T MaxVal = std::numeric_limits<T>::max();
constexpr T MinDigits = cntDigits<T, MinVal, Base>();
constexpr T MaxDigits = cntDigits<T, MaxVal, Base>();
return (MinDigits < MaxDigits) ? MaxDigits : MinDigits;
}
} // namespace detail
template <class Driver>
class Uart {
public:
using data_t = typename Driver::data_t;
// Constructing a uart object does not initialize the driver to allow different specializations with the same
// back-end to exists at the same time
// Note that init must be called every time when switching specializations with the same back-end
Uart() = default;
// Moving and copying uart objects is not supported
Uart(const Uart &) = delete;
Uart(Uart &&) = delete;
Uart &operator=(const Uart &) = delete;
Uart &operator=(Uart &&) = delete;
// Before using the uart init must be called
static void init()
{
Driver::init();
}
static void txByte(const data_t &byte)
{
Driver::txByte(byte);
}
static bool rxByte(data_t &byte)
{
return Driver::rxByte(byte);
}
static bool peek(data_t &byte)
{
return Driver::peek(byte);
}
static bool peek()
{
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");
while (char ch = *str++)
txByte(ch);
}
static void txString(const ::detail::FlashString *str)
{
static_assert(Driver::DATA_BITS == DataBits::EIGHT, "Strings are only supported with 8 data bits");
const char *strIt = reinterpret_cast<const char *>(str);
while (char ch = pgm_read_byte(strIt++))
txByte(ch);
}
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(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 std::size_t NumDigits = detail::maxNumDigits<T, Base>();
T digits = val;
if (digits < 0) {
digits = -digits;
txByte('-');
}
data_t buffer[NumDigits];
data_t *bufEnd = buffer + NumDigits - 1;
do {
const data_t lastDigit = digits % Base;
*bufEnd-- = (lastDigit < 10) ? ('0' + lastDigit) : (AlphaChar + lastDigit - 10);
digits /= Base;
} while (digits > 0);
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);
}
//////////////////////////////////////////////////////////////////////////
// Output stream overloads
Uart &operator<<(const char *str)
{
txString(str);
return *this;
}
Uart &operator<<(const ::detail::FlashString *str)
{
txString(str);
return *this;
}
Uart &operator<<(const char &val)
{
txByte(val);
return *this;
}
Uart &operator<<(const signed char &val)
{
txNumber(val);
return *this;
}
Uart &operator<<(const unsigned char &val)
{
txNumber(val);
return *this;
}
Uart &operator<<(const short &val)
{
txNumber(val);
return *this;
}
Uart &operator<<(const unsigned short &val)
{
txNumber(val);
return *this;
}
Uart &operator<<(const int &val)
{
txNumber(val);
return *this;
}
Uart &operator<<(const unsigned int &val)
{
txNumber(val);
return *this;
}
Uart &operator<<(const long &val)
{
txNumber(val);
return *this;
}
Uart &operator<<(const unsigned long &val)
{
txNumber(val);
return *this;
}
Uart &operator<<(const long long &val)
{
txNumber(val);
return *this;
}
Uart &operator<<(const unsigned long long &val)
{
txNumber(val);
return *this;
}
template <typename... Ts>
Uart &operator<<(float) const
{
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 supported by hardware");
}
template <typename... Ts>
Uart &operator<<(long double) const
{
static_assert(util::always_false_v<Ts...>, "Not supported by hardware");
}
Uart &operator<<(const bool &val)
{
txString(val ? F("true") : F("false"));
return *this;
}
Uart &operator<<(const void *val)
{
txString(F("0x"));
txNumber<std::uint16_t, 16, 4, '0', false>(reinterpret_cast<std::uint16_t>(val));
return *this;
}
//////////////////////////////////////////////////////////////////////////
// Input stream overloads
template <typename... Ts>
Uart &operator>>(char &) const
{
static_assert(util::always_false_v<Ts...>, "Not implemented");
}
template <typename... Ts>
Uart &operator>>(unsigned char &) const
{
static_assert(util::always_false_v<Ts...>, "Not implemented");
}
template <typename... Ts>
Uart &operator>>(short &) const
{
static_assert(util::always_false_v<Ts...>, "Not implemented");
}
template <typename... Ts>
Uart &operator>>(unsigned short &) const
{
static_assert(util::always_false_v<Ts...>, "Not implemented");
}
template <typename... Ts>
Uart &operator>>(int &) const
{
static_assert(util::always_false_v<Ts...>, "Not implemented");
}
template <typename... Ts>
Uart &operator>>(unsigned int &) const
{
static_assert(util::always_false_v<Ts...>, "Not implemented");
}
template <typename... Ts>
Uart &operator>>(long &) const
{
static_assert(util::always_false_v<Ts...>, "Not implemented");
}
template <typename... Ts>
Uart &operator>>(unsigned long &) const
{
static_assert(util::always_false_v<Ts...>, "Not implemented");
}
template <typename... Ts>
Uart &operator>>(long long &) const
{
static_assert(util::always_false_v<Ts...>, "Not implemented");
}
template <typename... Ts>
Uart &operator>>(unsigned long long &) const
{
static_assert(util::always_false_v<Ts...>, "Not implemented");
}
template <typename... Ts>
Uart &operator>>(float &) const
{
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 supported by hardware");
}
template <typename... Ts>
Uart &operator>>(long double &) const
{
static_assert(util::always_false_v<Ts...>, "Not supported by hardware");
}
template <typename... Ts>
Uart &operator>>(bool &) const
{
static_assert(util::always_false_v<Ts...>, "Not implemented");
}
template <typename... Ts>
Uart &operator>>(const void *&) const
{
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<cfg, Driven::INTERRUPT, Mode::ASYNCHRONOUS>>;
#ifdef HAS_UART1
template <typename cfg = Config<>>
using Uart1 = Uart<Hardware1<cfg, Driven::INTERRUPT, Mode::ASYNCHRONOUS>>;
#endif
} // namespace uart
#undef HAS_UART1

View File

@ -1,4 +0,0 @@
#pragma once
#define F_CPU 16'000'000
#include <util/delay.h>

@ -1 +0,0 @@
Subproject commit 6edb2e5a21e0ce58ff2df936caee8b84e240a46b

@ -1 +0,0 @@
Subproject commit 80de36ee7ee3e6b0842d5eaee81d54062cb496b2

View File

@ -1,283 +0,0 @@
#include "clock.hpp"
#include <avr/interrupt.h>
#include <stdint.h>
#include "flash/flash.hpp"
#include "io/io.hpp"
#include "uart/uart.hpp"
using uart0_interface_t =
uart::Uart<uart::Hardware0<uart::Config<115200>, uart::Driven::INTERRUPT, uart::Mode::ASYNCHRONOUS>>;
using uart1_interface_t = uart::Uart1<>;
REGISTER_UART0_INT_VECTORS(uart0_interface_t);
REGISTER_UART1_INT_VECTORS(uart1_interface_t);
void doubleSpeedTest()
{
uart0_interface_t serial;
serial.init();
uint8_t counter = 100;
uint8_t data;
while (counter) {
if (serial.rxByte(data)) {
serial.txByte(data);
--counter;
}
}
serial << F("\r\n");
serial.flushTx();
}
void newUartUsage()
{
using namespace uart;
Uart<Hardware0<Config<115200>, Driven::BLOCKING, Mode::ASYNCHRONOUS>> serial;
serial.init();
serial << "New uart hi from RAM. " << F("New uart hi from flash\r\n");
while (false) {
uint8_t received = 0;
while (!serial.peek())
;
{
serial << F("Peeked: ");
serial.txByte(received);
serial << F("\r\n");
}
if (serial.rxByte(received)) {
serial << F("Received: ");
serial.txByte(received);
serial << F("\r\n");
}
}
serial.flushTx();
}
void newUartUsage2()
{
uart1_interface_t serial1;
auto ramString = "Hello World from RAM. ";
auto flashString = F("Hello World from flash\r\n");
serial1.init();
serial1 << ramString;
serial1 << flashString;
serial1.flushTx();
}
void newUartStreamOverloads()
{
using namespace uart;
Uart<Hardware0<Config<115200>, Driven::BLOCKING, Mode::ASYNCHRONOUS>> serial;
serial.init();
bool bVal = true;
char chVal = 'c';
signed char schVal = 's';
unsigned char uchVal = 'u';
short shVal = -12345;
unsigned short ushVal = 64123;
int iVal = -14321;
unsigned int uiVal = 32146;
long lVal = -571474496;
unsigned long ulVal = 2718958144;
long long llVal = -45197516864960;
unsigned long long ullVal = 4611685969606738496;
serial << F("Stream overload test:") << F("\r\n");
serial << F("bool : ") << bVal << F("\r\n");
serial << F("char : ") << chVal << F("\r\n");
serial << F("signed char : ") << schVal << F("\r\n");
serial << F("unsigned char : ") << uchVal << F("\r\n");
serial << F("short : ") << shVal << F("\r\n");
serial << F("unsigned short : ") << ushVal << F("\r\n");
serial << F("int : ") << iVal << F("\r\n");
serial << F("unsigned int : ") << uiVal << F("\r\n");
serial << F("long : ") << lVal << F("\r\n");
serial << F("unsigned long : ") << ulVal << F("\r\n");
serial << F("long long : ") << llVal << F("\r\n");
serial << F("unsigned long long : ") << ullVal << F("\r\n");
serial << F("const void : ") << &bVal << F("\r\n");
auto number = 0xBADF00D;
serial << F("Binary : 0b");
serial.txNumber<decltype(number), 2>(number);
serial << F("\r\n");
serial << F("Octal : 0");
serial.txNumber<decltype(number), 8>(number);
serial << F("\r\n");
serial << F("Decimal : ");
serial.txNumber<decltype(number), 10>(number);
serial << F("\r\n");
serial << F("Hex : 0x");
serial.txNumber<decltype(number), 16>(number);
serial << F("\r\n");
serial.flushTx();
}
namespace spi {
enum class Cpol {
MODE_0,
MODE_1,
};
enum class Cpha {
MODE_0,
MODE_1,
};
enum class DataOrder {
MSB,
LSB,
};
template <Cpol cpol, Cpha cpha, DataOrder dataOrder>
struct Config {
static constexpr auto CPOL_MODE = cpol;
static constexpr auto CPHA_MODE = cpha;
static constexpr auto DATA_ORDER = dataOrder;
};
template <class Driver>
struct spi {
spi()
{
Driver::init();
}
};
} // namespace spi
namespace uart {
template <class Config>
class Hardware0<Config, Driven::INTERRUPT, Mode::SPI> {
public:
static void init()
{
UCSR0C |= (1 << UMSEL01) | (1 << UMSEL00);
if (DATA_ORDER == spi::DataOrder::MSB)
UCSR0C &= ~(1 << UCSZ01);
else
UCSR0C |= (1 << UCSZ01);
if (CPOL_MODE == spi::Cpol::MODE_0)
UCSR0C &= ~(1 << UCPOL0);
else
UCSR0C |= (1 << UCPOL0);
if (CPHA_MODE == spi::Cpha::MODE_0)
UCSR0C &= ~(1 << UCSZ00);
else
UCSR0C |= (1 << UCSZ00);
}
private:
static constexpr auto CPOL_MODE = Config::CPOL_MODE;
static constexpr auto CPHA_MODE = Config::CPHA_MODE;
static constexpr auto DATA_ORDER = Config::DATA_ORDER;
};
} // namespace uart
void spiTest()
{
using config = spi::Config<spi::Cpol::MODE_0, spi::Cpha::MODE_0, spi::DataOrder::MSB>;
using uartspi = uart::Hardware0<config, uart::Driven::INTERRUPT, uart::Mode::SPI>;
spi::spi<uartspi> uartSpi;
}
static inline void initUart(const uint32_t baudRate)
{
UBRR0 = static_cast<uint16_t>((F_CPU + 8 * baudRate) / (16 * baudRate) - 1);
UCSR0A = 0;
UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
UCSR0B = (1 << RXEN0) | (1 << TXEN0);
}
static inline void txUart(uint8_t byte)
{
while (!(UCSR0A & (1 << UDRE0)))
;
UDR0 = byte;
}
static inline void txString(const char *str)
{
while (char ch = *str++)
txUart(ch);
}
static inline void txString(const detail::FlashString *str)
{
const char *strIt = reinterpret_cast<const char *>(str);
while (char ch = pgm_read_byte(strIt++))
txUart(ch);
}
static inline void flushTx()
{
while (!(UCSR0A & (1 << UDRE0)))
;
while (!(UCSR0A & (1 << TXC0)))
;
UCSR0A |= (1 << TXC0);
}
void optimalUartTest()
{
auto ramString = "Hello World from RAM. ";
auto flashString = F("Hello World from flash\r\n");
initUart(115200);
txString(ramString);
txString(flashString);
flushTx();
}
int main()
{
sei();
doubleSpeedTest();
newUartUsage2();
optimalUartTest();
newUartStreamOverloads();
txString(F("\r\n"));
flushTx();
spiTest();
return 0;
}

@ -1 +0,0 @@
Subproject commit 119de3244588b19b4afb06f33f66f22bb80a89b5

View File

@ -1,254 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="14.0">
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
<ProjectVersion>7.0</ProjectVersion>
<ToolchainName>com.Atmel.AVRGCC8.CPP</ToolchainName>
<ProjectGuid>dce6c7e3-ee26-4d79-826b-08594b9ad897</ProjectGuid>
<avrdevice>ATmega1284P</avrdevice>
<avrdeviceseries>none</avrdeviceseries>
<OutputType>Executable</OutputType>
<Language>CPP</Language>
<OutputFileName>$(MSBuildProjectName)</OutputFileName>
<OutputFileExtension>.elf</OutputFileExtension>
<OutputDirectory>$(MSBuildProjectDirectory)\$(Configuration)</OutputDirectory>
<AssemblyName>uart</AssemblyName>
<Name>uart</Name>
<RootNamespace>uart</RootNamespace>
<ToolchainFlavour>avr-g++-9.1.0</ToolchainFlavour>
<KeepTimersRunning>true</KeepTimersRunning>
<OverrideVtor>false</OverrideVtor>
<CacheFlash>true</CacheFlash>
<ProgFlashFromRam>true</ProgFlashFromRam>
<RamSnippetAddress>0x20000000</RamSnippetAddress>
<UncachedRange />
<preserveEEPROM>true</preserveEEPROM>
<OverrideVtorValue>exception_table</OverrideVtorValue>
<BootSegment>2</BootSegment>
<ResetRule>0</ResetRule>
<eraseonlaunchrule>0</eraseonlaunchrule>
<EraseKey />
<avrtool>
</avrtool>
<avrtoolserialnumber>J41800099437</avrtoolserialnumber>
<avrdeviceexpectedsignature>0x1E950F</avrdeviceexpectedsignature>
<com_atmel_avrdbg_tool_jtagicemkii>
<ToolOptions>
<InterfaceProperties>
<IspClock>0</IspClock>
</InterfaceProperties>
<InterfaceName>ISP</InterfaceName>
</ToolOptions>
<ToolType>com.atmel.avrdbg.tool.jtagicemkii</ToolType>
<ToolNumber>070000004699</ToolNumber>
<ToolName>JTAGICE mkII</ToolName>
</com_atmel_avrdbg_tool_jtagicemkii>
<avrtoolinterface>ISP</avrtoolinterface>
<avrtoolinterfaceclock>125000</avrtoolinterfaceclock>
<AAFDebugger>
<AAFDebugFiles>
<DebugFile>
<path>\Debug\uart.lss</path>
<AAFSetting>
<Label>Lss Files</Label>
<Extention>.lss</Extention>
<Regex>^\s*(?&lt;address&gt;[a-f0-9]*):\s*.*$</Regex>
<DebugEnabled>true</DebugEnabled>
<RegexGroups>address</RegexGroups>
<DebuggerExpression>$pc</DebuggerExpression>
</AAFSetting>
</DebugFile>
</AAFDebugFiles>
</AAFDebugger>
<AsfFrameworkConfig>
<framework-data xmlns="">
<options />
<configurations />
<files />
<documentation help="" />
<offline-documentation help="" />
<dependencies>
<content-extension eid="atmel.asf" uuidref="Atmel.ASF" version="3.46.0" />
</dependencies>
</framework-data>
</AsfFrameworkConfig>
<com_atmel_avrdbg_tool_atmelice>
<ToolOptions>
<InterfaceProperties>
<IspClock>125000</IspClock>
<JtagDbgClock>5000000</JtagDbgClock>
</InterfaceProperties>
<InterfaceName>ISP</InterfaceName>
</ToolOptions>
<ToolType>com.atmel.avrdbg.tool.atmelice</ToolType>
<ToolNumber>J41800099437</ToolNumber>
<ToolName>Atmel-ICE</ToolName>
</com_atmel_avrdbg_tool_atmelice>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<ToolchainSettings>
<AvrGccCpp>
<avrgcc.common.Device>-mmcu=atmega1284p</avrgcc.common.Device>
<avrgcc.common.optimization.RelaxBranches>True</avrgcc.common.optimization.RelaxBranches>
<avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex>
<avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss>
<avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep>
<avrgcc.common.outputfiles.srec>True</avrgcc.common.outputfiles.srec>
<avrgcc.common.outputfiles.usersignatures>False</avrgcc.common.outputfiles.usersignatures>
<avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>
<avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>
<avrgcc.compiler.symbols.DefSymbols>
<ListValues>
<Value>NDEBUG</Value>
</ListValues>
</avrgcc.compiler.symbols.DefSymbols>
<avrgcc.compiler.directories.IncludePaths>
<ListValues>
<Value>%24(PackRepoDir)\Atmel\ATmega_DFP\1.3.300\include</Value>
</ListValues>
</avrgcc.compiler.directories.IncludePaths>
<avrgcc.compiler.optimization.level>Optimize for size (-Os)</avrgcc.compiler.optimization.level>
<avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum>
<avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings>
<avrgcc.compiler.warnings.ExtraWarnings>True</avrgcc.compiler.warnings.ExtraWarnings>
<avrgcc.compiler.warnings.Pedantic>True</avrgcc.compiler.warnings.Pedantic>
<avrgcc.compiler.miscellaneous.OtherFlags>-fno-threadsafe-statics -std=c11</avrgcc.compiler.miscellaneous.OtherFlags>
<avrgcccpp.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcccpp.compiler.general.ChangeDefaultCharTypeUnsigned>
<avrgcccpp.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcccpp.compiler.general.ChangeDefaultBitFieldUnsigned>
<avrgcccpp.compiler.symbols.DefSymbols>
<ListValues>
<Value>NDEBUG</Value>
</ListValues>
</avrgcccpp.compiler.symbols.DefSymbols>
<avrgcccpp.compiler.directories.IncludePaths>
<ListValues>
<Value>%24(PackRepoDir)\Atmel\ATmega_DFP\1.3.300\include</Value>
</ListValues>
</avrgcccpp.compiler.directories.IncludePaths>
<avrgcccpp.compiler.optimization.level>Optimize for size (-Os)</avrgcccpp.compiler.optimization.level>
<avrgcccpp.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcccpp.compiler.optimization.AllocateBytesNeededForEnum>
<avrgcccpp.compiler.warnings.AllWarnings>True</avrgcccpp.compiler.warnings.AllWarnings>
<avrgcccpp.compiler.warnings.Pedantic>True</avrgcccpp.compiler.warnings.Pedantic>
<avrgcccpp.compiler.miscellaneous.OtherFlags>-fno-threadsafe-statics -Wextra -std=c++17</avrgcccpp.compiler.miscellaneous.OtherFlags>
<avrgcccpp.linker.libraries.Libraries>
<ListValues>
<Value>libm</Value>
</ListValues>
</avrgcccpp.linker.libraries.Libraries>
<avrgcccpp.assembler.general.IncludePaths>
<ListValues>
<Value>%24(PackRepoDir)\Atmel\ATmega_DFP\1.3.300\include</Value>
</ListValues>
</avrgcccpp.assembler.general.IncludePaths>
</AvrGccCpp>
</ToolchainSettings>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<ToolchainSettings>
<AvrGccCpp>
<avrgcc.common.Device>-mmcu=atmega1284p</avrgcc.common.Device>
<avrgcc.common.optimization.RelaxBranches>True</avrgcc.common.optimization.RelaxBranches>
<avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex>
<avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss>
<avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep>
<avrgcc.common.outputfiles.srec>True</avrgcc.common.outputfiles.srec>
<avrgcc.common.outputfiles.usersignatures>False</avrgcc.common.outputfiles.usersignatures>
<avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>
<avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>
<avrgcc.compiler.symbols.DefSymbols>
<ListValues>
<Value>DEBUG</Value>
</ListValues>
</avrgcc.compiler.symbols.DefSymbols>
<avrgcc.compiler.directories.IncludePaths>
<ListValues>
<Value>%24(PackRepoDir)\Atmel\ATmega_DFP\1.3.300\include</Value>
</ListValues>
</avrgcc.compiler.directories.IncludePaths>
<avrgcc.compiler.optimization.level>Optimize (-O1)</avrgcc.compiler.optimization.level>
<avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum>
<avrgcc.compiler.optimization.DebugLevel>Maximum (-g3)</avrgcc.compiler.optimization.DebugLevel>
<avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings>
<avrgcc.compiler.warnings.ExtraWarnings>True</avrgcc.compiler.warnings.ExtraWarnings>
<avrgcc.compiler.warnings.Pedantic>True</avrgcc.compiler.warnings.Pedantic>
<avrgcc.compiler.miscellaneous.OtherFlags>-fno-threadsafe-statics -std=c11</avrgcc.compiler.miscellaneous.OtherFlags>
<avrgcccpp.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcccpp.compiler.general.ChangeDefaultCharTypeUnsigned>
<avrgcccpp.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcccpp.compiler.general.ChangeDefaultBitFieldUnsigned>
<avrgcccpp.compiler.symbols.DefSymbols>
<ListValues>
<Value>DEBUG</Value>
</ListValues>
</avrgcccpp.compiler.symbols.DefSymbols>
<avrgcccpp.compiler.directories.IncludePaths>
<ListValues>
<Value>%24(PackRepoDir)\Atmel\ATmega_DFP\1.3.300\include</Value>
</ListValues>
</avrgcccpp.compiler.directories.IncludePaths>
<avrgcccpp.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcccpp.compiler.optimization.AllocateBytesNeededForEnum>
<avrgcccpp.compiler.optimization.DebugLevel>Maximum (-g3)</avrgcccpp.compiler.optimization.DebugLevel>
<avrgcccpp.compiler.warnings.AllWarnings>True</avrgcccpp.compiler.warnings.AllWarnings>
<avrgcccpp.compiler.warnings.Pedantic>True</avrgcccpp.compiler.warnings.Pedantic>
<avrgcccpp.compiler.miscellaneous.OtherFlags>-fno-threadsafe-statics -Wextra -std=c++17</avrgcccpp.compiler.miscellaneous.OtherFlags>
<avrgcccpp.linker.libraries.Libraries>
<ListValues>
<Value>libm</Value>
</ListValues>
</avrgcccpp.linker.libraries.Libraries>
<avrgcccpp.assembler.general.IncludePaths>
<ListValues>
<Value>%24(PackRepoDir)\Atmel\ATmega_DFP\1.3.300\include</Value>
</ListValues>
</avrgcccpp.assembler.general.IncludePaths>
<avrgcccpp.assembler.debugging.DebugLevel>Default (-Wa,-g)</avrgcccpp.assembler.debugging.DebugLevel>
</AvrGccCpp>
</ToolchainSettings>
</PropertyGroup>
<ItemGroup>
<Compile Include="clock.hpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="flash\flash.hpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="io\io.hpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="main.cpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="uart\config.hpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="uart\hardware.hpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="uart\hardware0.hpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="uart\hardware1.hpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="uart\software.hpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="uart\uart.hpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="util\func.hpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="util\type.hpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="util\util.hpp">
<SubType>compile</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<Folder Include="io" />
<Folder Include="flash" />
<Folder Include="util" />
<Folder Include="uart" />
</ItemGroup>
<Import Project="$(AVRSTUDIO_EXE_PATH)\\Vs\\Compiler.targets" />
</Project>

@ -1 +0,0 @@
Subproject commit 81b3ae244c9773e7ea8ee08af43193275db48514