#pragma once #include "../clock.hpp" #include #include #include #include #include "../io/io.hpp" #include "../util/util.hpp" namespace spi { template class Software { static constexpr double calcClockDelay() { constexpr auto staticClockCycles = 10; constexpr auto maxFrequency = F_CPU / staticClockCycles; static_assert(Cfg::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 / Cfg::FREQ) - staticDelay) / 2; return (delayUs > 0 ? delayUs : 0); } public: static_assert(Cfg::BITS >= 1 && Cfg::BITS <= 64, "Word size must be in range [1-64]"); // clang-format off using word_t = std::conditional_t>>; // clang-format on static void init() { sm_sck = ((Cfg::MODE == Mode::MODE_0 || Cfg::MODE == Mode::MODE_1) ? false : true); sm_ss = true; sm_sck.dir(io::Dir::OUT); sm_miso.dir(io::Dir::IN); sm_miso.pullup(Cfg::PULLUP); sm_mosi.dir(io::Dir::OUT); sm_ss.dir(io::Dir::OUT); } static word_t transfer(const word_t data) { const auto oldInterruptState = disableInterrupts(); auto dataIn = word_t{}; util::for_constexpr( [&](const auto idx) { constexpr auto i = idx.value; constexpr auto bitPos = (Cfg::BIT_ORDER == BitOrder::MSB_FIRST ? Cfg::BITS - i - 1 : i); if constexpr (Cfg::MODE == Mode::MODE_0 || Cfg::MODE == Mode::MODE_2) { sm_mosi = data >> bitPos & 1; _delay_us(DELAY_US); sm_sck.toggle(); const auto receivedBit = sm_miso.read(); dataIn |= word_t{receivedBit} << bitPos; _delay_us(DELAY_US); sm_sck.toggle(); } else { sm_sck.toggle(); sm_mosi = data >> bitPos & 1; _delay_us(DELAY_US); sm_sck.toggle(); const auto receivedBit = sm_miso.read(); dataIn |= word_t{receivedBit} << bitPos; _delay_us(DELAY_US); } }, std::make_index_sequence{}); enableInterrupts(oldInterruptState); return dataIn; } static void select(const bool selectState) { sm_ss = !selectState; } private: static io::Pin sm_sck; static io::Pin sm_miso; static io::Pin sm_mosi; static io::Pin sm_ss; static constexpr auto DELAY_US = calcClockDelay(); 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 spi