From 659286f73887323e533dba0aa7a2669955bea55d Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sun, 27 Jun 2021 11:17:53 -0700 Subject: [PATCH] WIP new adafruit eink display lib at least starts --- platformio.ini | 1 + src/graphics/EInkDisplay.cpp | 152 ---------------------------------- src/graphics/EInkDisplay.h | 48 ----------- src/graphics/EInkDisplay2.cpp | 50 ++++++----- src/graphics/Screen.h | 2 +- variants/t-echo/variant.h | 2 +- 6 files changed, 31 insertions(+), 224 deletions(-) delete mode 100644 src/graphics/EInkDisplay.cpp delete mode 100644 src/graphics/EInkDisplay.h diff --git a/platformio.ini b/platformio.ini index 036f0574b..8fafeb6c4 100644 --- a/platformio.ini +++ b/platformio.ini @@ -362,6 +362,7 @@ src_filter = ${nrf52_base.src_filter} +<../variants/t-echo> lib_deps = ${nrf52840_base.lib_deps} https://github.com/geeksville/GxEPD2.git + adafruit/Adafruit BusIO ;upload_protocol = fs ; First prototype eink/nrf52840/sx1262 device (removed from build because didn't ship in quantity) diff --git a/src/graphics/EInkDisplay.cpp b/src/graphics/EInkDisplay.cpp deleted file mode 100644 index 44d38f22d..000000000 --- a/src/graphics/EInkDisplay.cpp +++ /dev/null @@ -1,152 +0,0 @@ -#include "configuration.h" - -#ifdef HAS_EINK -#include "EInkDisplay.h" -#include "SPILock.h" -#include "epd1in54.h" // Screen specific library -#include -#include // Graphics library and Sprite class - -Epd ePaper; // Create an instance ePaper - -TFT_eSPI glc = TFT_eSPI(); // Invoke the graphics library class -TFT_eSprite frame = TFT_eSprite(&glc); // Invoke the Sprite class for the image frame buffer -uint8_t *framePtr; // Pointer for the black frame buffer - -#define COLORED 0 -#define UNCOLORED 1 - -#define INK COLORED // Black ink -#define PAPER UNCOLORED // 'paper' background colour - -//------------------------------------------------------------------------------------ -// Update display - different displays have different function names in the default -// Waveshare libraries :-( -//------------------------------------------------------------------------------------ -#if defined(EPD1IN54B_H) || defined(EPD1IN54C_H) || defined(EPD2IN13B_H) || defined(EPD2IN7B_H) || defined(EPD2IN9B_H) || \ - defined(EPD4IN2_H) -void updateDisplay(uint8_t *blackFrame = blackFramePtr, uint8_t *redFrame = redFramePtr) -{ - ePaper.DisplayFrame(blackFrame, redFrame); // Update 3 colour display -#else -void updateDisplay(uint8_t *blackFrame = framePtr) -{ -#if defined(EPD2IN7_H) || defined(EPD4IN2_H) - ePaper.DisplayFrame(blackFrame); // Update 2 color display - -#elif defined(EPD1IN54_H) || defined(EPD2IN13_H) || defined(EPD2IN9_H) - ePaper.SetFrameMemory(blackFrame); // Update 2 colour display - ePaper.DisplayFrame(); -#else -#error "Selected ePaper library is not supported" -#endif -#endif -} - -EInkDisplay::EInkDisplay(uint8_t address, int sda, int scl) -{ - setGeometry(GEOMETRY_RAWMODE, EPD_WIDTH, EPD_HEIGHT); - // setGeometry(GEOMETRY_RAWMODE, 128, 64); // old resolution - // setGeometry(GEOMETRY_128_64); // We originally used this because I wasn't sure if rawmode worked - it does -} - -// FIXME quick hack to limit drawing to a very slow rate -uint32_t lastDrawMsec; - -/** - * Force a display update if we haven't drawn within the specified msecLimit - */ -bool EInkDisplay::forceDisplay(uint32_t msecLimit) -{ - // No need to grab this lock because we are on our own SPI bus - // concurrency::LockGuard g(spiLock); - - uint32_t now = millis(); - uint32_t sinceLast = now - lastDrawMsec; - - if (framePtr && (sinceLast > msecLimit || lastDrawMsec == 0)) { - lastDrawMsec = now; - - // FIXME - only draw bits have changed (use backbuf similar to the other displays) - // tft.drawBitmap(0, 0, buffer, 128, 64, TFT_YELLOW, TFT_BLACK); - for (uint8_t y = 0; y < displayHeight; y++) { - for (uint8_t x = 0; x < displayWidth; x++) { - - // get src pixel in the page based ordering the OLED lib uses FIXME, super inefficent - auto b = buffer[x + (y / 8) * displayWidth]; - auto isset = b & (1 << (y & 7)); - frame.drawPixel(x, y, isset ? INK : PAPER); - } - } - - ePaper.Reset(); // wake the screen from sleep - - DEBUG_MSG("Updating eink... "); - updateDisplay(); // Send image to display and refresh - DEBUG_MSG("done\n"); - - // Put screen to sleep to save power - ePaper.Sleep(); - return true; - } else { - // DEBUG_MSG("Skipping eink display\n"); - return false; - } -} - -// Write the buffer to the display memory -void EInkDisplay::display(void) -{ - // We don't allow regular 'dumb' display() calls to draw on eink until we've shown - // at least one forceDisplay() keyframe. This prevents flashing when we should the critical - // bootscreen (that we want to look nice) - if (lastDrawMsec) - forceDisplay(slowUpdateMsec); // Show the first screen a few seconds after boot, then slower -} - -// Send a command to the display (low level function) -void EInkDisplay::sendCommand(uint8_t com) -{ - (void)com; - // Drop all commands to device (we just update the buffer) -} - -// Connect to the display -bool EInkDisplay::connect() -{ - DEBUG_MSG("Doing EInk init\n"); - -#ifdef PIN_EINK_PWR_ON - digitalWrite(PIN_EINK_PWR_ON, HIGH); // If we need to assert a pin to power external peripherals - pinMode(PIN_EINK_PWR_ON, OUTPUT); -#endif - -#ifdef PIN_EINK_EN - // backlight power, HIGH is backlight on, LOW is off - digitalWrite(PIN_EINK_EN, LOW); - pinMode(PIN_EINK_EN, OUTPUT); -#endif - - // Initialise the ePaper library - // FIXME - figure out how to use lut_partial_update - if (ePaper.Init(lut_full_update) != 0) { - DEBUG_MSG("ePaper init failed\n"); - return false; - } else { - frame.setColorDepth(1); // Must set the bits per pixel to 1 for ePaper displays - // Set bit depth BEFORE creating Sprite, default is 16! - - // Create a frame buffer in RAM of defined size and save the pointer to it - // RAM needed is about (EPD_WIDTH * EPD_HEIGHT)/8 , ~5000 bytes for 200 x 200 pixels - // Note: always create the Sprite before setting the Sprite rotation - framePtr = (uint8_t *)frame.createSprite(EPD_WIDTH, EPD_HEIGHT); - - frame.fillSprite(PAPER); // Fill frame with white - /* frame.drawLine(0, 0, frame.width() - 1, frame.height() - 1, INK); - frame.drawLine(0, frame.height() - 1, frame.width() - 1, 0, INK); - updateDisplay(); */ - return true; - } -} - -#endif diff --git a/src/graphics/EInkDisplay.h b/src/graphics/EInkDisplay.h deleted file mode 100644 index 18b900305..000000000 --- a/src/graphics/EInkDisplay.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include - -/** - * An adapter class that allows using the TFT_eSPI library as if it was an OLEDDisplay implementation. - * - * Remaining TODO: - * optimize display() to only draw changed pixels (see other OLED subclasses for examples) - * implement displayOn/displayOff to turn off the TFT device (and backlight) - * Use the fast NRF52 SPI API rather than the slow standard arduino version - * - * turn radio back on - currently with both on spi bus is fucked? or are we leaving chip select asserted? - */ -class EInkDisplay : public OLEDDisplay -{ - /// How often should we update the display - /// thereafter we do once per 5 minutes - uint32_t slowUpdateMsec = 5 * 60 * 1000; - - public: - /* constructor - FIXME - the parameters are not used, just a temporary hack to keep working like the old displays - */ - EInkDisplay(uint8_t address, int sda, int scl); - - // Write the buffer to the display memory (for eink we only do this occasionally) - virtual void display(void); - - /** - * Force a display update if we haven't drawn within the specified msecLimit - * - * @return true if we did draw the screen - */ - bool forceDisplay(uint32_t msecLimit = 1000); - - protected: - // the header size of the buffer used, e.g. for the SPI command header - virtual int getBufferOffset(void) { return 0; } - - // Send a command to the display (low level function) - virtual void sendCommand(uint8_t com); - - // Connect to the display - virtual bool connect(); -}; - - diff --git a/src/graphics/EInkDisplay2.cpp b/src/graphics/EInkDisplay2.cpp index 51ac3d702..7080e222c 100644 --- a/src/graphics/EInkDisplay2.cpp +++ b/src/graphics/EInkDisplay2.cpp @@ -1,22 +1,22 @@ #include "configuration.h" -#ifdef HAS_EINK2 -#include "EInkDisplay.h" +#ifdef HAS_EINK +#include "EInkDisplay2.h" #include "SPILock.h" #include +#include "GxEPD2_BW.h" -#define COLORED 0 -#define UNCOLORED 1 +#define COLORED GxEPD_BLACK +#define UNCOLORED GxEPD_WHITE -#define INK COLORED // Black ink -#define PAPER UNCOLORED // 'paper' background colour -#define EPD_WIDTH 200 // FIXME -#define EPD_HEIGHT 200 +#define TECHO_DISPLAY_MODEL GxEPD2_154_D67 + +GxEPD2_BW *adafruitDisplay; EInkDisplay::EInkDisplay(uint8_t address, int sda, int scl) { - setGeometry(GEOMETRY_RAWMODE, EPD_WIDTH, EPD_HEIGHT); + setGeometry(GEOMETRY_RAWMODE, TECHO_DISPLAY_MODEL::WIDTH, TECHO_DISPLAY_MODEL::HEIGHT); // setGeometry(GEOMETRY_RAWMODE, 128, 64); // old resolution // setGeometry(GEOMETRY_128_64); // We originally used this because I wasn't sure if rawmode worked - it does } @@ -35,7 +35,7 @@ bool EInkDisplay::forceDisplay(uint32_t msecLimit) uint32_t now = millis(); uint32_t sinceLast = now - lastDrawMsec; - if (sinceLast > msecLimit || lastDrawMsec == 0) { + if (false && adafruitDisplay && (sinceLast > msecLimit || lastDrawMsec == 0)) { lastDrawMsec = now; // FIXME - only draw bits have changed (use backbuf similar to the other displays) @@ -46,15 +46,15 @@ bool EInkDisplay::forceDisplay(uint32_t msecLimit) // get src pixel in the page based ordering the OLED lib uses FIXME, super inefficent auto b = buffer[x + (y / 8) * displayWidth]; auto isset = b & (1 << (y & 7)); - // frame.drawPixel(x, y, isset ? INK : PAPER); + adafruitDisplay->drawPixel(x, y, isset ? COLORED : UNCOLORED); } } DEBUG_MSG("Updating eink... "); // ePaper.Reset(); // wake the screen from sleep - // updateDisplay(); // Send image to display and refresh - // Put screen to sleep to save power - // ePaper.Sleep(); + adafruitDisplay->display(false); // FIXME, use partial update mode + // Put screen to sleep to save power (possibly not necessary because we already did poweroff inside of display) + // adafruitDisplay->hibernate(); DEBUG_MSG("done\n"); return true; @@ -97,14 +97,20 @@ bool EInkDisplay::connect() pinMode(PIN_EINK_EN, OUTPUT); #endif - // Initialise the ePaper library - // FIXME - figure out how to use lut_partial_update - if (false) { - DEBUG_MSG("ePaper init failed\n"); - return false; - } else { - return true; - } + auto lowLevel = new TECHO_DISPLAY_MODEL(PIN_EINK_CS, + PIN_EINK_DC, + PIN_EINK_RES, + PIN_EINK_BUSY); + + adafruitDisplay = new GxEPD2_BW(*lowLevel); + adafruitDisplay->init(); + adafruitDisplay->setRotation(1); + adafruitDisplay->setFullWindow(); + adafruitDisplay->fillScreen(UNCOLORED); + adafruitDisplay->drawCircle(100, 100, 20, COLORED); + adafruitDisplay->display(false); + + return true; } #endif diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index 65e7b0a12..554e9a8e7 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -30,7 +30,7 @@ class Screen #include #endif -#include "EInkDisplay.h" +#include "EInkDisplay2.h" #include "TFTDisplay.h" #include "TypedQueue.h" #include "commands.h" diff --git a/variants/t-echo/variant.h b/variants/t-echo/variant.h index 9c38c99f4..3e2317f73 100644 --- a/variants/t-echo/variant.h +++ b/variants/t-echo/variant.h @@ -213,7 +213,7 @@ External serial flash WP25R1635FZUIL0 // FIXME - I think this is actually just the board power enable - it enables power to the CPU also #define PIN_EINK_PWR_ON (0 + 12) -#define HAS_EINK2 +#define HAS_EINK // No screen wipes on eink #define SCREEN_TRANSITION_MSECS 0