diff --git a/platformio.ini b/platformio.ini index a5d69d1f5..aeb399ea6 100644 --- a/platformio.ini +++ b/platformio.ini @@ -234,9 +234,11 @@ board = eink # add our variants files to the include and src paths # define build flags for the TFT_eSPI library build_flags = ${nrf52_base.build_flags} -Ivariants/eink + -DBUSY_PIN=3 -DRST_PIN=2 -DDC_PIN=28 -DCS_PIN=30 src_filter = ${nrf52_base.src_filter} +<../variants/eink> lib_deps = ${arduino_base.lib_deps} + https://github.com/geeksville/EPD_Libraries.git TFT_eSPI ; The https://github.com/BigCorvus/SX1262-LoRa-BLE-Relay board by @BigCorvus diff --git a/src/gps/NMEAGPS.cpp b/src/gps/NMEAGPS.cpp index 991a7726b..32c75634d 100644 --- a/src/gps/NMEAGPS.cpp +++ b/src/gps/NMEAGPS.cpp @@ -14,10 +14,16 @@ bool NMEAGPS::setup() { #ifdef PIN_GPS_WAKE // FIXME, support using this pin to control GPS sleep - // digitalWrite(PIN_GPS_WAKE, LOW); + // digitalWrite(PIN_GPS_WAKE, HIGH); // pinMode(PIN_GPS_WAKE, OUTPUT); #endif +#ifdef PIN_GPS_PPS + // pulse per second + // FIXME - move into shared GPS code + pinMode(PIN_GPS_PPS, INPUT); +#endif + return true; } diff --git a/src/graphics/EInkDisplay.cpp b/src/graphics/EInkDisplay.cpp new file mode 100644 index 000000000..2156f5919 --- /dev/null +++ b/src/graphics/EInkDisplay.cpp @@ -0,0 +1,125 @@ +#include "configuration.h" + +#ifdef HAS_EINK +#include "EInkDisplay.h" +#include "SPILock.h" +#include "epd1in54.h" // Screen specific library +#include "graphics/configs.h" +#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_128_64); // FIXME - currently we lie and claim 128x64 because I'm not yet sure other resolutions will + // work ie GEOMETRY_RAWMODE +} + +// FIXME quick hack to limit drawing to a very slow rate +uint32_t lastDrawMsec; + +// Write the buffer to the display memory +void EInkDisplay::display(void) +{ + concurrency::LockGuard g(spiLock); + + uint32_t now = millis(); + uint32_t sinceLast = now - lastDrawMsec; + + if (framePtr && (sinceLast > 30 * 1000 || 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 < SCREEN_HEIGHT; y++) { + for (uint8_t x = 0; x < SCREEN_WIDTH; x++) { + + // get src pixel in the page based ordering the OLED lib uses FIXME, super inefficent + auto b = buffer[x + (y / 8) * SCREEN_WIDTH]; + auto isset = b & (1 << (y & 7)); + frame.drawPixel(x, y, isset ? INK : PAPER); + } + } + + updateDisplay(); // Send image to display and refresh + + // Put screen to sleep to save power (if wanted) + // ePaper.Sleep(); + } +} + +// 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_EN + digitalWrite(PIN_EINK_EN, HIGH); + 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 new file mode 100644 index 000000000..f12ad6882 --- /dev/null +++ b/src/graphics/EInkDisplay.h @@ -0,0 +1,35 @@ +#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 +{ + 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 + virtual void display(void); + + 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/Screen.h b/src/graphics/Screen.h index c9ab85449..cd24a7e3a 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -10,7 +10,8 @@ #include #endif -#include "TFT.h" +#include "EInkDisplay.h" +#include "TFTDisplay.h" #include "TypedQueue.h" #include "commands.h" #include "concurrency/LockGuard.h" @@ -47,7 +48,6 @@ class DebugInfo void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); void drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); - std::string channelName; /// Protects all of internal state. @@ -237,6 +237,8 @@ class Screen : public concurrency::PeriodicTask /** FIXME cleanup display abstraction */ #ifdef ST7735_CS TFTDisplay dispdev; +#elif defined(HAS_EINK) + EInkDisplay dispdev; #elif defined(USE_SH1106) SH1106Wire dispdev; #else diff --git a/src/graphics/TFT.cpp b/src/graphics/TFTDisplay.cpp similarity index 97% rename from src/graphics/TFT.cpp rename to src/graphics/TFTDisplay.cpp index dcaf53ec4..b9484b9d6 100644 --- a/src/graphics/TFT.cpp +++ b/src/graphics/TFTDisplay.cpp @@ -2,7 +2,7 @@ #ifdef ST7735_CS #include "SPILock.h" -#include "TFT.h" +#include "TFTDisplay.h" #include "graphics/configs.h" #include #include // Graphics and font library for ST7735 driver chip @@ -20,7 +20,6 @@ void TFTDisplay::display(void) { concurrency::LockGuard g(spiLock); -#if 1 // 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 < SCREEN_HEIGHT; y++) { @@ -32,7 +31,6 @@ void TFTDisplay::display(void) tft.drawPixel(x, y, isset ? TFT_WHITE : TFT_BLACK); } } -#endif } // Send a command to the display (low level function) @@ -52,12 +50,10 @@ bool TFTDisplay::connect() pinMode(ST7735_BACKLIGHT_EN, OUTPUT); #endif -#if 1 tft.init(); tft.setRotation(3); // Orient horizontal and wide underneath the silkscreen name label tft.fillScreen(TFT_BLACK); // tft.drawRect(0, 0, 40, 10, TFT_PURPLE); // wide rectangle in upper left -#endif return true; } diff --git a/src/graphics/TFT.h b/src/graphics/TFTDisplay.h similarity index 100% rename from src/graphics/TFT.h rename to src/graphics/TFTDisplay.h diff --git a/src/main.cpp b/src/main.cpp index 3b327a4e8..e20572a6f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -232,7 +232,7 @@ void setup() #endif // Initialize the screen first so we can show the logo while we start up everything else. -#ifdef ST7735_CS +#if defined(ST7735_CS) || defined(HAS_EINK) screen.setup(); #else if (ssd1306_found) diff --git a/src/nrf52/main-nrf52.cpp b/src/nrf52/main-nrf52.cpp index 9cce9bd11..928c2aa1c 100644 --- a/src/nrf52/main-nrf52.cpp +++ b/src/nrf52/main-nrf52.cpp @@ -1,6 +1,6 @@ #include "NRF52Bluetooth.h" #include "configuration.h" -#include "graphics/TFT.h" +#include "graphics/TFTDisplay.h" #include #include #include diff --git a/src/pgmspace.h b/src/pgmspace.h new file mode 100644 index 000000000..5ad8035be --- /dev/null +++ b/src/pgmspace.h @@ -0,0 +1,5 @@ +#pragma once + +// dummy file to keep old arduino code happy +#define PROGMEM +#define pgm_read_byte(addr) (*((unsigned const char *)addr)) \ No newline at end of file diff --git a/variants/eink/variant.cpp b/variants/eink/variant.cpp index 866f46185..cae079b74 100644 --- a/variants/eink/variant.cpp +++ b/variants/eink/variant.cpp @@ -38,4 +38,7 @@ void initVariant() pinMode(PIN_LED2, OUTPUT); ledOff(PIN_LED2); + + pinMode(PIN_LED3, OUTPUT); + ledOff(PIN_LED3); } diff --git a/variants/eink/variant.h b/variants/eink/variant.h index 0900b2365..3fc61f4ff 100644 --- a/variants/eink/variant.h +++ b/variants/eink/variant.h @@ -27,18 +27,22 @@ /* @geeksville eink TODO: -enable reset as a button -fix battery pin usage -drive TCXO DIO3 enable high whenever we want the clock -use PIN_GPS_WAKE to sleep the GPS -use tp_ser_io as a button, it goes high when pressed - +button not working +get pps blinking on second board +clean up eink drawing to not have the nasty timeout hack +put eink to sleep when we think the screen is off +enable flash on spi0, share chip selects on spi1. +https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.nrf52832.ps.v1.1%2Fspi.html enable reset as a button in +bootloader fix battery pin usage drive TCXO DIO3 enable high whenever we want the clock use PIN_GPS_WAKE to sleep the GPS use +tp_ser_io as a button, it goes high when pressed unify eink display classes eink probably is // #include // 1.54" b/w //G702-A https://github.com/Xinyuan-LilyGO/LilyGO_T5_V24 200 x 200 feedback to ttgo: +are D10 and D8 backwards on the GPS schematic? gps comms no work? +leds don't work? use pcf8563 part for waking CPU? or remove it the mx25 flash chip is great! name: TTGO LoraCard (nice googablity, unique name, sounds slick, implies lora and small) @@ -65,16 +69,16 @@ extern "C" { #define NUM_ANALOG_OUTPUTS (0) // LEDs -#define PIN_LED1 (0 + 13) -#define PIN_LED2 (0 + 14) -#define PIN_LED3 (0 + 15) +#define PIN_LED1 (0 + 13) // green +#define PIN_LED2 (0 + 14) // red +#define PIN_LED3 (0 + 15) // blue #define LED_RED PIN_LED2 #define LED_GREEN PIN_LED1 #define LED_BLUE PIN_LED3 #define LED_BUILTIN LED_GREEN -#define LED_CONN LED_CONN +#define LED_CONN PIN_BLUE #define LED_STATE_ON 0 // State when LED is lit @@ -171,21 +175,28 @@ FIXME define/FIX flash access */ #define PIN_EINK_EN (32 + 11) -#define PIN_EINK_CS (0 + 31) +#define PIN_EINK_CS (0 + 30) #define PIN_EINK_BUSY (0 + 3) #define PIN_EINK_DC (0 + 28) #define PIN_EINK_RES (0 + 2) #define PIN_EINK_SCLK (0 + 31) #define PIN_EINK_MOSI (0 + 29) // also called SDI +#define HAS_EINK + +#define PIN_SPI1_MISO \ + (32 + 7) // FIXME not really needed, but for now the SPI code requires something to be defined, pick an used GPIO +#define PIN_SPI1_MOSI PIN_EINK_MOSI +#define PIN_SPI1_SCK PIN_EINK_SCLK + /* * Air530 GPS pins */ #define PIN_GPS_WAKE (32 + 2) #define PIN_GPS_PPS (32 + 4) -#define PIN_GPS_TX (32 + 9) // This is for bits going TOWARDS the GPS -#define PIN_GPS_RX (32 + 8) // This is for bits going TOWARDS the CPU +#define PIN_GPS_TX (32 + 8) // This is for bits going TOWARDS the GPS +#define PIN_GPS_RX (32 + 9) // This is for bits going TOWARDS the CPU #define PIN_SERIAL1_RX PIN_GPS_RX #define PIN_SERIAL1_TX PIN_GPS_TX @@ -193,17 +204,17 @@ FIXME define/FIX flash access /* * SPI Interfaces */ -#define SPI_INTERFACES_COUNT 1 +#define SPI_INTERFACES_COUNT 2 // For LORA #define PIN_SPI_MISO (0 + 23) #define PIN_SPI_MOSI (0 + 22) #define PIN_SPI_SCK (0 + 19) -static const uint8_t SS = SX1262_CS; -static const uint8_t MOSI = PIN_SPI_MOSI; -static const uint8_t MISO = PIN_SPI_MISO; -static const uint8_t SCK = PIN_SPI_SCK; +// static const uint8_t SS = SX1262_CS; +// static const uint8_t MOSI = PIN_SPI_MOSI; +// static const uint8_t MISO = PIN_SPI_MISO; +// static const uint8_t SCK = PIN_SPI_SCK; // To debug via the segger JLINK console rather than the CDC-ACM serial device #define USE_SEGGER