eink/eink_spi.hpp

158 lines
3.2 KiB
C++
Raw Normal View History

2022-06-01 20:16:09 +02:00
#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 {
2022-06-02 23:15:22 +02:00
template <std::uint32_t StaticClockCycles>
2022-06-01 20:16:09 +02:00
static constexpr double calcClockDelay()
{
2022-06-02 23:15:22 +02:00
constexpr auto maxFrequency = F_CPU / StaticClockCycles;
2022-06-01 20:16:09 +02:00
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);
}
2022-06-02 23:15:22 +02:00
static constexpr double calcWriteClockDelay()
{
return calcClockDelay<8>();
}
static constexpr double calcReadClockDelay()
{
return calcClockDelay<5>();
}
2022-06-01 20:16:09 +02:00
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);
}
2022-06-02 23:15:22 +02:00
static void write(word_t data, const bool command = true)
2022-06-01 20:16:09 +02:00
{
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;
2022-06-02 23:15:22 +02:00
_delay_us(WRITE_DELAY_US);
2022-06-01 20:16:09 +02:00
sm_scl.toggle();
2022-06-02 23:15:22 +02:00
_delay_us(WRITE_DELAY_US);
2022-06-01 20:16:09 +02:00
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;
2022-06-02 23:15:22 +02:00
_delay_us(READ_DELAY_US);
2022-06-01 20:16:09 +02:00
sm_scl.toggle();
2022-06-02 23:15:22 +02:00
_delay_us(READ_DELAY_US);
2022-06-01 20:16:09 +02:00
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;
2022-06-02 23:15:22 +02:00
_delay_us(READ_DELAY_US);
2022-06-01 20:16:09 +02:00
sm_scl.toggle();
const auto receivedBit = sm_sda.read();
res |= word_t{receivedBit} << bitPos;
2022-06-02 23:15:22 +02:00
_delay_us(READ_DELAY_US);
2022-06-01 20:16:09 +02:00
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;
2022-06-02 23:15:22 +02:00
static constexpr auto WRITE_DELAY_US = calcWriteClockDelay();
static constexpr auto READ_DELAY_US = calcReadClockDelay();
2022-06-01 20:16:09 +02:00
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