Compare commits
9 Commits
d991959bff
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 5407e94337 | |||
| bf94ebaac1 | |||
| d89322bdaa | |||
| 37b8f77fd3 | |||
| 80de36ee7e | |||
| a30b78fb81 | |||
| cde4f9beda | |||
| 986ceef65d | |||
| 571f28ab05 |
21
LICENSE
Normal file
21
LICENSE
Normal 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.
|
||||
287
io.hpp
287
io.hpp
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cstdint>
|
||||
|
||||
#include <avr/io.h>
|
||||
#include <avr/sfr_defs.h>
|
||||
@@ -45,8 +45,6 @@
|
||||
#define PIN_C7_AVAILABLE
|
||||
#endif
|
||||
|
||||
#define FORCE_INLINE __attribute__((always_inline))
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Library implementation
|
||||
|
||||
@@ -104,6 +102,8 @@ enum class P {
|
||||
D6 = 0x36,
|
||||
D7 = 0x37,
|
||||
#endif
|
||||
|
||||
NONE,
|
||||
};
|
||||
|
||||
enum class Bus {
|
||||
@@ -119,6 +119,8 @@ enum class Bus {
|
||||
#ifdef PORT_D_AVAILABLE
|
||||
D = 0x03,
|
||||
#endif
|
||||
|
||||
NONE,
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -127,17 +129,23 @@ enum class Bus {
|
||||
namespace detail {
|
||||
|
||||
/*
|
||||
The following works in avr-gcc 5.4.0, but is not legal C++, because ptr's are not legal constexpr's
|
||||
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
|
||||
For this the sfr_defs.h provides a macro which returns the address of a register
|
||||
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 = _SFR_ADDR(DDRA);
|
||||
static constexpr uintptr_t PORT_A_OUTPUT_REG_ADDR = _SFR_ADDR(PORTA);
|
||||
static constexpr uintptr_t PORT_A_INPUT_REG_ADDR = _SFR_ADDR(PINA);
|
||||
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 uintptr_t PORT_A_DIR_REG_ADDR = 0;
|
||||
static constexpr uintptr_t PORT_A_OUTPUT_REG_ADDR = 0;
|
||||
@@ -146,9 +154,9 @@ static constexpr uintptr_t PORT_A_INPUT_REG_ADDR = 0;
|
||||
|
||||
#ifdef PORT_B_AVAILABLE
|
||||
|
||||
static constexpr uintptr_t PORT_B_DIR_REG_ADDR = _SFR_ADDR(DDRB);
|
||||
static constexpr uintptr_t PORT_B_OUTPUT_REG_ADDR = _SFR_ADDR(PORTB);
|
||||
static constexpr uintptr_t PORT_B_INPUT_REG_ADDR = _SFR_ADDR(PINB);
|
||||
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 uintptr_t PORT_B_DIR_REG_ADDR = 0;
|
||||
static constexpr uintptr_t PORT_B_OUTPUT_REG_ADDR = 0;
|
||||
@@ -156,9 +164,9 @@ static constexpr uintptr_t PORT_B_INPUT_REG_ADDR = 0;
|
||||
#endif
|
||||
|
||||
#ifdef PORT_C_AVAILABLE
|
||||
static constexpr uintptr_t PORT_C_DIR_REG_ADDR = _SFR_ADDR(DDRC);
|
||||
static constexpr uintptr_t PORT_C_OUTPUT_REG_ADDR = _SFR_ADDR(PORTC);
|
||||
static constexpr uintptr_t PORT_C_INPUT_REG_ADDR = _SFR_ADDR(PINC);
|
||||
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 uintptr_t PORT_C_DIR_REG_ADDR = 0;
|
||||
static constexpr uintptr_t PORT_C_OUTPUT_REG_ADDR = 0;
|
||||
@@ -166,32 +174,34 @@ static constexpr uintptr_t PORT_C_INPUT_REG_ADDR = 0;
|
||||
#endif
|
||||
|
||||
#ifdef PORT_D_AVAILABLE
|
||||
static constexpr uintptr_t PORT_D_DIR_REG_ADDR = _SFR_ADDR(DDRD);
|
||||
static constexpr uintptr_t PORT_D_OUTPUT_REG_ADDR = _SFR_ADDR(PORTD);
|
||||
static constexpr uintptr_t PORT_D_INPUT_REG_ADDR = _SFR_ADDR(PIND);
|
||||
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 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 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_ADDR;
|
||||
case 1: // Bus::B
|
||||
@@ -205,7 +215,7 @@ static constexpr auto getDDR(const Bus bus)
|
||||
|
||||
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_ADDR;
|
||||
case 1: // Bus::B
|
||||
@@ -219,7 +229,7 @@ static constexpr auto getPORT(const Bus bus)
|
||||
|
||||
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_ADDR;
|
||||
case 1: // Bus::B
|
||||
@@ -231,7 +241,7 @@ static constexpr auto getPIN(const Bus bus)
|
||||
}
|
||||
}
|
||||
|
||||
using reg_ptr_t = volatile uint8_t *;
|
||||
using reg_ptr_t = volatile std::uint8_t *;
|
||||
|
||||
template <uintptr_t Address>
|
||||
static inline reg_ptr_t getRegPtr()
|
||||
@@ -239,6 +249,41 @@ 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
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -256,79 +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)
|
||||
{
|
||||
if constexpr (pin != P::NONE) {
|
||||
constexpr auto bus = detail::getBus(pin);
|
||||
constexpr auto pinBit = detail::getPinBit(pin);
|
||||
|
||||
detail::reg_ptr_t dirRegPtr = detail::getRegPtr<detail::getDDR(bus)>();
|
||||
auto dirRegPtr = detail::getRegPtr<detail::getDDR(bus)>();
|
||||
|
||||
if (dir == Dir::IN)
|
||||
*dirRegPtr &= ~(1 << pinBit);
|
||||
*dirRegPtr = *dirRegPtr & ~(1 << pinBit);
|
||||
else if (dir == Dir::OUT)
|
||||
*dirRegPtr |= (1 << pinBit);
|
||||
*dirRegPtr = *dirRegPtr | (1 << pinBit);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void pullup(const bool enable) FORCE_INLINE
|
||||
[[gnu::always_inline]] static inline void pullup(const bool enable)
|
||||
{
|
||||
if constexpr (pin != P::NONE) {
|
||||
constexpr auto bus = detail::getBus(pin);
|
||||
constexpr auto pinBit = detail::getPinBit(pin);
|
||||
|
||||
detail::reg_ptr_t portRegPtr = detail::getRegPtr<detail::getPORT(bus)>();
|
||||
auto portRegPtr = detail::getRegPtr<detail::getPORT(bus)>();
|
||||
|
||||
if (enable)
|
||||
*portRegPtr |= (1 << pinBit);
|
||||
*portRegPtr = *portRegPtr | (1 << pinBit);
|
||||
else
|
||||
*portRegPtr &= ~(1 << pinBit);
|
||||
*portRegPtr = *portRegPtr & ~(1 << pinBit);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void write(const bool value) FORCE_INLINE
|
||||
[[gnu::always_inline]] static inline void write(const bool value)
|
||||
{
|
||||
if constexpr (pin != P::NONE) {
|
||||
constexpr auto bus = detail::getBus(pin);
|
||||
constexpr auto pinBit = detail::getPinBit(pin);
|
||||
|
||||
detail::reg_ptr_t portRegPtr = detail::getRegPtr<detail::getPORT(bus)>();
|
||||
auto portRegPtr = detail::getRegPtr<detail::getPORT(bus)>();
|
||||
|
||||
if (value)
|
||||
*portRegPtr |= (1 << pinBit);
|
||||
*portRegPtr = *portRegPtr | (1 << pinBit);
|
||||
else
|
||||
*portRegPtr &= ~(1 << pinBit);
|
||||
*portRegPtr = *portRegPtr & ~(1 << pinBit);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void toggle() FORCE_INLINE
|
||||
[[gnu::always_inline]] static inline void toggle()
|
||||
{
|
||||
if constexpr (pin != P::NONE) {
|
||||
constexpr auto bus = detail::getBus(pin);
|
||||
constexpr auto pinBit = detail::getPinBit(pin);
|
||||
|
||||
#ifdef HARDWARE_TOGGLE
|
||||
detail::reg_ptr_t pinRegPtr = detail::getRegPtr<detail::getPIN(bus)>();
|
||||
*pinRegPtr |= (1 << pinBit);
|
||||
auto pinRegPtr = detail::getRegPtr<detail::getPIN(bus)>();
|
||||
*pinRegPtr = *pinRegPtr | (1 << pinBit);
|
||||
#else
|
||||
detail::reg_ptr_t portRegPtr = detail::getRegPtr<detail::getPORT(bus)>();
|
||||
*portRegPtr ^= (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()
|
||||
{
|
||||
if constexpr (pin != P::NONE) {
|
||||
constexpr auto bus = detail::getBus(pin);
|
||||
constexpr auto pinBit = detail::getPinBit(pin);
|
||||
|
||||
detail::reg_ptr_t pinRegPtr = detail::getRegPtr<detail::getPIN(bus)>();
|
||||
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();
|
||||
}
|
||||
@@ -349,57 +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)
|
||||
{
|
||||
detail::reg_ptr_t dirRegPtr = detail::getRegPtr<detail::getDDR(port)>();
|
||||
if constexpr (port != Bus::NONE) {
|
||||
auto dirRegPtr = detail::getRegPtr<detail::getDDR(port)>();
|
||||
|
||||
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)
|
||||
{
|
||||
detail::reg_ptr_t portRegPtr = detail::getRegPtr<detail::getPORT(port)>();
|
||||
if constexpr (port != Bus::NONE) {
|
||||
auto portRegPtr = detail::getRegPtr<detail::getPORT(port)>();
|
||||
|
||||
if (enable)
|
||||
*portRegPtr = 0xFF;
|
||||
else
|
||||
*portRegPtr = 0x00;
|
||||
}
|
||||
|
||||
static inline void write(const uint8_t value) FORCE_INLINE
|
||||
{
|
||||
detail::reg_ptr_t portRegPtr = detail::getRegPtr<detail::getPORT(port)>();
|
||||
*portRegPtr = value;
|
||||
}
|
||||
|
||||
static inline void invert() FORCE_INLINE
|
||||
[[gnu::always_inline]] static inline void write([[maybe_unused]] const std::uint8_t value)
|
||||
{
|
||||
if constexpr (port != Bus::NONE) {
|
||||
auto portRegPtr = detail::getRegPtr<detail::getPORT(port)>();
|
||||
*portRegPtr = value;
|
||||
}
|
||||
}
|
||||
|
||||
[[gnu::always_inline]] static inline void invert()
|
||||
{
|
||||
if constexpr (port != Bus::NONE) {
|
||||
#ifdef HARDWARE_TOGGLE
|
||||
detail::reg_ptr_t pinRegPtr = detail::getRegPtr<detail::getPIN(port)>();
|
||||
auto pinRegPtr = detail::getRegPtr<detail::getPIN(port)>();
|
||||
*pinRegPtr = 0xFF;
|
||||
#else
|
||||
detail::reg_ptr_t portRegPtr = detail::getRegPtr<detail::getPORT(port)>();
|
||||
auto portRegPtr = detail::getRegPtr<detail::getPORT(port)>();
|
||||
*portRegPtr = ~(*portRegPtr);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline uint8_t read() FORCE_INLINE
|
||||
{
|
||||
detail::reg_ptr_t pinRegPtr = detail::getRegPtr<detail::getPIN(port)>();
|
||||
|
||||
return *pinRegPtr;
|
||||
}
|
||||
|
||||
inline Port &operator=(const uint8_t value) FORCE_INLINE
|
||||
[[gnu::always_inline]] static inline std::uint8_t read()
|
||||
{
|
||||
if constexpr (port != Bus::NONE) {
|
||||
auto pinRegPtr = detail::getRegPtr<detail::getPIN(port)>();
|
||||
return *pinRegPtr;
|
||||
}
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
[[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();
|
||||
}
|
||||
@@ -421,5 +574,3 @@ class Port {
|
||||
#undef PIN_B6_AVAILABLE
|
||||
#undef PIN_B7_AVAILABLE
|
||||
#undef PIN_C7_AVAILABLE
|
||||
|
||||
#undef FORCE_INLINE
|
||||
|
||||
Reference in New Issue
Block a user