Compare commits

...

3 Commits

Author SHA1 Message Date
Ken Van Hoeylandt
6c3c4f906f Driver correctness 2025-08-12 21:49:43 +02:00
Ken Van Hoeylandt
84bb29d089 Update 2025-08-12 21:30:50 +02:00
Ken Van Hoeylandt
3cb0784e01 Progress on display 2025-08-12 21:19:52 +02:00
14 changed files with 647 additions and 883 deletions

View File

@ -1,13 +1,12 @@
#include "Tactility/lvgl/LvglSync.h"
#include "hal/TdeckDisplay.h"
#include "hal/TdeckDisplayConstants.h"
#include "hal/TdeckKeyboard.h"
#include "hal/TdeckPower.h"
#include "hal/TdeckSdCard.h"
#include <Tactility/hal/Configuration.h>
#define TDECK_SPI_TRANSFER_SIZE_LIMIT (TDECK_LCD_HORIZONTAL_RESOLUTION * TDECK_LCD_SPI_TRANSFER_HEIGHT * (LV_COLOR_DEPTH / 8))
#define TDECK_SPI_TRANSFER_SIZE_LIMIT (240 * 320 * (LV_COLOR_DEPTH / 8))
bool tdeckInit();
@ -60,9 +59,9 @@ extern const Configuration lilygo_tdeck = {
.device = SPI2_HOST,
.dma = SPI_DMA_CH_AUTO,
.config = {
.mosi_io_num = GPIO_NUM_41,
.miso_io_num = GPIO_NUM_38,
.sclk_io_num = GPIO_NUM_40,
.mosi_io_num = GPIO_NUM_33,
.miso_io_num = GPIO_NUM_47,
.sclk_io_num = GPIO_NUM_36,
.quadwp_io_num = GPIO_NUM_NC, // Quad SPI LCD driver is not yet supported
.quadhd_io_num = GPIO_NUM_NC, // Quad SPI LCD driver is not yet supported
.data4_io_num = GPIO_NUM_NC,

View File

@ -1,114 +0,0 @@
// Display Library for SPI e-paper panels from Dalian Good Display and boards from Waveshare.
// Requires HW SPI and Adafruit_GFX. Caution: the e-paper panels require 3.3V supply AND data lines!
//
// Display Library based on Demo Example from Good Display: https://www.good-display.com/companyfile/32/
//
// Author: Jean-Marc Zingg
//
// Version: see library.properties
//
// Library: https://github.com/ZinggJM/GxEPD2
#pragma once
// color definitions for GxEPD, GxEPD2 and GxEPD_HD, values correspond to RGB565 values for TFTs
#define GxEPD_BLACK 0x0000
#define GxEPD_WHITE 0xFFFF
// some controllers for b/w EPDs support grey levels
#define GxEPD_DARKGREY 0x7BEF // 128, 128, 128
#define GxEPD_LIGHTGREY 0xC618 // 192, 192, 192
// values for 3-color or 7-color EPDs
#define GxEPD_RED 0xF800 // 255, 0, 0
#define GxEPD_YELLOW 0xFFE0 // 255, 255, 0 !!no longer same as GxEPD_RED!!
#define GxEPD_COLORED GxEPD_RED
// values for 7-color EPDs only
#define GxEPD_BLUE 0x001F // 0, 0, 255
#define GxEPD_GREEN 0x07E0 // 0, 255, 0
#define GxEPD_ORANGE 0xFC00 // 255, 128, 0
class GxEPD2
{
public:
enum Panel
{
GDEW0102T4, Waveshare_1_02_bw = GDEW0102T4,
GDEP015OC1, Waveshare_1_54_bw = GDEP015OC1,
DEPG0150BN,
GDEH0154D67, Waveshare_1_54_bw_D67 = GDEH0154D67,
GDEW0154T8,
GDEW0154M09,
GDEW0154M10,
GDEY0154D67,
GDE0213B1, Waveshare_2_13_bw = GDE0213B1,
GDEH0213B72, Waveshare_2_13_bw_B72 = GDEH0213B72,
GDEH0213B73, Waveshare_2_13_bw_B73 = GDEH0213B73,
GDEM0213B74,
GDEW0213I5F, Waveshare_2_13_flex = GDEW0213I5F,
GDEW0213M21,
GDEW0213T5D,
DEPG0213BN,
GDEY0213B74,
GDEW026T0, Waveshare_2_6_bw = GDEW026T0,
GDEW026M01,
DEPG0266BN,
GDEY0266T90,
GDEH029A1, Waveshare_2_9_bw = GDEH029A1,
GDEW029T5, Waveshare_2_9_bw_T5 = GDEW029T5,
GDEW029T5D,
GDEW029I6FD,
GDEW029M06,
GDEM029T94,
GDEY029T94,
DEPG0290BS,
GDEW027W3, Waveshare_2_7_bw = GDEW027W3,
GDEY027T91,
GDEQ031T10,
ED037TC1,
GDEW0371W7, Waveshare_3_7_bw = GDEW0371W7,
GDEW042T2, Waveshare_4_2_bw = GDEW042T2,
GDEW042M01,
GDEY042T81,
GDEQ0426T82,
GDEW0583T7, Waveshare_5_83_bw = GDEW0583T7,
GDEW0583T8,
GDEQ0583T31,
GDEW075T8, Waveshare_7_5_bw = GDEW075T8,
GDEW075T7, Waveshare_7_5_bw_T7 = GDEW075T7,
GDEY075T7,
GDEH116T91,
GDEW1248T3, Waveshare_12_24_bw = GDEW1248T3,
ED060SCT, // on Waveshare IT8951 Driver HAT
ED060KC1, // on Waveshare IT8951 Driver HAT 1448x1072
ED078KC2, // on Waveshare IT8951 Driver HAT 1872x1404
ES103TC1, // on Waveshare IT8951 Driver HAT 1872x1404
// 3-color
GDEW0154Z04, Waveshare_1_54_bwr = GDEW0154Z04,
GDEH0154Z90, Waveshare_1_54_bwr_Z90 = GDEH0154Z90,
GDEW0213Z16, Waveshare_2_13_bwr = GDEW0213Z16,
GDEW0213Z19,
GDEY0213Z98,
GDEW029Z10, Waveshare_2_9_bwr = GDEW029Z10,
GDEH029Z13,
GDEM029C90,
GDEY0266Z90, Waveshare_2_66_bwr = GDEY0266Z90,
GDEW027C44, Waveshare_2_7_bwr = GDEW027C44,
GDEW042Z15, Waveshare_4_2_bwr = GDEW042Z15,
GDEQ042Z21, Waveshare_4_2_V2_bwr = GDEQ042Z21,
GDEW0583Z21, Waveshare_5_83_bwr = GDEW0583Z21,
GDEW0583Z83,
GDEW075Z09, Waveshare_7_5_bwr = GDEW075Z09,
GDEW075Z08, Waveshare_7_5_bwr_Z08 = GDEW075Z08,
GDEH075Z90, Waveshare_7_5_bwr_Z90 = GDEH075Z90,
GDEY1248Z51,
// 4-color
GDEY0266F51H,
GDEY029F51H,
Waveshare3inch4color,
GDEY0420F51,
Waveshare437inch4color,
// 7-color
ACeP565, Waveshare_5_65_7c = ACeP565,
GDEY073D46,
ACeP730, Waveshare_7_30_7c = ACeP730
};
};

View File

@ -1,297 +0,0 @@
// Display Library for SPI e-paper panels from Dalian Good Display and boards from Waveshare.
// Requires HW SPI and Adafruit_GFX. Caution: the e-paper panels require 3.3V supply AND data lines!
//
// based on Demo Example from Good Display: https://www.good-display.com/product/426.html
// Panel: GDEQ031T10 : https://www.good-display.com/product/426.html
// Controller: UC8253 : https://v4.cecdn.yun300.cn/100001_1909185148/UC8253.pdf
//
// Author: Jean-Marc Zingg
//
// Version: see library.properties
//
// Library: https://github.com/ZinggJM/GxEPD2
#include "GxEPD2_310_GDEQ031T10.h"
#include <Tactility/kernel/Kernel.h>
constexpr uint32_t LOW = 0;
constexpr uint32_t HIGH = 1;
GxEPD2_310_GDEQ031T10::GxEPD2_310_GDEQ031T10(int16_t cs, int16_t dc, int16_t rst, int16_t busy) :
GxEPD2_EPD(cs, dc, rst, busy, LOW, 10000000, WIDTH, HEIGHT, panel, hasColor, hasPartialUpdate, hasFastPartialUpdate) {}
void GxEPD2_310_GDEQ031T10::clearScreen(uint8_t value) {
// full refresh needed for all cases (previous != screen)
_writeScreenBuffer(0x10, value); // set previous
_writeScreenBuffer(0x13, value); // set current
refresh(false); // full refresh
_initial_write = false;
}
void GxEPD2_310_GDEQ031T10::writeScreenBuffer(uint8_t value) {
if (_initial_write) return clearScreen(value);
_writeScreenBuffer(0x13, value); // set current
}
void GxEPD2_310_GDEQ031T10::writeScreenBufferAgain(uint8_t value) {
_writeScreenBuffer(0x10, value); // set previous
//_writeScreenBuffer(0x13, value); // set current, not needed
}
void GxEPD2_310_GDEQ031T10::_writeScreenBuffer(uint8_t command, uint8_t value) {
if (!_init_display_done) _InitDisplay();
_writeCommand(command);
_startTransfer();
for (uint32_t i = 0; i < uint32_t(WIDTH) * uint32_t(HEIGHT) / 8; i++) { _transfer(value); }
_endTransfer();
}
void GxEPD2_310_GDEQ031T10::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm) { _writeImage(0x13, bitmap, x, y, w, h, invert, mirror_y, pgm); }
void GxEPD2_310_GDEQ031T10::writeImageForFullRefresh(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm) {
_writeImage(0x10, bitmap, x, y, w, h, invert, mirror_y, pgm); // set previous
_writeImage(0x13, bitmap, x, y, w, h, invert, mirror_y, pgm); // set current
}
void GxEPD2_310_GDEQ031T10::writeImageAgain(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm) {
_writeImage(0x10, bitmap, x, y, w, h, invert, mirror_y, pgm); // set previous
//_writeImage(0x13, bitmap, x, y, w, h, invert, mirror_y, pgm); // set current, not needed
}
void GxEPD2_310_GDEQ031T10::_writeImage(uint8_t command, const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm) {
tt::kernel::delayMillis(1); // WDT hack
uint16_t wb = (w + 7) / 8; // width bytes, bitmaps are padded
x -= x % 8; // byte boundary
w = wb * 8; // byte boundary
int16_t x1 = x < 0 ? 0 : x; // limit
int16_t y1 = y < 0 ? 0 : y; // limit
int16_t w1 = x + w < int16_t(WIDTH) ? w : int16_t(WIDTH) - x; // limit
int16_t h1 = y + h < int16_t(HEIGHT) ? h : int16_t(HEIGHT) - y; // limit
int16_t dx = x1 - x;
int16_t dy = y1 - y;
w1 -= dx;
h1 -= dy;
if ((w1 <= 0) || (h1 <= 0)) return;
if (!_init_display_done) _InitDisplay();
if (_initial_write) writeScreenBuffer(); // initial full screen buffer clean
_writeCommand(0x91); // partial in
_setPartialRamArea(x1, y1, w1, h1);
_writeCommand(command);
_startTransfer();
for (int16_t i = 0; i < h1; i++) {
for (int16_t j = 0; j < w1 / 8; j++) {
uint8_t data;
// use wb, h of bitmap for index!
uint16_t idx = mirror_y ? j + dx / 8 + uint16_t((h - 1 - (i + dy))) * wb : j + dx / 8 + uint16_t(i + dy) * wb;
if (pgm) {
#if defined(__AVR) || defined(ESP8266) || defined(ESP32)
data = pgm_read_byte(&bitmap[idx]);
#else
data = bitmap[idx];
#endif
} else { data = bitmap[idx]; }
if (invert) data = ~data;
_transfer(data);
}
}
_endTransfer();
_writeCommand(0x92); // partial out
tt::kernel::delayMillis(1); // WDT hack
}
void GxEPD2_310_GDEQ031T10::writeImagePart(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm) { _writeImagePart(0x13, bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm); }
void GxEPD2_310_GDEQ031T10::writeImagePartAgain(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm) {
_writeImagePart(0x10, bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm); // set previous
//_writeImagePart(0x13, bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm); // set current, not needed
}
void GxEPD2_310_GDEQ031T10::_writeImagePart(uint8_t command, const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm) {
tt::kernel::delayMillis(1); // WDT hack
if ((w_bitmap < 0) || (h_bitmap < 0) || (w < 0) || (h < 0)) return;
if ((x_part < 0) || (x_part >= w_bitmap)) return;
if ((y_part < 0) || (y_part >= h_bitmap)) return;
uint16_t wb_bitmap = (w_bitmap + 7) / 8; // width bytes, bitmaps are padded
x_part -= x_part % 8; // byte boundary
w = w_bitmap - x_part < w ? w_bitmap - x_part : w; // limit
h = h_bitmap - y_part < h ? h_bitmap - y_part : h; // limit
x -= x % 8; // byte boundary
w = 8 * ((w + 7) / 8); // byte boundary, bitmaps are padded
int16_t x1 = x < 0 ? 0 : x; // limit
int16_t y1 = y < 0 ? 0 : y; // limit
int16_t w1 = x + w < int16_t(WIDTH) ? w : int16_t(WIDTH) - x; // limit
int16_t h1 = y + h < int16_t(HEIGHT) ? h : int16_t(HEIGHT) - y; // limit
int16_t dx = x1 - x;
int16_t dy = y1 - y;
w1 -= dx;
h1 -= dy;
if ((w1 <= 0) || (h1 <= 0)) return;
if (!_init_display_done) _InitDisplay();
if (_initial_write) writeScreenBuffer(); // initial full screen buffer clean
_writeCommand(0x91); // partial in
_setPartialRamArea(x1, y1, w1, h1);
_writeCommand(command);
_startTransfer();
for (int16_t i = 0; i < h1; i++) {
for (int16_t j = 0; j < w1 / 8; j++) {
uint8_t data;
// use wb_bitmap, h_bitmap of bitmap for index!
uint16_t idx = mirror_y ? x_part / 8 + j + dx / 8 + uint16_t((h_bitmap - 1 - (y_part + i + dy))) * wb_bitmap : x_part / 8 + j + dx / 8 + uint16_t(y_part + i + dy) * wb_bitmap;
if (pgm) {
#if defined(__AVR) || defined(ESP8266) || defined(ESP32)
data = pgm_read_byte(&bitmap[idx]);
#else
data = bitmap[idx];
#endif
} else { data = bitmap[idx]; }
if (invert) data = ~data;
_transfer(data);
}
}
_endTransfer();
_writeCommand(0x92); // partial out
tt::kernel::delayMillis(1); // WDT hack
}
void GxEPD2_310_GDEQ031T10::writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm) { if (black) { writeImage(black, x, y, w, h, invert, mirror_y, pgm); } }
void GxEPD2_310_GDEQ031T10::writeImagePart(const uint8_t* black, const uint8_t* color, int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm) { if (black) { writeImagePart(black, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm); } }
void GxEPD2_310_GDEQ031T10::writeNative(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm) { if (data1) { writeImage(data1, x, y, w, h, invert, mirror_y, pgm); } }
void GxEPD2_310_GDEQ031T10::drawImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm) {
writeImage(bitmap, x, y, w, h, invert, mirror_y, pgm);
refresh(x, y, w, h);
writeImageAgain(bitmap, x, y, w, h, invert, mirror_y, pgm);
}
void GxEPD2_310_GDEQ031T10::drawImagePart(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm) {
writeImagePart(bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
refresh(x, y, w, h);
writeImagePartAgain(bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
}
void GxEPD2_310_GDEQ031T10::drawImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm) { if (black) { drawImage(black, x, y, w, h, invert, mirror_y, pgm); } }
void GxEPD2_310_GDEQ031T10::drawImagePart(const uint8_t* black, const uint8_t* color, int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm) { if (black) { drawImagePart(black, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm); } }
void GxEPD2_310_GDEQ031T10::drawNative(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm) { if (data1) { drawImage(data1, x, y, w, h, invert, mirror_y, pgm); } }
void GxEPD2_310_GDEQ031T10::refresh(bool partial_update_mode) {
if (partial_update_mode) refresh(0, 0, WIDTH, HEIGHT);
else {
_Update_Full();
_initial_refresh = false; // initial full update done
}
}
void GxEPD2_310_GDEQ031T10::refresh(int16_t x, int16_t y, int16_t w, int16_t h) {
if (_initial_refresh) return refresh(false); // initial update needs be full update
// intersection with screen
int16_t w1 = x < 0 ? w + x : w; // reduce
int16_t h1 = y < 0 ? h + y : h; // reduce
int16_t x1 = x < 0 ? 0 : x; // limit
int16_t y1 = y < 0 ? 0 : y; // limit
w1 = x1 + w1 < int16_t(WIDTH) ? w1 : int16_t(WIDTH) - x1; // limit
h1 = y1 + h1 < int16_t(HEIGHT) ? h1 : int16_t(HEIGHT) - y1; // limit
if ((w1 <= 0) || (h1 <= 0)) return;
// make x1, w1 multiple of 8
w1 += x1 % 8;
if (w1 % 8 > 0) w1 += 8 - w1 % 8;
x1 -= x1 % 8;
if (usePartialUpdateWindow) _writeCommand(0x91); // partial in
_setPartialRamArea(x1, y1, w1, h1);
_Update_Part();
if (usePartialUpdateWindow) _writeCommand(0x92); // partial out
}
void GxEPD2_310_GDEQ031T10::powerOff(void) { _PowerOff(); }
void GxEPD2_310_GDEQ031T10::hibernate() {
_PowerOff();
if (_rst >= 0) {
_writeCommand(0x07); // deep sleep
_writeData(0xA5); // check code
_hibernating = true;
_init_display_done = false;
}
}
void GxEPD2_310_GDEQ031T10::_setPartialRamArea(uint16_t x, uint16_t y, uint16_t w, uint16_t h) {
uint16_t xe = (x + w - 1) | 0x0007; // byte boundary inclusive (last byte)
uint16_t ye = y + h - 1;
x &= 0xFFF8; // byte boundary
_writeCommand(0x90); // partial window
_writeData(x);
_writeData(xe);
_writeData(y / 256);
_writeData(y % 256);
_writeData(ye / 256);
_writeData(ye % 256);
_writeData(0x01);
}
void GxEPD2_310_GDEQ031T10::_PowerOn() {
if (!_power_is_on) {
_writeCommand(0x04);
_waitWhileBusy("_PowerOn", power_on_time);
}
_power_is_on = true;
}
void GxEPD2_310_GDEQ031T10::_PowerOff() {
if (_power_is_on) {
_writeCommand(0x02); // power off
_waitWhileBusy("_PowerOff", power_off_time);
}
_power_is_on = false;
}
void GxEPD2_310_GDEQ031T10::_InitDisplay() {
_writeCommand(0x00); // PANEL SETTING
_writeData(0x1e); // soft reset
_writeData(0x0d);
tt::kernel::delayMillis(1);
_power_is_on = false;
_writeCommand(0x00); // PANEL SETTING
_writeData(0x1f); // KW: 3f, KWR: 2F, BWROTP: 0f, BWOTP: 1f
_writeData(0x0d);
_init_display_done = true;
}
void GxEPD2_310_GDEQ031T10::_Update_Full() {
if (useFastFullUpdate) {
_writeCommand(0xE0); // Cascade Setting (CCSET)
_writeData(0x02); // TSFIX
_writeCommand(0xE5); // Force Temperature (TSSET)
_writeData(0x5A); // 90, 1015000us
//_writeData(0x6E); // 110, 1542001
}
_writeCommand(0x50);
_writeData(0x97);
_PowerOn();
_writeCommand(0x12); //display refresh
_waitWhileBusy("_Update_Full", full_refresh_time);
_init_display_done = false; // needed, reason unknown
}
void GxEPD2_310_GDEQ031T10::_Update_Part() {
if (hasFastPartialUpdate) {
_writeCommand(0xE0); // Cascade Setting (CCSET)
_writeData(0x02); // TSFIX
_writeCommand(0xE5); // Force Temperature (TSSET)
_writeData(0x79); // 121
}
_writeCommand(0x50);
_writeData(0xD7);
_PowerOn();
_writeCommand(0x12); //display refresh
_waitWhileBusy("_Update_Part", partial_refresh_time);
_init_display_done = false; // needed, reason unknown
}

View File

@ -1,100 +0,0 @@
// Display Library for SPI e-paper panels from Dalian Good Display and boards from Waveshare.
// Requires HW SPI and Adafruit_GFX. Caution: the e-paper panels require 3.3V supply AND data lines!
//
// based on Demo Example from Good Display: https://www.good-display.com/product/426.html
// Panel: GDEQ031T10 : https://www.good-display.com/product/426.html
// Controller: UC8253 : https://v4.cecdn.yun300.cn/100001_1909185148/UC8253.pdf
//
// Author: Jean-Marc Zingg
//
// Version: see library.properties
//
// Library: https://github.com/ZinggJM/GxEPD2
#pragma once
#include "GxEPD2_EPD.h"
class GxEPD2_310_GDEQ031T10 : public GxEPD2_EPD
{
public:
// attributes
static const uint16_t WIDTH = 240;
static const uint16_t WIDTH_VISIBLE = WIDTH;
static const uint16_t HEIGHT = 320;
static const GxEPD2::Panel panel = GxEPD2::GDEQ031T10;
static const bool hasColor = false;
static const bool hasPartialUpdate = true;
static const bool usePartialUpdateWindow = true; // set false for better image
static const bool hasFastPartialUpdate = true; // set this false to force full refresh always
static const bool useFastFullUpdate = true;
// set false for extended (low) temperature range, 1015000us vs 3082001us
static const uint16_t power_on_time = 50; // ms, e.g. 45000us
static const uint16_t power_off_time = 50; // ms, e.g. 45000us
static const uint16_t full_refresh_time = 1100; // ms, e.g. 1015000us
static const uint16_t partial_refresh_time = 700; // ms, e.g. 650000us
// constructor
GxEPD2_310_GDEQ031T10(int16_t cs, int16_t dc, int16_t rst, int16_t busy);
// methods (virtual)
// Support for Bitmaps (Sprites) to Controller Buffer and to Screen
void clearScreen(uint8_t value = 0xFF); // init controller memory and screen (default white)
void writeScreenBuffer(uint8_t value = 0xFF); // init controller memory (default white)
void writeScreenBufferAgain(uint8_t value = 0xFF); // init previous buffer controller memory (default white)
// write to controller memory, without screen refresh; x and w should be multiple of 8
void writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false,
bool mirror_y = false, bool pgm = false);
void writeImageForFullRefresh(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h,
bool invert = false, bool mirror_y = false, bool pgm = false);
void writeImagePart(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false,
bool pgm = false);
void writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h,
bool invert = false, bool mirror_y = false, bool pgm = false);
void writeImagePart(const uint8_t* black, const uint8_t* color, int16_t x_part, int16_t y_part, int16_t w_bitmap,
int16_t h_bitmap,
int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false,
bool pgm = false);
// for differential update: set current and previous buffers equal (for fast partial update to work correctly)
void writeImageAgain(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false,
bool mirror_y = false, bool pgm = false);
void writeImagePartAgain(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false,
bool pgm = false);
// write sprite of native data to controller memory, without screen refresh; x and w should be multiple of 8
void writeNative(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h,
bool invert = false, bool mirror_y = false, bool pgm = false);
// write to controller memory, with screen refresh; x and w should be multiple of 8
void drawImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false,
bool mirror_y = false, bool pgm = false);
void drawImagePart(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false,
bool pgm = false);
void drawImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h,
bool invert = false, bool mirror_y = false, bool pgm = false);
void drawImagePart(const uint8_t* black, const uint8_t* color, int16_t x_part, int16_t y_part, int16_t w_bitmap,
int16_t h_bitmap,
int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false,
bool pgm = false);
// write sprite of native data to controller memory, with screen refresh; x and w should be multiple of 8
void drawNative(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h,
bool invert = false, bool mirror_y = false, bool pgm = false);
void refresh(bool partial_update_mode = false); // screen refresh from controller memory to full screen
void refresh(int16_t x, int16_t y, int16_t w, int16_t h); // screen refresh from controller memory, partial screen
void powerOff(); // turns off generation of panel driving voltages, avoids screen fading over time
void hibernate();
// turns powerOff() and sets controller to deep sleep for minimum power use, ONLY if wakeable by RST (rst >= 0)
private:
void _writeScreenBuffer(uint8_t command, uint8_t value);
void _writeImage(uint8_t command, const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h,
bool invert = false, bool mirror_y = false, bool pgm = false);
void _writeImagePart(uint8_t command, const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap,
int16_t h_bitmap,
int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false,
bool pgm = false);
void _setPartialRamArea(uint16_t x, uint16_t y, uint16_t w, uint16_t h);
void _PowerOn();
void _PowerOff();
void _InitDisplay();
void _Update_Full();
void _Update_Part();
};

View File

@ -1,161 +0,0 @@
// Display Library for SPI e-paper panels from Dalian Good Display and boards from Waveshare.
// Requires HW SPI and Adafruit_GFX. Caution: the e-paper panels require 3.3V supply AND data lines!
//
// Display Library based on Demo Example from Good Display: https://www.good-display.com/companyfile/32/
//
// Author: Jean-Marc Zingg
//
// Version: see library.properties
//
// Library: https://github.com/ZinggJM/GxEPD2
#include "GxEPD2_EPD.h"
#include <Tactility/hal/spi/Spi.h>
#include <Tactility/kernel/Kernel.h>
constexpr auto* TAG = "GxEPD2";
GxEPD2_EPD::GxEPD2_EPD(int16_t cs, int16_t dc, int16_t rst, int16_t busy, int16_t busy_level, uint32_t busy_timeout,
uint16_t w, uint16_t h, GxEPD2::Panel p, bool c, bool pu, bool fpu) :
WIDTH(w), HEIGHT(h), panel(p), hasColor(c), hasPartialUpdate(pu), hasFastPartialUpdate(fpu),
_cs(cs), _dc(dc), _rst(rst), _busy(busy), _busy_level(busy_level), _busy_timeout(busy_timeout) {
_initial_write = true;
_initial_refresh = true;
_power_is_on = false;
_using_partial_mode = false;
_hibernating = false;
_init_display_done = false;
_busy_callback = 0;
_busy_callback_parameter = 0;
}
void GxEPD2_EPD::init() { init(true, 10, false); }
void GxEPD2_EPD::init(bool initial, uint16_t reset_duration, bool pulldown_rst_mode) {
_initial_write = initial;
_initial_refresh = initial;
_pulldown_rst_mode = pulldown_rst_mode;
_power_is_on = false;
_using_partial_mode = false;
_hibernating = false;
_init_display_done = false;
}
void GxEPD2_EPD::setBusyCallback(void (*busyCallback)(const void*), const void* busy_callback_parameter) {
_busy_callback = busyCallback;
_busy_callback_parameter = busy_callback_parameter;
}
void GxEPD2_EPD::_waitWhileBusy(const char* comment, uint16_t busy_time) {
if (_busy >= 0) {
tt::kernel::delayMillis(1); // add some margin to become active
unsigned long start = tt::kernel::getMicros();
while (1) {
if (digitalRead(_busy) != _busy_level) break;
if (_busy_callback) _busy_callback(_busy_callback_parameter);
else tt::kernel::delayMillis(1);
if (digitalRead(_busy) != _busy_level) break;
if (tt::kernel::getMicros() - start > _busy_timeout) {
TT_LOG_W(TAG, "Busy timeout");
break;
}
vPortYield(); // avoid wdt
}
(void)start;
} else tt::kernel::delayMillis(busy_time);
}
void GxEPD2_EPD::_writeCommand(uint8_t c) {
_pSPIx->beginTransaction(_spi_settings);
if (_dc >= 0) digitalWrite(_dc, LOW);
if (_cs >= 0) digitalWrite(_cs, LOW);
_pSPIx->transfer(c);
if (_cs >= 0) digitalWrite(_cs, HIGH);
if (_dc >= 0) digitalWrite(_dc, HIGH);
_pSPIx->endTransaction();
}
void GxEPD2_EPD::_writeData(uint8_t d) {
_pSPIx->beginTransaction(_spi_settings);
if (_cs >= 0) digitalWrite(_cs, LOW);
_pSPIx->transfer(d);
if (_cs >= 0) digitalWrite(_cs, HIGH);
_pSPIx->endTransaction();
}
void GxEPD2_EPD::_writeData(const uint8_t* data, uint16_t n) {
_pSPIx->beginTransaction(_spi_settings);
if (_cs >= 0) digitalWrite(_cs, LOW);
for (uint16_t i = 0; i < n; i++) { _pSPIx->transfer(*data++); }
if (_cs >= 0) digitalWrite(_cs, HIGH);
_pSPIx->endTransaction();
}
void GxEPD2_EPD::_writeDataPGM(const uint8_t* data, uint16_t n, int16_t fill_with_zeroes) {
_pSPIx->beginTransaction(_spi_settings);
if (_cs >= 0) digitalWrite(_cs, LOW);
for (uint16_t i = 0; i < n; i++) { _pSPIx->transfer(pgm_read_byte(&*data++)); }
while (fill_with_zeroes > 0) {
_pSPIx->transfer(0x00);
fill_with_zeroes--;
}
if (_cs >= 0) digitalWrite(_cs, HIGH);
_pSPIx->endTransaction();
}
void GxEPD2_EPD::_writeDataPGM_sCS(const uint8_t* data, uint16_t n, int16_t fill_with_zeroes) {
_pSPIx->beginTransaction(_spi_settings);
for (uint8_t i = 0; i < n; i++) {
if (_cs >= 0) digitalWrite(_cs, LOW);
_pSPIx->transfer(pgm_read_byte(&*data++));
if (_cs >= 0) digitalWrite(_cs, HIGH);
}
while (fill_with_zeroes > 0) {
if (_cs >= 0) digitalWrite(_cs, LOW);
_pSPIx->transfer(0x00);
fill_with_zeroes--;
if (_cs >= 0) digitalWrite(_cs, HIGH);
}
_pSPIx->endTransaction();
}
void GxEPD2_EPD::_writeCommandData(const uint8_t* pCommandData, uint8_t datalen) {
_pSPIx->beginTransaction(_spi_settings);
if (_dc >= 0) digitalWrite(_dc, LOW);
if (_cs >= 0) digitalWrite(_cs, LOW);
_pSPIx->transfer(*pCommandData++);
if (_dc >= 0) digitalWrite(_dc, HIGH);
for (uint8_t i = 0; i < datalen - 1; i++) // sub the command
{
_pSPIx->transfer(*pCommandData++);
}
if (_cs >= 0) digitalWrite(_cs, HIGH);
_pSPIx->endTransaction();
}
void GxEPD2_EPD::_writeCommandDataPGM(const uint8_t* pCommandData, uint8_t datalen) {
_pSPIx->beginTransaction(_spi_settings);
if (_dc >= 0) digitalWrite(_dc, LOW);
if (_cs >= 0) digitalWrite(_cs, LOW);
_pSPIx->transfer(pgm_read_byte(&*pCommandData++));
if (_dc >= 0) digitalWrite(_dc, HIGH);
for (uint8_t i = 0; i < datalen - 1; i++) // sub the command
{
_pSPIx->transfer(pgm_read_byte(&*pCommandData++));
}
if (_cs >= 0) digitalWrite(_cs, HIGH);
_pSPIx->endTransaction();
}
void GxEPD2_EPD::_startTransfer() {
_pSPIx->beginTransaction(_spi_settings);
if (_cs >= 0) digitalWrite(_cs, LOW);
}
void GxEPD2_EPD::_transfer(uint8_t value) { _pSPIx->transfer(value); }
void GxEPD2_EPD::_endTransfer() {
if (_cs >= 0) digitalWrite(_cs, HIGH);
_pSPIx->endTransaction();
}

View File

@ -1,143 +0,0 @@
// Display Library for SPI e-paper panels from Dalian Good Display and boards from Waveshare.
// Requires HW SPI and Adafruit_GFX. Caution: the e-paper panels require 3.3V supply AND data lines!
//
// Display Library based on Demo Example from Good Display: https://www.good-display.com/companyfile/32/
//
// Author: Jean-Marc Zingg
//
// Version: see library.properties
//
// Library: https://github.com/ZinggJM/GxEPD2
#pragma once
#include "GxEPD2.h"
#include <cstdint>
#pragma GCC diagnostic ignored "-Wunused-parameter"
class GxEPD2_EPD
{
public:
// attributes
const uint16_t WIDTH;
const uint16_t HEIGHT;
const GxEPD2::Panel panel;
const bool hasColor;
const bool hasPartialUpdate;
const bool hasFastPartialUpdate;
// constructor
GxEPD2_EPD(int16_t cs, int16_t dc, int16_t rst, int16_t busy, int16_t busy_level, uint32_t busy_timeout,
uint16_t w, uint16_t h, GxEPD2::Panel p, bool c, bool pu, bool fpu);
virtual void init();
virtual void init(bool initial, uint16_t reset_duration = 10, bool pulldown_rst_mode = false);
// Support for Bitmaps (Sprites) to Controller Buffer and to Screen
virtual void clearScreen(uint8_t value) = 0; // init controller memory and screen (default white)
virtual void writeScreenBuffer(uint8_t value) = 0; // init controller memory (default white)
// write to controller memory, without screen refresh; x and w should be multiple of 8
virtual void writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false,
bool mirror_y = false, bool pgm = false) = 0;
virtual void writeImageForFullRefresh(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h,
bool invert = false, bool mirror_y = false, bool pgm = false)
{
// writeImage is independent from refresh mode for most controllers, exception e.g. SSD1681
writeImage(bitmap, x, y, w, h, invert, mirror_y, pgm);
}
virtual void writeImagePart(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap,
int16_t h_bitmap,
int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false,
bool pgm = false) = 0;
// virtual void writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false) = 0;
// virtual void writeImagePart(const uint8_t* black, const uint8_t* color, int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
// int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false) = 0;
// write sprite of native data to controller memory, without screen refresh; x and w should be multiple of 8
// virtual void writeNative(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false) = 0;
// for differential update: set current and previous buffers equal (for fast partial update to work correctly)
virtual void writeScreenBufferAgain(uint8_t value = 0xFF) // init controller memory (default white)
{
// most controllers with differential update do switch buffers on refresh, can use:
writeScreenBuffer(value);
}
virtual void writeImageAgain(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h,
bool invert = false, bool mirror_y = false, bool pgm = false)
{
// most controllers with differential update do switch buffers on refresh, can use:
writeImage(bitmap, x, y, w, h, invert, mirror_y, pgm);
}
virtual void writeImagePartAgain(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap,
int16_t h_bitmap,
int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false,
bool mirror_y = false, bool pgm = false)
{
// most controllers with differential update do switch buffers on refresh, can use:
writeImagePart(bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
}
// write to controller memory, with screen refresh; x and w should be multiple of 8
// virtual void drawImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false) = 0;
// virtual void drawImagePart(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
// int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false) = 0;
// virtual void drawImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false) = 0;
// virtual void drawImagePart(const uint8_t* black, const uint8_t* color, int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
// int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false) = 0;
// write sprite of native data to controller memory, with screen refresh; x and w should be multiple of 8
// virtual void drawNative(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false) = 0;
// a demo bitmap can use yet another bitmap format, e.g. 7-color bitmap from Good Display for GDEY073D46
virtual void writeDemoBitmap(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h,
int16_t mode = 0, bool mirror_y = false, bool pgm = false)
{
};
virtual void drawDemoBitmap(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h,
int16_t mode = 0, bool mirror_y = false, bool pgm = false)
{
};
virtual void refresh(bool partial_update_mode = false) = 0; // screen refresh from controller memory to full screen
virtual void refresh(int16_t x, int16_t y, int16_t w, int16_t h) = 0;
// screen refresh from controller memory, partial screen
virtual void powerOff() = 0; // turns off generation of panel driving voltages, avoids screen fading over time
virtual void hibernate() = 0;
// turns powerOff() and sets controller to deep sleep for minimum power use, ONLY if wakeable by RST (rst >= 0)
virtual void setPaged()
{
}; // for GxEPD2_154c paged workaround
// register a callback function to be called during _waitWhileBusy continuously.
void setBusyCallback(void (*busyCallback)(const void*), const void* busy_callback_parameter = 0);
static inline uint16_t gx_uint16_min(uint16_t a, uint16_t b)
{
return (a < b ? a : b);
};
static inline uint16_t gx_uint16_max(uint16_t a, uint16_t b)
{
return (a > b ? a : b);
};
protected:
void _waitWhileBusy(const char* comment = 0, uint16_t busy_time = 5000);
void _writeCommand(uint8_t c);
void _writeData(uint8_t d);
void _writeData(const uint8_t* data, uint16_t n);
void _writeDataPGM(const uint8_t* data, uint16_t n, int16_t fill_with_zeroes = 0);
void _writeDataPGM_sCS(const uint8_t* data, uint16_t n, int16_t fill_with_zeroes = 0);
void _writeCommandData(const uint8_t* pCommandData, uint8_t datalen);
void _writeCommandDataPGM(const uint8_t* pCommandData, uint8_t datalen);
void _startTransfer();
void _transfer(uint8_t value);
void _endTransfer();
protected:
int16_t _cs, _dc, _rst, _busy, _busy_level;
uint32_t _busy_timeout;
bool _pulldown_rst_mode;
bool _initial_write, _initial_refresh;
bool _power_is_on, _using_partial_mode, _hibernating;
bool _init_display_done;
void (*_busy_callback)(const void*);
const void* _busy_callback_parameter;
};

View File

@ -0,0 +1,96 @@
// From https://github.com/Xinyuan-LilyGO/T-Deck-Pro/blob/master/examples/test_lvgl/utilities.h
#pragma once
#define BOARD_I2C_ADDR_TOUCH 0x1A // Touch --- CST328
#define BOARD_I2C_ADDR_LTR_553ALS 0x23 // Light sensor --- LTR_553ALS
#define BOARD_I2C_ADDR_GYROSCOPDE 0x28 // Gyroscope --- BHI260AP
#define BOARD_I2C_ADDR_KEYBOARD 0x34 // Keyboard --- TCA8418
#define BOARD_I2C_ADDR_PMU_SY6970 0x6A // PMU --- SY6970
// IIC
#define BOARD_I2C_SDA 13
#define BOARD_I2C_SCL 14
// Keyboard
#define BOARD_KEYBOARD_SCL BOARD_I2C_SCL
#define BOARD_KEYBOARD_SDA BOARD_I2C_SDA
#define BOARD_KEYBOARD_INT 15
#define BOARD_KEYBOARD_LED 42
// Touch
#define BOARD_TOUCH_SCL BOARD_I2C_SCL
#define BOARD_TOUCH_SDA BOARD_I2C_SDA
#define BOARD_TOUCH_INT 12
#define BOARD_TOUCH_RST 45
// LTR-553ALS-WA beam sensor
#define BOARD_ALS_SCL BOARD_I2C_SCL
#define BOARD_ALS_SDA BOARD_I2C_SDA
#define BOARD_ALS_INT 16
// Gyroscope
#define BOARD_GYROSCOPDE_SCL BOARD_I2C_SCL
#define BOARD_GYROSCOPDE_SDA BOARD_I2C_SDA
#define BOARD_GYROSCOPDE_INT 21
#define BOARD_GYROSCOPDE_RST -1
// SPI
#define BOARD_SPI_SCK 36
#define BOARD_SPI_MOSI 33
#define BOARD_SPI_MISO 47
#define BOARD_SPI_HOST SPI2_HOST
// Display
#define BOARD_EPD_SCK BOARD_SPI_SCK
#define BOARD_EPD_MOSI BOARD_SPI_MOSI
#define BOARD_EPD_SPI_HOST BOARD_SPI_HOST
#define BOARD_EPD_DC 35
#define BOARD_EPD_CS 34
#define BOARD_EPD_BUSY 37
// SD card
#define BOARD_SD_CS 48
#define BOARD_SD_SCK BOARD_SPI_SCK
#define BOARD_SD_MOSI BOARD_SPI_MOSI
#define BOARD_SD_MISO BOARD_SPI_MISO
#define BOARD_SD_SPI_HOST BOARD_SPI_HOST
// Lora
#define BOARD_LORA_SCK BOARD_SPI_SCK
#define BOARD_LORA_MOSI BOARD_SPI_MOSI
#define BOARD_LORA_MISO BOARD_SPI_MISO
#define BOARD_LORA_SPI_HOST BOARD_SPI_HOST
#define BOARD_LORA_CS 3
#define BOARD_LORA_BUSY 6
#define BOARD_LORA_RST 4
#define BOARD_LORA_INT 5
// GPS
#define BOARD_GPS_RXD 44
#define BOARD_GPS_TXD 43
#define BOARD_GPS_PPS 1
// A7682E Modem
#define BOARD_A7682E_RI 7
#define BOARD_A7682E_ITR 8
#define BOARD_A7682E_RST 9
#define BOARD_A7682E_RXD 10
#define BOARD_A7682E_TXD 11
#define BOARD_A7682E_PWRKEY 40
// Boot pin
#define BOARD_BOOT_PIN 0
// Motor pin
#define BOARD_MOTOR_PIN 2
// EN
#define BOARD_GPS_EN 39 // enable GPS module
#define BOARD_1V8_EN 38 // enable gyroscope module
#define BOARD_6609_EN 41 // enable 7682 module
#define BOARD_LORA_EN 46 // enable LORA module
// Mic
#define BOARD_MIC_DATA 17
#define BOARD_MIC_CLOCK 18

View File

@ -1,45 +1,127 @@
#include "TdeckDisplay.h"
#include "TdeckDisplayConstants.h"
#include "esp_lcd_panel_gdeq.h"
#include "TdeckConstants.h"
#include <Gt911Touch.h>
#include <PwmBacklight.h>
#include <St7789Display.h>
#include <Tactility/Log.h>
#include <driver/spi_master.h>
#include <esp_lvgl_port.h>
#define TAG "tdeck_display"
#define TAG "EpaperDisplay"
static std::shared_ptr<tt::hal::touch::TouchDevice> createTouch() {
// Note for future changes: Reset pin is 48 and interrupt pin is 47
auto configuration = std::make_unique<Gt911Touch::Configuration>(
I2C_NUM_0,
240,
320,
true,
true,
false
);
bool TdeckDisplay::start() {
TT_LOG_I(TAG, "Starting");
return std::make_shared<Gt911Touch>(std::move(configuration));
constexpr esp_lcd_panel_io_spi_config_t panel_io_config = {
.cs_gpio_num = BOARD_EPD_CS,
.dc_gpio_num = BOARD_EPD_DC,
.spi_mode = 0,
.pclk_hz = 4000000,
.trans_queue_depth = 10,
.on_color_trans_done = nullptr,
.user_ctx = nullptr,
.lcd_cmd_bits = 8,
.lcd_param_bits = 8,
.cs_ena_pretrans = 0,
.cs_ena_posttrans = 0,
.flags = {
.dc_high_on_cmd = 0,
.dc_low_on_data = 0,
.dc_low_on_param = 0,
.octal_mode = 0,
.quad_mode = 0,
.sio_mode = 0,
.lsb_first = 0,
.cs_high_active = 0
}
};
if (esp_lcd_new_panel_io_spi(SPI2_HOST, &panel_io_config, &ioHandle) != ESP_OK) {
TT_LOG_E(TAG, "Failed to create panel");
return false;
}
const esp_lcd_panel_dev_config_t panel_config = {
.reset_gpio_num = GPIO_NUM_NC,
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, // Doesn't matter
.data_endian = LCD_RGB_DATA_ENDIAN_LITTLE,
.bits_per_pixel = 1,
.flags = {
.reset_active_high = false
},
.vendor_config = nullptr
};
if (esp_lcd_new_panel_gdeq031t10(ioHandle, &panel_config, &panelHandle) != ESP_OK) {
TT_LOG_E(TAG, "Failed to create panel");
return false;
}
if (esp_lcd_panel_reset(panelHandle) != ESP_OK) {
TT_LOG_E(TAG, "Failed to reset panel");
return false;
}
if (esp_lcd_panel_init(panelHandle) != ESP_OK) {
TT_LOG_E(TAG, "Failed to init panel");
return false;
}
if (esp_lcd_panel_disp_on_off(panelHandle, true) != ESP_OK) {
TT_LOG_E(TAG, "Failed to turn display on");
return false;
}
uint32_t buffer_size = 240 * 320; // Note: Pixel count, not bytes!
const lvgl_port_display_cfg_t disp_cfg = {
.io_handle = ioHandle,
.panel_handle = panelHandle,
.control_handle = nullptr,
.buffer_size = buffer_size,
.double_buffer = false,
.trans_size = 0,
.hres = 240,
.vres = 320,
.monochrome = true,
.rotation = {
.swap_xy = false,
.mirror_x = false,
.mirror_y = false,
},
.color_format = LV_COLOR_FORMAT_I1,
.flags = {
.buff_dma = false,
.buff_spiram = false,
.sw_rotate = false,
.swap_bytes = false,
.full_refresh = false,
.direct_mode = false
}
};
displayHandle = lvgl_port_add_disp(&disp_cfg);
TT_LOG_I(TAG, "Finished");
return displayHandle != nullptr;
}
bool TdeckDisplay::stop() {
assert(displayHandle != nullptr);
lvgl_port_remove_disp(displayHandle);
if (esp_lcd_panel_del(panelHandle) != ESP_OK) {
return false;
}
if (esp_lcd_panel_io_del(ioHandle) != ESP_OK) {
return false;
}
displayHandle = nullptr;
return true;
}
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
auto touch = createTouch();
auto configuration = std::make_unique<St7789Display::Configuration>(
TDECK_LCD_SPI_HOST,
TDECK_LCD_PIN_CS,
TDECK_LCD_PIN_DC,
320,
240,
touch,
true,
true,
false,
true
);
configuration->backlightDutyFunction = driver::pwmbacklight::setBacklightDuty;
return std::make_shared<St7789Display>(std::move(configuration));
return std::make_shared<TdeckDisplay>();
}

View File

@ -1,40 +1,31 @@
#pragma once
#include "Tactility/hal/display/DisplayDevice.h"
#include <esp_lcd_panel_io.h>
#include <esp_lcd_types.h>
#include <lvgl.h>
class TdeckDisplay : public tt::hal::display::DisplayDevice {
private:
class TdeckDisplay final : public tt::hal::display::DisplayDevice {
esp_lcd_panel_io_handle_t ioHandle = nullptr;
esp_lcd_panel_handle_t panelHandle = nullptr;
lv_display_t* displayHandle = nullptr;
bool poweredOn = false;
public:
std::string getName() const final { return "ST7789"; }
std::string getDescription() const final { return "SPI display"; }
explicit TdeckDisplay() {}
bool start() override;
std::string getName() const { return "Epaper"; }
std::string getDescription() const { return "Epaper display"; }
bool stop() override;
bool start();
void setPowerOn(bool turnOn) override;
bool isPoweredOn() const override { return poweredOn; };
bool supportsPowerControl() const override { return true; }
bool stop();
std::shared_ptr<tt::hal::touch::TouchDevice> _Nullable createTouch() override;
std::shared_ptr<tt::hal::touch::TouchDevice> _Nullable createTouch() { return nullptr; }
void setBacklightDuty(uint8_t backlightDuty) override;
bool supportsBacklightDuty() const override { return true; }
void setGammaCurve(uint8_t index) override;
uint8_t getGammaCurveCount() const override { return 4; };
lv_display_t* _Nullable getLvglDisplay() const override { return displayHandle; }
lv_display_t* _Nullable getLvglDisplay() const { return displayHandle; }
};
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay();

View File

@ -1,8 +0,0 @@
#pragma once
#define TDECK_LCD_SPI_HOST SPI2_HOST
#define TDECK_LCD_PIN_CS GPIO_NUM_12
#define TDECK_LCD_PIN_DC GPIO_NUM_11 // RS
#define TDECK_LCD_HORIZONTAL_RESOLUTION 320
#define TDECK_LCD_VERTICAL_RESOLUTION 240
#define TDECK_LCD_SPI_TRANSFER_HEIGHT (TDECK_LCD_VERTICAL_RESOLUTION / 10)

View File

@ -0,0 +1,107 @@
/*
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
/* SSD1681 panel commands */
#define GDEQ_CMD_POWER_ON 0x04
#define GDEQ_CMD_POWER_OFF 0x02
// // --- reset
// #define SSD1681_CMD_SWRST 0x12
// // --- Driver output control
// #define SSD1681_CMD_OUTPUT_CTRL 0x01
// #define SSD1681_PARAM_OUTPUT_CTRL ((uint8_t[]) {0xc7, 0x00, 0x00})
// // --- Data Entry Sequence Setting
// #define SSD1681_CMD_DATA_ENTRY_MODE 0x11
// // A [1:0] = ID[1:0], A[2] = AM
// // the address counter is updated in the X direction
// // 000 - Y decrement, X decrement
// #define SSD1681_PARAM_DATA_ENTRY_MODE_0 0x00
// // 001 Y decrement, X increment
// #define SSD1681_PARAM_DATA_ENTRY_MODE_1 0x01
// // 010 - Y increment, X decrement
// #define SSD1681_PARAM_DATA_ENTRY_MODE_2 0x02
// // 011 - Y increment, X increment
// // AM = 1, the address counter is updated in the Y direction
// #define SSD1681_PARAM_DATA_ENTRY_MODE_3 0x03
// // 100 - Y decrement, X decrement
// #define SSD1681_PARAM_DATA_ENTRY_MODE_4 0x04
// // 101 Y decrement, X increment
// #define SSD1681_PARAM_DATA_ENTRY_MODE_5 0x05
// // 110 - Y increment, X decrement
// #define SSD1681_PARAM_DATA_ENTRY_MODE_6 0x06
// // 111 - Y increment, X increment
// #define SSD1681_PARAM_DATA_ENTRY_MODE_7 0x07
// // --- Set RAMX Start/End Position
// #define SSD1681_CMD_SET_RAMX_START_END_POS 0x44
// // --- Set RAMY Start/End Position
// #define SSD1681_CMD_SET_RAMY_START_END_POS 0x45
// // --- Border Waveform Control
// #define SSD1681_CMD_SET_BORDER_WAVEFORM 0x3c
// // Select VBD as GS Transition,
// // Fix Level Setting for VBD VSS,
// // GS Transition control Follow LUT
// // GS Transition setting for VBD LUT1
// #define SSD1681_PARAM_BORDER_WAVEFORM 0x01
// // --- Temperature Sensor Control
// #define SSD1681_CMD_SET_TEMP_SENSOR 0x18
// // Select to use internal sensor, 0x48 for external
// #define SSD1681_PARAM_TEMP_SENSOR 0x80
// // --- Display Update Control 2
// #define SSD1681_CMD_SET_DISP_UPDATE_CTRL 0x22
// // Load temperature value
// // Load LUT with DISPLAY mode 1
// // Disable clock signal
// #define SSD1681_PARAM_DISP_UPDATE_MODE_1 0xb1
// // Display with DISPLAY Mode 2
// #define SSD1681_PARAM_DISP_WITH_MODE_2 0xcf
// // Enable clock signal
// // Enable Analog
// // Display with DISPLAY Mode 2
// // Disable Analog
// // Disable OSC
// #define SSD1681_PARAM_DISP_UPDATE_MODE_2 0xcf
// // --- Active display update sequence
// #define SSD1681_CMD_ACTIVE_DISP_UPDATE_SEQ 0x20
// // ---
// #define SSD1681_CMD_DISP_UPDATE_CTRL 0x21
// #define SSD1681_PARAM_COLOR_BW_INVERSE_BIT (1<<3)
// #define SSD1681_PARAM_COLOR_RW_INVERSE_BIT (1<<7)
// // --- Init settings for the RAM address
// #define SSD1681_CMD_SET_INIT_X_ADDR_COUNTER 0x4e
// #define SSD1681_CMD_SET_INIT_Y_ADDR_COUNTER 0x4f
// // --- Options for LUT
// // Write LUT Register
// // Write LUT register from MCU interface
// // [153 bytes], which contains the content of
// // VS[nX-LUTm], TP[nX], RP[n], SR[nXY],
// // and FR[n]
// #define SSD1681_CMD_SET_LUT_REG 0x32
// // 153 bytes of data
// // End Option
// #define SSD1681_CMD_SET_END_OPTION 0x3f
// #define SSD1681_PARAM_END_OPTION_KEEP 0x07
// // Gate driving voltage
// #define SSD1681_CMD_SET_GATE_DRIVING_VOLTAGE 0x03
// // 20V
// #define SSD1681_PARAM_GATE_DRIVING_VOLTAGE 0x17
// // Source driving voltage
// #define SSD1681_CMD_SET_SRC_DRIVING_VOLTAGE 0x04
// #define SSD1681_PARAM_SRC_DRIVING_VOLTAGE ((uint8_t[]) {0x41, 0x00, 0x32})
// // Write VCOM Register
// #define SSD1681_CMD_SET_VCOM_REG 0x2c
// // -0.8V
// #define SSD1681_PARAM_VCOM_VOLTAGE 0x20
// // --- Commands for VRAM
// #define SSD1681_CMD_WRITE_BLACK_VRAM 0x24
// #define SSD1681_CMD_WRITE_RED_VRAM 0x26
//
// #define SSD1681_CMD_SLEEP_CTRL 0x10
// #define SSD1681_PARAM_SLEEP_MODE_1 0x01

View File

@ -0,0 +1,266 @@
/**
* This code is based on the esp_lcd_ssd1681 driver from https://github.com/espressif/esp-bsp/tree/master/components/lcd/esp_lcd_ssd1681
* The esp_lcd_ssd1681 driver is copyrighted by "2023 Espressif Systems (Shanghai) CO LTD" and was distributed under Apache License:
* https://github.com/espressif/esp-bsp/blob/master/components/lcd/esp_lcd_ssd1681/license.txt
*
* The gdeq driver is licensed under Tactility's GPL v2 license.
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "sdkconfig.h"
#if CONFIG_LCD_ENABLE_DEBUG_LOG
// The local log level must be defined before including esp_log.h
// Set the maximum log level for this source file
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#endif
#include "esp_log.h"
#include "esp_check.h"
#include "esp_attr.h"
#include "driver/gpio.h"
#include "esp_lcd_panel_gdeq.h"
#include "TdeckConstants.h"
#include "esp_lcd_panel_interface.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_gdeq_commands.h"
static const char* TAG = "lcd_panel.epaper";
typedef struct {
esp_lcd_panel_t base;
esp_lcd_panel_io_handle_t io;
// --- Normal configurations
// Configurations from epaper_ssd1681_conf
bool full_refresh;
bool fast_refresh;
int busy_gpio_num;
uint8_t* _framebuffer;
} epaper_panel_t;
// --- Utility functions
static esp_err_t panel_epaper_wait_busy(esp_lcd_panel_t* panel);
// --- Used to implement esp_lcd_panel_interface
static esp_err_t epaper_panel_del(esp_lcd_panel_t* panel);
static esp_err_t epaper_panel_reset(esp_lcd_panel_t* panel);
static esp_err_t epaper_panel_init(esp_lcd_panel_t* panel);
static esp_err_t epaper_panel_draw_bitmap(esp_lcd_panel_t* panel, int x_start, int y_start, int x_end, int y_end, const void* color_data);
static esp_err_t epaper_panel_invert_color(esp_lcd_panel_t* panel, bool invert_color_data);
static esp_err_t epaper_panel_mirror(esp_lcd_panel_t* panel, bool mirror_x, bool mirror_y);
static esp_err_t epaper_panel_swap_xy(esp_lcd_panel_t* panel, bool swap_axes);
static esp_err_t epaper_panel_set_gap(esp_lcd_panel_t* panel, int x_gap, int y_gap);
static esp_err_t epaper_panel_disp_on_off(esp_lcd_panel_t* panel, bool on_off);
static esp_err_t panel_epaper_wait_busy(esp_lcd_panel_t* panel) {
// TODO: Fix this
// epaper_panel_t* epaper_panel = __containerof(panel, epaper_panel_t, base);
// Wait until busy pin is high (busy means low)
// while (gpio_get_level(epaper_panel->busy_gpio_num) != 0) { vTaskDelay(pdMS_TO_TICKS(15)); }
return ESP_OK;
}
static esp_err_t epaper_set_area(esp_lcd_panel_io_handle_t io, uint32_t start_x, uint32_t start_y, uint32_t end_x, uint32_t end_y) {
uint32_t x = start_x;
uint32_t y = start_y;
uint32_t w = end_x - start_x;
uint32_t h = end_y - start_y;
uint16_t xe = (x + w - 1) | 0x0007; // byte boundary inclusive (last byte)
uint16_t ye = y + h - 1;
x &= 0xFFF8; // byte boundary
uint8_t data[7] = {x, xe, y / 256, y % 256, ye / 256, ye % 256, 0x01};
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0x90, data, sizeof(data)), TAG, "set_area err");
return ESP_OK;
}
esp_err_t epaper_panel_refresh_screen(esp_lcd_panel_t* panel, bool fast, bool full) {
ESP_RETURN_ON_FALSE(panel, ESP_ERR_INVALID_ARG, TAG, "panel handler is NULL");
epaper_panel_t* epaper_panel = __containerof(panel, epaper_panel_t, base);
if (fast) {
const int ccset_cmd = 0xe0;
const uint8_t tsfix_data[1] = {0x02};
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(epaper_panel->io, ccset_cmd, tsfix_data, sizeof(tsfix_data)), TAG, "cascade setting err");
const int ttset_cmd = 0xe5;
uint8_t ttset_data[1];
if (full) {
ttset_data[0] = 0x5a;
} else {
ttset_data[0] = 0x79; // 121
}
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(epaper_panel->io, ttset_cmd, ttset_data, sizeof(ttset_data)), TAG, "temperature setting err");
}
const int refresh_cmd = 0x50;
uint8_t refresh_data[1];
if (full) {
refresh_data[0] = 0x97;
} else {
refresh_data[0] = 0xd7;
}
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(epaper_panel->io, refresh_cmd, refresh_data, sizeof(refresh_data)), TAG, "refresh err");
// TODO: Remove?
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(epaper_panel->io, GDEQ_CMD_POWER_ON, NULL, 0), TAG, "power on err");
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(epaper_panel->io, 0x12, NULL, 0), TAG, "refresh(2) err");
return ESP_OK;
}
esp_err_t esp_lcd_new_panel_gdeq031t10(
const esp_lcd_panel_io_handle_t io,
const esp_lcd_panel_dev_config_t* const panel_dev_config,
esp_lcd_panel_handle_t* const ret_panel
) {
#if CONFIG_LCD_ENABLE_DEBUG_LOG
esp_log_level_set(TAG, ESP_LOG_DEBUG);
#endif
ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "1 or more args is NULL");
esp_err_t ret = ESP_OK;
// --- Allocate epaper_panel memory on HEAP
epaper_panel_t* epaper_panel = malloc(sizeof(epaper_panel_t));
ESP_GOTO_ON_FALSE(epaper_panel, ESP_ERR_NO_MEM, err, TAG, "no mem for epaper panel");
// --- Construct panel & implement interface
// defaults
epaper_panel->_framebuffer = NULL;
epaper_panel->full_refresh = true;
// configurations
epaper_panel->io = io;
// functions
epaper_panel->base.del = epaper_panel_del;
epaper_panel->base.reset = epaper_panel_reset;
epaper_panel->base.init = epaper_panel_init;
epaper_panel->base.draw_bitmap = epaper_panel_draw_bitmap;
epaper_panel->base.invert_color = epaper_panel_invert_color;
epaper_panel->base.set_gap = epaper_panel_set_gap;
epaper_panel->base.mirror = epaper_panel_mirror;
epaper_panel->base.swap_xy = epaper_panel_swap_xy;
epaper_panel->base.disp_on_off = epaper_panel_disp_on_off;
*ret_panel = &(epaper_panel->base);
// --- Init framebuffer
int buffer_size = 240 * 320 / 8;
epaper_panel->_framebuffer = heap_caps_malloc(buffer_size, MALLOC_CAP_DMA);
memset(epaper_panel->_framebuffer, 0xff, buffer_size);
ESP_RETURN_ON_FALSE(epaper_panel->_framebuffer, ESP_ERR_NO_MEM, TAG, "epaper_panel_draw_bitmap allocating buffer memory err");
// --- Init GPIO
// init BUSY GPIO
ESP_LOGD(TAG, "new epaper panel @%p", epaper_panel);
return ret;
err:
if (epaper_panel) {
if (panel_dev_config->reset_gpio_num >= 0) { gpio_reset_pin(panel_dev_config->reset_gpio_num); }
free(epaper_panel);
}
return ret;
}
static esp_err_t epaper_panel_del(esp_lcd_panel_t* panel) {
epaper_panel_t* epaper_panel = __containerof(panel, epaper_panel_t, base);
// --- Free allocated RAM
if (epaper_panel->_framebuffer) {
// Should not free if buffer is not allocated by driver
free(epaper_panel->_framebuffer);
}
ESP_LOGD(TAG, "del ssd1681 epaper panel @%p", epaper_panel);
free(epaper_panel);
return ESP_OK;
}
static esp_err_t epaper_panel_reset(esp_lcd_panel_t* panel) {
epaper_panel_t* epaper_panel = __containerof(panel, epaper_panel_t, base);
esp_lcd_panel_io_handle_t io = epaper_panel->io;
const uint8_t soft_reset_data[2] = {0x1e, 0x0d};
const int panel_setting_cmd = 0x00;
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, panel_setting_cmd, soft_reset_data, sizeof(soft_reset_data)), TAG, "io tx param failed");
// TODO: Remove?
vTaskDelay(pdMS_TO_TICKS(1));
panel_epaper_wait_busy(panel);
return ESP_OK;
}
static esp_err_t epaper_panel_init(esp_lcd_panel_t* panel) {
epaper_panel_t* epaper_panel = __containerof(panel, epaper_panel_t, base);
esp_lcd_panel_io_handle_t io = epaper_panel->io;
epaper_panel->busy_gpio_num = BOARD_EPD_BUSY; // TODO: make it properly configurable
gpio_set_direction(epaper_panel->busy_gpio_num, GPIO_MODE_INPUT);
const uint8_t soft_reset_data[2] = {0x1e, 0x0d};
const int panel_setting_cmd = 0x00;
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, panel_setting_cmd, soft_reset_data, sizeof(soft_reset_data)), TAG, "io tx param failed");
vTaskDelay(pdMS_TO_TICKS(1));
const uint8_t bwotp_data[2] = {0x1f, 0x0d}; // KW: 3f, KWR: 2F, BWROTP: 0f, BWOTP: 1f
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, panel_setting_cmd, bwotp_data, sizeof(bwotp_data)), TAG, "io tx param failed");
panel_epaper_wait_busy(panel);
int buffer_size = 240 * 320 / 8;
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_color(epaper_panel->io, 0x10, epaper_panel->_framebuffer, buffer_size), TAG, "tx buffer 0x10 failed");
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_color(epaper_panel->io, 0x13, epaper_panel->_framebuffer, buffer_size), TAG, "tx buffer 0x13 failed");
epaper_panel_refresh_screen(panel, false, true);
return ESP_OK;
}
static esp_err_t epaper_panel_draw_bitmap(esp_lcd_panel_t* panel, int x_start, int y_start, int x_end, int y_end, const void* color_data) {
epaper_panel_t* epaper_panel = __containerof(panel, epaper_panel_t, base);
// --- Assert & check configuration
ESP_RETURN_ON_FALSE(color_data, ESP_ERR_INVALID_ARG, TAG, "bitmap is null");
ESP_RETURN_ON_FALSE((x_start < x_end) && (y_start < y_end), ESP_ERR_INVALID_ARG, TAG, "start position must be smaller than end position");
// --- Calculate coordinates & sizes
int len_x = abs(x_start - x_end);
int len_y = abs(y_start - y_end);
int buffer_size = len_x * len_y / 8;
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(epaper_panel->io, 0x91, NULL, 0), TAG, "tx partial in failed");
epaper_set_area(epaper_panel->io, x_start, y_start, x_end, y_end);
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_color(epaper_panel->io, 0x13, color_data, buffer_size), TAG, "tx color failed");
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(epaper_panel->io, 0x92, NULL, 0), TAG, "tx partial in failed");
epaper_panel_refresh_screen(panel, false, false);
return ESP_OK;
}
static esp_err_t epaper_panel_invert_color(esp_lcd_panel_t* panel, bool invert_color_data) {
// Not implemented
return ESP_FAIL;
}
static esp_err_t epaper_panel_mirror(esp_lcd_panel_t* panel, bool mirror_x, bool mirror_y) {
// Not implemented
return ESP_FAIL;
}
static esp_err_t epaper_panel_swap_xy(esp_lcd_panel_t* panel, bool swap_axes) {
// Not implemented
return ESP_FAIL;
}
static esp_err_t epaper_panel_set_gap(esp_lcd_panel_t* panel, int x_gap, int y_gap) {
// Not implemented
return ESP_FAIL;
}
static esp_err_t epaper_panel_disp_on_off(esp_lcd_panel_t* panel, bool on_off) {
epaper_panel_t* epaper_panel = __containerof(panel, epaper_panel_t, base);
if (on_off) { ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(epaper_panel->io, GDEQ_CMD_POWER_ON, NULL, 0), TAG, "power on err"); } else { ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(epaper_panel->io, GDEQ_CMD_POWER_OFF, NULL, 0), TAG, "power off err"); }
panel_epaper_wait_busy(panel);
return ESP_OK;
}

View File

@ -0,0 +1,46 @@
/**
* This code is based on the esp_lcd_ssd1681 driver from https://github.com/espressif/esp-bsp/tree/master/components/lcd/esp_lcd_ssd1681
* The esp_lcd_ssd1681 driver is copyrighted by "2023 Espressif Systems (Shanghai) CO LTD" and was distributed under Apache License:
* https://github.com/espressif/esp-bsp/blob/master/components/lcd/esp_lcd_ssd1681/license.txt
*
* The gdeq driver is licensed under Tactility's GPL v2 license.
*/
#pragma once
#include <stdbool.h>
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_interface.h"
#include "esp_lcd_panel_io.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Create panel for display model gdeq031t10
* @param[in] io panel IO handle
* @param[in] panel_dev_config general panel device configuration
* @param[out] ret_panel Returned LCD panel handle
* @return
* - ESP_ERR_INVALID_ARG if parameter is invalid
* - ESP_ERR_NO_MEM if out of memory
* - ESP_OK on success
*/
esp_err_t esp_lcd_new_panel_gdeq031t10(esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *ret_panel);
/**
* @brief Refresh the display
*
* @param[in] panel LCD panel handle
* @param[in] fast whether to refresh fast or regularly
* @param[in] full whether to do a full refresh (slower) or a partial one
*
* @return
* - ESP_ERR_INVALID_ARG if parameter is invalid
* - ESP_OK on success
*/
esp_err_t epaper_panel_refresh_screen(esp_lcd_panel_t *panel, bool fast, bool full);
#ifdef __cplusplus
}
#endif

View File

@ -39,7 +39,7 @@ CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
CONFIG_FLASHMODE_QIO=y
# Hardware: SPI RAM
CONFIG_ESP32S3_SPIRAM_SUPPORT=y
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_MODE_OCT=n
CONFIG_SPIRAM_SPEED_120M=y
CONFIG_SPIRAM_USE_MALLOC=y
CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y