* Tmini Eink fix * tuning * better refresh * Fix to lora pins to be like the original. * Update pins_arduino.h * removed dead flags from previous tests * Update src/graphics/niche/Drivers/EInk/GDEW0102T4.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
179 lines
4.5 KiB
C++
179 lines
4.5 KiB
C++
#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS
|
|
|
|
#include "./GDEW0102T4.h"
|
|
|
|
#include <cstring>
|
|
|
|
using namespace NicheGraphics::Drivers;
|
|
|
|
// LUTs from GxEPD2_102.cpp (GDEW0102T4 / UC8175).
|
|
static const uint8_t LUT_W_FULL[] = {
|
|
0x60, 0x5A, 0x5A, 0x00, 0x00, 0x01, //
|
|
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, //
|
|
};
|
|
|
|
static const uint8_t LUT_B_FULL[] = {
|
|
0x90, 0x5A, 0x5A, 0x00, 0x00, 0x01, //
|
|
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, //
|
|
};
|
|
|
|
static const uint8_t LUT_W_FAST[] = {
|
|
0x60, 0x01, 0x01, 0x00, 0x00, 0x01, //
|
|
0x80, 0x12, 0x00, 0x00, 0x00, 0x01, //
|
|
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, //
|
|
};
|
|
|
|
static const uint8_t LUT_B_FAST[] = {
|
|
0x90, 0x01, 0x01, 0x00, 0x00, 0x01, //
|
|
0x40, 0x14, 0x00, 0x00, 0x00, 0x01, //
|
|
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, //
|
|
};
|
|
|
|
GDEW0102T4::GDEW0102T4() : UC8175(width, height, supported) {}
|
|
|
|
void GDEW0102T4::setFastConfig(FastConfig cfg)
|
|
{
|
|
// Clamp out only clearly invalid PLL settings.
|
|
if (cfg.reg30 < 0x05)
|
|
cfg.reg30 = 0x05;
|
|
fastConfig = cfg;
|
|
}
|
|
|
|
GDEW0102T4::FastConfig GDEW0102T4::getFastConfig() const
|
|
{
|
|
return fastConfig;
|
|
}
|
|
|
|
void GDEW0102T4::configCommon()
|
|
{
|
|
// Init path aligned with GxEPD2_GDEW0102T4 (UC8175 family).
|
|
sendCommand(0xD2);
|
|
sendData(0x3F);
|
|
|
|
sendCommand(0x00);
|
|
sendData(0x6F);
|
|
|
|
sendCommand(0x01);
|
|
sendData(0x03);
|
|
sendData(0x00);
|
|
sendData(0x2B);
|
|
sendData(0x2B);
|
|
|
|
sendCommand(0x06);
|
|
sendData(0x3F);
|
|
|
|
sendCommand(0x2A);
|
|
sendData(0x00);
|
|
sendData(0x00);
|
|
|
|
sendCommand(0x30); // PLL / drive clock
|
|
sendData(0x13);
|
|
|
|
sendCommand(0x50); // Last border/data interval; subtle but can affect artifacts
|
|
sendData(0x57);
|
|
|
|
sendCommand(0x60);
|
|
sendData(0x22);
|
|
|
|
sendCommand(0x61);
|
|
sendData(width);
|
|
sendData(height);
|
|
|
|
sendCommand(0x82); // VCOM DC setting
|
|
sendData(0x12);
|
|
|
|
sendCommand(0xE3);
|
|
sendData(0x33);
|
|
}
|
|
|
|
void GDEW0102T4::configFull()
|
|
{
|
|
sendCommand(0x23);
|
|
sendData(LUT_W_FULL, sizeof(LUT_W_FULL));
|
|
sendCommand(0x24);
|
|
sendData(LUT_B_FULL, sizeof(LUT_B_FULL));
|
|
|
|
powerOn();
|
|
}
|
|
|
|
void GDEW0102T4::configFast()
|
|
{
|
|
uint8_t lutW[sizeof(LUT_W_FAST)];
|
|
uint8_t lutB[sizeof(LUT_B_FAST)];
|
|
memcpy(lutW, LUT_W_FAST, sizeof(LUT_W_FAST));
|
|
memcpy(lutB, LUT_B_FAST, sizeof(LUT_B_FAST));
|
|
|
|
// Second stage duration bytes are the main "darkness vs ghosting" control for this panel.
|
|
lutW[7] = fastConfig.lutW2;
|
|
lutB[7] = fastConfig.lutB2;
|
|
|
|
sendCommand(0x30);
|
|
sendData(fastConfig.reg30);
|
|
|
|
sendCommand(0x50);
|
|
sendData(fastConfig.reg50);
|
|
|
|
sendCommand(0x82);
|
|
sendData(fastConfig.reg82);
|
|
|
|
sendCommand(0x23);
|
|
sendData(lutW, sizeof(lutW));
|
|
sendCommand(0x24);
|
|
sendData(lutB, sizeof(lutB));
|
|
|
|
powerOn();
|
|
}
|
|
|
|
void GDEW0102T4::writeOldImage()
|
|
{
|
|
// On this panel, FULL refresh is most reliable when "old image" is all white.
|
|
if (updateType == FULL) {
|
|
sendCommand(0x10);
|
|
// Use buffered writes of 0xFF to avoid per-byte SPI transactions.
|
|
const uint16_t chunkSize = 64;
|
|
uint8_t ffBuf[chunkSize];
|
|
memset(ffBuf, 0xFF, sizeof(ffBuf));
|
|
|
|
uint32_t remaining = bufferSize;
|
|
while (remaining > 0) {
|
|
uint16_t toSend = remaining > chunkSize ? chunkSize : static_cast<uint16_t>(remaining);
|
|
sendData(ffBuf, toSend);
|
|
remaining -= toSend;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// FAST refresh uses differential data (previous frame as old image).
|
|
if (previousBuffer) {
|
|
writeImage(0x10, previousBuffer);
|
|
} else {
|
|
writeImage(0x10, buffer);
|
|
}
|
|
}
|
|
|
|
void GDEW0102T4::finalizeUpdate()
|
|
{
|
|
// Keep panel out of deep-sleep between updates for better reliability of repeated FAST refresh.
|
|
powerOff();
|
|
}
|
|
|
|
#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS
|