diff --git a/eink.hpp b/eink.hpp index b62ad9a..47b4c0c 100644 --- a/eink.hpp +++ b/eink.hpp @@ -7,11 +7,11 @@ #include #include -#include - #include "eink_spi.hpp" +#include "otp.hpp" #include "../clock.hpp" +#include "../flash/flash.hpp" #include "../io/io.hpp" #include "../util/util.hpp" @@ -24,17 +24,6 @@ class Eink { static io::Pin m_rst; static io::Pin m_busy; - template - static T pgm_load(const T &object) - { - auto buffer = T{}; - auto rawBuffer = reinterpret_cast(&buffer); - for (auto i = std::size_t{0}; i < sizeof(T); ++i) { - rawBuffer[i] = static_cast(pgm_read_byte(&reinterpret_cast(&object)[i])); - } - return buffer; - } - public: enum class Cmd : std::uint8_t { DRIVER_OUTPUT_CONTROL = 0x01, @@ -46,7 +35,10 @@ class Eink { DISPLAY_UPDATE_CONTROL_2 = 0x22, WRITE_RAM_BLACK = 0x24, WRITE_RAM_RED = 0x26, + READ_RAM = 0x27, + LOAD_OTP_TO_RAM = 0x31, BORDER_WAVEFORM_CONTROL = 0x3C, + READ_RAM_CHANNEL = 0x41, SET_RAM_X_ADDR_POSITIONS = 0x44, SET_RAM_Y_ADDR_POSITIONS = 0x45, SET_RAM_X_ADDR = 0x4E, @@ -54,9 +46,9 @@ class Eink { }; enum class Color : std::uint8_t { - BLACK, - WHITE, - RED, + BLACK = 0x00, + RED = 0x01, + WHITE = 0x02, }; enum class RamDirection : std::uint8_t { @@ -152,7 +144,7 @@ class Eink { constexpr auto sendImageChannel = [](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]); + const auto [count, data] = flash::load(image[j]); for (auto i = std::uint16_t{0}; i < count; ++i) { if (command == Cmd::WRITE_RAM_BLACK) { sendData(data); @@ -176,12 +168,12 @@ class Eink { { constexpr auto getFillData = [](const auto &color) -> std::pair { switch (color) { - case Color::WHITE: - return {0xFF, 0x00}; case Color::BLACK: return {0x00, 0x00}; case Color::RED: return {0xFF, 0xFF}; + case Color::WHITE: + return {0xFF, 0x00}; } return {0xFF, 0x00}; }; @@ -264,6 +256,60 @@ class Eink { sendData((pos >> 8) & 0b1); } + template + static void dumpOTP(PrintFn &&printFn) + { + constexpr auto byteWidth = Width / 8; + constexpr auto ramHeight = Height + 46; + + constexpr auto xRamRange = std::pair{0, byteWidth - 1}; + constexpr auto yRamRange = std::pair{0, ramHeight - 1}; + + setDataEntryMode(RamDirection::INCREMENT, RamDirection::INCREMENT, FastestMovingIndex::X); + setRamRange(xRamRange, yRamRange); + setRamXPos(0); + setRamYPos(0); + + sendCommand(Cmd::WRITE_RAM_BLACK); + for (auto i = std::uint16_t{0}; i < byteWidth * ramHeight; i++) { + sendData(0xFF); + } + sendCommand(Cmd::WRITE_RAM_RED); + for (auto i = std::uint16_t{0}; i < byteWidth * ramHeight; i++) { + sendData(0x00); + } + + sendCommand(Cmd::DISPLAY_UPDATE_CONTROL_2); + sendData(0xF7); + sendCommand(Cmd::UPDATE_DISPLAY); + waitUntilIdle(); + + ////////////////////////////////////////////////////////////////////////// + + sendCommand(Cmd::DISPLAY_UPDATE_CONTROL_2); + sendData(0x80); + sendCommand(Cmd::UPDATE_DISPLAY); + waitUntilIdle(); + + sendCommand(Cmd::LOAD_OTP_TO_RAM); + waitUntilIdle(); + + setRamXPos(0); + setRamYPos(0); + + sendCommand(Cmd::READ_RAM_CHANNEL); + sendData(static_cast(Color::BLACK)); + + sendCommand(Cmd::READ_RAM); + readData(); // First byte must be discarded + + for (auto i = std::size_t{0}; i < sizeof(OTP); ++i) { + printFn(readData()); + } + + init(); + } + static void sleep() { sendCommand(Cmd::DEEP_SLEEP_MODE); diff --git a/otp.hpp b/otp.hpp new file mode 100644 index 0000000..b79ac10 --- /dev/null +++ b/otp.hpp @@ -0,0 +1,353 @@ +#pragma once + +#include +#include + +namespace eink { + +// [SOURCE]_[VCOM] +enum class Voltage : std::uint8_t { + VSS_DCVCOM = 0b00, + VSH1_VSH1DCVCOM = 0b01, + VSL_VSLDCVCOM = 0b10, + VSH2_NA = 0b11, +}; + +struct [[gnu::packed]] Phases +{ + Voltage phaseD : 2; + Voltage phaseC : 2; + Voltage phaseB : 2; + Voltage phaseA : 2; +}; +static_assert(sizeof(Phases) == 1); + +struct [[gnu::packed]] VoltageGroups +{ + Phases group[12]; +}; +static_assert(sizeof(VoltageGroups) == 12); + +struct [[gnu::packed]] Timings +{ + std::uint8_t frameCountPhaseA; + std::uint8_t frameCountPhaseB; + std::uint8_t repeatSubPhaseAB; + std::uint8_t frameCountPhaseC; + std::uint8_t frameCountPhaseD; + std::uint8_t repeatSubPhaseCD; + std::uint8_t repeat; +}; +static_assert(sizeof(Timings) == 7); + +struct [[gnu::packed]] FrameRates +{ + std::uint8_t group11 : 4; + std::uint8_t group10 : 4; + std::uint8_t group9 : 4; + std::uint8_t group8 : 4; + std::uint8_t group7 : 4; + std::uint8_t group6 : 4; + std::uint8_t group5 : 4; + std::uint8_t group4 : 4; + std::uint8_t group3 : 4; + std::uint8_t group2 : 4; + std::uint8_t group1 : 4; + std::uint8_t group0 : 4; +}; +static_assert(sizeof(FrameRates) == 6); + +struct [[gnu::packed]] SubPhaseGateStates +{ + bool group11SubPhaseCD : 1; + bool group11SubPhaseAB : 1; + bool group10SubPhaseCD : 1; + bool group10SubPhaseAB : 1; + bool group9SubPhaseCD : 1; + bool group9SubPhaseAB : 1; + bool group8SubPhaseCD : 1; + bool group8SubPhaseAB : 1; + bool group7SubPhaseCD : 1; + bool group7SubPhaseAB : 1; + bool group6SubPhaseCD : 1; + bool group6SubPhaseAB : 1; + bool group5SubPhaseCD : 1; + bool group5SubPhaseAB : 1; + bool group4SubPhaseCD : 1; + bool group4SubPhaseAB : 1; + bool group3SubPhaseCD : 1; + bool group3SubPhaseAB : 1; + bool group2SubPhaseCD : 1; + bool group2SubPhaseAB : 1; + bool group1SubPhaseCD : 1; + bool group1SubPhaseAB : 1; + bool group0SubPhaseCD : 1; + bool group0SubPhaseAB : 1; +}; +static_assert(sizeof(SubPhaseGateStates) == 3); + +struct [[gnu::packed]] Waveform +{ + VoltageGroups lut[5]; + Timings timings[12]; + FrameRates frameRates; + SubPhaseGateStates subPhaseGateStates; +}; +static_assert(sizeof(Waveform) == 153); + +enum class LutEndOption : std::uint8_t { + KEEP_PREVIOUS_SOURCE_OUTPUT_LEVEL_BEFORE_POWER_OFF = 0x07, + NORMAL = 0x22, +}; +static_assert(sizeof(LutEndOption) == 1); + +enum class GateVoltage : std::uint8_t { + V_20_DEFAULT = 0x00, + V_10 = 0x03, + V_10_5 = 0x04, + V_11 = 0x05, + V_11_5 = 0x06, + V_12 = 0x07, + V_12_5 = 0x08, + V_13 = 0x09, + V_13_5 = 0x0A, + V_14 = 0x0B, + V_14_5 = 0x0C, + V_15 = 0x0D, + V_15_5 = 0x0E, + V_16 = 0x0F, + V_16_5 = 0x10, + V_17 = 0x11, + V_17_5 = 0x12, + V_18 = 0x13, + V_18_5 = 0x14, + V_19 = 0x15, + V_19_5 = 0x16, + V_20 = 0x17, +}; +static_assert(sizeof(GateVoltage) == 1); + +enum class VoltageSourceHigh : std::uint8_t { + V_9 = 0x23, + V_9_2 = 0x24, + V_9_4 = 0x25, + V_9_6 = 0x26, + V_9_8 = 0x27, + V_10 = 0x28, + V_10_2 = 0x29, + V_10_4 = 0x2A, + V_10_6 = 0x2B, + V_10_8 = 0x2C, + V_11 = 0x2D, + V_11_2 = 0x2E, + V_11_4 = 0x2F, + V_11_6 = 0x30, + V_11_8 = 0x31, + V_12 = 0x32, + V_12_2 = 0x33, + V_12_4 = 0x34, + V_12_6 = 0x35, + V_12_8 = 0x36, + V_13 = 0x37, + V_13_2 = 0x38, + V_13_4 = 0x39, + V_13_6 = 0x3A, + V_13_8 = 0x3B, + V_14 = 0x3C, + V_14_2 = 0x3D, + V_14_4 = 0x3E, + V_14_6 = 0x3F, + V_14_8 = 0x40, + V_15 = 0x41, + V_15_2 = 0x42, + V_15_4 = 0x43, + V_15_6 = 0x44, + V_15_8 = 0x45, + V_16 = 0x46, + V_16_2 = 0x47, + V_16_4 = 0x48, + V_16_6 = 0x49, + V_16_8 = 0x4A, + V_17 = 0x4B, + V_2_4 = 0x8E, + V_2_5 = 0x8F, + V_2_6 = 0x90, + V_2_7 = 0x91, + V_2_8 = 0x92, + V_2_9 = 0x93, + V_3 = 0x94, + V_3_1 = 0x95, + V_3_2 = 0x96, + V_3_3 = 0x97, + V_3_4 = 0x98, + V_3_5 = 0x99, + V_3_6 = 0x9A, + V_3_7 = 0x9B, + V_3_8 = 0x9C, + V_3_9 = 0x9D, + V_4 = 0x9E, + V_4_1 = 0x9F, + V_4_2 = 0xA0, + V_4_3 = 0xA1, + V_4_4 = 0xA2, + V_4_5 = 0xA3, + V_4_6 = 0xA4, + V_4_7 = 0xA5, + V_4_8 = 0xA6, + V_4_9 = 0xA7, + V_5 = 0xA8, + V_5_1 = 0xA9, + V_5_2 = 0xAA, + V_5_3 = 0xAB, + V_5_4 = 0xAC, + V_5_5 = 0xAD, + V_5_6 = 0xAE, + V_5_7 = 0xAF, + V_5_8 = 0xB0, + V_5_9 = 0xB1, + V_6 = 0xB2, + V_6_1 = 0xB3, + V_6_2 = 0xB4, + V_6_3 = 0xB5, + V_6_4 = 0xB6, + V_6_5 = 0xB7, + V_6_6 = 0xB8, + V_6_7 = 0xB9, + V_6_8 = 0xBA, + V_6_9 = 0xBB, + V_7 = 0xBC, + V_7_1 = 0xBD, + V_7_2 = 0xBE, + V_7_3 = 0xBF, + V_7_4 = 0xC0, + V_7_5 = 0xC1, + V_7_6 = 0xC2, + V_7_7 = 0xC3, + V_7_8 = 0xC4, + V_7_9 = 0xC5, + V_8 = 0xC6, + V_8_1 = 0xC7, + V_8_2 = 0xC8, + V_8_3 = 0xC9, + V_8_4 = 0xCA, + V_8_5 = 0xCB, + V_8_6 = 0xCC, + V_8_7 = 0xCD, + V_8_8 = 0xCE, +}; +static_assert(sizeof(VoltageSourceHigh) == 1); + +enum class VoltageSourceLow : std::uint8_t { + NEG_5 = 0x0A, + NEG_5_5 = 0x0C, + NEG_6 = 0x0E, + NEG_6_5 = 0x10, + NEG_7 = 0x12, + NEG_7_5 = 0x14, + NEG_8 = 0x16, + NEG_8_5 = 0x18, + NEG_9 = 0x1A, + NEG_9_5 = 0x1C, + NEG_10 = 0x1E, + NEG_10_5 = 0x20, + NEG_11 = 0x22, + NEG_11_5 = 0x24, + NEG_12 = 0x26, + NEG_12_5 = 0x28, + NEG_13 = 0x2A, + NEG_13_5 = 0x2C, + NEG_14 = 0x2E, + NEG_14_5 = 0x30, + NEG_15 = 0x32, + NEG_15_5 = 0x34, + NEG_16 = 0x36, + NEG_16_5 = 0x38, + NEG_17 = 0x3A, +}; +static_assert(sizeof(VoltageSourceLow) == 1); + +struct [[gnu::packed]] SourceVoltage +{ + VoltageSourceHigh vsh1; + VoltageSourceHigh vsh2; + VoltageSourceLow vsl; +}; +static_assert(sizeof(SourceVoltage) == 3); + +enum class CommonVoltage : std::uint8_t { + NEG_0_2 = 0x08, + NEG_0_3 = 0x0C, + NEG_0_4 = 0x10, + NEG_0_5 = 0x14, + NEG_0_6 = 0x18, + NEG_0_7 = 0x1C, + NEG_0_8 = 0x20, + NEG_0_9 = 0x24, + NEG_1 = 0x28, + NEG_1_1 = 0x2C, + NEG_1_2 = 0x30, + NEG_1_3 = 0x34, + NEG_1_4 = 0x38, + NEG_1_5 = 0x3C, + NEG_1_6 = 0x40, + NEG_1_7 = 0x44, + NEG_1_8 = 0x48, + NEG_1_9 = 0x4C, + NEG_2 = 0x50, + NEG_2_1 = 0x54, + NEG_2_2 = 0x58, + NEG_2_3 = 0x5C, + NEG_2_4 = 0x60, + NEG_2_5 = 0x64, + NEG_2_6 = 0x68, + NEG_2_7 = 0x6C, + NEG_2_8 = 0x70, + NEG_2_9 = 0x74, + NEG_3 = 0x78, +}; +static_assert(sizeof(CommonVoltage) == 1); + +struct [[gnu::packed]] WaveformSetting +{ + Waveform waveform; + LutEndOption lutEndOption; + GateVoltage gateVoltage; + SourceVoltage sourceVoltage; + CommonVoltage commonVoltage; +}; +static_assert(sizeof(WaveformSetting) == 159); + +struct [[gnu::packed]] TemperatureRange +{ + std::uint8_t lowerBoundLowBits; + std::uint8_t lowerBoundHighBits : 4; + std::uint8_t upperBoundLowBits : 4; + std::uint8_t upperBoundHighBits; + + inline constexpr operator std::pair() const + { + constexpr auto convertTemp = [](const auto &temp) { + auto signedTemp = static_cast(temp); + if (signedTemp >> 11 & 1) { + signedTemp |= 0b1111 << 12; + } + const auto tempDegrees = signedTemp / 16.0f; + return tempDegrees; + }; + + const auto lowerBound = static_cast(lowerBoundHighBits) << 8 | lowerBoundLowBits; + const auto upperBound = static_cast(upperBoundHighBits) << 4 | upperBoundLowBits; + + return {convertTemp(lowerBound), convertTemp(upperBound)}; + }; +}; +static_assert(sizeof(TemperatureRange) == 3); + +struct [[gnu::packed]] OTP +{ + WaveformSetting waveforms[36]; + TemperatureRange temperatures[36]; +}; +static_assert(sizeof(OTP) == 5832); + +} // namespace eink