#pragma once #include #include #include #include #include #include "eink_spi.hpp" #include "../clock.hpp" #include "../io/io.hpp" #include "../util/util.hpp" namespace eink { template class Eink { using word_t = typename Spi::word_t; static io::Pin m_rst; static io::Pin m_busy; public: enum class Cmd : std::uint8_t { DRIVER_OUTPUT_CONTROL = 0x01, DEEP_SLEEP_MODE = 0x10, DATA_ENTRY_MODE = 0x11, SW_RESET = 0x12, READ_TEMPERATURE_SENSOR = 0x18, UPDATE_DISPLAY = 0x20, DISPLAY_UPDATE_CONTROL_2 = 0x22, WRITE_RAM_BLACK = 0x24, WRITE_RAM_RED = 0x26, BORDER_WAVEFORM_CONTROL = 0x3C, SET_RAM_X_ADDR_POSITIONS = 0x44, SET_RAM_Y_ADDR_POSITIONS = 0x45, SET_RAM_X_ADDR = 0x4E, SET_RAM_Y_ADDR = 0x4F, }; static void init() { m_rst.dir(io::Dir::OUT); m_busy.pullup(false); m_busy.dir(io::Dir::IN); Spi::init(); reset(); waitUntilIdle(); sendCommand(Cmd::SW_RESET); waitUntilIdle(); sendCommand(Cmd::DRIVER_OUTPUT_CONTROL); sendData(0xC7); sendData(0x00); sendData(0x01); sendCommand(Cmd::DATA_ENTRY_MODE); sendData(0x02); sendCommand(Cmd::SET_RAM_X_ADDR_POSITIONS); sendData(Width / 8 - 1); sendData(0x00); sendCommand(Cmd::SET_RAM_Y_ADDR_POSITIONS); sendData(0x00); sendData(0x00); sendData(Height - 1); sendData(0x00); sendCommand(Cmd::BORDER_WAVEFORM_CONTROL); sendData(0x05); sendCommand(Cmd::READ_TEMPERATURE_SENSOR); sendData(0x80); sendCommand(Cmd::SET_RAM_X_ADDR); sendData(Width / 8 - 1); sendCommand(Cmd::SET_RAM_Y_ADDR); sendData(0x00); sendData(0x00); waitUntilIdle(); } static void sendCommand(const Cmd command) { Spi::select(true); Spi::write(static_cast(command), true); Spi::select(false); } static void sendData(word_t data) { Spi::select(true); Spi::write(data, false); Spi::select(false); } static word_t readData() { Spi::select(true); const auto res = Spi::read(); Spi::select(false); return res; } static void waitUntilIdle() { while (m_busy) { _delay_ms(100); } } static void reset() { m_rst = true; _delay_ms(200); m_rst = false; _delay_ms(10); m_rst = true; _delay_ms(200); } template static void draw(const RleImage &rleImage) { constexpr auto pgm_load = [](const auto &object) { using object_t = std::remove_cvref_t; auto buffer = object_t{}; auto rawBuffer = reinterpret_cast(&buffer); for (auto i = std::size_t{0}; i < sizeof(object_t); ++i) { rawBuffer[i] = static_cast(pgm_read_byte(&reinterpret_cast(&object)[i])); } return buffer; }; constexpr auto sendImageChannel = [pgm_load](const auto command, const auto &image) { sendCommand(command); for (auto j = std::size_t{0}; j < image.size(); ++j) { const auto [count, data] = pgm_load(image[j]); for (auto i = std::uint16_t{0}; i < count; ++i) { if (command == Cmd::WRITE_RAM_BLACK) { sendData(data); } else { sendData(~data); } } } }; sendImageChannel(Cmd::WRITE_RAM_BLACK, std::get<0>(rleImage)); sendImageChannel(Cmd::WRITE_RAM_RED, std::get<1>(rleImage)); sendCommand(Cmd::DISPLAY_UPDATE_CONTROL_2); sendData(0xF7); sendCommand(Cmd::UPDATE_DISPLAY); waitUntilIdle(); } static void clear() { sendCommand(Cmd::WRITE_RAM_BLACK); for (auto i = std::uint16_t{0}; i < Width * Height / 8; i++) { sendData(0xff); } sendCommand(Cmd::WRITE_RAM_RED); for (auto i = std::uint16_t{0}; i < Width * Height / 8; i++) { sendData(0x00); } sendCommand(Cmd::DISPLAY_UPDATE_CONTROL_2); sendData(0xF7); sendCommand(Cmd::UPDATE_DISPLAY); waitUntilIdle(); } static void autoPatternFill() { constexpr auto RED_PATTERN_FILL_CMD = static_cast(0x46); constexpr auto BLACK_PATTERN_FILL_CMD = static_cast(0x47); constexpr auto RED_PATTERN = 0b0'001'0'001; constexpr auto BLACK_PATTERN = 0b1'000'0'000; sendCommand(RED_PATTERN_FILL_CMD); sendData(RED_PATTERN); waitUntilIdle(); sendCommand(BLACK_PATTERN_FILL_CMD); sendData(BLACK_PATTERN); waitUntilIdle(); sendCommand(Cmd::DISPLAY_UPDATE_CONTROL_2); sendData(0xF7); sendCommand(Cmd::UPDATE_DISPLAY); waitUntilIdle(); } static void sleep() { sendCommand(Cmd::DEEP_SLEEP_MODE); sendData(0x01); _delay_ms(100); } }; } // namespace eink