diff --git a/src/graphics/niche/Drivers/EInk/DEPG0213BNS800.cpp b/src/graphics/niche/Drivers/EInk/DEPG0213BNS800.cpp new file mode 100644 index 000000000..2c8df96ed --- /dev/null +++ b/src/graphics/niche/Drivers/EInk/DEPG0213BNS800.cpp @@ -0,0 +1,132 @@ +#include "./DEPG0213BNS800.h" + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +using namespace NicheGraphics::Drivers; + +// Describes the operation performed when a "fast refresh" is performed +// Source: Modified from GxEPD2 (GxEPD2_213_BN) +static const uint8_t LUT_FAST[] = { + // 1 2 3 + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // B2B (Existing black pixels) + 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // B2W (New white pixels) + 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // W2B (New black pixels) + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // W2W (Existing white pixels) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // VCOM + + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // 1. Any pixels changing W2B or B2W. Two medium taps. + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 2. All pixels. One short tap. + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 3. Cooldown + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, // +}; + +// How strongly the pixels are pulled and pushed +void DEPG0213BNS800::configVoltages() +{ + switch (updateType) { + case FAST: + // Reference: display datasheet, GxEPD1 + sendCommand(0x03); // Gate voltage + sendData(0x17); // VGH: 20V + + // Reference: display datasheet, GxEPD1 + sendCommand(0x04); // Source voltage + sendData(0x41); // VSH1: 15V + sendData(0x00); // VSH2: NA + sendData(0x32); // VSL: -15V + + // GxEPD1 sets this at -1.2V, but that seems to be drive the pixels very hard + sendCommand(0x2C); // VCOM voltage + sendData(0x08); // VCOM: -0.2V + break; + + case FULL: + default: + // From OTP memory + break; + } +} + +// Load settings about how the pixels are moved from old state to new state during a refresh +// - manually specified, +// - or with stored values from displays OTP memory +void DEPG0213BNS800::configWaveform() +{ + switch (updateType) { + case FAST: + sendCommand(0x3C); // Border waveform: + sendData(0x80); // VSS + + sendCommand(0x32); // Write LUT register from MCU: + sendData(LUT_FAST, sizeof(LUT_FAST)); // (describes operation for a FAST refresh) + break; + + case FULL: + default: + // From OTP memory + break; + } +} + +// Describes the sequence of events performed by the displays controller IC during a refresh +// Includes "power up", "load settings from memory", "update the pixels", etc +void DEPG0213BNS800::configUpdateSequence() +{ + switch (updateType) { + case FAST: + sendCommand(0x22); // Set "update sequence" + sendData(0xCF); // Differential, use manually loaded waveform + break; + + case FULL: + default: + sendCommand(0x22); // Set "update sequence" + sendData(0xF7); // Non-differential, load waveform from OTP + break; + } +} + +// Once the refresh operation has been started, +// begin periodically polling the display to check for completion, using the normal Meshtastic threading code +// Only used when refresh is "async" +void DEPG0213BNS800::detachFromUpdate() +{ + switch (updateType) { + case FAST: + return beginPolling(50, 500); // At least 500ms, then poll every 50ms + case FULL: + default: + return beginPolling(100, 3500); // At least 3500ms, then poll every 100ms + } +} + +// For this display, we do not need to re-write the new image. +// We're overriding SSD16XX::finalizeUpdate to make this small optimization. +// The display does also work just fine with the generic SSD16XX method, though. +void DEPG0213BNS800::finalizeUpdate() +{ + // Put a copy of the image into the "old memory". + // Used with differential refreshes (e.g. FAST update), to determine which px need to move, and which can remain in place + // We need to keep the "old memory" up to date, because don't know whether next refresh will be FULL or FAST etc. + if (updateType != FULL) { + // writeNewImage(); // Not required for this display + writeOldImage(); + sendCommand(0x7F); // Terminate image write without update + wait(); + } + + // Enter deep-sleep to save a few µA + // Waking from this requires that display's reset pin is broken out + if (pin_rst != 0xFF) + deepSleep(); +} +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS \ No newline at end of file diff --git a/src/graphics/niche/Drivers/EInk/DEPG0213BNS800.h b/src/graphics/niche/Drivers/EInk/DEPG0213BNS800.h new file mode 100644 index 000000000..e1bb96450 --- /dev/null +++ b/src/graphics/niche/Drivers/EInk/DEPG0213BNS800.h @@ -0,0 +1,44 @@ +/* + +E-Ink display driver + - DEPG0213BNS800 + - Manufacturer: DKE + - Size: 2.13 inch + - Resolution: 122px x 250px + - Flex connector marking: FPC-7528B + + Note: this is from an older generation of DKE panels, which still used Solomon Systech controller ICs. + DKE's website suggests that the latest DEPG0213BN displays may use Fitipower controllers instead. +*/ + +#pragma once + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "configuration.h" + +#include "./SSD16XX.h" + +namespace NicheGraphics::Drivers +{ +class DEPG0213BNS800 : public SSD16XX +{ + // Display properties + private: + static constexpr uint32_t width = 122; + static constexpr uint32_t height = 250; + static constexpr UpdateTypes supported = (UpdateTypes)(FULL | FAST); + + public: + DEPG0213BNS800() : SSD16XX(width, height, supported, 1) {} // Note: left edge of this display is offset by 1 byte + + protected: + void configVoltages() override; + void configWaveform() override; + void configUpdateSequence() override; + void detachFromUpdate() override; + void finalizeUpdate() override; // Only overriden for a slight optimization +}; + +} // namespace NicheGraphics::Drivers +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS \ No newline at end of file