Compare commits

...

3 Commits

Author SHA1 Message Date
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

128
io.hpp
View File

@@ -130,10 +130,10 @@ namespace detail {
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.
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.
The workaround therefore is to disable the pointer-cast-and-dereference macro _MMIO_BYTE temporarily.
*/
#pragma push_macro("_MMIO_BYTE")
@@ -247,6 +247,41 @@ static inline reg_ptr_t getRegPtr()
return reinterpret_cast<reg_ptr_t>(Address);
}
template <template <P, uint8_t> typename Func, P pin, P... pins>
struct CallHelper {
template <typename... Args>
FORCE_INLINE static inline void call(Args... args)
{
Func<pin, sizeof...(pins)>::call(args...);
CallHelper<Func, pins...>::call(args...);
}
};
template <template <P, uint8_t> typename Func, P pin>
struct CallHelper<Func, pin> {
template <typename... Args>
FORCE_INLINE static inline void call(Args... args)
{
Func<pin, 0>::call(args...);
}
};
template <template <P> typename Func, P pin, P... pins>
struct ReadCallHelper {
static inline uint8_t call() FORCE_INLINE
{
return Func<pin>::call() << sizeof...(pins) | ReadCallHelper<Func, pins...>::call();
}
};
template <template <P> typename Func, P pin>
struct ReadCallHelper<Func, pin> {
static inline uint8_t call() FORCE_INLINE
{
return Func<pin>::call();
}
};
} // namespace detail
//////////////////////////////////////////////////////////////////////////
@@ -413,6 +448,95 @@ class Port {
}
};
//////////////////////////////////////////////////////////////////////////
// Zero overhead Virtual Port object for pretty code without losing performance
namespace detail {
template <P pin, uint8_t offset>
struct Callers {
static void call(const Dir dir) FORCE_INLINE
{
Pin<pin>::dir(dir);
};
static void call(const bool enable) FORCE_INLINE
{
Pin<pin>::pullup(enable);
};
static void call(const uint8_t value) FORCE_INLINE
{
Pin<pin>::write(value >> offset & 1);
};
static void call() FORCE_INLINE
{
Pin<pin>::toggle();
};
};
template <P pin>
struct readCaller {
static bool call() FORCE_INLINE
{
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;
static inline void dir(const Dir dir) FORCE_INLINE
{
detail::CallHelper<detail::Callers, pins...>::call(dir);
}
static inline void pullup(const bool enable) FORCE_INLINE
{
detail::CallHelper<detail::Callers, pins...>::call(enable);
}
static inline void write(const uint8_t value) FORCE_INLINE
{
detail::CallHelper<detail::Callers, pins...>::call(value);
}
static inline void invert() FORCE_INLINE
{
detail::CallHelper<detail::Callers, pins...>::call();
}
static inline uint8_t read() FORCE_INLINE
{
return detail::ReadCallHelper<detail::readCaller, pins...>::call();
}
inline VirtPort &operator=(const uint8_t value) FORCE_INLINE
{
write(value);
return *this;
}
inline operator uint8_t() const FORCE_INLINE
{
return read();
}
};
} // namespace io
//////////////////////////////////////////////////////////////////////////