mirror of
https://github.com/esphome/esphome.git
synced 2025-02-14 17:08:22 +00:00
Add partial update of GDEW029T5 e-paper display (#8162)
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
This commit is contained in:
parent
84836f15db
commit
abdf215d3a
@ -120,7 +120,7 @@ MODELS = {
|
||||
"2.13in-ttgo-b74": ("a", WaveshareEPaperTypeAModel.TTGO_EPAPER_2_13_IN_B74),
|
||||
"2.90in": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN),
|
||||
"2.90inv2": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN_V2),
|
||||
"gdew029t5": ("b", GDEW029T5),
|
||||
"gdew029t5": ("c", GDEW029T5),
|
||||
"2.70in": ("b", WaveshareEPaper2P7In),
|
||||
"2.70in-b": ("b", WaveshareEPaper2P7InB),
|
||||
"2.70in-bv2": ("b", WaveshareEPaper2P7InBV2),
|
||||
|
@ -1596,15 +1596,108 @@ void WaveshareEPaper2P9InV2R2::set_full_update_every(uint32_t full_update_every)
|
||||
// Datasheet:
|
||||
// - https://v4.cecdn.yun300.cn/100001_1909185148/SSD1680.pdf
|
||||
// - https://github.com/adafruit/Adafruit_EPD/blob/master/src/panels/ThinkInk_290_Grayscale4_T5.h
|
||||
// - https://github.com/ZinggJM/GxEPD2/blob/master/src/epd/GxEPD2_290_T5.cpp
|
||||
// - http://www.e-paper-display.com/GDEW029T5%20V3.1%20Specification5c22.pdf?
|
||||
// ========================================================
|
||||
|
||||
void GDEW029T5::initialize() {
|
||||
// from https://www.waveshare.com/w/upload/b/bb/2.9inch-e-paper-b-specification.pdf, page 37
|
||||
// EPD hardware init start
|
||||
this->reset_();
|
||||
// full screen update LUT
|
||||
static const uint8_t LUT_20_VCOMDC_29_5[] = {
|
||||
0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x60, 0x28, 0x28, 0x00, 0x00, 0x01, 0x00, 0x14, 0x00,
|
||||
0x00, 0x00, 0x01, 0x00, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const uint8_t LUT_21_WW_29_5[] = {
|
||||
0x40, 0x08, 0x00, 0x00, 0x00, 0x02, 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, 0x40, 0x14,
|
||||
0x00, 0x00, 0x00, 0x01, 0xA0, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const uint8_t LUT_22_BW_29_5[] = {
|
||||
0x40, 0x08, 0x00, 0x00, 0x00, 0x02, 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, 0x40, 0x14,
|
||||
0x00, 0x00, 0x00, 0x01, 0xA0, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const uint8_t LUT_23_WB_29_5[] = {
|
||||
0x80, 0x08, 0x00, 0x00, 0x00, 0x02, 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, 0x80, 0x14,
|
||||
0x00, 0x00, 0x00, 0x01, 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const uint8_t LUT_24_BB_29_5[] = {
|
||||
0x80, 0x08, 0x00, 0x00, 0x00, 0x02, 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, 0x80, 0x14,
|
||||
0x00, 0x00, 0x00, 0x01, 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
// partial screen update LUT
|
||||
static const uint8_t LUT_20_VCOMDC_PARTIAL_29_5[] = {
|
||||
0x00, 0x20, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const uint8_t LUT_21_WW_PARTIAL_29_5[] = {
|
||||
0x00, 0x20, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const uint8_t LUT_22_BW_PARTIAL_29_5[] = {
|
||||
0x80, 0x20, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const uint8_t LUT_23_WB_PARTIAL_29_5[] = {
|
||||
0x40, 0x20, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const uint8_t LUT_24_BB_PARTIAL_29_5[] = {
|
||||
0x00, 0x20, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
void GDEW029T5::power_on_() {
|
||||
if (!this->power_is_on_) {
|
||||
this->command(0x04);
|
||||
this->wait_until_idle_();
|
||||
}
|
||||
this->power_is_on_ = true;
|
||||
}
|
||||
|
||||
void GDEW029T5::power_off_() {
|
||||
this->command(0x02);
|
||||
this->wait_until_idle_();
|
||||
this->power_is_on_ = false;
|
||||
}
|
||||
|
||||
void GDEW029T5::deep_sleep() {
|
||||
this->power_off_();
|
||||
if (this->deep_sleep_between_updates_) {
|
||||
this->command(0x07); // deep sleep
|
||||
this->data(0xA5); // check code
|
||||
ESP_LOGD(TAG, "go to deep sleep");
|
||||
this->is_deep_sleep_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void GDEW029T5::init_display_() {
|
||||
// from https://github.com/ZinggJM/GxEPD2/blob/master/src/epd/GxEPD2_290_T5.cpp
|
||||
|
||||
// Hardware Initialization
|
||||
if (this->deep_sleep_between_updates_ && this->is_deep_sleep_) {
|
||||
ESP_LOGI(TAG, "wake up from deep sleep");
|
||||
this->reset_();
|
||||
this->is_deep_sleep_ = false;
|
||||
}
|
||||
|
||||
// COMMAND POWER SETTINGS
|
||||
this->command(0x00);
|
||||
this->command(0x01);
|
||||
this->data(0x03);
|
||||
this->data(0x00);
|
||||
this->data(0x2b);
|
||||
@ -1617,40 +1710,122 @@ void GDEW029T5::initialize() {
|
||||
this->data(0x17);
|
||||
this->data(0x17);
|
||||
|
||||
// COMMAND POWER ON
|
||||
this->command(0x04);
|
||||
this->wait_until_idle_();
|
||||
|
||||
// Not sure what this does but it's in the Adafruit EPD library
|
||||
this->command(0xFF);
|
||||
this->wait_until_idle_();
|
||||
this->power_on_();
|
||||
|
||||
// COMMAND PANEL SETTING
|
||||
this->command(0x00);
|
||||
// 128x296 resolution: 10
|
||||
// LUT from OTP: 0
|
||||
// LUT from register: 1
|
||||
// B/W mode (doesn't work): 1
|
||||
// scan-up: 1
|
||||
// shift-right: 1
|
||||
// booster ON: 1
|
||||
// no soft reset: 1
|
||||
this->data(0b10011111);
|
||||
this->data(0b10111111);
|
||||
this->data(0x0d); // VCOM to 0V fast
|
||||
this->command(0x30); // PLL setting
|
||||
this->data(0x3a); // 3a 100HZ 29 150Hz 39 200HZ 31 171HZ
|
||||
this->command(0x61); // resolution setting
|
||||
this->data(this->get_width_internal());
|
||||
this->data(this->get_height_internal() >> 8);
|
||||
this->data(this->get_height_internal() & 0xFF);
|
||||
|
||||
// COMMAND RESOLUTION SETTING
|
||||
// set to 128x296 by COMMAND PANEL SETTING
|
||||
|
||||
// COMMAND VCOM AND DATA INTERVAL SETTING
|
||||
// use defaults for white border and ESPHome image polarity
|
||||
|
||||
// EPD hardware init end
|
||||
ESP_LOGD(TAG, "panel setting done");
|
||||
}
|
||||
|
||||
void GDEW029T5::initialize() {
|
||||
// from https://www.waveshare.com/w/upload/b/bb/2.9inch-e-paper-b-specification.pdf, page 37
|
||||
if (this->reset_pin_ != nullptr)
|
||||
this->deep_sleep_between_updates_ = true;
|
||||
|
||||
// old buffer for partial update
|
||||
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
|
||||
this->old_buffer_ = allocator.allocate(this->get_buffer_length_());
|
||||
if (this->old_buffer_ == nullptr) {
|
||||
ESP_LOGE(TAG, "Could not allocate old buffer for display!");
|
||||
return;
|
||||
}
|
||||
for (size_t i = 0; i < this->get_buffer_length_(); i++) {
|
||||
this->old_buffer_[i] = 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
// initialize for full(normal) update
|
||||
void GDEW029T5::init_full_() {
|
||||
this->init_display_();
|
||||
this->command(0x82); // vcom_DC setting
|
||||
this->data(0x08);
|
||||
this->command(0X50); // VCOM AND DATA INTERVAL SETTING
|
||||
this->data(0x97); // WBmode:VBDF 17|D7 VBDW 97 VBDB 57 WBRmode:VBDF F7 VBDW 77 VBDB 37 VBDR B7
|
||||
this->command(0x20);
|
||||
this->write_lut_(LUT_20_VCOMDC_29_5, sizeof(LUT_20_VCOMDC_29_5));
|
||||
this->command(0x21);
|
||||
this->write_lut_(LUT_21_WW_29_5, sizeof(LUT_21_WW_29_5));
|
||||
this->command(0x22);
|
||||
this->write_lut_(LUT_22_BW_29_5, sizeof(LUT_22_BW_29_5));
|
||||
this->command(0x23);
|
||||
this->write_lut_(LUT_23_WB_29_5, sizeof(LUT_23_WB_29_5));
|
||||
this->command(0x24);
|
||||
this->write_lut_(LUT_24_BB_29_5, sizeof(LUT_24_BB_29_5));
|
||||
ESP_LOGD(TAG, "initialized full update");
|
||||
}
|
||||
|
||||
// initialzie for partial update
|
||||
void GDEW029T5::init_partial_() {
|
||||
this->init_display_();
|
||||
this->command(0x82); // vcom_DC setting
|
||||
this->data(0x08);
|
||||
this->command(0X50); // VCOM AND DATA INTERVAL SETTING
|
||||
this->data(0x17); // WBmode:VBDF 17|D7 VBDW 97 VBDB 57 WBRmode:VBDF F7 VBDW 77 VBDB 37 VBDR B7
|
||||
this->command(0x20);
|
||||
this->write_lut_(LUT_20_VCOMDC_PARTIAL_29_5, sizeof(LUT_20_VCOMDC_PARTIAL_29_5));
|
||||
this->command(0x21);
|
||||
this->write_lut_(LUT_21_WW_PARTIAL_29_5, sizeof(LUT_21_WW_PARTIAL_29_5));
|
||||
this->command(0x22);
|
||||
this->write_lut_(LUT_22_BW_PARTIAL_29_5, sizeof(LUT_22_BW_PARTIAL_29_5));
|
||||
this->command(0x23);
|
||||
this->write_lut_(LUT_23_WB_PARTIAL_29_5, sizeof(LUT_23_WB_PARTIAL_29_5));
|
||||
this->command(0x24);
|
||||
this->write_lut_(LUT_24_BB_PARTIAL_29_5, sizeof(LUT_24_BB_PARTIAL_29_5));
|
||||
ESP_LOGD(TAG, "initialized partial update");
|
||||
}
|
||||
|
||||
void HOT GDEW029T5::display() {
|
||||
bool full_update = this->at_update_ == 0;
|
||||
if (full_update) {
|
||||
this->init_full_();
|
||||
} else {
|
||||
this->init_partial_();
|
||||
this->command(0x91); // partial in
|
||||
// set partial window
|
||||
this->command(0x90);
|
||||
// this->data(0);
|
||||
this->data(0);
|
||||
// this->data(0);
|
||||
this->data((this->get_width_internal() - 1) % 256);
|
||||
this->data(0);
|
||||
this->data(0);
|
||||
this->data(((this->get_height_internal() - 1)) / 256);
|
||||
this->data(((this->get_height_internal() - 1)) % 256);
|
||||
this->data(0x01);
|
||||
}
|
||||
// input old buffer data
|
||||
this->command(0x10);
|
||||
delay(2);
|
||||
this->start_data_();
|
||||
for (size_t i = 0; i < this->get_buffer_length_(); i++) {
|
||||
this->write_byte(this->old_buffer_[i]);
|
||||
}
|
||||
this->end_data_();
|
||||
delay(2);
|
||||
|
||||
// COMMAND DATA START TRANSMISSION 2 (B/W only)
|
||||
this->command(0x13);
|
||||
delay(2);
|
||||
this->start_data_();
|
||||
for (size_t i = 0; i < this->get_buffer_length_(); i++) {
|
||||
this->write_byte(this->buffer_[i]);
|
||||
this->old_buffer_[i] = this->buffer_[i];
|
||||
}
|
||||
this->end_data_();
|
||||
delay(2);
|
||||
@ -1660,10 +1835,28 @@ void HOT GDEW029T5::display() {
|
||||
delay(2);
|
||||
this->wait_until_idle_();
|
||||
|
||||
// COMMAND POWER OFF
|
||||
// NOTE: power off < deep sleep
|
||||
this->command(0x02);
|
||||
if (full_update) {
|
||||
ESP_LOGD(TAG, "full update done");
|
||||
} else {
|
||||
this->command(0x92); // partial out
|
||||
ESP_LOGD(TAG, "partial update done");
|
||||
}
|
||||
|
||||
this->at_update_ = (this->at_update_ + 1) % this->full_update_every_;
|
||||
// COMMAND deep sleep
|
||||
this->deep_sleep();
|
||||
}
|
||||
|
||||
void GDEW029T5::write_lut_(const uint8_t *lut, const uint8_t size) {
|
||||
// COMMAND WRITE LUT REGISTER
|
||||
this->start_data_();
|
||||
for (uint8_t i = 0; i < size; i++)
|
||||
this->write_byte(lut[i]);
|
||||
this->end_data_();
|
||||
}
|
||||
|
||||
void GDEW029T5::set_full_update_every(uint32_t full_update_every) { this->full_update_every_ = full_update_every; }
|
||||
|
||||
int GDEW029T5::get_width_internal() { return 128; }
|
||||
int GDEW029T5::get_height_internal() { return 296; }
|
||||
void GDEW029T5::dump_config() {
|
||||
@ -1672,6 +1865,7 @@ void GDEW029T5::dump_config() {
|
||||
LOG_PIN(" Reset Pin: ", this->reset_pin_);
|
||||
LOG_PIN(" DC Pin: ", this->dc_pin_);
|
||||
LOG_PIN(" Busy Pin: ", this->busy_pin_);
|
||||
ESP_LOGCONFIG(TAG, " Full Update Every: %" PRIu32, this->full_update_every_);
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
|
@ -254,16 +254,27 @@ class GDEW029T5 : public WaveshareEPaper {
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
void deep_sleep() override {
|
||||
// COMMAND DEEP SLEEP
|
||||
this->command(0x07);
|
||||
this->data(0xA5); // check byte
|
||||
}
|
||||
void deep_sleep() override;
|
||||
void set_full_update_every(uint32_t full_update_every);
|
||||
|
||||
protected:
|
||||
void init_display_();
|
||||
void init_full_();
|
||||
void init_partial_();
|
||||
void write_lut_(const uint8_t *lut, uint8_t size);
|
||||
void power_off_();
|
||||
void power_on_();
|
||||
int get_width_internal() override;
|
||||
|
||||
int get_height_internal() override;
|
||||
|
||||
private:
|
||||
uint32_t full_update_every_{30};
|
||||
uint32_t at_update_{0};
|
||||
bool deep_sleep_between_updates_{false};
|
||||
bool power_is_on_{false};
|
||||
bool is_deep_sleep_{false};
|
||||
uint8_t *old_buffer_{nullptr};
|
||||
};
|
||||
|
||||
class WaveshareEPaper2P7InV2 : public WaveshareEPaper {
|
||||
|
@ -459,6 +459,7 @@ display:
|
||||
reset_pin:
|
||||
allow_other_uses: true
|
||||
number: ${reset_pin}
|
||||
full_update_every: 30
|
||||
lambda: |-
|
||||
it.rectangle(0, 0, it.get_width(), it.get_height());
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user