#pragma once #include "../clock.hpp" #include #include #include #include "../io/io.hpp" #include "../util/util.hpp" namespace eink { template class Spi { template static constexpr double calcClockDelay() { constexpr auto maxFrequency = F_CPU / StaticClockCycles; static_assert(Freq <= maxFrequency, "SPI frequency not achievable using selected clock speed"); constexpr auto staticDelay = (1.0 * 1000 * 1000 / maxFrequency); const auto delayUs = ((1.0 * 1000 * 1000 / Freq) - staticDelay) / 2; return (delayUs > 0 ? delayUs : 0); } static constexpr double calcWriteClockDelay() { return calcClockDelay<8>(); } static constexpr double calcReadClockDelay() { return calcClockDelay<5>(); } static constexpr auto THREE_WIRE_SPI = (DcPin == io::P::NONE); public: using word_t = std::conditional_t; static void init() { sm_scl = false; sm_cs = true; sm_scl.dir(io::Dir::OUT); sm_sda.dir(io::Dir::OUT); sm_cs.dir(io::Dir::OUT); sm_dc.dir(io::Dir::OUT); } static void write(word_t data, const bool command = true) { constexpr auto numBits = THREE_WIRE_SPI ? 9 : 8; const auto oldInterruptState = disableInterrupts(); sm_sda.dir(io::Dir::OUT); if constexpr (THREE_WIRE_SPI) { if (command) { data &= ~(1 << 8); } else { data |= 1 << 8; } } sm_dc = !command; util::for_constexpr( [&](const auto idx) { constexpr auto bitPos = numBits - idx.value - 1; sm_sda = data >> bitPos & 1; _delay_us(WRITE_DELAY_US); sm_scl.toggle(); _delay_us(WRITE_DELAY_US); sm_scl.toggle(); }, std::make_index_sequence{}); enableInterrupts(oldInterruptState); } static word_t read() { constexpr auto numBits = 8; const auto oldInterruptState = disableInterrupts(); if constexpr (THREE_WIRE_SPI) { sm_sda = true; _delay_us(READ_DELAY_US); sm_scl.toggle(); _delay_us(READ_DELAY_US); sm_scl.toggle(); } sm_sda.pullup(false); sm_sda.dir(io::Dir::IN); sm_dc = true; auto res = word_t{}; util::for_constexpr( [&](const auto idx) { constexpr auto bitPos = numBits - idx.value - 1; _delay_us(READ_DELAY_US); sm_scl.toggle(); const auto receivedBit = sm_sda.read(); res |= word_t{receivedBit} << bitPos; _delay_us(READ_DELAY_US); sm_scl.toggle(); }, std::make_index_sequence{}); enableInterrupts(oldInterruptState); return res; } static inline void select(const bool selectState) { sm_cs = !selectState; } private: static io::Pin sm_scl; static io::Pin sm_sda; static io::Pin sm_cs; static io::Pin sm_dc; static constexpr auto WRITE_DELAY_US = calcWriteClockDelay(); static constexpr auto READ_DELAY_US = calcReadClockDelay(); static inline std::uint8_t disableInterrupts() { const auto oldInterruptState = SREG; cli(); return oldInterruptState; } static inline void enableInterrupts(const std::uint8_t oldInterruptState) { SREG = oldInterruptState; } }; } // namespace eink