From 402fa489343f7a5300f067683be0931b2422befb Mon Sep 17 00:00:00 2001 From: martinberlin Date: Wed, 10 Mar 2021 18:51:15 +0100 Subject: [PATCH] #35 gdew075HD new epaper --- components/CalEPD/CMakeLists.txt | 1 + components/CalEPD/include/gdew075HD.h | 72 +++++++ components/CalEPD/models/gdew075HD.cpp | 268 +++++++++++++++++++++++++ 3 files changed, 341 insertions(+) create mode 100644 components/CalEPD/include/gdew075HD.h create mode 100644 components/CalEPD/models/gdew075HD.cpp diff --git a/components/CalEPD/CMakeLists.txt b/components/CalEPD/CMakeLists.txt index 55a7885..441c867 100644 --- a/components/CalEPD/CMakeLists.txt +++ b/components/CalEPD/CMakeLists.txt @@ -1,6 +1,7 @@ # Add only the display srcs you are going to use if you want to keep compiled srcs to a minimum: set(srcs "models/wave12i48.cpp" + "models/gdew075HD.cpp" "models/gdew075T7.cpp" "models/gdew075T8.cpp" "models/gdew0583t7.cpp" diff --git a/components/CalEPD/include/gdew075HD.h b/components/CalEPD/include/gdew075HD.h new file mode 100644 index 0000000..8ed84f5 --- /dev/null +++ b/components/CalEPD/include/gdew075HD.h @@ -0,0 +1,72 @@ +// 7.5 HD 880*528 b/w Controller: ?? +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "esp_system.h" +#include +#include +#include "sdkconfig.h" +#include "esp_log.h" +#include +#include +#include +#include +#include "soc/rtc_wdt.h" +#include + +#define GDEW075HD_WIDTH 880 +#define GDEW075HD_HEIGHT 528 + +// EPD comment: Pixel number expressed in bytes; this is neither the buffer size nor the size of the buffer in the controller +// We are not adding page support so here this is our Buffer size +#define GDEW075HD_BUFFER_SIZE (uint32_t(GDEW075HD_WIDTH) * uint32_t(GDEW075HD_HEIGHT) / 8) +// 8 pix of this color in a buffer byte: +#define GDEW075HD_8PIX_BLACK 0x00 +#define GDEW075HD_8PIX_WHITE 0xFF + +class Gdew075HD : public Epd +{ + public: + + Gdew075HD(EpdSpi& IO); + uint8_t colors_supported = 1; + + void drawPixel(int16_t x, int16_t y, uint16_t color); // Override GFX own drawPixel method + + // EPD tests + void init(bool debug = false); + + // Partial update of rectangle from buffer to screen, does not power off + void updateWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h, bool using_rotation); + void fillScreen(uint16_t color); + void update(); + + private: + EpdSpi& IO; + + uint8_t _buffer[GDEW075HD_BUFFER_SIZE]; + bool _using_partial_mode = false; + bool _initial = true; + + uint16_t _setPartialRamArea(uint16_t x, uint16_t y, uint16_t xe, uint16_t ye); + void _wakeUp(); + void _sleep(); + void _waitBusy(const char* message); + void _rotate(uint16_t& x, uint16_t& y, uint16_t& w, uint16_t& h); + + // Command & data structs + // LUT tables for this display are filled with zeroes at the end with writeLuts() + static const epd_init_42 lut_20_LUTC_partial; + static const epd_init_42 lut_21_LUTWW_partial; + static const epd_init_42 lut_22_LUTKW_partial; + static const epd_init_42 lut_23_LUTWK_partial; + static const epd_init_42 lut_24_LUTKK_partial; + static const epd_init_42 lut_25_LUTBD_partial; + + static const epd_power_4 epd_wakeup_power; + static const epd_init_1 epd_panel_setting_full; + static const epd_init_1 epd_panel_setting_partial; + static const epd_init_1 epd_pll; + static const epd_init_4 epd_resolution; +}; \ No newline at end of file diff --git a/components/CalEPD/models/gdew075HD.cpp b/components/CalEPD/models/gdew075HD.cpp new file mode 100644 index 0000000..c4fdf51 --- /dev/null +++ b/components/CalEPD/models/gdew075HD.cpp @@ -0,0 +1,268 @@ +#include "gdew075HD.h" +#include +#include +#include "esp_log.h" +#include "freertos/task.h" + +// Partial Update Delay, may have an influence on degradation +#define GDEW075HD_PU_DELAY 100 + +//Place data into DRAM. Constant data gets placed into DROM by default, which is not accessible by DMA. + +// 0x07 (2nd) VGH=20V,VGL=-20V +// 0x3f (1st) VDH= 15V +// 0x3f (2nd) VDH=-15V +DRAM_ATTR const epd_power_4 Gdew075HD::epd_wakeup_power = { + 0x01, {0x07, 0x07, 0x3f, 0x3f}, 4}; + +DRAM_ATTR const epd_init_1 Gdew075HD::epd_panel_setting_full = { + 0x00, {0x1f}, 1}; + +DRAM_ATTR const epd_init_1 Gdew075HD::epd_panel_setting_partial = { + 0x00, {0x3f}, 1}; + +DRAM_ATTR const epd_init_4 Gdew075HD::epd_resolution = { + 0x61, {GDEW075HD_WIDTH / 256, //source 800 + GDEW075HD_WIDTH % 256, + GDEW075HD_HEIGHT / 256, //gate 480 + GDEW075HD_HEIGHT % 256}, + 4}; + +// Constructor +Gdew075HD::Gdew075HD(EpdSpi &dio) : Adafruit_GFX(GDEW075HD_WIDTH, GDEW075HD_HEIGHT), + Epd(GDEW075HD_WIDTH, GDEW075HD_HEIGHT), IO(dio) +{ + printf("Gdew075HD() constructor injects IO and extends Adafruit_GFX(%d,%d) Pix Buffer[%d]\n", + GDEW075HD_WIDTH, GDEW075HD_HEIGHT, GDEW075HD_BUFFER_SIZE); + printf("\nAvailable heap after Epd bootstrap:%d\n", xPortGetFreeHeapSize()); +} + +//Initialize the display +void Gdew075HD::init(bool debug) +{ + debug_enabled = debug; + if (debug_enabled) + printf("Gdew075HD::init(debug:%d)\n", debug); + //Initialize SPI at 4MHz frequency. true for debug + IO.init(4, false); + fillScreen(EPD_WHITE); +} + +void Gdew075HD::fillScreen(uint16_t color) +{ + uint8_t data = (color == EPD_BLACK) ? GDEW075HD_8PIX_BLACK : GDEW075HD_8PIX_WHITE; + for (uint16_t x = 0; x < sizeof(_buffer); x++) + { + _buffer[x] = data; + } +} + +void Gdew075HD::_wakeUp() +{ + IO.reset(200); + + IO.cmd(0x12); + + _waitBusy("Reset_12"); + + IO.cmd(0x46); // Auto Write Red RAM + IO.data(0xf7); + _waitBusy("Write Red RAM"); + + IO.cmd(0x47); // Auto Write B/W RAM + IO.data(0xf7); + _waitBusy("Write B/W RAM"); + + IO.cmd(0x0C); // Soft start setting + IO.data(0xAE); + IO.data(0xC7); + IO.data(0xC3); + IO.data(0xC0); + IO.data(0x40); + + IO.cmd(0x01); // Set MUX as 527 + IO.data(0xAF); + IO.data(0x02); + IO.data(0x01); //0x01 + + IO.cmd(0x11); // Data entry mode + IO.data(0x01); + + IO.cmd(0x44); + IO.data(0x00); // RAM x address start at 0 + IO.data(0x00); + IO.data(0x6F); + IO.data(0x03); + + IO.cmd(0x45); + IO.data(0xAF); + IO.data(0x02); + IO.data(0x00); + IO.data(0x00); + + IO.cmd(0x3C); // VBD + IO.data(0x01); // LUT1, for white + + IO.cmd(0x18); // Temperature Sensor Control + IO.data(0X80); + + IO.cmd(0x22); + IO.data(0XB1); //Load Temperature and waveform setting. + IO.cmd(0x20); + _waitBusy("Load Temperature"); + + IO.cmd(0x4E); // set RAM x address count to 0; + IO.data(0x00); + IO.data(0x00); + + IO.cmd(0x4F); + IO.data(0x00); + IO.data(0x00); + + IO.cmd(0x4F); + IO.data(0x00); + IO.data(0x00); + + + IO.cmd(0x24);//BLOCK +} + +void Gdew075HD::update() +{ + uint64_t startTime = esp_timer_get_time(); + _using_partial_mode = false; + _wakeUp(); + + IO.cmd(0x24); //Black RAM + printf("Sending a %d bytes buffer via SPI\n", sizeof(_buffer)); + + // v2 SPI optimizing. Check: https://github.com/martinberlin/cale-idf/wiki/About-SPI-optimization + uint16_t i = 0; + uint8_t xLineBytes = GDEW075HD_WIDTH / 8; + uint8_t x1buf[xLineBytes]; + for (uint16_t y = 1; y <= GDEW075HD_HEIGHT; y++) + { + for (uint16_t x = 1; x <= xLineBytes; x++) + { + uint8_t data = i < sizeof(_buffer) ? _buffer[i] : 0x00; + x1buf[x - 1] = data; + if (x == xLineBytes) + { // Flush the X line buffer to SPI + IO.data(x1buf, sizeof(x1buf)); + } + ++i; + } + } + + /* + for (uint16_t i = 1; i <= GDEW075HD_BUFFER_SIZE; i++) + { + IO.data(_buffer[i]); + if (i<50) { + printf("%x ", _buffer[i]); + } + } */ + + uint64_t endTime = esp_timer_get_time(); + IO.cmd(0x22); // Show + IO.data(0xF7); // 0xF7 + IO.cmd(0x20); + + vTaskDelay(200 / portTICK_PERIOD_MS); + _waitBusy("Update"); + + uint64_t updateTime = esp_timer_get_time(); + printf("\n\nSTATS (ms)\n%llu _wakeUp settings+send Buffer\n%llu update \n%llu total time in millis\n", + (endTime - startTime) / 1000, (updateTime - endTime) / 1000, (updateTime - startTime) / 1000); + + // Additional 2 seconds wait before sleeping since in low temperatures full update takes longer + vTaskDelay(2000 / portTICK_PERIOD_MS); + + _sleep(); +} + +void Gdew075HD::updateWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h, bool using_rotation) +{ + printf("updateWindow: Not implemented\n"); +} + +void Gdew075HD::_waitBusy(const char *message) +{ + if (debug_enabled) + { + ESP_LOGI(TAG, "_waitBusy for %s", message); + } + int64_t time_since_boot = esp_timer_get_time(); + + while (1) + { + if (gpio_get_level((gpio_num_t)CONFIG_EINK_BUSY) == 0) { + break; + } + vTaskDelay(1); + if (esp_timer_get_time() - time_since_boot > 2000000) + { + if (debug_enabled) + ESP_LOGI(TAG, "Busy Timeout"); + break; + } + } +} + +void Gdew075HD::_sleep() +{ + IO.cmd(0x10); + IO.data(0x01); +} + +void Gdew075HD::_rotate(uint16_t &x, uint16_t &y, uint16_t &w, uint16_t &h) +{ + switch (getRotation()) + { + case 1: + swap(x, y); + swap(w, h); + x = GDEW075HD_WIDTH - x - w - 1; + break; + case 2: + x = GDEW075HD_WIDTH - x - w - 1; + y = GDEW075HD_HEIGHT - y - h - 1; + break; + case 3: + swap(x, y); + swap(w, h); + y = GDEW075HD_HEIGHT - y - h - 1; + break; + } +} + +void Gdew075HD::drawPixel(int16_t x, int16_t y, uint16_t color) +{ + if ((x < 0) || (x >= width()) || (y < 0) || (y >= height())) + return; + switch (getRotation()) + { + case 1: + swap(x, y); + x = GDEW075HD_WIDTH - x - 1; + break; + case 2: + x = GDEW075HD_WIDTH - x - 1; + y = GDEW075HD_HEIGHT - y - 1; + break; + case 3: + swap(x, y); + y = GDEW075HD_HEIGHT - y - 1; + break; + } + uint16_t i = x / 8 + y * GDEW075HD_WIDTH / 8; + + if (color) + { + _buffer[i] = (_buffer[i] | (1 << (7 - x % 8))); + } + else + { + _buffer[i] = (_buffer[i] & (0xFF ^ (1 << (7 - x % 8)))); + } +}