Compare commits

...

18 Commits

Author SHA1 Message Date
5407e94337 Make use of C++ standard library 2022-05-29 16:14:22 +02:00
bf94ebaac1 Fix volatile compound assignments being deprecated in C++20 2022-05-29 14:45:05 +02:00
d89322bdaa Implement empty/unused pin and port 2022-05-28 15:41:26 +02:00
37b8f77fd3 Replace FORCE_INLINE macro with C++17 attribute syntax 2022-05-28 14:51:40 +02:00
80de36ee7e Improved grammar 2020-02-01 22:28:44 +01:00
a30b78fb81 Added static assert for number of pins on virtual port 2020-02-01 22:13:27 +01:00
cde4f9beda Implemented virtual port convenience object 2020-02-01 22:07:52 +01:00
986ceef65d Added MIT license file 2020-02-01 15:25:30 +01:00
571f28ab05 Undid accidental regression 2019-08-15 18:49:09 +02:00
d991959bff Fixed workaround with correct macro 2019-08-15 17:37:13 +02:00
1265702325 Changed git ignore to ignore make output 2019-08-11 10:03:47 +02:00
86d1db21f7 Fixed nullptr not being a valid uintptr_t 2019-08-10 13:58:42 +02:00
f1634a4dd7 Improved formatting 2019-08-10 13:39:04 +02:00
661bbfea7e Added reg_ptr_t type for cleaner code 2019-08-10 13:36:50 +02:00
a47e9a1a66 Refactored pegister pointer code 2019-08-10 13:32:35 +02:00
1bc8a38988 Fixed non-compliant code storing a ptr as a constexpr 2019-08-10 13:16:02 +02:00
4f40aa70dc Renamed getPin function to clearly differentiate it from getPIN 2019-08-10 12:55:53 +02:00
25bd873f94 Fixed non-portable preprocessor defines 2019-08-10 12:54:23 +02:00
3 changed files with 362 additions and 150 deletions

7
.gitignore vendored
View File

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

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 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.

484
io.hpp
View File

@@ -1,33 +1,49 @@
#pragma once
#include <stdint.h>
#include <cstdint>
#include <avr/io.h>
#include <avr/sfr_defs.h>
//////////////////////////////////////////////////////////////////////////
// Preprocessor defines
#define GPIO_32 \
defined(__AVR_ATmega32__) || defined(__AVR_ATmega32A__) || defined(__AVR_ATmega644P__) || \
defined(__AVR_ATmega1284P__)
#define GPIO_23 \
defined(__AVR_ATmega8__) || defined(__AVR_ATmega8A__) || defined(__AVR_ATmega168A__) || defined(__AVR_ATmega328P__)
#define GPIO_6 defined(__AVR_ATtiny13A__) || defined(__AVR_ATtiny85__)
#if defined(__AVR_ATmega32__) || defined(__AVR_ATmega32A__) || defined(__AVR_ATmega644P__) || \
defined(__AVR_ATmega1284P__)
#define GPIO_32
#endif
#define HARDWARE_TOGGLE \
defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega168A__) || \
defined(__AVR_ATmega328P__) || defined(__AVR_ATtiny13A__) || defined(__AVR_ATtiny85__)
#if defined(__AVR_ATmega8__) || defined(__AVR_ATmega8A__) || defined(__AVR_ATmega168A__) || defined(__AVR_ATmega328P__)
#define GPIO_23
#endif
#define PORT_A_AVAILABLE GPIO_32
#define PORT_B_AVAILABLE GPIO_32 || GPIO_23 || GPIO_6
#define PORT_C_AVAILABLE GPIO_32 || GPIO_23
#define PORT_D_AVAILABLE GPIO_32 || GPIO_23
#if defined(__AVR_ATtiny13A__) || defined(__AVR_ATtiny85__)
#define GPIO_6
#endif
#define PIN_B6_AVAILABLE GPIO_32 || GPIO_23
#define PIN_B7_AVAILABLE GPIO_32 || GPIO_23
#define PIN_C7_AVAILABLE GPIO_32
#if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega168A__) || \
defined(__AVR_ATmega328P__) || defined(__AVR_ATtiny13A__) || defined(__AVR_ATtiny85__)
#define HARDWARE_TOGGLE
#endif
#define FORCE_INLINE __attribute__((always_inline))
#ifdef GPIO_32
#define PORT_A_AVAILABLE
#endif
#if defined(GPIO_32) || defined(GPIO_23) || defined(GPIO_6)
#define PORT_B_AVAILABLE
#endif
#if defined(GPIO_32) || defined(GPIO_23)
#define PORT_C_AVAILABLE
#define PORT_D_AVAILABLE
#define PIN_B6_AVAILABLE
#define PIN_B7_AVAILABLE
#endif
#if defined(GPIO_32)
#define PIN_C7_AVAILABLE
#endif
//////////////////////////////////////////////////////////////////////////
// Library implementation
@@ -37,7 +53,7 @@ namespace io {
enum class Dir { IN, OUT };
enum class P {
#if PORT_A_AVAILABLE
#ifdef PORT_A_AVAILABLE
A0 = 0x00,
A1 = 0x01,
A2 = 0x02,
@@ -48,22 +64,22 @@ enum class P {
A7 = 0x07,
#endif
#if PORT_B_AVAILABLE
#ifdef PORT_B_AVAILABLE
B0 = 0x10,
B1 = 0x11,
B2 = 0x12,
B3 = 0x13,
B4 = 0x14,
B5 = 0x15,
#if PIN_B6_AVAILABLE
#ifdef PIN_B6_AVAILABLE
B6 = 0x16,
#endif
#if PIN_B7_AVAILABLE
#ifdef PIN_B7_AVAILABLE
B7 = 0x17,
#endif
#endif
#if PORT_C_AVAILABLE
#ifdef PORT_C_AVAILABLE
C0 = 0x20,
C1 = 0x21,
C2 = 0x22,
@@ -71,12 +87,12 @@ enum class P {
C4 = 0x24,
C5 = 0x25,
C6 = 0x26,
#if PIN_C7_AVAILABLE
#ifdef PIN_C7_AVAILABLE
C7 = 0x27,
#endif
#endif
#if PORT_D_AVAILABLE
#ifdef PORT_D_AVAILABLE
D0 = 0x30,
D1 = 0x31,
D2 = 0x32,
@@ -86,21 +102,25 @@ enum class P {
D6 = 0x36,
D7 = 0x37,
#endif
NONE,
};
enum class Bus {
#if PORT_A_AVAILABLE
#ifdef PORT_A_AVAILABLE
A = 0x00,
#endif
#if PORT_B_AVAILABLE
#ifdef PORT_B_AVAILABLE
B = 0x01,
#endif
#if PORT_C_AVAILABLE
#ifdef PORT_C_AVAILABLE
C = 0x02,
#endif
#if PORT_D_AVAILABLE
#ifdef PORT_D_AVAILABLE
D = 0x03,
#endif
NONE,
};
//////////////////////////////////////////////////////////////////////////
@@ -108,102 +128,162 @@ enum class Bus {
namespace detail {
#if PORT_A_AVAILABLE
static constexpr volatile uint8_t *PORT_A_DIR_REG = &DDRA;
static constexpr volatile uint8_t *PORT_A_OUTPUT_REG = &PORTA;
static constexpr volatile uint8_t *PORT_A_INPUT_REG = &PINA;
/*
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 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
#ifdef PORT_A_AVAILABLE
static constexpr uintptr_t PORT_A_DIR_REG_ADDR = DDRA;
static constexpr uintptr_t PORT_A_OUTPUT_REG_ADDR = PORTA;
static constexpr uintptr_t PORT_A_INPUT_REG_ADDR = PINA;
#else
static constexpr volatile uint8_t *PORT_A_DIR_REG = nullptr;
static constexpr volatile uint8_t *PORT_A_OUTPUT_REG = nullptr;
static constexpr volatile uint8_t *PORT_A_INPUT_REG = nullptr;
static constexpr uintptr_t PORT_A_DIR_REG_ADDR = 0;
static constexpr uintptr_t PORT_A_OUTPUT_REG_ADDR = 0;
static constexpr uintptr_t PORT_A_INPUT_REG_ADDR = 0;
#endif
#if PORT_B_AVAILABLE
static constexpr volatile uint8_t *PORT_B_DIR_REG = &DDRB;
static constexpr volatile uint8_t *PORT_B_OUTPUT_REG = &PORTB;
static constexpr volatile uint8_t *PORT_B_INPUT_REG = &PINB;
#ifdef PORT_B_AVAILABLE
static constexpr uintptr_t PORT_B_DIR_REG_ADDR = DDRB;
static constexpr uintptr_t PORT_B_OUTPUT_REG_ADDR = PORTB;
static constexpr uintptr_t PORT_B_INPUT_REG_ADDR = PINB;
#else
static constexpr volatile uint8_t *PORT_B_DIR_REG = nullptr;
static constexpr volatile uint8_t *PORT_B_OUTPUT_REG = nullptr;
static constexpr volatile uint8_t *PORT_B_INPUT_REG = nullptr;
static constexpr uintptr_t PORT_B_DIR_REG_ADDR = 0;
static constexpr uintptr_t PORT_B_OUTPUT_REG_ADDR = 0;
static constexpr uintptr_t PORT_B_INPUT_REG_ADDR = 0;
#endif
#if PORT_C_AVAILABLE
static constexpr volatile uint8_t *PORT_C_DIR_REG = &DDRC;
static constexpr volatile uint8_t *PORT_C_OUTPUT_REG = &PORTC;
static constexpr volatile uint8_t *PORT_C_INPUT_REG = &PINC;
#ifdef PORT_C_AVAILABLE
static constexpr uintptr_t PORT_C_DIR_REG_ADDR = DDRC;
static constexpr uintptr_t PORT_C_OUTPUT_REG_ADDR = PORTC;
static constexpr uintptr_t PORT_C_INPUT_REG_ADDR = PINC;
#else
static constexpr volatile uint8_t *PORT_C_DIR_REG = nullptr;
static constexpr volatile uint8_t *PORT_C_OUTPUT_REG = nullptr;
static constexpr volatile uint8_t *PORT_C_INPUT_REG = nullptr;
static constexpr uintptr_t PORT_C_DIR_REG_ADDR = 0;
static constexpr uintptr_t PORT_C_OUTPUT_REG_ADDR = 0;
static constexpr uintptr_t PORT_C_INPUT_REG_ADDR = 0;
#endif
#if PORT_D_AVAILABLE
static constexpr volatile uint8_t *PORT_D_DIR_REG = &DDRD;
static constexpr volatile uint8_t *PORT_D_OUTPUT_REG = &PORTD;
static constexpr volatile uint8_t *PORT_D_INPUT_REG = &PIND;
#ifdef PORT_D_AVAILABLE
static constexpr uintptr_t PORT_D_DIR_REG_ADDR = DDRD;
static constexpr uintptr_t PORT_D_OUTPUT_REG_ADDR = PORTD;
static constexpr uintptr_t PORT_D_INPUT_REG_ADDR = PIND;
#else
static constexpr volatile uint8_t *PORT_D_DIR_REG = nullptr;
static constexpr volatile uint8_t *PORT_D_OUTPUT_REG = nullptr;
static constexpr volatile uint8_t *PORT_D_INPUT_REG = nullptr;
static constexpr uintptr_t PORT_D_DIR_REG_ADDR = 0;
static constexpr uintptr_t PORT_D_OUTPUT_REG_ADDR = 0;
static constexpr uintptr_t PORT_D_INPUT_REG_ADDR = 0;
#endif
#pragma pop_macro("_MMIO_BYTE")
static constexpr auto getBus(const P pin)
{
// Upper 4 bits of pin encode which port this pin is on
uint8_t port = static_cast<uint8_t>(pin) >> 4 & 0x0F;
const auto port = static_cast<std::uint8_t>(pin) >> 4 & 0x0F;
return static_cast<Bus>(port);
}
static constexpr auto getPin(const P pin)
static constexpr auto getPinBit(const P pin)
{
// Lower 4 bits of pin encode which pin bit it is
uint8_t pinBit = static_cast<uint8_t>(pin) & 0x0F;
const auto pinBit = static_cast<std::uint8_t>(pin) & 0x0F;
return pinBit;
}
static constexpr auto getDDR(const Bus bus)
{
switch (static_cast<uint8_t>(bus)) {
switch (static_cast<std::uint8_t>(bus)) {
case 0: // Bus::A
return PORT_A_DIR_REG;
return PORT_A_DIR_REG_ADDR;
case 1: // Bus::B
return PORT_B_DIR_REG;
return PORT_B_DIR_REG_ADDR;
case 2: // Bus::C
return PORT_C_DIR_REG;
return PORT_C_DIR_REG_ADDR;
case 3: // Bus::D
return PORT_D_DIR_REG;
return PORT_D_DIR_REG_ADDR;
}
}
static constexpr auto getPORT(const Bus bus)
{
switch (static_cast<uint8_t>(bus)) {
switch (static_cast<std::uint8_t>(bus)) {
case 0: // Bus::A
return PORT_A_OUTPUT_REG;
return PORT_A_OUTPUT_REG_ADDR;
case 1: // Bus::B
return PORT_B_OUTPUT_REG;
return PORT_B_OUTPUT_REG_ADDR;
case 2: // Bus::C
return PORT_C_OUTPUT_REG;
return PORT_C_OUTPUT_REG_ADDR;
case 3: // Bus::D
return PORT_D_OUTPUT_REG;
return PORT_D_OUTPUT_REG_ADDR;
}
}
static constexpr auto getPIN(const Bus bus)
{
switch (static_cast<uint8_t>(bus)) {
switch (static_cast<std::uint8_t>(bus)) {
case 0: // Bus::A
return PORT_A_INPUT_REG;
return PORT_A_INPUT_REG_ADDR;
case 1: // Bus::B
return PORT_B_INPUT_REG;
return PORT_B_INPUT_REG_ADDR;
case 2: // Bus::C
return PORT_C_INPUT_REG;
return PORT_C_INPUT_REG_ADDR;
case 3: // Bus::D
return PORT_D_INPUT_REG;
return PORT_D_INPUT_REG_ADDR;
}
}
using reg_ptr_t = volatile std::uint8_t *;
template <uintptr_t Address>
static inline reg_ptr_t getRegPtr()
{
return reinterpret_cast<reg_ptr_t>(Address);
}
template <template <P, std::uint8_t> typename Func, P pin, P... pins>
struct CallHelper {
template <typename... Args>
[[gnu::always_inline]] static inline void call(Args... args)
{
Func<pin, sizeof...(pins)>::call(args...);
CallHelper<Func, pins...>::call(args...);
}
};
template <template <P, std::uint8_t> typename Func, P pin>
struct CallHelper<Func, pin> {
template <typename... Args>
[[gnu::always_inline]] static inline void call(Args... args)
{
Func<pin, 0>::call(args...);
}
};
template <template <P> typename Func, P pin, P... pins>
struct ReadCallHelper {
[[gnu::always_inline]] static inline std::uint8_t call()
{
return Func<pin>::call() << sizeof...(pins) | ReadCallHelper<Func, pins...>::call();
}
};
template <template <P> typename Func, P pin>
struct ReadCallHelper<Func, pin> {
[[gnu::always_inline]] static inline std::uint8_t call()
{
return Func<pin>::call();
}
};
} // namespace detail
//////////////////////////////////////////////////////////////////////////
@@ -221,78 +301,88 @@ class Pin {
// The only valid way to create a Pin object is with the default constructor
Pin() = default;
static inline void dir(const Dir dir) FORCE_INLINE
[[gnu::always_inline]] static inline void dir(const Dir dir)
{
constexpr auto bus = detail::getBus(pin);
constexpr auto dirReg = detail::getDDR(bus);
constexpr auto pinBit = detail::getPin(pin);
if constexpr (pin != P::NONE) {
constexpr auto bus = detail::getBus(pin);
constexpr auto pinBit = detail::getPinBit(pin);
if (dir == Dir::IN)
*dirReg &= ~(1 << pinBit);
else if (dir == Dir::OUT)
*dirReg |= (1 << pinBit);
auto dirRegPtr = detail::getRegPtr<detail::getDDR(bus)>();
if (dir == Dir::IN)
*dirRegPtr = *dirRegPtr & ~(1 << pinBit);
else if (dir == Dir::OUT)
*dirRegPtr = *dirRegPtr | (1 << pinBit);
}
}
static inline void pullup(const bool enable) FORCE_INLINE
[[gnu::always_inline]] static inline void pullup(const bool enable)
{
constexpr auto bus = detail::getBus(pin);
constexpr auto portReg = detail::getPORT(bus);
constexpr auto pinBit = detail::getPin(pin);
if constexpr (pin != P::NONE) {
constexpr auto bus = detail::getBus(pin);
constexpr auto pinBit = detail::getPinBit(pin);
if (enable)
*portReg |= (1 << pinBit);
else
*portReg &= ~(1 << pinBit);
auto portRegPtr = detail::getRegPtr<detail::getPORT(bus)>();
if (enable)
*portRegPtr = *portRegPtr | (1 << pinBit);
else
*portRegPtr = *portRegPtr & ~(1 << pinBit);
}
}
static inline void write(const bool value) FORCE_INLINE
[[gnu::always_inline]] static inline void write(const bool value)
{
constexpr auto bus = detail::getBus(pin);
constexpr auto portReg = detail::getPORT(bus);
constexpr auto pinBit = detail::getPin(pin);
if constexpr (pin != P::NONE) {
constexpr auto bus = detail::getBus(pin);
constexpr auto pinBit = detail::getPinBit(pin);
if (value)
*portReg |= (1 << pinBit);
else
*portReg &= ~(1 << pinBit);
auto portRegPtr = detail::getRegPtr<detail::getPORT(bus)>();
if (value)
*portRegPtr = *portRegPtr | (1 << pinBit);
else
*portRegPtr = *portRegPtr & ~(1 << pinBit);
}
}
static inline void toggle() FORCE_INLINE
[[gnu::always_inline]] static inline void toggle()
{
#if HARDWARE_TOGGLE
constexpr auto bus = detail::getBus(pin);
constexpr auto pinReg = detail::getPIN(bus);
constexpr auto pinBit = detail::getPin(pin);
if constexpr (pin != P::NONE) {
constexpr auto bus = detail::getBus(pin);
constexpr auto pinBit = detail::getPinBit(pin);
*pinReg |= (1 << pinBit);
#ifdef HARDWARE_TOGGLE
auto pinRegPtr = detail::getRegPtr<detail::getPIN(bus)>();
*pinRegPtr = *pinRegPtr | (1 << pinBit);
#else
constexpr auto bus = detail::getBus(pin);
constexpr auto portReg = detail::getPORT(bus);
constexpr auto pinBit = detail::getPin(pin);
*portReg ^= (1 << pinBit);
auto portRegPtr = detail::getRegPtr<detail::getPORT(bus)>();
*portRegPtr = *portRegPtr ^ (1 << pinBit);
#endif
}
}
static inline bool read() FORCE_INLINE
[[gnu::always_inline]] static inline bool read()
{
constexpr auto bus = detail::getBus(pin);
constexpr auto pinReg = detail::getPIN(bus);
constexpr auto pinBit = detail::getPin(pin);
if constexpr (pin != P::NONE) {
constexpr auto bus = detail::getBus(pin);
constexpr auto pinBit = detail::getPinBit(pin);
if (*pinReg >> pinBit & 1)
return true;
auto pinRegPtr = detail::getRegPtr<detail::getPIN(bus)>();
if (*pinRegPtr >> pinBit & 1)
return true;
}
return false;
}
Pin &operator=(const bool value) FORCE_INLINE
[[gnu::always_inline]] Pin &operator=(const bool value)
{
write(value);
return *this;
}
operator bool() const FORCE_INLINE
[[gnu::always_inline]] operator bool() const
{
return read();
}
@@ -313,60 +403,156 @@ class Port {
// The only valid way to create a Port object is with the default constructor
Port() = default;
static inline void dir(const Dir dir) FORCE_INLINE
[[gnu::always_inline]] static inline void dir(const Dir dir)
{
constexpr auto dirReg = detail::getDDR(port);
if constexpr (port != Bus::NONE) {
auto dirRegPtr = detail::getRegPtr<detail::getDDR(port)>();
if (dir == Dir::IN)
*dirReg = 0x00;
else if (dir == Dir::OUT)
*dirReg = 0xFF;
if (dir == Dir::IN)
*dirRegPtr = 0x00;
else if (dir == Dir::OUT)
*dirRegPtr = 0xFF;
}
}
static inline void pullup(const bool enable) FORCE_INLINE
[[gnu::always_inline]] static inline void pullup(const bool enable)
{
constexpr auto portReg = detail::getPORT(port);
if constexpr (port != Bus::NONE) {
auto portRegPtr = detail::getRegPtr<detail::getPORT(port)>();
if (enable)
*portReg = 0xFF;
else
*portReg = 0x00;
if (enable)
*portRegPtr = 0xFF;
else
*portRegPtr = 0x00;
}
}
static inline void write(const uint8_t value) FORCE_INLINE
[[gnu::always_inline]] static inline void write([[maybe_unused]] const std::uint8_t value)
{
constexpr auto portReg = detail::getPORT(port);
*portReg = value;
if constexpr (port != Bus::NONE) {
auto portRegPtr = detail::getRegPtr<detail::getPORT(port)>();
*portRegPtr = value;
}
}
static inline void invert() FORCE_INLINE
[[gnu::always_inline]] static inline void invert()
{
#if HARDWARE_TOGGLE
constexpr auto pinReg = detail::getPIN(port);
*pinReg = 0xFF;
if constexpr (port != Bus::NONE) {
#ifdef HARDWARE_TOGGLE
auto pinRegPtr = detail::getRegPtr<detail::getPIN(port)>();
*pinRegPtr = 0xFF;
#else
constexpr auto portReg = detail::getPORT(port);
*portReg = ~(*portReg);
auto portRegPtr = detail::getRegPtr<detail::getPORT(port)>();
*portRegPtr = ~(*portRegPtr);
#endif
}
}
static inline uint8_t read() FORCE_INLINE
[[gnu::always_inline]] static inline std::uint8_t read()
{
constexpr auto pinReg = detail::getPIN(port);
return *pinReg;
if constexpr (port != Bus::NONE) {
auto pinRegPtr = detail::getRegPtr<detail::getPIN(port)>();
return *pinRegPtr;
}
return 0x00;
}
inline Port &operator=(const uint8_t value) FORCE_INLINE
[[gnu::always_inline]] inline Port &operator=(const std::uint8_t value)
{
write(value);
return *this;
}
inline operator uint8_t() const FORCE_INLINE
[[gnu::always_inline]] inline operator std::uint8_t() const
{
return read();
}
};
//////////////////////////////////////////////////////////////////////////
// Zero overhead Virtual Port object for pretty code without losing performance
namespace detail {
template <P pin, std::uint8_t offset>
struct Callers {
[[gnu::always_inline]] static void call(const Dir dir)
{
Pin<pin>::dir(dir);
};
[[gnu::always_inline]] static void call(const bool enable)
{
Pin<pin>::pullup(enable);
};
[[gnu::always_inline]] static void call(const std::uint8_t value)
{
Pin<pin>::write(value >> offset & 1);
};
[[gnu::always_inline]] static void call()
{
Pin<pin>::toggle();
};
};
template <P pin>
struct readCaller {
[[gnu::always_inline]] static bool call()
{
return Pin<pin>::read();
};
};
} // namespace detail
template <P... pins>
class VirtPort {
public:
static_assert(sizeof...(pins) <= 8, "A virtual port cannot have more than 8 pins");
// VirtPort objects cannot be moved or copied
VirtPort(const VirtPort &) = delete;
VirtPort(VirtPort &&) = delete;
VirtPort &operator=(const VirtPort &) = delete;
VirtPort &operator=(VirtPort &&) = delete;
// The only valid way to create a VirtPort object is with the default constructor
VirtPort() = default;
[[gnu::always_inline]] static inline void dir(const Dir dir)
{
detail::CallHelper<detail::Callers, pins...>::call(dir);
}
[[gnu::always_inline]] static inline void pullup(const bool enable)
{
detail::CallHelper<detail::Callers, pins...>::call(enable);
}
[[gnu::always_inline]] static inline void write(const std::uint8_t value)
{
detail::CallHelper<detail::Callers, pins...>::call(value);
}
[[gnu::always_inline]] static inline void invert()
{
detail::CallHelper<detail::Callers, pins...>::call();
}
[[gnu::always_inline]] static inline std::uint8_t read()
{
return detail::ReadCallHelper<detail::readCaller, pins...>::call();
}
[[gnu::always_inline]] inline VirtPort &operator=(const std::uint8_t value)
{
write(value);
return *this;
}
[[gnu::always_inline]] inline operator std::uint8_t() const
{
return read();
}
@@ -388,5 +574,3 @@ class Port {
#undef PIN_B6_AVAILABLE
#undef PIN_B7_AVAILABLE
#undef PIN_C7_AVAILABLE
#undef FORCE_INLINE