eink/eink_spi.hpp

148 lines
3.0 KiB
C++

#pragma once
#include "../clock.hpp"
#include <type_traits>
#include <cstdint>
#include <avr/interrupt.h>
#include "../io/io.hpp"
#include "../util/util.hpp"
namespace eink {
template <io::P SclPin, io::P SdaPin, io::P CsPin, io::P DcPin, std::uint32_t Freq = 100'000>
class Spi {
static constexpr double calcClockDelay()
{
// TODO: Verify static clock cycles
constexpr auto staticClockCycles = 10;
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 auto THREE_WIRE_SPI = (DcPin == io::P::NONE);
public:
using word_t = std::conditional_t<THREE_WIRE_SPI, std::uint16_t, std::uint8_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(const 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(DELAY_US);
sm_scl.toggle();
_delay_us(DELAY_US);
sm_scl.toggle();
},
std::make_index_sequence<numBits>{});
enableInterrupts(oldInterruptState);
}
static word_t read()
{
constexpr auto numBits = 8;
const auto oldInterruptState = disableInterrupts();
if constexpr (THREE_WIRE_SPI) {
sm_sda = true;
_delay_us(DELAY_US);
sm_scl.toggle();
_delay_us(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(DELAY_US);
sm_scl.toggle();
const auto receivedBit = sm_sda.read();
res |= word_t{receivedBit} << bitPos;
_delay_us(DELAY_US);
sm_scl.toggle();
},
std::make_index_sequence<numBits>{});
enableInterrupts(oldInterruptState);
return res;
}
static inline void select(const bool selectState)
{
sm_cs = !selectState;
}
private:
static io::Pin<SclPin> sm_scl;
static io::Pin<SdaPin> sm_sda;
static io::Pin<CsPin> sm_cs;
static io::Pin<DcPin> sm_dc;
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 eink