diff --git a/examples/ThinkInk_quadcolor/ThinkInk_quadcolor.ino b/examples/ThinkInk_quadcolor/ThinkInk_quadcolor.ino index db481be..b5a9560 100644 --- a/examples/ThinkInk_quadcolor/ThinkInk_quadcolor.ino +++ b/examples/ThinkInk_quadcolor/ThinkInk_quadcolor.ino @@ -30,6 +30,10 @@ ThinkInk_213_Quadcolor_AJHE5 display(EPD_DC, EPD_RESET, EPD_CS, SRAM_CS, EPD_BUSY, EPD_SPI); +// 3.52" Quadcolor EPD with JD79667 chipset +ThinkInk_352_Quadcolor_AJHE5 display(EPD_DC, EPD_RESET, EPD_CS, SRAM_CS, EPD_BUSY, + EPD_SPI); + void setup() { Serial.begin(115200); while (!Serial) { diff --git a/src/Adafruit_ThinkInk.h b/src/Adafruit_ThinkInk.h index 8cb388d..fd4dc42 100644 --- a/src/Adafruit_ThinkInk.h +++ b/src/Adafruit_ThinkInk.h @@ -34,6 +34,7 @@ #include "panels/ThinkInk_290_Tricolor_Z10.h" #include "panels/ThinkInk_290_Tricolor_Z13.h" #include "panels/ThinkInk_290_Tricolor_Z94.h" +#include "panels/ThinkInk_352_Quadcolor_AJHE5.h" #include "panels/ThinkInk_370_Mono_BAAMFGN.h" #include "panels/ThinkInk_370_Tricolor_BABMFGNR.h" #include "panels/ThinkInk_420_Grayscale4_MFGN.h" diff --git a/src/drivers/Adafruit_JD79661.cpp b/src/drivers/Adafruit_JD79661.cpp index 78e2d07..5d68010 100644 --- a/src/drivers/Adafruit_JD79661.cpp +++ b/src/drivers/Adafruit_JD79661.cpp @@ -131,10 +131,10 @@ void Adafruit_JD79661::drawPixel(int16_t x, int16_t y, uint16_t color) { uint8_t* pBuf; - // deal with non-8-bit heights + // deal with non-4-bit heights uint16_t _WIDTH = WIDTH; - if (_WIDTH % 8 != 0) { - _WIDTH += 8 - (_WIDTH % 8); + if (_WIDTH % 4 != 0) { + _WIDTH += 4 - (_WIDTH % 4); } // check rotation, move pixel around if necessary diff --git a/src/drivers/Adafruit_JD79667.cpp b/src/drivers/Adafruit_JD79667.cpp new file mode 100644 index 0000000..70c6f64 --- /dev/null +++ b/src/drivers/Adafruit_JD79667.cpp @@ -0,0 +1,315 @@ +#include "Adafruit_JD79667.h" + +#include "Adafruit_EPD.h" + +#define EPD_RAM_BW 0x10 +#define EPD_RAM_RED 0x13 + +#define BUSY_WAIT 500 + +// clang-format off + +const uint8_t jd79667_default_init_code[] { + 0xFF, 10, // wait a lil bit + 0x4D, 1, 0x78, + JD79667_PANEL_SETTING, 2, 0x0F, 0x29, // PSR, Display resolution is 180x384 + JD79667_POWER_SETTING, 2, 0x07, 0x00, // PWR + 0x03, 3, 0x10, 0x54, 0x44, // POFS + JD79667_BOOSTER_SOFTSTART, 7, 0x05, 0x00, 0x3F, 0x0A, 0x25, 0x12, 0x1A, + JD79667_CDI, 1, 0x37, // CDI + 0x60, 2, 0x02, 0x02, // TCON + JD79667_RESOLUTION, 4, 0, 180, 1, 128, // TRES 180x384 + 0xE7, 1, 0x1C, + 0xE3, 1, 0x22, + 0xB4, 1, 0xD0, + 0xB5, 1, 0x03, + 0xE9, 1, 0x01, + JD79667_PLL_CONTROL, 1, 0x08, + JD79667_POWER_ON, 0, + 0xFE}; + +// clang-format on + +/**************************************************************************/ +/*! + @brief constructor if using external SRAM chip and software SPI + @param width the width of the display in pixels + @param height the height of the display in pixels + @param SID the SID pin to use + @param SCLK the SCLK pin to use + @param DC the data/command pin to use + @param RST the reset pin to use + @param CS the chip select pin to use + @param SRCS the SRAM chip select pin to use + @param MISO the MISO pin to use + @param BUSY the busy pin to use +*/ +/**************************************************************************/ +Adafruit_JD79667::Adafruit_JD79667(int width, int height, int16_t SID, + int16_t SCLK, int16_t DC, int16_t RST, + int16_t CS, int16_t SRCS, int16_t MISO, + int16_t BUSY) + : Adafruit_EPD(width, height, SID, SCLK, DC, RST, CS, SRCS, MISO, BUSY) { + if ((width % 8) != 0) { + width += 8 - (width % 8); + } + buffer1_size = width * height / 4; + buffer2_size = 0; + + if (SRCS >= 0) { + use_sram = true; + buffer1_addr = 0; + buffer2_addr = 0; + } else { + buffer1 = (uint8_t*)malloc(buffer1_size); + buffer2 = buffer1; + } + singleByteTxns = true; +} + +// constructor for hardware SPI - we indicate DataCommand, ChipSelect, Reset + +/**************************************************************************/ +/*! + @brief constructor if using on-chip RAM and hardware SPI + @param width the width of the display in pixels + @param height the height of the display in pixels + @param DC the data/command pin to use + @param RST the reset pin to use + @param CS the chip select pin to use + @param SRCS the SRAM chip select pin to use + @param BUSY the busy pin to use +*/ +/**************************************************************************/ +Adafruit_JD79667::Adafruit_JD79667(int width, int height, int16_t DC, + int16_t RST, int16_t CS, int16_t SRCS, + int16_t BUSY, SPIClass* spi) + : Adafruit_EPD(width, height, DC, RST, CS, SRCS, BUSY, spi) { + if ((width % 8) != 0) { + width += 8 - (width % 8); + } + buffer1_size = width * height / 4; + buffer2_size = 0; + + if (SRCS >= 0) { + use_sram = true; + buffer1_addr = 0; + buffer2_addr = 0; + } else { + buffer1 = (uint8_t*)malloc(buffer1_size); + buffer2 = buffer1; + } + + + singleByteTxns = true; +} + +/**************************************************************************/ +/*! + @brief clear all data buffers +*/ +/**************************************************************************/ +void Adafruit_JD79667::clearBuffer() { + if (use_sram) { + sram.erase(colorbuffer_addr, buffer1_size, 0x55); + } else { + memset(buffer1, 0x55, buffer1_size); + } +} + +/**************************************************************************/ +/*! + @brief draw a single pixel on the screen + @param x the x axis position + @param y the y axis position + @param color the color of the pixel +*/ +/**************************************************************************/ +void Adafruit_JD79667::drawPixel(int16_t x, int16_t y, uint16_t color) { + if ((x < 0) || (x >= width()) || (y < 0) || (y >= height())) + return; + + uint8_t* pBuf; + + // deal with non-4-bit heights + uint16_t _WIDTH = WIDTH; + if (_WIDTH % 4 != 0) { + _WIDTH += 4 - (_WIDTH % 4); + } + + // check rotation, move pixel around if necessary + switch (getRotation()) { + case 1: + EPD_swap(x, y); + x = _WIDTH - x - 1; + // remove the offset + x -= _WIDTH - WIDTH; + break; + case 2: + x = _WIDTH - x - 1; + y = HEIGHT - y - 1; + // re-add the offset + x += _WIDTH - WIDTH; + break; + case 3: + EPD_swap(x, y); + y = HEIGHT - y - 1; + break; + } + uint32_t addr = ((uint32_t)x + (uint32_t)y * _WIDTH) / 4; + uint8_t color_c; + + if (use_sram) { + color_c = sram.read8(colorbuffer_addr + addr); + pBuf = &color_c; + } else { + pBuf = color_buffer + addr; + } + + if (color == EPD_BLACK) { + color = JD79667_BLACK; + } else if (color == EPD_RED) { + color = JD79667_RED; + } else if (color == EPD_YELLOW) { + color = JD79667_YELLOW; + } else if (color == EPD_WHITE) { + color = JD79667_WHITE; + } + + uint8_t byte_offset_mask = 0x3 << (3 - (x % 4)) * 2; + uint8_t byte_offset_value = (color & 0x3) << (3 - (x % 4)) * 2; + + *pBuf &= ~byte_offset_mask; // save reverse mask + *pBuf |= byte_offset_value; // now add in the new color + + if (use_sram) { + sram.write8(colorbuffer_addr + addr, *pBuf); + } +} + +/**************************************************************************/ +/*! + @brief wait for busy signal to end +*/ +/**************************************************************************/ +void Adafruit_JD79667::busy_wait(void) { + if (_busy_pin >= 0) { + while (!digitalRead(_busy_pin)) { // wait for busy HIGH! + delay(10); + } + } else { + delay(BUSY_WAIT); + } +} + +/**************************************************************************/ +/*! + @brief begin communication with and set up the display. + @param reset if true the reset pin will be toggled. +*/ +/**************************************************************************/ +void Adafruit_JD79667::begin(bool reset) { + Adafruit_EPD::begin(reset); + delay(100); +} + +/**************************************************************************/ +/*! + @brief signal the display to update +*/ +/**************************************************************************/ +void Adafruit_JD79667::update() { + uint8_t buf[1]; + + // display update sequence + buf[0] = 0x00; + EPD_command(JD79667_DISPLAY_REFRESH, buf, 1); + + busy_wait(); + + if (_busy_pin <= -1) { + delay(1000); + } +} + +void Adafruit_JD79667::hardwareReset(void) { + if (_reset_pin >= 0) { + // Setup reset pin direction + pinMode(_reset_pin, OUTPUT); + // VDD (3.3V) goes high at start, lets just chill for a ms + digitalWrite(_reset_pin, HIGH); + delay(20); + // bring reset low + digitalWrite(_reset_pin, LOW); + // wait 40ms + delay(40); + // bring out of reset + digitalWrite(_reset_pin, HIGH); + delay(50); + } +} + +/**************************************************************************/ +/*! + @brief start up the display +*/ +/**************************************************************************/ + +void Adafruit_JD79667::powerUp() { + hardwareReset(); + busy_wait(); + + const uint8_t* init_code = jd79667_default_init_code; + if (_epd_init_code != NULL) { + init_code = _epd_init_code; + } + EPD_commandList(init_code); + + busy_wait(); +} + +/**************************************************************************/ +/*! + @brief wind down the display +*/ +/**************************************************************************/ +void Adafruit_JD79667::powerDown() { + uint8_t buf[1]; + // Only deep sleep if we can get out of it + if (_reset_pin >= 0) { + // deep sleep + buf[0] = 0x00; + EPD_command(JD79667_POWER_OFF, buf, 1); + busy_wait(); + delay(100); + buf[0] = 0xA5; + EPD_command(JD79667_DEEP_SLEEP, buf, 1); + } +} + +/**************************************************************************/ +/*! + @brief Send the specific command to start writing to EPD display RAM + @param index The index for which buffer to write (0 or 1 or tri-color + displays) Ignored for monochrome displays. + @returns The byte that is read from SPI at the same time as sending the + command +*/ +/**************************************************************************/ +uint8_t Adafruit_JD79667::writeRAMCommand(uint8_t index) { + (void)index; + EPD_command(JD79667_DATA_START_XMIT); + return true; +} + +/**************************************************************************/ +/*! + @brief Some displays require setting the RAM address pointer + @param x X address counter value + @param y Y address counter value +*/ +/**************************************************************************/ +void Adafruit_JD79667::setRAMAddress(uint16_t x, uint16_t y) { + (void)x; + (void)y; +} diff --git a/src/drivers/Adafruit_JD79667.h b/src/drivers/Adafruit_JD79667.h new file mode 100644 index 0000000..90bb9fb --- /dev/null +++ b/src/drivers/Adafruit_JD79667.h @@ -0,0 +1,53 @@ +#ifndef LIB_ADAFRUIT_JD79667 +#define LIB_ADAFRUIT_JD79667 + +#include + +#include "Adafruit_EPD.h" + +#define JD79667_PANEL_SETTING 0x00 +#define JD79667_POWER_SETTING 0x01 +#define JD79667_POWER_OFF 0x02 +#define JD79667_POWER_ON 0x04 +#define JD79667_BOOSTER_SOFTSTART 0x06 +#define JD79667_DATA_START_XMIT 0x10 +#define JD79667_DISPLAY_REFRESH 0x12 +#define JD79667_DEEP_SLEEP 0x07 +#define JD79667_PLL_CONTROL 0x30 +#define JD79667_CDI 0x50 +#define JD79667_RESOLUTION 0x61 + +#define JD79667_BLACK 0b00 +#define JD79667_WHITE 0b01 +#define JD79667_YELLOW 0b10 +#define JD79667_RED 0b11 + +/**************************************************************************/ +/*! + @brief Class for interfacing with JD79667 EPD drivers +*/ +/**************************************************************************/ +class Adafruit_JD79667 : public Adafruit_EPD { + public: + Adafruit_JD79667(int width, int height, int16_t SID, int16_t SCLK, int16_t DC, + int16_t RST, int16_t CS, int16_t SRCS, int16_t MISO, + int16_t BUSY = -1); + Adafruit_JD79667(int width, int height, int16_t DC, int16_t RST, int16_t CS, + int16_t SRCS, int16_t BUSY = -1, SPIClass* spi = &SPI); + + void begin(bool reset = true); + void powerUp(); + void update(void); + void powerDown(); + + void clearBuffer(); + void drawPixel(int16_t x, int16_t y, uint16_t color); + void hardwareReset(void); + + protected: + void busy_wait(); + void setRAMAddress(uint16_t x, uint16_t y); + uint8_t writeRAMCommand(uint8_t index); +}; + +#endif diff --git a/src/panels/ThinkInk_352_Quadcolor_AJHE5.h b/src/panels/ThinkInk_352_Quadcolor_AJHE5.h new file mode 100644 index 0000000..693f3cc --- /dev/null +++ b/src/panels/ThinkInk_352_Quadcolor_AJHE5.h @@ -0,0 +1,30 @@ +#ifndef _THINKINK_352_QUADCOLOR_AJHE5_H +#define _THINKINK_352_QUADCOLOR_AJHE5_H + +// This file is #included by Adafruit_ThinkInk.h and does not need to +// #include anything else to pick up the EPD header or ink mode enum. + +class ThinkInk_352_Quadcolor_AJHE5 : public Adafruit_JD79667 { + public: + ThinkInk_352_Quadcolor_AJHE5(int16_t SID, int16_t SCLK, int16_t DC, + int16_t RST, int16_t CS, int16_t SRCS, + int16_t MISO, int16_t BUSY = -1) + : Adafruit_JD79667(180, 384, SID, SCLK, DC, RST, CS, SRCS, MISO, BUSY){}; + + ThinkInk_352_Quadcolor_AJHE5(int16_t DC, int16_t RST, int16_t CS, + int16_t SRCS, int16_t BUSY = -1, + SPIClass* spi = &SPI) + : Adafruit_JD79667(180, 384, DC, RST, CS, SRCS, BUSY, spi){}; + + void begin(thinkinkmode_t mode = THINKINK_QUADCOLOR) { + Adafruit_JD79667::begin(true); + + inkmode = mode; // Preserve ink mode for ImageReader or others + + default_refresh_delay = 13000; + setRotation(1); + powerDown(); + } +}; + +#endif // _THINKINK_352_Quad