diff --git a/config.hpp b/config.hpp index c9761d7..f3b1f79 100644 --- a/config.hpp +++ b/config.hpp @@ -35,7 +35,7 @@ enum class BitOrder { template -struct Config { +struct HardwareConfig { static constexpr auto FREQ = freq; static constexpr auto MODE = mode; static constexpr auto SIDE = side; @@ -44,4 +44,18 @@ struct Config { static constexpr auto PULLUP = pullup; }; +template +struct SoftwareConfig { + static constexpr auto SCK_PIN = sckPin; + static constexpr auto MISO_PIN = misoPin; + static constexpr auto MOSI_PIN = mosiPin; + static constexpr auto SS_PIN = ssPin; + static constexpr auto FREQ = freq; + static constexpr auto MODE = mode; + static constexpr auto BIT_ORDER = bitOrder; + static constexpr auto BITS = bits; + static constexpr auto PULLUP = pullup; +}; + } // namespace spi diff --git a/software.hpp b/software.hpp new file mode 100644 index 0000000..e1019f6 --- /dev/null +++ b/software.hpp @@ -0,0 +1,94 @@ +#pragma once + +#include "../clock.hpp" + +#include + +#include "../io/io.hpp" +#include "../util/type.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); + } + + static_assert(Cfg::BITS >= 1 && Cfg::BITS <= 64, "Word size must be in range [1-64]"); + // clang-format off + using word_t = util::conditional_t>>; + // clang-format on + + public: + 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) + { + 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); + } + }, + util::make_index_sequence{}); + + 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(); +}; + +} // namespace spi diff --git a/spi.hpp b/spi.hpp index 3d535a6..e584567 100644 --- a/spi.hpp +++ b/spi.hpp @@ -2,6 +2,7 @@ #include "config.hpp" #include "hardware.hpp" +#include "software.hpp" #include "../io/io.hpp"