mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-02-18 19:03:16 +00:00
Compare commits
3 Commits
e1fa8b946a
...
6c3c4f906f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6c3c4f906f | ||
|
|
84bb29d089 | ||
|
|
3cb0784e01 |
@ -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,
|
||||
|
||||
@ -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
|
||||
};
|
||||
};
|
||||
@ -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
|
||||
}
|
||||
@ -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();
|
||||
};
|
||||
@ -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();
|
||||
}
|
||||
@ -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;
|
||||
};
|
||||
96
Boards/LilygoTdeckPro/Source/hal/TdeckConstants.h
Normal file
96
Boards/LilygoTdeckPro/Source/hal/TdeckConstants.h
Normal 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
|
||||
@ -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>();
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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)
|
||||
107
Boards/LilygoTdeckPro/Source/hal/esp_lcd_gdeq_commands.h
Normal file
107
Boards/LilygoTdeckPro/Source/hal/esp_lcd_gdeq_commands.h
Normal 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
|
||||
266
Boards/LilygoTdeckPro/Source/hal/esp_lcd_panel_gdeq.c
Normal file
266
Boards/LilygoTdeckPro/Source/hal/esp_lcd_panel_gdeq.c
Normal 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;
|
||||
}
|
||||
46
Boards/LilygoTdeckPro/Source/hal/esp_lcd_panel_gdeq.h
Normal file
46
Boards/LilygoTdeckPro/Source/hal/esp_lcd_panel_gdeq.h
Normal 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
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user