diff --git a/fontconvert/Makefile b/fontconvert/Makefile index 639223c..d52ea84 100644 --- a/fontconvert/Makefile +++ b/fontconvert/Makefile @@ -4,7 +4,7 @@ CC = gcc CFLAGS = -Wall -I/usr/local/include/freetype2 -I/usr/include/freetype2 -I/usr/include -I/opt/homebrew/include/freetype2 LIBS = -L/opt/homebrew/lib -lfreetype -fontconvert: main.c +fontconvert: main.c ../src/Group5.h $(CC) $(CFLAGS) main.c $(LIBS) -o fontconvert strip fontconvert diff --git a/fontconvert/main.c b/fontconvert/main.c index 5e2bbd2..ef5bbf3 100644 --- a/fontconvert/main.c +++ b/fontconvert/main.c @@ -91,12 +91,15 @@ int RotateBitmap(int iAngle, uint8_t *pSrc, int iWidth, int iHeight, int iSrcPit // // Create the comments and const array boilerplate for the hex data bytes // -void StartHexFile(FILE *f, int iLen, const char *fname) +void StartHexFile(FILE *f, int iLen, const char *fname, int size, int first, int last) { int i, j; char szTemp[256]; fprintf(f, "//\n// Created with fontconvert, written by Larry Bank\n"); + fprintf(f, "// Point size: %d, first: %d, last: %d\n", size, first, last); fprintf(f, "// compressed font data size = %d bytes\n//\n", iLen); + fprintf(f, "// for non-Arduino builds...\n#ifndef PROGMEM\n#define PROGMEM\n#endif\n"); + strcpy(szTemp, fname); i = strlen(szTemp); if (szTemp[i-2] == '.') szTemp[i-2] = 0; // get the leaf name for the data @@ -106,7 +109,7 @@ void StartHexFile(FILE *f, int iLen, const char *fname) j--; } if (szTemp[j] == '/') j++; - fprintf(f, "const uint8_t %s[] = {\n", &szTemp[j]); + fprintf(f, "const uint8_t %s[] PROGMEM = {\n", &szTemp[j]); } /* StartHexFile() */ // // Add N bytes of hex data to the output @@ -162,8 +165,12 @@ int main(int argc, char *argv[]) FT_Bitmap *bitmap; FT_BitmapGlyphRec *g; // BitBank Font structures + int bSmallFont = 0; // indicates if we're creating a normal or small font file BB_GLYPH *pGlyphs; + BB_GLYPH_SMALL *pSmallGlyphs; BB_FONT bbff; + BB_FONT_SMALL bbf2; + int bHFile; // flag indicating if the output will be a .H file of hex data if (argc < 4) { @@ -171,6 +178,7 @@ int main(int argc, char *argv[]) return 1; } size = atoi(argv[3]); + bSmallFont = (size < 60); // Glyph info can fit in signed 8-bit values pTemp = (uint8_t *)argv[2] + strlen(argv[2]) - 1; bHFile = (pTemp[0] == 'H' || pTemp[0] == 'h'); // output an H file? @@ -191,10 +199,18 @@ int main(int argc, char *argv[]) printf("Something went wrong - the starting character comes after the ending character. Try again...\n"); return 1; } - pGlyphs = (BB_GLYPH *)malloc((last - first + 1) * sizeof(BB_GLYPH)); - if (!pGlyphs) { - printf("Error allocating memory for glyph data\n"); - return 1; + if (bSmallFont) { + pSmallGlyphs = (BB_GLYPH_SMALL *)malloc((last - first + 1) * sizeof(BB_GLYPH_SMALL)); + if (!pSmallGlyphs) { + printf("Error allocating memory for glyph data\n"); + return 1; + } + } else { + pGlyphs = (BB_GLYPH *)malloc((last - first + 1) * sizeof(BB_GLYPH)); + if (!pGlyphs) { + printf("Error allocating memory for glyph data\n"); + return 1; + } } pBitmap = (uint8_t *)malloc(OUTBUF_SIZE); // Enough to hold the output pTemp = (uint8_t *)malloc(OUTBUF_SIZE); // for rotated bitmaps @@ -259,27 +275,21 @@ int main(int argc, char *argv[]) bitmap = &face->glyph->bitmap; g = (FT_BitmapGlyphRec *)glyph; - // Minimal font and per-glyph information is stored to - // reduce flash space requirements. Glyph bitmaps are - // fully bit-packed; no per-scanline pad, though end of - // each character may be padded to next byte boundary - // when needed. 16-bit offset means 64K max for bitmaps, - // code currently doesn't check for overflow. (Doesn't - // check that size & offsets are within bounds either for - // that matter...please convert fonts responsibly.) - - // Check that the requested font size fits within the limits - // of the 8-bit member variables - if (bitmap->width > 255 || (face->glyph->advance.x>>6) > 255) { - printf("The requested size is too large to fit in the BB_GLYPH member vars\nEither request a smaller size or change the member variables to be 16-bits\n"); - return -1; + if (bSmallFont) { + pSmallGlyphs[index].bitmapOffset = iOffset; + pSmallGlyphs[index].width = bitmap->width; + pSmallGlyphs[index].height = bitmap->rows; + pSmallGlyphs[index].xAdvance = face->glyph->advance.x >> 6; + pSmallGlyphs[index].xOffset = g->left; + pSmallGlyphs[index].yOffset = 1 - g->top; + } else { + pGlyphs[index].bitmapOffset = iOffset; + pGlyphs[index].width = bitmap->width; + pGlyphs[index].height = bitmap->rows; + pGlyphs[index].xAdvance = face->glyph->advance.x >> 6; + pGlyphs[index].xOffset = g->left; + pGlyphs[index].yOffset = 1 - g->top; } - pGlyphs[index].bitmapOffset = iOffset; - pGlyphs[index].width = bitmap->width; - pGlyphs[index].height = bitmap->rows; - pGlyphs[index].xAdvance = face->glyph->advance.x >> 6; - pGlyphs[index].xOffset = g->left; - pGlyphs[index].yOffset = 1 - g->top; s = bitmap->buffer; iPitch = bitmap->pitch; if (iRotation != 0) { @@ -308,29 +318,55 @@ int main(int argc, char *argv[]) return 1; } // Write the file header - bbff.u16Marker = BB_FONT_MARKER; - bbff.first = first; - bbff.last = last; - bbff.rotation = iRotation; // save rotation angle - if (face->size->metrics.height == 0) { - // No face height info, assume fixed width and get from a glyph. - bbff.height = pGlyphs[0].height; + if (bSmallFont) { + bbf2.u16Marker = BB_FONT_MARKER_SMALL; + bbf2.first = first; + bbf2.last = last; + bbf2.rotation = iRotation; // save rotation angle + if (face->size->metrics.height == 0) { + // No face height info, assume fixed width and get from a glyph. + bbf2.height = pGlyphs[0].height; + } else { + bbf2.height = (face->size->metrics.height >> 6); + } + iLen = sizeof(bbf2) + (last-first+1)*sizeof(BB_GLYPH_SMALL) + iOffset; + if (bHFile) { // create an H file of hex values + StartHexFile(fOut, iLen, argv[2], size, first, last); + AddHexBytes(fOut, &bbf2, sizeof(bbf2), 0); + AddHexBytes(fOut, pSmallGlyphs, (last-first+1) * sizeof(BB_GLYPH_SMALL), 0); + AddHexBytes(fOut, pBitmap, iOffset, 1); + } else { + fwrite(&bbf2, 1, sizeof(bbf2), fOut); + // Write the glyph table + fwrite(pSmallGlyphs, (last-first+1), sizeof(BB_GLYPH_SMALL), fOut); + // Write the compressed bitmap data + fwrite(pBitmap, 1, iOffset, fOut); + } } else { - bbff.height = (face->size->metrics.height >> 6); - } - iLen = sizeof(bbff) + (last-first+1)*sizeof(BB_GLYPH) + iOffset; - if (bHFile) { // create an H file of hex values - StartHexFile(fOut, iLen, argv[2]); - AddHexBytes(fOut, &bbff, sizeof(bbff), 0); - AddHexBytes(fOut, pGlyphs, (last-first+1) * sizeof(BB_GLYPH), 0); - AddHexBytes(fOut, pBitmap, iOffset, 1); - } else { - fwrite(&bbff, 1, sizeof(bbff), fOut); - // Write the glyph table - fwrite(pGlyphs, (last-first+1), sizeof(BB_GLYPH), fOut); - // Write the compressed bitmap data - fwrite(pBitmap, 1, iOffset, fOut); - } + bbff.u16Marker = BB_FONT_MARKER; + bbff.first = first; + bbff.last = last; + bbff.rotation = iRotation; // save rotation angle + if (face->size->metrics.height == 0) { + // No face height info, assume fixed width and get from a glyph. + bbff.height = pGlyphs[0].height; + } else { + bbff.height = (face->size->metrics.height >> 6); + } + iLen = sizeof(bbff) + (last-first+1)*sizeof(BB_GLYPH) + iOffset; + if (bHFile) { // create an H file of hex values + StartHexFile(fOut, iLen, argv[2], size, first, last); + AddHexBytes(fOut, &bbff, sizeof(bbff), 0); + AddHexBytes(fOut, pGlyphs, (last-first+1) * sizeof(BB_GLYPH), 0); + AddHexBytes(fOut, pBitmap, iOffset, 1); + } else { + fwrite(&bbff, 1, sizeof(bbff), fOut); + // Write the glyph table + fwrite(pGlyphs, (last-first+1), sizeof(BB_GLYPH), fOut); + // Write the compressed bitmap data + fwrite(pBitmap, 1, iOffset, fOut); + } + } // large fonts fflush(fOut); fclose(fOut); // done! FT_Done_FreeType(library); diff --git a/src/Group5.h b/src/Group5.h index 7641ea4..c972de2 100644 --- a/src/Group5.h +++ b/src/Group5.h @@ -12,14 +12,18 @@ // Written by Larry Bank // Copyright (c) 2024 BitBank Software, Inc. // -// Use of this software is governed by the Business Source License -// included in the file ./LICENSE. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// ./APL.txt. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//=========================================================================== // + // The name "Group5" is derived from the CCITT Group4 standard // This code is based on a lot of the good ideas from CCITT T.6 // for FAX image compression, but modified to work in a very @@ -111,13 +115,13 @@ inline uint32_t TIFFMOTOLONG(uint8_t *p) // G5 Encoder // -typedef struct pil_buffered_bits +typedef struct g5_buffered_bits { unsigned char *pBuf; // buffer pointer uint32_t ulBits; // buffered bits uint32_t ulBitOff; // current bit offset uint32_t ulDataSize; // available data -} BUFFERED_BITS; +} G5_BUFFERED_BITS; // // Encoder state @@ -131,7 +135,7 @@ typedef struct g5_enc_image_tag int iDataSize; // generated output size uint8_t *pOutBuf; int16_t *pCur, *pRef; // pointers to swap current and reference lines - BUFFERED_BITS bb; + G5_BUFFERED_BITS bb; int16_t CurFlips[MAX_IMAGE_FLIPS]; int16_t RefFlips[MAX_IMAGE_FLIPS]; } G5ENCIMAGE; @@ -139,20 +143,33 @@ typedef struct g5_enc_image_tag // 16-bit marker at the start of a BB_FONT file // (BitBank FontFile) #define BB_FONT_MARKER 0xBBFF +#define BB_FONT_MARKER_SMALL 0xBBF2 + // 16-bit marker at the start of a BB_BITMAP file // (BitBank BitmapFile) #define BB_BITMAP_MARKER 0xBBBF -// Font info per character (glyph) +// Font info per large character (glyph) typedef struct { uint16_t bitmapOffset; // Offset to compressed bitmap data for this glyph (starting from the end of the BB_GLYPH[] array) - uint8_t width; // bitmap width in pixels - uint8_t xAdvance; // total width in pixels (bitmap + padding) + uint16_t width; // bitmap width in pixels + uint16_t xAdvance; // total width in pixels (bitmap + padding) uint16_t height; // bitmap height in pixels int16_t xOffset; // left padding to upper left corner int16_t yOffset; // padding from baseline to upper left corner (usually negative) } BB_GLYPH; +// Font info per small character (glyph) +typedef struct bbg_small { + uint16_t bitmapOffset; // Offset to compressed bitmap data for this glyph (starting from the end of the BB_GLYPH[] array) + uint8_t width; // bitmap width in pixels + uint8_t xAdvance; // total width in pixels (bitmap + padding) + uint8_t height; // bitmap height in pixels + int8_t xOffset; // left padding to upper left corner + int8_t yOffset; // padding from baseline to upper left corner (usually negative) + int8_t struct_padding; // pad the structure to 8 bytes to prevent alignment problems on different target compilers/settings +} BB_GLYPH_SMALL; + // This structure is stored at the beginning of a BB_FONT file typedef struct { uint16_t u16Marker; // 16-bit Marker defining a BB_FONT file @@ -163,6 +180,16 @@ typedef struct { BB_GLYPH glyphs[]; // Array of glyphs (one for each char) } BB_FONT; +// This structure is stored at the beginning of a BB_FONT_SMALL file +typedef struct { + uint16_t u16Marker; // 16-bit Marker defining a BB_FONT_SMALL file + uint16_t first; // first char (ASCII value) + uint16_t last; // last char (ASCII value) + uint16_t height; // total height of font + uint32_t rotation; // store this as 32-bits to not have a struct packing problem + BB_GLYPH_SMALL glyphs[]; // Array of glyphs (one for each char) +} BB_FONT_SMALL; + // This structure defines the start of a compressed bitmap file typedef struct { uint16_t u16Marker; // 16-bit marker defining a BB_BITMAP file diff --git a/src/arduino_io.inl b/src/arduino_io.inl index 09e9fd7..3617d15 100644 --- a/src/arduino_io.inl +++ b/src/arduino_io.inl @@ -64,10 +64,14 @@ void bbepInitIO(BBEPDISP *pBBEP, uint8_t u8DC, uint8_t u8RST, uint8_t u8BUSY, ui SPI.begin(); // other architectures have fixed SPI pins #endif SPI.beginTransaction(SPISettings(u32Speed, MSBFIRST, SPI_MODE0)); +#ifdef ARDUINO_ARCH_ESP32 +// For NRF52, you have to leave an 'open' transaction SPI.endTransaction(); // N.B. - if you call beginTransaction() again without a matching endTransaction(), it will hang on ESP32 +#endif + pBBEP->is_awake = 1; +// Before we can start sending pixels, many panels need to know the display resolution + bbepSendCMDSequence(pBBEP, pBBEP->pInitFull); if (pBBEP->iFlags & BBEP_7COLOR) { // need to send before you can send it data - pBBEP->is_awake = 1; - bbepSendCMDSequence(pBBEP, pBBEP->pInitFull); if (pBBEP->iFlags & BBEP_SPLIT_BUFFER) { // Send the same sequence to the second controller pBBEP->iCSPin = pBBEP->iCS2Pin; diff --git a/src/bb_ep.inl b/src/bb_ep.inl index b3a8e45..cf9f12b 100644 --- a/src/bb_ep.inl +++ b/src/bb_ep.inl @@ -23,6 +23,8 @@ void bbepSetPixelFast4Clr(void *pb, int x, int y, unsigned char ucColor); void bbepSetPixelFast3Clr(void *pb, int x, int y, unsigned char ucColor); void bbepSetPixelFast2Clr(void *pb, int x, int y, unsigned char ucColor); void bbepSetPixelFast16Clr(void *pb, int x, int y, unsigned char ucColor); +void bbepSetPixelFast4Gray(void *pb, int x, int y, unsigned char ucColor); +int bbepSetPixel4Gray(void *pb, int x, int y, unsigned char ucColor); int bbepSetPixel4Clr(void *pb, int x, int y, unsigned char ucColor); int bbepSetPixel3Clr(void *pb, int x, int y, unsigned char ucColor); int bbepSetPixel2Clr(void *pb, int x, int y, unsigned char ucColor); @@ -41,7 +43,13 @@ const uint8_t u8Colors_3clr[16] = { // red and yellow are the same for these dis BBEP_BLACK, BBEP_WHITE, BBEP_RED, BBEP_RED, BBEP_BLACK, BBEP_RED, BBEP_BLACK, BBEP_RED, BBEP_RED, BBEP_BLACK, BBEP_RED, BBEP_BLACK, BBEP_RED, BBEP_BLACK, BBEP_RED, BBEP_BLACK }; +// 2-bit grayscale mode (bits are inverted) +const uint8_t u8Colors_4gray[16] = { +BBEP_GRAY3, BBEP_GRAY2, BBEP_GRAY1, BBEP_GRAY0, BBEP_GRAY3, BBEP_GRAY2, BBEP_GRAY1, BBEP_GRAY0, +BBEP_GRAY3, BBEP_GRAY2, BBEP_GRAY1, BBEP_GRAY0, BBEP_GRAY3, BBEP_GRAY2, BBEP_GRAY1, BBEP_GRAY0 +}; +// the 4-color mode const uint8_t u8Colors_4clr[16] = { // black white red yellow BBEP_BLACK, BBEP_WHITE, BBEP_RED, BBEP_YELLOW, BBEP_BLACK, BBEP_WHITE, BBEP_RED, BBEP_YELLOW, BBEP_BLACK, BBEP_WHITE, BBEP_RED, BBEP_YELLOW, BBEP_BLACK, BBEP_WHITE, BBEP_RED, BBEP_YELLOW @@ -104,9 +112,83 @@ const uint8_t epd097r_init_sequence_full[] PROGMEM = { 0x00 }; +const uint8_t epd213r2_init_sequence_full[] PROGMEM = { + 0x02, UC8151_PSR, 0xcf, + 0x06, UC8151_PWR, 0x03, 0x10, 0x3f, 0x3f, 0x0d, + 0x01, UC8151_PON, // power on + BUSY_WAIT, + 0x04, UC8151_TRES, 122, 0, 250, // resolution + 0x04, UC8151_BTST, 0x17,0x17,0x17, // booster soft-start config - START_10MS | STRENGTH_3 | OFF_6_58US + // 0x02, 0x00, 0x0f, // LUT from OTP + 0x02, UC8151_CDI, 0x17, + 0x00 +}; +const uint8_t epd213r2_init_sequence_fast[] PROGMEM = { + 0x02, UC8151_PSR, 0xbf, // LUT from register (0x20 or'd with first byte) + 0x06, UC8151_PWR, 0x03, 0x00, 0x26, 0x32, 3, + 0x01, UC8151_PON, // power on + BUSY_WAIT, +// 0x02, UC8151_VDCS, 0x04, // VCOM DC = -0.3V + // 0x02, UC8151_PLL, 0x3a, // PLL set to 72Hz + 0x04, UC8151_TRES, 122, 0, 250, // resolution + 0x04, UC8151_BTST, 0x17,0x17,0x17, // booster soft-start config - START_10MS | STRENGTH_3 | OFF_6_58US + // 0x02, 0x00, 0x0f, // LUT from OTP + 0x02, UC8151_CDI, 0x17, + 45, 0x20, // VCOM LUT + 0x00, 0x1F, 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, + + 43, 0x21, // WW lut (not used in BWR mode) + 0x00, 0x1F, 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, + + 43, 0x22, // LUTR +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x01, 0x1f, 0x01, 0x1f, 0x02, + 0x00, 0x01, 0x00, 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, + + 43, 0x23, // LUTW + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x12, 0x01, 0x10, 0x01, 0x2f, 0x02, +// 0x00, 0x01, 0x00, 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, + + 43, 0x24, // LUTB + 0x21, 0x01, 0x10, 0x01, 0x2f, 0x02, + 0x00, 0x01, 0x00, 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 +}; + // init sequence for GDEW042Z15 const uint8_t epd42r2_init_sequence_full[] PROGMEM = { - 0x03, UC8151_PSR, 0xcf, 0x8d, + 0x02, UC8151_PSR, 0xcf, 0x06, UC8151_PWR, 0x03, 0x10, 0x3f, 0x3f, 0x0d, 0x01, UC8151_PON, // power on BUSY_WAIT, @@ -117,6 +199,65 @@ const uint8_t epd42r2_init_sequence_full[] PROGMEM = { 0x00 }; +const uint8_t epd42r2_init_sequence_fast[] PROGMEM = { + 0x02, UC8151_PSR, 0xbf, // LUT from register (0x20 or'd with first byte) + 0x06, UC8151_PWR, 0x03, 0x00, 0x26, 0x32, 3, + 0x01, UC8151_PON, // power on + BUSY_WAIT, +// 0x02, UC8151_VDCS, 0x04, // VCOM DC = -0.3V + // 0x02, UC8151_PLL, 0x3a, // PLL set to 72Hz + 0x05, UC8151_TRES, 0x01, 0x90, 1, 0x2c, // resolution + 0x04, UC8151_BTST, 0x17,0x17,0x17, // booster soft-start config - START_10MS | STRENGTH_3 | OFF_6_58US + // 0x02, 0x00, 0x0f, // LUT from OTP + 0x02, UC8151_CDI, 0x5c, // inverted, white border + 45, 0x20, // VCOM LUT + 0x00, 0x1F, 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, + + 43, 0x21, // WW lut (not used in BWR mode) + 0x00, 0x1F, 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, + + 43, 0x22, // LUTR + 0x80, 0x1F, 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, + + 43, 0x23, // LUTW + 0x40, 0x1F, 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, + + 43, 0x24, // LUTB + 0x00, 0x1F, 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 +}; + // init sequence for 2.13" Random screen double connector const uint8_t EPD213R_104x212_d_init_sequence_full[] PROGMEM = { 0x04, UC8151_BTST, 0x17,0x17,0x17, // booster soft-start config - START_10MS | STRENGTH_3 | OFF_6_58US @@ -186,9 +327,9 @@ const uint8_t epd29_init_sequence_part[] PROGMEM = { 0x04, UC8151_TRES, 0x80, 1, 0x28, // resolution 0x02, UC8151_PLL, 0x3a, // HZ_100 0x02, UC8151_VDCS, 0x12, // VCOM DC setting - 0x02, UC8151_CDI, 0xBC, // white border & non-inverted partial update old->new changes + 0x02, UC8151_CDI, 0x1C, // white border & non-inverted partial update old->new changes 0x2d, UC8151_LUT_VCOM, // VCOM LUT - 0x00, 0x19, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -197,7 +338,7 @@ const uint8_t epd29_init_sequence_part[] PROGMEM = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ,0x00, 0x00, 0x2b, UC8151_LUT_WW, // WW LUT - 0x00, 0x19, 0x01, 0x00, 0x00, 0x01, + 0x80, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -205,7 +346,7 @@ const uint8_t epd29_init_sequence_part[] PROGMEM = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, UC8151_LUT_BW, - 0x80, 0x19, 0x01, 0x00, 0x00, 0x01, + 0x80, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -213,7 +354,7 @@ const uint8_t epd29_init_sequence_part[] PROGMEM = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, UC8151_LUT_WB, - 0x40, 0x19, 0x01, 0x00, 0x00, 0x01, + 0x40, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -221,13 +362,148 @@ const uint8_t epd29_init_sequence_part[] PROGMEM = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, UC8151_LUT_BB, - 0x00, 0x19, 0x01, 0x00, 0x00, 0x01, + 0x40, 0x1e, 0x00, 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, + 0x2b, UC8151_LUT_VCOM2, // VCOM2 LUT = border + 0x00, 0x1e, 0x00, 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 // end of table +}; +const uint8_t epd29_init_sequence_fast[] PROGMEM = { + 0x02, UC8151_PSR, 0x80 | 0x20 | 0x10 | 0x08 | 0x04 | 0x02 | 0x01, // RES_128x296, LUT_REG, FORMAT_BW, SHIFT_LEFT, BOOSTER_ON, RESET_NONE + 0x06, UC8151_PWR, 0x03, 0x00, 0x2b, 0x2b, 0x03, + 0x04, UC8151_BTST, 0x17,0x17,0x17, // booster soft-start config - START_10MS | STRENGTH_3 | OFF_6_58US + 0x01, UC8151_PON, // power on + BUSY_WAIT, + 0x04, UC8151_TRES, 0x80, 1, 0x28, // resolution + 0x02, UC8151_PLL, 0x3a, // HZ_100 + 0x02, UC8151_VDCS, 0x12, // VCOM DC setting + 0x02, UC8151_CDI, 0x1C, // white border & non-inverted partial update old->new changes + 0x2d, UC8151_LUT_VCOM, // VCOM LUT + 0x00, 0x10, 0x2e, 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, + 0x2b, UC8151_LUT_WW, // WW LUT + 0x60, 0x10, 0x2e, 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, + 0x2b, UC8151_LUT_BW, + 0x60, 0x10, 0x2e, 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, + 0x2b, UC8151_LUT_WB, + 0x90, 0x10, 0x2e, 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, + 0x2b, UC8151_LUT_BB, + 0x90, 0x10, 0x2e, 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, + 0x2b, UC8151_LUT_VCOM2, // VCOM2 LUT = border + 0x00, 0x10, 0x2e, 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 // end of table +}; +const uint8_t epd29_init_sequence_gray[] PROGMEM = { + 0x02, UC8151_PSR, 0x80 | 0x20 | 0x10 | 0x08 | 0x04 | 0x02 | 0x01, // RES_128x296, LUT_REG, FORMAT_BW, SHIFT_LEFT, BOOSTER_ON, RESET_NONE + 0x06, UC8151_PWR, 0x03, 0x00, 0x2b, 0x2b, 0x03, + 0x04, UC8151_BTST, 0x17,0x17,0x17, // booster soft-start config - START_10MS | STRENGTH_3 | OFF_6_58US + 0x01, UC8151_PON, // power on + BUSY_WAIT, + 0x04, UC8151_TRES, 0x80, 1, 0x28, // resolution + 0x02, UC8151_PLL, 0x3a, // HZ_100 + 0x02, UC8151_VDCS, 0x12, // VCOM DC setting + 0x02, UC8151_CDI, 0x0c, // white border & non-inverted partial update old->new changes + 43, 0x20, // LUT 0~3 gray + 0x00, 0x1A, 0x1a, 0x1a, 0x1a, 0x01, +// 0x00, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x60, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x0A, 0x00, 0x00, 0x01, + 0x00, 0x13, 0x0A, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 43, 0x21, // WW +// 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x99, 0x1A, 0x1a, 0x1a, 0x1a, 0x01, + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x10, 0x14, 0x0A, 0x00, 0x00, 0x01, + 0xA0, 0x13, 0x0A, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 43, 0x22, // BW + 0x99, 0x1A, 0x1a, 0x1a, 0x1a, 0x01, +// 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x0A, 0x00, 0x00, 0x01, + 0x99, 0x0C, 0x01, 0x02, 0x05, 0x01, // gray 2 = 10 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 43, 0x23, // WB + 0x99, 0x1A, 0x1a, 0x1a, 0x1a, 0x01, +// 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x0A, 0x00, 0x00, 0x01, // gray 1 = 01 + 0x99, 0x0B, 0x04, 0x04, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 43, 0x24, // BB + 0x99, 0x1A, 0x1a, 0x1a, 0x1a, 0x01, +// 0x80, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x20, 0x14, 0x0A, 0x00, 0x00, 0x01, // gray 3 = 11 + 0x50, 0x13, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 43, 0x25, // Border LUT + 0x99, 0x1A, 0x1a, 0x1a, 0x1a, 0x01, +// 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x10, 0x14, 0x0A, 0x00, 0x00, 0x01, + 0xA0, 0x13, 0x0A, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // end of table }; const uint8_t epd42r_init_sequence_full[] PROGMEM = { @@ -357,20 +633,10 @@ const uint8_t epd154r_init_sequence_full[] PROGMEM = { const uint8_t epd75_init_sequence_partial[] PROGMEM = { 0x06, UC8151_PWR, 0x17, 0x17, 0x3f, 0x3f, 0x11, 0x02, 0x82, 0x26, // VCOM DC - // 0x05, UC8151_BTST, 0x27,0x27,0x2f,0x17, - // 0x02, 0x30, 0x06, // oscillator 0x01, UC8151_PON, // power on BUSY_WAIT, 0x02, UC8151_PSR, 0x3f, // from register - // 0x05, 0x61, 0x03, 0x20, 0x01, 0xe0, // resolution - // 0x02, 0x15, 0x00, // SPI mode 0x03, 0x50, 0x39, 0x07, // VCOM Data interval - // 0x02, 0x60, 0x22, // TCON - // 0x05, 0x65, 0x00, 0x00, 0x00, 0x00, // resolution - //#ifdef FUTURE - // 0x02, UC8151_PLL, 0x3a, // HZ_100 - // 0x02, UC8151_VDCS, 0x12, // VCOM DC setting - // 0x02, UC8151_CDI, 0x97, 0x2d, UC8151_LUT_VCOM, // VCOM LUT 0x00, 30, 5, 30, 5, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -417,6 +683,78 @@ const uint8_t epd75_init_sequence_partial[] PROGMEM = { //#endif // FUTURE 0x00 // end of table }; +const uint8_t epd75_init_sequence_fast[] PROGMEM = { +// 2, 0x00, 0x1e, // soft reset + 0x01, UC8151_PON, + BUSY_WAIT, + 6, 0x01, 0x07, 0x07, 0x3f, 0x3f, 0x09, // PSR + 5, 0x06, 0x17, 0x17, 0x28, 0x17, // BSTS + 5, 0x61, 0x03, 0x20, 0x01, 0xe0, // TRES + 2, 0x15, 0x00, // DUSPI + 2, 0x60, 0x22, // TCON + 2, 0xe3, 0x22, // PWS + 2, 0x00, 0x3f, // PSR + 3, 0x50, 0x21, 0x07, // VCOM LUTBD (00=white border, 30=black border) + 2, 0x82, 0x30, // -2.5v +// 0x06, UC8151_PWR, 0x17, 0x17, 0x3f, 0x3f, 0x11, +// 0x02, 0x82, 0x26, // VCOM DC +// 0x01, UC8151_PON, // power on +// BUSY_WAIT, +// 0x02, UC8151_PSR, 0x3f, // from register +// 0x03, 0x50, 0x39, 0x07, // VCOM Data interval (white border = 0x00, black = 0x30) + 0x2d, UC8151_LUT_VCOM, // VCOM LUT + 0x00, 0x01, 0x10, 0x01, 0x00, 0x01, + 0x00, 0x01, 0x1e, 0x01, 0x00, 0x01, // These need to be 00 (off) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // and have the same number + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // of frames as the other + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // LUTs in this update + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2b, UC8151_LUT_WW, // WW LUT + 0x10, 0x01, 0x10, 0x01, 0x00, 0x01, + 0x20, 0x01, 0x1e, 0x01, 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, + 0x2b, UC8151_LUT_BW, + 0x10, 0x01, 0x10, 0x01, 0x00, 0x01, + 0x20, 0x01, 0x1e, 0x01, 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, + 0x2b, UC8151_LUT_WB, + 0x20, 0x01, 0x10, 0x01, 0x00, 0x01, + 0x10, 0x01, 0x1e, 0x01, 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, + 0x2b, UC8151_LUT_BB, + 0x20, 0x01, 0x10, 0x01, 0x00, 0x01, + 0x10, 0x01, 0x1e, 0x01, 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, + 0x2b, UC8151_LUT_VCOM2, + 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, 0x00, 0x00, 0x00, 0x00, + + 0x00 // end of table +}; + // 1.54" 152x152 const uint8_t epd154a_init_sequence_full[] PROGMEM = { @@ -644,6 +982,86 @@ const uint8_t epd213b_init_sequence_part[] PROGMEM = 0x00 // end of table }; +const uint8_t epd213z_init_full[] PROGMEM = +{ + 0x01, 0x12, // soft reset + BUSY_WAIT, + 0x04, 0x01, 0xf9, 0x00, 0x00, + 0x02, 0x011, 0x03, + 0x03, 0x44, 0x00, 0x0f, + 0x05, 0x45, 0x00, 0x00, 0xf9, 0x00, + 0x02, 0x3c, 0x01, + BUSY_WAIT, + 0x02, 0x18, 0x80, + 0x02, 0x4e, 0x00, + 0x03, 0x4f, 0x00, 0x00, + BUSY_WAIT, + 0 // end +}; /* epd213z_init_full */ + +const uint8_t epd213z_init_fast[] PROGMEM = +{ + 0x01, 0x12, // soft reset + BUSY_WAIT, + 0x04, 0x01, 0xf9, 0x00, 0x00, + 0x02, 0x011, 0x03, + 0x03, 0x44, 0x00, 0x0f, + 0x05, 0x45, 0x00, 0x00, 0xf9, 0x00, + 0x02, 0x3c, 0x01, + BUSY_WAIT, + 0x02, 0x18, 0x80, + 0x02, 0x4e, 0x00, + 0x03, 0x4f, 0x00, 0x00, + BUSY_WAIT, + 0x01, 0x12, // soft reset + 0x02, 0x18, 0x80, + 0x02, 0x22, 0xb1, + 0x01, 0x20, + BUSY_WAIT, + 0x03, 0x1a, 0x64, 0x00, + 0x02, 0x22, 0x91, + 0x01, 0x20, + BUSY_WAIT, + 0 // end +}; /* epd213z_init_fast */ + +const uint8_t epd29z_init_full[] PROGMEM = +{ + BUSY_WAIT, + 0x04, 0x01,0x27,0x01,0x00, + 0x02, 0x11, 0x03, + 0x03, 0x44, 0x00, 0x0f, + 0x05, 0x45, 0x00, 0x00, 0x27, 0x01, + 0x02, 0x3c, 0x01, + 0x02, 0x18, 0x80, + 0x02, 0x4e, 0x00, + 0x03, 0x4f, 0x00, 0x00, + BUSY_WAIT, + 0, // end +}; /* epd29z_init_full */ + +const uint8_t epd29z_init_fast[] PROGMEM = +{ + BUSY_WAIT, + 0x04, 0x01,0x27,0x01,0x00, + 0x02, 0x11, 0x03, + 0x03, 0x44, 0x00, 0x0f, + 0x05, 0x45, 0x00, 0x00, 0x27, 0x01, + 0x02, 0x3c, 0x01, + 0x02, 0x18, 0x80, + 0x02, 0x4e, 0x00, + 0x03, 0x4f, 0x00, 0x00, + BUSY_WAIT, + 0x02, 0x22, 0xb1, + 0x01, 0x20, + BUSY_WAIT, + 0x03, 0x1a, 0x64, 0x00, + 0x02, 0x22, 0x91, + 0x01, 0x20, + BUSY_WAIT, + 0, // end +}; /* epd29z_init_fast */ + const uint8_t epd294_init_sequence_full[] PROGMEM = { // 0x02, 0x74, 0x54, @@ -971,6 +1389,41 @@ const uint8_t ep7_init_partial[] PROGMEM = 0 }; +// 2-bit grayscale mode initialization +const uint8_t epd426g_init[] PROGMEM = +{ + 0x01, SSD1608_SW_RESET, + BUSY_WAIT, + 0x02, 0x18, 0x80, // read built-in temp sensor + + 0x06, 0x0c, 0xae, 0xc7, 0xc3, 0xc0, 0x80, // set soft start + 0x04, 0x01, 0xdf, 0x01, 0x02, // driver output control + 0x02, 0x3c, 0x00, // border waveform (0=white, 3=black) + 0x02, 0x11, 0x01, // data entry mode + 0x05, 0x44, 0x00, 0x00, 0xdf, 0x01, // ram start/end + 0x05, 0x45, 0x00, 0x00, 0x1f, 0x03, + 0x03, 0x4e, 0x00, 0x00, + 0x03, 0x4f, 0x00, 0x00, + BUSY_WAIT, + 106, 0x32, // VCOM LUT (105 bytes) + 0x80,0x48,0x4A,0x22,0x00,0x00,0x00,0x00,0x00,0x00, // 00-09 + 0x0A,0x48,0x68,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 10-19 + 0x88,0x48,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 20-29 + 0xA8,0x48,0x45,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 30-39 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 40-49 + 0x07,0x1E,0x1C,0x02,0x00,0x05,0x01,0x05,0x01,0x02, // 50-59 + 0x08,0x01,0x01,0x04,0x04,0x00,0x02,0x00,0x02,0x01, // 60-69 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 70-79 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 80-89 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01, // 90-99 + 0x22,0x22,0x22,0x22,0x22, // 100-104 + 0x02, 0x03, 0x17, // VGH + 0x4, 0x04, 0x41, 0xa8, 0x32, // VSH1,VSH2,VSL + 0x2, 0x2c, 0x30, // VCOM voltage + 0 // end +}; // 2-bit grayscale mode + +// 1-bit full update mode const uint8_t epd426_init_full[] PROGMEM = { 0x01, SSD1608_SW_RESET, @@ -991,6 +1444,31 @@ const uint8_t epd426_init_full[] PROGMEM = 0x00 // end of table }; +// 1-bit fast update mode +const uint8_t epd426_init_fast[] PROGMEM = +{ + 0x01, SSD1608_SW_RESET, + BUSY_WAIT, + 0x02, 0x18, 0x80, // read built-in temp sensor + + 0x06, 0x0c, 0xae, 0xc7, 0xc3, 0xc0, 0x80, // set soft start + 0x04, 0x01, 0xdf, 0x01, 0x02, // driver output control + 0x02, 0x3c, 0x01, // border waveform + 0x02, 0x11, 0x01, // data entry mode + 0x05, 0x44, 0x00, 0x00, 0xdf, 0x01, // ram start/end + 0x05, 0x45, 0x00, 0x00, 0x1f, 0x03, + + 0x03, 0x4e, 0x00, 0x00, + 0x03, 0x4f, 0x00, 0x00, + BUSY_WAIT, +// secret sauce for doing a 1.5s update + 0x02, 0x1a, 0x5a, // temp? + 0x02, 0x22, 0x91, // ?? + 0x01, 0x20, // ?? + BUSY_WAIT, + 0x00 // end of table +}; + const uint8_t epd42b_init_sequence_full[] PROGMEM = { 0x01, SSD1608_SW_RESET, @@ -1128,16 +1606,16 @@ const uint8_t epd213_122x250_init_sequence_part[] PROGMEM = { 0 }; -const uint8_t epd75_init_sequence_fast[] PROGMEM = { - 2, 0x00, 0x1f, // panel setting - 3, 0x50, 0x10, 0x07, // VCOM - 1, 0x04, // Power On - BUSY_WAIT, - 5, 0x06, 0x27, 0x27, 0x18, 0x17, // booster soft start - 2, 0xe0, 0x02, - 2, 0xe5, 0x5a, - 0 -}; +//const uint8_t epd75_init_sequence_fast[] PROGMEM = { +// 2, 0x00, 0x1f, // panel setting +// 3, 0x50, 0x10, 0x07, // VCOM +// 1, 0x04, // Power On +// BUSY_WAIT, +// 5, 0x06, 0x27, 0x27, 0x18, 0x17, // booster soft start +// 2, 0xe0, 0x02, +// 2, 0xe5, 0x5a, +// 0 +//}; const uint8_t epd583_init_sequence_full[] PROGMEM = { 2, UC8151_PSR, 0x9f, @@ -1280,6 +1758,64 @@ const uint8_t epd42_init_sequence_part[] PROGMEM = { 0x00 // end of table }; +const uint8_t epd42_init_sequence_fast[] PROGMEM = { + 1, UC8151_PON, + BUSY_WAIT, + 3, 0x00, 0x1f, 0x0b, // panel setting +// 2, 0x50, 0x97, // VCOM + 5, 0x01, 0x03,0x00,0x2b,0x2b, // power setting + 4, 0x06, 0x17,0x17,0x17, // boost soft start + 1, 0x04, // power on + BUSY_WAIT, + 3, 0x00, 0xbf, 0x0b, // panel setting + 2, 0x30, 0x3c, // 3A 100HZ + 5, 0x61, 0x01, 0x90, 0x01, 0x2c, // resolution 400x300 + 2, 0x82, 0x12, // vcom_DC + 2, 0x50, 0xB7, // mode + 45, 0x20, // VCOM LUT + 0x00, 0x01, 0x20, 0x01, 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, + 43, 0x21, // WW LUT + 0x10, 0x01, 0x10, 0x01, 0x00, 0x01, + 0x20, 0x01, 0x1e, 0x01, 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, + 43, 0x22, // BW LUT + 0x10, 0x01, 0x10, 0x01, 0x00, 0x01, + 0x20, 0x01, 0x1e, 0x01, 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, + 43, 0x23, // WB LUT + 0x20, 0x01, 0x10, 0x01, 0x00, 0x01, + 0x10, 0x01, 0x1e, 0x01, 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, + 43, 0x24, // BB LUT + 0x20, 0x01, 0x10, 0x01, 0x00, 0x01, + 0x10, 0x01, 0x1e, 0x01, 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 // end of table +}; + const uint8_t epd294_init_sequence_part[] = { 0x0b, 0x37, 0,0,0,0,0,0x40,0,0,0,0, @@ -1310,6 +1846,87 @@ const uint8_t epd213_inky_init_sequence_full[] PROGMEM = { 0 }; + +// 2-bit (4 grayscale mode) for GDEY075T7 (older model) +const uint8_t epd75_old_gray_init[] PROGMEM = { + 2, 0x00, 0x1e, // soft reset + BUSY_WAIT, + 6, 0x01, 0x07, 0x07, 0x3f, 0x3f, 0x09, // PSR + 5, 0x06, 0x17, 0x17, 0x28, 0x17, // BSTS + 5, 0x61, 0x03, 0x20, 0x01, 0xe0, // TRES + 2, 0x15, 0x00, // DUSPI + 2, 0x60, 0x22, // TCON + 2, 0xe3, 0x22, // PWS + 2, 0x00, 0x3f, // PSR + 3, 0x50, 0x00, 0x07, // VCOM LUTBD (00=white border, 30=black border) + 2, 0x82, 0x30, // -2.5v + 43, 0x20, // LUT 0~3 gray + 0x00, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x60, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x0A, 0x00, 0x00, 0x01, + 0x00, 0x13, 0x0A, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 43, 0x21, // WW + 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x20, 0x14, 0x0A, 0x00, 0x00, 0x01, + 0xA0, 0x13, 0x0A, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 43, 0x22, // BW + 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x0A, 0x00, 0x00, 0x01, + 0x99, 0x0C, 0x01, 0x03, 0x04, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 43, 0x23, // WB + 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x0A, 0x00, 0x00, 0x01, + 0x99, 0x0B, 0x04, 0x04, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 43, 0x24, // BB + 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x10, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x50, 0x14, 0x0A, 0x00, 0x00, 0x01, + 0x50, 0x23, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 43, 0x25, // Border LUT + 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x10, 0x14, 0x0A, 0x00, 0x00, 0x01, + 0xA0, 0x13, 0x0A, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 2, 0xe0, 0x00, // CCSET + 2, 0x41, 0x00, // TSE + 1, 0x04, // Power on + BUSY_WAIT, + 0 // end +}; + +// 2-bit (4 grayscale mode) +const uint8_t epd75_gray_init[] PROGMEM = { + 2, 0x00, 0x1f, // panel setting + 3, 0x50, 0x10, 0x07, // VCOM + 1, UC8151_PON, // power on + BUSY_WAIT, + 5, 0x06, 0x27, 0x27, 0x18, 0x17, // booster soft start + 2, 0xe0, 0x02, // 1.5s + 2, 0xe5, 0x5f, // 4gray mode + 0 +}; // 2-bit grayscale mode + const uint8_t epd75_init_sequence_full[] PROGMEM = { 6, UC8151_PWR, 0x07, 0x07, 0x3f, 0x3f, 0x03, 1, UC8151_PON, @@ -1502,7 +2119,7 @@ uint8_t u8Cache[512]; // const EPD_PANEL panelDefs[] PROGMEM = { {0,0,0,NULL,NULL,NULL,0,0,NULL}, // undefined panel - {400, 300, 0, epd42_init_sequence_full, NULL, epd42_init_sequence_part, 0, BBEP_CHIP_UC81xx, u8Colors_2clr}, // EP42_400x300 + {400, 300, 0, epd42_init_sequence_full, epd42_init_sequence_fast, epd42_init_sequence_part, 0, BBEP_CHIP_UC81xx, u8Colors_2clr}, // EP42_400x300 {400, 300, 0, epd42b_init_sequence_full, epd42b_init_sequence_fast, epd42b_init_sequence_part, 0, BBEP_CHIP_SSD16xx, u8Colors_2clr}, // EP42B_400x300 {122, 250, 0, epd213_122x250_init_sequence_full, NULL, epd213_122x250_init_sequence_part, 0, BBEP_CHIP_SSD16xx, u8Colors_2clr}, // EP213_122x250 WaveShare {122, 250, 0, epd213b_init_sequence_full, NULL, epd213b_init_sequence_part, 0, BBEP_CHIP_SSD16xx, u8Colors_2clr}, // EP213B_122x250, newer Inky phat 2.13" B/W @@ -1516,11 +2133,14 @@ const EPD_PANEL panelDefs[] PROGMEM = { {192, 176, 0, epd122_init_sequence_full, epd122_init_sequence_fast, epd122_init_sequence_part, 0, BBEP_CHIP_SSD16xx, u8Colors_2clr}, // EP122_192x176 {152, 152, 0, epd154r_init_sequence_full, NULL, NULL, BBEP_3COLOR, BBEP_CHIP_SSD16xx, u8Colors_3clr}, // EP154R_152x152 {400, 300, 0, epd42r_init_sequence_full, NULL, NULL, BBEP_3COLOR, BBEP_CHIP_SSD16xx, u8Colors_3clr}, // EP42R_400x300, Inky wHAT 4.2" 400x300 B/W/R - {400, 300, 0, epd42r2_init_sequence_full, NULL, NULL, BBEP_RED_SWAPPED | BBEP_3COLOR, BBEP_CHIP_UC81xx, u8Colors_3clr}, // EP42R2_400x300 + {400, 300, 0, epd42r2_init_sequence_full, epd42r2_init_sequence_fast, NULL, BBEP_RED_SWAPPED | BBEP_3COLOR, BBEP_CHIP_UC81xx, u8Colors_3clr}, // EP42R2_400x300 {240, 416, 0, epd37_init_sequence_full, NULL, epd37_init_sequence_part, 0, BBEP_CHIP_UC81xx, u8Colors_2clr}, // EP37_240x416 {104, 212, 0, epd213_inky_init_sequence_full, NULL, NULL, 0, BBEP_CHIP_UC81xx, u8Colors_2clr}, // EP213_104x212, older InkyPHAT black and white {800, 480, 0, epd75_init_sequence_full, epd75_init_sequence_fast, epd75_init_sequence_partial, 0, BBEP_CHIP_UC81xx, u8Colors_2clr}, // EP75_800x480 - {128, 296, 0, epd29_init_sequence_full, NULL, epd29_init_sequence_part, 0, BBEP_CHIP_UC81xx, u8Colors_2clr}, // Badger 2040 + {800, 480, 0, epd75_gray_init, NULL, NULL, BBEP_4GRAY, BBEP_CHIP_UC81xx, u8Colors_4gray}, // EP75_800x480_4GRAY + {800, 480, 0, epd75_old_gray_init, NULL, NULL, BBEP_4GRAY, BBEP_CHIP_UC81xx, u8Colors_4gray}, // EP75_800x480_4GRAY_OLD + {128, 296, 0, epd29_init_sequence_full, epd29_init_sequence_fast, epd29_init_sequence_part, 0, BBEP_CHIP_UC81xx, u8Colors_2clr}, // Badger 2040 + {128, 296, 0, epd29_init_sequence_gray, NULL, NULL, BBEP_4GRAY, BBEP_CHIP_UC81xx, u8Colors_4gray}, // Badger 2040 4Gray mode {122, 250, 1, epd213r_inky_init_sequence_full, NULL, NULL, BBEP_3COLOR, BBEP_CHIP_SSD16xx, u8Colors_3clr}, // EP213R_122x250 Inky phat 2.13" B/W/R {200, 200, 0, epd154_init_sequence_full, epd154_init_sequence_fast, epd154_init_sequence_part, 0, BBEP_CHIP_SSD16xx, u8Colors_2clr}, // EP154_200x200 {200, 200, 0, epd154_init_sequence_full, epd154_init_sequence_fast, epd154b_init_sequence_part, 0, BBEP_CHIP_SSD16xx, u8Colors_2clr}, // EP154B_200x200 @@ -1536,11 +2156,15 @@ const EPD_PANEL panelDefs[] PROGMEM = { {640, 384, 0, epd74r_init, NULL, NULL, BBEP_3COLOR | BBEP_4BPP_DATA, BBEP_CHIP_UC81xx, u8Colors_3clr}, // EP74R_640x384, 3-color 640x384 {600, 448, 0, epd583r_init, NULL, NULL, BBEP_3COLOR | BBEP_4BPP_DATA, BBEP_CHIP_UC81xx, u8Colors_3clr}, // EP583R_600x448, 3-color 600x448 {800, 480, 0, epd75r_init, NULL, NULL, BBEP_3COLOR | BBEP_RED_SWAPPED, BBEP_CHIP_UC81xx, u8Colors_3clr}, // EP75R_800x480, waveshare 7.5 800x480 B/W/R - {800, 480, 0, epd426_init_full, NULL, epd426_init_part, 0, BBEP_CHIP_SSD16xx, u8Colors_2clr}, + {800, 480, 0, epd426_init_full, epd426_init_fast, epd426_init_part, 0, BBEP_CHIP_SSD16xx, u8Colors_2clr}, + {800, 480, 0, epd426g_init, NULL, NULL, BBEP_4GRAY, BBEP_CHIP_SSD16xx, u8Colors_4gray}, // 2-bit grayscale mode {128, 296, 0, epd29r2_init_sequence_full, NULL, NULL, BBEP_RED_SWAPPED | BBEP_3COLOR, BBEP_CHIP_UC81xx, u8Colors_3clr}, // EP29R2_128x296 Adafruit 2.9" 128x296 Tricolor FeatherWing {640, 400, 0, epd41_init_sequence_full, NULL, NULL, BBEP_4BPP_DATA, BBEP_CHIP_UC81xx, u8Colors_2clr}, // EP41_640x400 Eink ED040TC1 {1024, 576, 0, epd81c_init_full, NULL, NULL, BBEP_SPLIT_BUFFER | BBEP_7COLOR, BBEP_CHIP_UC81xx, u8Colors_spectra}, // 8.1" 1024x576 dual cable Spectra 6 EP81_SPECTRA_1024x576 - {960, 640, 0, ep7_init, NULL, ep7_init_partial, 0, BBEP_CHIP_SSD16xx, u8Colors_2clr} // EP7_960x640 (ED070EC1) + {960, 640, 0, ep7_init, NULL, ep7_init_partial, 0, BBEP_CHIP_SSD16xx, u8Colors_2clr}, // EP7_960x640 (ED070EC1) + {122, 250, 0, epd213r2_init_sequence_full, epd213r2_init_sequence_fast, NULL, BBEP_RED_SWAPPED | BBEP_3COLOR, BBEP_CHIP_UC81xx, u8Colors_3clr}, // EP213R2_122x250 3 color + {128, 296, 0, epd29z_init_full, epd29z_init_fast, epd29z_init_full, 0, BBEP_CHIP_SSD16xx, u8Colors_2clr}, // CrowPanel 2.9 (EP29Z_128x296) + {122, 250, 0, epd213z_init_full, epd213z_init_fast, epd213z_init_full, 0, BBEP_CHIP_SSD16xx, u8Colors_2clr}, // CrowPanel 2.13 (EP213Z_122x250) }; // // Set the e-paper panel type @@ -1551,7 +2175,6 @@ int bbepSetPanelType(BBEPDISP *pBBEP, int iPanel) if (pBBEP == NULL || iPanel <= EP_PANEL_UNDEFINED || iPanel >= EP_PANEL_COUNT) return BBEP_ERROR_BAD_PARAMETER; - memset(pBBEP, 0, sizeof(BBEPDISP)); pBBEP->native_width = pBBEP->width = panelDefs[iPanel].width; pBBEP->native_height = pBBEP->height = panelDefs[iPanel].height; pBBEP->x_offset = panelDefs[iPanel].x_offset; @@ -1566,6 +2189,9 @@ int bbepSetPanelType(BBEPDISP *pBBEP, int iPanel) if (pBBEP->iFlags & BBEP_4COLOR) { pBBEP->pfnSetPixel = bbepSetPixel4Clr; pBBEP->pfnSetPixelFast = bbepSetPixelFast4Clr; + } else if (pBBEP->iFlags & BBEP_4GRAY) { + pBBEP->pfnSetPixel = bbepSetPixel4Gray; + pBBEP->pfnSetPixelFast = bbepSetPixelFast4Gray; } else if (pBBEP->iFlags & BBEP_3COLOR) { pBBEP->pfnSetPixel = bbepSetPixel3Clr; pBBEP->pfnSetPixelFast = bbepSetPixelFast3Clr; @@ -1592,6 +2218,10 @@ int bbepCreateVirtual(BBEPDISP *pBBEP, int iWidth, int iHeight, int iFlags) pBBEP->pColorLookup = u8Colors_4clr; pBBEP->pfnSetPixel = bbepSetPixel4Clr; pBBEP->pfnSetPixelFast = bbepSetPixelFast4Clr; + } else if (iFlags & BBEP_4GRAY) { + pBBEP->pColorLookup = u8Colors_4clr; + pBBEP->pfnSetPixel = bbepSetPixel4Gray; + pBBEP->pfnSetPixelFast = bbepSetPixelFast4Gray; } else if (iFlags & BBEP_3COLOR) { pBBEP->pColorLookup = u8Colors_3clr; pBBEP->pfnSetPixel = bbepSetPixel3Clr; @@ -1610,6 +2240,16 @@ int bbepCreateVirtual(BBEPDISP *pBBEP, int iWidth, int iHeight, int iFlags) return BBEP_ERROR_BAD_PARAMETER; } } +// Put the ESP32 into light sleep for N milliseconds +void bbepLightSleep(uint32_t u32Millis) +{ +#ifdef ARDUINO_ARCH_ESP32 + esp_sleep_enable_timer_wakeup(u32Millis * 1000L); + esp_light_sleep_start(); +#else + delay(u32Millis); +#endif +} // // Wait for the busy status line to show idle // The polarity of the busy signal is reversed on the UC81xx compared @@ -1626,7 +2266,8 @@ void bbepWaitBusy(BBEPDISP *pBBEP) delay(1); // some panels need a short delay before testing the BUSY line while (iTimeout < 5000) { if (digitalRead(pBBEP->iBUSYPin) == busy_idle) break; - delay(1); + // delay(1); + bbepLightSleep(200); // save battery power by checking every 200ms } } /* bbepWaitBusy() */ // @@ -1693,7 +2334,7 @@ void bbepSetAddrWindow(BBEPDISP *pBBEP, int x, int y, int cx, int cy) uc[i++] = tx; // start x uc[i++] = (tx+cx-1) | 7; // end x } - if (pBBEP->native_height >= 256) { + if (pBBEP->native_height >= 250) { uc[i++] = (uint8_t)(ty>>8); // start y uc[i++] = (uint8_t)ty; uc[i++] = (uint8_t)((ty+cy-1)>>8); // end y @@ -1709,7 +2350,7 @@ void bbepSetAddrWindow(BBEPDISP *pBBEP, int x, int y, int cx, int cy) // bbepCMD2(pBBEP, SSD1608_DATA_MODE, 0x3); bbepWriteCmd(pBBEP, SSD1608_SET_RAMXPOS); tx += pBBEP->x_offset; - if (pBBEP->type == EP7_960x640 || pBBEP->type == EP426_800x480) { // pixels, not bytes version + if (pBBEP->type == EP7_960x640 || pBBEP->type == EP426_800x480 || pBBEP->type == EP426_800x480_4GRAY) { // pixels, not bytes version if (pBBEP->type == EP7_960x640) { tx <<= 3; } @@ -1732,7 +2373,7 @@ void bbepSetAddrWindow(BBEPDISP *pBBEP, int x, int y, int cx, int cy) } bbepWriteCmd(pBBEP, SSD1608_SET_RAMYPOS); - if (pBBEP->type == EP426_800x480) { // flipped y + if (pBBEP->type == EP426_800x480 || pBBEP->type == EP426_800x480_4GRAY) { // flipped y uc[2] = (uint8_t)ty; // start y uc[3] = (uint8_t)(ty>>8); uc[0] = (uint8_t)(ty+cy-1); // end y @@ -1885,6 +2526,9 @@ void bbepFill(BBEPDISP *pBBEP, unsigned char ucColor, int iPlane) iPlane = PLANE_0; // only 1 2-bit memory plane uc1 = ucColor | (ucColor << 2); uc1 |= (uc1 << 4); // set color in all 4 pixels of the byte + } else if (pBBEP->iFlags & BBEP_4GRAY) { + uc1 = (ucColor & 1) ? 0xff : 0x00; + uc2 = (ucColor & 2) ? 0xff : 0x00; } else { // B/W if (ucColor == BBEP_WHITE) { uc1 = uc2 = 0xff; @@ -1896,15 +2540,17 @@ void bbepFill(BBEPDISP *pBBEP, unsigned char ucColor, int iPlane) if (pBBEP->iFlags & BBEP_7COLOR) { memset(pBBEP->ucScreen, uc1, iSize); return; - } else if ((pBBEP->iFlags & BBEP_3COLOR) || iPlane == PLANE_BOTH) { + } else if ((pBBEP->iFlags & (BBEP_4GRAY | BBEP_3COLOR)) || iPlane == PLANE_BOTH) { memset(pBBEP->ucScreen, uc1, iSize); memset(&pBBEP->ucScreen[iSize], uc2, iSize); } else if (iPlane == PLANE_DUPLICATE) { memset(pBBEP->ucScreen, uc1, iSize); - memset(&pBBEP->ucScreen[iSize], uc1, iSize); + if (pBBEP->iFlags & BBEP_HAS_SECOND_PLANE) { + memset(&pBBEP->ucScreen[iSize], uc1, iSize); + } } else if (iPlane == PLANE_0) { memset(pBBEP->ucScreen, uc1, iSize); - } else if (iPlane == PLANE_1) { + } else if (iPlane == PLANE_1 && (pBBEP->iFlags & BBEP_HAS_SECOND_PLANE)) { memset(&pBBEP->ucScreen[iSize], uc2, iSize); } } else { // write directly to the EPD's framebuffer @@ -1916,7 +2562,7 @@ void bbepFill(BBEPDISP *pBBEP, unsigned char ucColor, int iPlane) } else if (ucColor == BBEP_RED) { uc1 = 0x00; uc2 = 0xff; } - } else if (!(pBBEP->iFlags & BBEP_4COLOR)) { // for B/W, both planes get the same data + } else if (!(pBBEP->iFlags & (BBEP_4GRAY | BBEP_4COLOR))) { // for B/W, both planes get the same data if (ucColor == BBEP_WHITE) ucColor = 0xff; else if (ucColor == BBEP_BLACK) ucColor = 0; uc1 = uc2 = ucColor; @@ -1979,9 +2625,11 @@ int bbepRefresh(BBEPDISP *pBBEP, int iMode) } break; case REFRESH_FAST: - if (!pBBEP->pInitFast) - return BBEP_ERROR_BAD_PARAMETER; - bbepSendCMDSequence(pBBEP, pBBEP->pInitFast); + if (!pBBEP->pInitFast) { // fall back to full + bbepSendCMDSequence(pBBEP, pBBEP->pInitFull); + } else { + bbepSendCMDSequence(pBBEP, pBBEP->pInitFast); + } break; case REFRESH_PARTIAL: if (!pBBEP->pInitPart) @@ -1992,7 +2640,7 @@ int bbepRefresh(BBEPDISP *pBBEP, int iMode) return BBEP_ERROR_BAD_PARAMETER; } // switch on mode if (pBBEP->chip_type == BBEP_CHIP_UC81xx) { - if (pBBEP->iFlags & (BBEP_4COLOR | BBEP_7COLOR)) { + if (pBBEP->iFlags & (BBEP_4GRAY | BBEP_4COLOR | BBEP_7COLOR)) { bbepCMD2(pBBEP, UC8151_DRF, 0x00); if (pBBEP->iFlags & BBEP_SPLIT_BUFFER) { // Send the same sequence to the second controller @@ -2006,13 +2654,18 @@ int bbepRefresh(BBEPDISP *pBBEP, int iMode) } } else { const uint8_t u8CMD[4] = {0xf7, 0xc7, 0xff, 0xc0}; // normal, fast, partial, partial2 - if (pBBEP->iFlags & BBEP_3COLOR) { + const uint8_t u8CMDz[4] = {0xf4, 0xc7, 0xfc, 0}; // special set for SSD1680 + if (pBBEP->iFlags & (BBEP_4GRAY | BBEP_3COLOR | BBEP_4COLOR)) { iMode = REFRESH_FAST; - } // 3-color = 0xc7 + } // 3/4-color = 0xc7 if (iMode == REFRESH_PARTIAL && pBBEP->iFlags & BBEP_PARTIAL2) { iMode = REFRESH_PARTIAL2; // special case for custom LUT } - bbepCMD2(pBBEP, SSD1608_DISP_CTRL2, u8CMD[iMode]); + if (pBBEP->type == EP29Z_128x296 || pBBEP->type == EP213Z_122x250) { + bbepCMD2(pBBEP, SSD1608_DISP_CTRL2, u8CMDz[iMode]); + } else { + bbepCMD2(pBBEP, SSD1608_DISP_CTRL2, u8CMD[iMode]); + } bbepWriteCmd(pBBEP, SSD1608_MASTER_ACTIVATE); // refresh } return BBEP_SUCCESS; @@ -2498,12 +3151,12 @@ static void bbepWriteImage(BBEPDISP *pBBEP, uint8_t ucCMD, uint8_t *pBuffer, int // // Write the local copy of the memory plane(s) to the eink's internal framebuffer // -int bbepWritePlane(BBEPDISP *pBBEP, int iPlane) +int bbepWritePlane(BBEPDISP *pBBEP, int iPlane, int bInvert) { uint8_t ucCMD1, ucCMD2; int iOffset; - if (pBBEP == NULL || pBBEP->ucScreen == NULL || iPlane < PLANE_0 || iPlane > PLANE_DUPLICATE) { + if (pBBEP == NULL || pBBEP->ucScreen == NULL || iPlane < PLANE_0 || iPlane > PLANE_FALSE_DIFF) { return BBEP_ERROR_BAD_PARAMETER; } bbepSetAddrWindow(pBBEP, 0,0, pBBEP->native_width, pBBEP->native_height); @@ -2512,7 +3165,7 @@ int bbepWritePlane(BBEPDISP *pBBEP, int iPlane) if (pBBEP->iFlags & BBEP_3COLOR) { bbepWriteImage4bppSpecial(pBBEP, 0x10); } else { - bbepWriteImage1to4bpp(pBBEP, 0x10, pBBEP->ucScreen, 0); + bbepWriteImage1to4bpp(pBBEP, 0x10, pBBEP->ucScreen, bInvert); } return BBEP_SUCCESS; } @@ -2546,18 +3199,28 @@ int bbepWritePlane(BBEPDISP *pBBEP, int iPlane) } switch (iPlane) { case PLANE_0: - bbepWriteImage(pBBEP, ucCMD1, pBBEP->ucScreen, 0); + bbepWriteImage(pBBEP, ucCMD1, pBBEP->ucScreen, bInvert); break; case PLANE_1: - bbepWriteImage(pBBEP, ucCMD2, &pBBEP->ucScreen[iOffset], 0); + bbepWriteImage(pBBEP, ucCMD2, &pBBEP->ucScreen[iOffset], bInvert); + break; + case PLANE_0_TO_1: + bbepWriteImage(pBBEP, ucCMD2, pBBEP->ucScreen, bInvert); break; case PLANE_BOTH: - bbepWriteImage(pBBEP, ucCMD1, pBBEP->ucScreen, 0); - bbepWriteImage(pBBEP, ucCMD2, &pBBEP->ucScreen[iOffset], 0); + bbepWriteImage(pBBEP, ucCMD1, pBBEP->ucScreen, bInvert); + if (pBBEP->iFlags & BBEP_HAS_SECOND_PLANE) { + bbepWriteImage(pBBEP, ucCMD2, &pBBEP->ucScreen[iOffset], bInvert); + } break; case PLANE_DUPLICATE: + bbepWriteImage(pBBEP, ucCMD1, pBBEP->ucScreen, bInvert); + bbepWriteImage(pBBEP, ucCMD2, pBBEP->ucScreen, bInvert); + break; + + case PLANE_FALSE_DIFF: // send inverted image to 'old' plane bbepWriteImage(pBBEP, ucCMD1, pBBEP->ucScreen, 0); - bbepWriteImage(pBBEP, ucCMD2, pBBEP->ucScreen, 0); + bbepWriteImage(pBBEP, ucCMD2, pBBEP->ucScreen, 1); break; } return BBEP_SUCCESS; diff --git a/src/bb_ep_gfx.inl b/src/bb_ep_gfx.inl index 04ab9c0..c5e69cd 100644 --- a/src/bb_ep_gfx.inl +++ b/src/bb_ep_gfx.inl @@ -25,7 +25,7 @@ static G5DECIMAGE g5dec; // forward declarations void InvertBytes(uint8_t *pData, uint8_t bLen); - +void bbepUnicodeString(const char *szMsg, uint8_t *szExtMsg); const uint8_t ucFont[]PROGMEM = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x5f,0x5f,0x06,0x00, 0x00,0x07,0x07,0x00,0x07,0x07,0x00,0x14,0x7f,0x7f,0x14,0x7f,0x7f,0x14, @@ -174,6 +174,77 @@ const uint8_t ucSmallFont[] PROGMEM = { 0x00,0x41,0x41,0x3e,0x08, 0x02,0x01,0x02,0x01,0x00, 0x3c,0x26,0x23,0x26,0x3c}; +// +// Get the size of text in a custom font area +// +void bbepGetStringBox(BBEPDISP *pBBEP, const char *szMsg, BB_RECT *pRect) +{ +int cx = 0; +unsigned int c, i = 0; +BB_FONT *pBBF; +BB_FONT_SMALL *pBBFS; +BB_GLYPH *pGlyph; +BB_GLYPH_SMALL *pSmallGlyph; +int miny, maxy; +uint8_t szExtMsg[80]; + + miny = 4000; maxy = 0; + if (pBBEP == NULL || pRect == NULL || szMsg == NULL) return; // bad pointers + + if (pBBEP->pFont == NULL) { // built-in font + miny = 0; + switch (pBBEP->iFont) { + case FONT_6x8: + cx = 6; + maxy = 8; + break; + case FONT_8x8: + cx = 8; + maxy = 8; + break; + case FONT_12x16: + cx = 12; + maxy = 16; + break; + case FONT_16x16: + cx = 16; + maxy = 16; + break; + } + cx *= strlen(szMsg); + } else { // proportional fonts + bbepUnicodeString(szMsg, szExtMsg); // convert to extended ASCII + if (pgm_read_word(pBBEP->pFont) == BB_FONT_MARKER) { + pBBF = (BB_FONT *)pBBEP->pFont; pBBFS = NULL; + } else { // small font + pBBFS = (BB_FONT_SMALL *)pBBEP->pFont; pBBF = NULL; + } + while (szExtMsg[i]) { + c = szExtMsg[i++]; + if (pBBF) { // big font + if (c < pgm_read_byte(&pBBF->first) || c > pgm_read_byte(&pBBF->last)) // undefined character + continue; // skip it + c -= pgm_read_byte(&pBBF->first); // first char of font defined + pGlyph = &pBBF->glyphs[c]; + cx += pgm_read_word(&pGlyph->xAdvance); + if ((int16_t)pgm_read_word(&pGlyph->yOffset) < miny) miny = pgm_read_word(&pGlyph->yOffset); + if (pgm_read_word(&pGlyph->height)+(int16_t)pgm_read_word(&pGlyph->yOffset) > maxy) maxy = pgm_read_word(&pGlyph->height)+(int16_t)pgm_read_word(&pGlyph->yOffset); + } else { // small font + if (c < pgm_read_byte(&pBBFS->first) || c > pgm_read_byte(&pBBFS->last)) // undefined character + continue; // skip it + c -= pgm_read_byte(&pBBFS->first); // first char of font defined + pSmallGlyph = &pBBFS->glyphs[c]; + cx += pgm_read_byte(&pSmallGlyph->xAdvance); + if ((int8_t)pgm_read_byte(&pSmallGlyph->yOffset) < miny) miny = (int8_t)pgm_read_byte(&pSmallGlyph->yOffset); + if (pgm_read_byte(&pSmallGlyph->height)+(int8_t)pgm_read_byte(&pSmallGlyph->yOffset) > maxy) maxy = pgm_read_byte(&pSmallGlyph->height)+(int8_t)pgm_read_byte(&pSmallGlyph->yOffset); + } // small + } + } // prop fonts + pRect->w = cx; + pRect->x = pBBEP->iCursorX; + pRect->y = pBBEP->iCursorY + miny; + pRect->h = maxy - miny + 1; +} /* bbepGetStringBox() */ // // Draw a sprite of any size in any position @@ -327,6 +398,61 @@ BBEPDISP *pBBEP = (BBEPDISP *)pb; pBBEP->ucScreen[i] = u8; } /* bbepSetPixelFast4Clr() */ +int bbepSetPixel4Gray(void *pb, int x, int y, unsigned char ucColor) +{ +int i; +int iPitch, iSize; +BBEPDISP *pBBEP = (BBEPDISP *)pb; +const uint8_t u8Mask = 0x80 >> (x & 7); + + // only available for local buffer operations + if (!pBBEP || !pBBEP->ucScreen) return BBEP_ERROR_BAD_PARAMETER; + ucColor = pBBEP->pColorLookup[ucColor & 0xf]; // translate the color for this display type + + iPitch = (pBBEP->width+7)>>3; + iSize = ((pBBEP->native_width+7)>>3) * pBBEP->native_height; + + i = (x >> 3) + (y * iPitch); + if (x < 0 || x >= pBBEP->width || i < 0 || i > iSize-1) { // off the screen + pBBEP->last_error = BBEP_ERROR_BAD_PARAMETER; + return BBEP_ERROR_BAD_PARAMETER; + } + if (ucColor & 1) { // first plane + pBBEP->ucScreen[i] |= u8Mask; + } else { + pBBEP->ucScreen[i] &= ~u8Mask; + } + if (ucColor & 2) { // second plane + pBBEP->ucScreen[iSize + i] |= u8Mask; + } else { + pBBEP->ucScreen[iSize + i] &= ~u8Mask; + } + return BBEP_SUCCESS; +} /* bbepSetPixel4Gray() */ + +void bbepSetPixelFast4Gray(void *pb, int x, int y, unsigned char ucColor) +{ +int i; +int iPitch, iSize; +BBEPDISP *pBBEP = (BBEPDISP *)pb; +const uint8_t u8Mask = 0x80 >> (x & 7); + + iPitch = (pBBEP->width+7)>>3; + iSize = ((pBBEP->native_width+7)>>3) * pBBEP->native_height; + i = (x >> 3) + (y * iPitch); + + if (ucColor & 1) { // first plane + pBBEP->ucScreen[i] |= u8Mask; + } else { + pBBEP->ucScreen[i] &= ~u8Mask; + } + if (ucColor & 2) { // second plane + pBBEP->ucScreen[iSize + i] |= u8Mask; + } else { + pBBEP->ucScreen[iSize + i] &= ~u8Mask; + } +} /* bbepSetPixelFast4Gray() */ + int bbepSetPixel3Clr(void *pb, int x, int y, unsigned char ucColor) { int i; @@ -959,13 +1085,16 @@ uint16_t u16CP; // 16-bit codepoint encoded by the multi-byte sequence // // Draw a string of BB_FONT characters directly into the EPD framebuffer // -int bbepWriteStringCustom(BBEPDISP *pBBEP, BB_FONT *pFont, int x, int y, char *szMsg, int iColor, uint8_t iPlane) +int bbepWriteStringCustom(BBEPDISP *pBBEP, void *pFont, int x, int y, char *szMsg, int iColor, uint8_t iPlane) { int rc, i, h, w, j, end_y, dx, dy, tx, ty, tw, iSrcPitch, iPitch, iBG; signed int n; unsigned int c, bInvert = 0; uint8_t *s, uc0, uc1; + BB_FONT *pBBF; + BB_FONT_SMALL *pBBFS; BB_GLYPH *pGlyph; + BB_GLYPH_SMALL *pSmallGlyph; uint8_t *pBits, u8CMD1, u8CMD2, u8CMD, u8EndMask; uint8_t szExtMsg[256]; // translated extended ASCII message text uint8_t first, last; @@ -975,6 +1104,15 @@ int bbepWriteStringCustom(BBEPDISP *pBBEP, BB_FONT *pFont, int x, int y, char *s pBBEP->last_error = BBEP_ERROR_BAD_PARAMETER; return BBEP_ERROR_BAD_PARAMETER; // invalid param } +// Determine if we're using a small or large font + if (pgm_read_word(pFont) == BB_FONT_MARKER) { + pBBF = (BB_FONT *)pFont; + pBBFS = NULL; + } else { + pBBFS = (BB_FONT_SMALL *)pFont; + pBBF = NULL; + } + if (iColor != BBEP_TRANSPARENT) { iColor = pBBEP->pColorLookup[iColor & 0xf]; // translate the color for this display type } @@ -992,8 +1130,13 @@ int bbepWriteStringCustom(BBEPDISP *pBBEP, BB_FONT *pFont, int x, int y, char *s x = pBBEP->iCursorX; if (y == -1) y = pBBEP->iCursorY; - first = pgm_read_byte(&pFont->first); - last = pgm_read_byte(&pFont->last); + if (pBBF) { + first = pgm_read_byte(&pBBF->first); + last = pgm_read_byte(&pBBF->last); + } else { + first = pgm_read_byte(&pBBFS->first); + last = pgm_read_byte(&pBBFS->last); + } if (x == CENTER_X) { // center the string on the e-paper dx = i = 0; while (szExtMsg[i]) { @@ -1001,37 +1144,67 @@ int bbepWriteStringCustom(BBEPDISP *pBBEP, BB_FONT *pFont, int x, int y, char *s if (c < first || c > last) // undefined character continue; // skip it c -= first; // first char of font defined - pGlyph = &pFont->glyphs[c]; // glyph info for this character - dx += pgm_read_byte(&pGlyph->xAdvance); + if (pBBF) { + pGlyph = &pBBF->glyphs[c]; // glyph info for this character + dx += pgm_read_word(&pGlyph->xAdvance); + } else { // small font + pSmallGlyph = &pBBFS->glyphs[c]; // glyph info for this character + dx += pgm_read_byte(&pSmallGlyph->xAdvance); + } } x = (pBBEP->width - dx)/2; if (x < 0) x = 0; } // Point to the start of the compressed data - pBits = (uint8_t *)pFont; - pBits += sizeof(BB_FONT); - pBits += (last - first + 1) * sizeof(BB_GLYPH); + if (pBBF) { + pBits = (uint8_t *)pBBF; + pBits += sizeof(BB_FONT); + pBits += (last - first + 1) * sizeof(BB_GLYPH); + } else { + pBits = (uint8_t *)pBBFS; + pBits += sizeof(BB_FONT_SMALL); + pBits += (last - first + 1) * sizeof(BB_GLYPH_SMALL); + } i = 0; while (szExtMsg[i] && x < pBBEP->width && y < pBBEP->height) { + int16_t xOffset, yOffset; + uint32_t u32Offset, u32Rot, xAdvance; c = szExtMsg[i++]; if (c < first || c > last) // undefined character continue; // skip it c -= first; // first char of font defined - pGlyph = &pFont->glyphs[c]; // glyph info for this character - if (pgm_read_byte(&pGlyph->width) > 1) { // skip this if drawing a space - s = pBits + pgm_read_word(&pGlyph->bitmapOffset); // start of compressed bitmap data - if (pgm_read_dword(&pFont->rotation) == 0 || pgm_read_dword(&pFont->rotation) == 180) { - h = pgm_read_word(&pGlyph->height); - w = pgm_read_byte(&pGlyph->width); - dx = x + (int16_t)pgm_read_word(&pGlyph->xOffset); // offset from character UL to start drawing - dy = y + (int16_t)pgm_read_word(&pGlyph->yOffset); + if (pBBF) { + pGlyph = &pBBF->glyphs[c]; // glyph info for this character + w = pgm_read_word(&pGlyph->width); + h = pgm_read_word(&pGlyph->height); + u32Offset = pgm_read_word(&pGlyph->bitmapOffset); + u32Rot = pgm_read_dword(&pBBF->rotation); + xAdvance = pgm_read_word(&pGlyph->xAdvance); + xOffset = (int16_t)pgm_read_word(&pGlyph->xOffset); + yOffset = (int16_t)pgm_read_word(&pGlyph->yOffset); + } else { // small font + pSmallGlyph = &pBBFS->glyphs[c]; // glyph info for this character + w = pgm_read_byte(&pSmallGlyph->width); + h = pgm_read_byte(&pSmallGlyph->height); + u32Offset = pgm_read_word(&pSmallGlyph->bitmapOffset); + u32Rot = pgm_read_dword(&pBBFS->rotation); + xAdvance = pgm_read_byte(&pSmallGlyph->xAdvance); + xOffset = (int8_t)pgm_read_byte(&pSmallGlyph->xOffset); + yOffset = (int8_t)pgm_read_byte(&pSmallGlyph->yOffset); + } + if (w > 1) { // skip this if drawing a space + s = pBits + u32Offset; // start of compressed bitmap data + if (u32Rot == 0 || u32Rot == 180) { + dx = x + xOffset; // offset from character UL to start drawing + dy = y + yOffset; } else { // rotated - w = pgm_read_word(&pGlyph->height); - h = pgm_read_byte(&pGlyph->width); - n = (int16_t)pgm_read_word(&pGlyph->yOffset); // offset from character UL to start drawing + dx = w; // swap height/width + w = h; + h = dx; + n = yOffset; // offset from character UL to start drawing dx = x; if (-n < w) dx -= (w+n); // since we draw from the baseline - dy = y + (int16_t)pgm_read_word(&pGlyph->xOffset); + dy = y + xOffset; } if ((dy + h) > pBBEP->height) { // trim it h = pBBEP->height - dy; @@ -1041,7 +1214,11 @@ int bbepWriteStringCustom(BBEPDISP *pBBEP, BB_FONT *pFont, int x, int y, char *s u8EndMask <<= (8-(w & 7)); } end_y = dy + h; - ty = (pgm_read_word(&pGlyph[1].bitmapOffset) - (intptr_t)(s - pBits)); // compressed size + if (pBBF) { + ty = (pgm_read_word(&pGlyph[1].bitmapOffset) - (intptr_t)(s - pBits)); // compressed size + } else { + ty = (pgm_read_word(&pSmallGlyph[1].bitmapOffset) - (intptr_t)(s - pBits)); // compressed size + } if (ty < 0 || ty > 4096) ty = 4096; // DEBUG rc = g5_decode_init(&g5dec, w, h, s, ty); if (rc != G5_SUCCESS) { @@ -1101,6 +1278,9 @@ int bbepWriteStringCustom(BBEPDISP *pBBEP, BB_FONT *pFont, int x, int y, char *s else if (iColor == BBEP_RED && (pBBEP->iFlags & BBEP_3COLOR)) { u8CMD = u8CMD2; // second plane is red (inverted) } + if (!(pBBEP->iFlags & BBEP_3COLOR) && pBBEP->iPlane == 1) { + u8CMD = u8CMD2; // set second plane for partial updates + } bbepWriteCmd(pBBEP, u8CMD); // memory write command for (ty=dy; tynative_height; ty++) { rc = g5_decode_line(&g5dec, u8Cache); @@ -1124,10 +1304,10 @@ int bbepWriteStringCustom(BBEPDISP *pBBEP, BB_FONT *pFont, int x, int y, char *s } // for y } } // if not drawing a space - if (pgm_read_dword(&pFont->rotation) == 0 || pgm_read_dword(&pFont->rotation) == 180) { - x += pgm_read_byte(&pGlyph->xAdvance); // width of this character + if (u32Rot == 0 || u32Rot == 180) { + x += xAdvance; // width of this character } else { - y += pgm_read_byte(&pGlyph->xAdvance); + y += xAdvance; } } // while drawing characters pBBEP->iCursorX = x; @@ -1611,7 +1791,7 @@ void bbepGetStringBox(BBEPDISP *pBBEP, BB_FONT *pFont, const char *szMsg, int *w *bottom = maxy; } /* bbepGetStringBox() */ -int bbepAllocBuffer(BBEPDISP *pBBEP) +int bbepAllocBuffer(BBEPDISP *pBBEP, int bDoubleSize) { #ifndef NO_RAM int iSize; @@ -1619,7 +1799,10 @@ int bbepAllocBuffer(BBEPDISP *pBBEP) iSize = (pBBEP->native_width >> 1) * pBBEP->native_height; } else { // B/W or B/W/R or B/W/R/Y iSize = ((pBBEP->native_width+7)>>3) * pBBEP->native_height; - iSize *= 2; // 2 bit planes + if (bDoubleSize) { + iSize *= 2; // 2 bit planes + pBBEP->iFlags |= BBEP_HAS_SECOND_PLANE; + } } #if defined (HAL_ESP32_HAL_H_) && !defined(__riscv) if (iSize > 98000) { // need to use PSRAM diff --git a/src/bb_epaper.cpp b/src/bb_epaper.cpp index 1cdd323..364bfdc 100644 --- a/src/bb_epaper.cpp +++ b/src/bb_epaper.cpp @@ -65,6 +65,54 @@ void BBEPAPER::setAddrWindow(int x, int y, int w, int h) bbepSetAddrWindow(&_bbep, x, y, w, h); } +int BBEPAPER::begin(int iProduct) +{ +int rc = BBEP_ERROR_BAD_PARAMETER; + + switch (iProduct) { + case EPD_LILYGO_S3_MINI: // DC:12 CS:13 RST: 11 BUSY: 10 SCK: 14 MOSI: 15 + if (setPanelType(EP102_80x128) == BBEP_SUCCESS) { + initIO(12, 11, 10, 13, 15, 14, 8000000); + return BBEP_SUCCESS; + } + break; + case EPD_BADGER2040: // DC:20 CS:17 RST:21 BUSY: 26 PWR: 10 + if (setPanelType(EP29_128x296) == BBEP_SUCCESS) { + initIO(20, 21, 26, 17, -1, -1, 12000000); + setRotation(270); + pinMode(10, OUTPUT); + digitalWrite(10, HIGH); // keep power turned on + return BBEP_SUCCESS; + } + break; + case EPD_TRMNL_OG: // DC:5 CS:6 RST:10 BUSY:4 MOSI:8 SCK:7 + if (setPanelType(EP75_800x480) == BBEP_SUCCESS) { + initIO(5, 10, 4, 6, 8, 7, 10000000); + return BBEP_SUCCESS; + } + break; + case EPD_CROWPANEL29: // DC:46 CS:45 RST:47 BUSY:48 MOSI:11 SCK:12 + pinMode(7, OUTPUT); + digitalWrite(7, HIGH); // screen power on + if (setPanelType(EP29Z_128x296) == BBEP_SUCCESS) { + initIO(46, 47, 48, 45, 11, 12, 12000000); + setRotation(270); + return BBEP_SUCCESS; + } + break; + case EPD_CROWPANEL213: + pinMode(7, OUTPUT); + digitalWrite(7, HIGH); // screen power on + if (setPanelType(EP213Z_122x250) == BBEP_SUCCESS) { + initIO(13, 10, 9, 14, 11, 12, 12000000); + setRotation(270); + return BBEP_SUCCESS; + } + break; + } // switch on product type + return rc; +} /* begin() */ + int BBEPAPER::setPanelType(int iPanel) { return bbepSetPanelType(&_bbep, iPanel); @@ -91,11 +139,11 @@ void BBEPAPER::initIO(int iDC, int iReset, int iBusy, int iCS, int iSPIChannel, bbepInitIO(&_bbep, u32Speed); } /* initIO() */ #endif -int BBEPAPER::writePlane(int iPlane) +int BBEPAPER::writePlane(int iPlane, bool bInvert) { long l = millis(); int rc; - rc = bbepWritePlane(&_bbep, iPlane); + rc = bbepWritePlane(&_bbep, iPlane, (int)bInvert); _bbep.iDataTime = (int)(millis() - l); return rc; } /* writePlane() */ @@ -144,11 +192,15 @@ void BBEPAPER::backupPlane(void) memcpy(&_bbep.ucScreen[iSize], _bbep.ucScreen, iSize); } } -int BBEPAPER::allocBuffer(void) +int BBEPAPER::allocBuffer(bool bSecondPlane) { - return bbepAllocBuffer(&_bbep); + return bbepAllocBuffer(&_bbep, (int)bSecondPlane); } /* allocBuffer() */ +uint8_t * BBEPAPER::getCache(void) +{ + return u8Cache; +} void * BBEPAPER::getBuffer(void) { return (void *)_bbep.ucScreen; @@ -211,10 +263,6 @@ bool BBEPAPER::hasPartialRefresh() void BBEPAPER::setTextColor(int iFG, int iBG) { - if ((_bbep.iFlags & (BBEP_3COLOR | BBEP_4COLOR | BBEP_7COLOR)) == 0) { - if (iFG == BBEP_RED || iFG == BBEP_YELLOW) iFG = BBEP_BLACK; // can't set red color - if (iBG == BBEP_RED || iFG == BBEP_YELLOW) iBG = BBEP_BLACK; - } _bbep.iFG = iFG; _bbep.iBG = (iBG == -1) ? iFG : iBG; } /* setTextColor() */ @@ -419,22 +467,49 @@ static uint8_t u8Unicode0, u8Unicode1; bbepWriteString(&_bbep, -1, -1, szTemp, _bbep.iFont, _bbep.iFG, _bbep.iBG); } } else { // Custom font - BB_FONT *pBBF = (BB_FONT *)_bbep.pFont; + BB_FONT *pBBF; + BB_FONT_SMALL *pBBFS; + BB_GLYPH *pGlyph; + BB_GLYPH_SMALL *pSmallGlyph; + int first, last; + if (pgm_read_word(_bbep.pFont) == BB_FONT_MARKER) { + pBBF = (BB_FONT *)_bbep.pFont; pBBFS = NULL; + first = pgm_read_byte(&pBBF->first); + last = pgm_read_byte(&pBBF->last); + } else { // small font + pBBFS = (BB_FONT_SMALL *)_bbep.pFont; pBBF = NULL; + first = pgm_read_byte(&pBBFS->first); + last = pgm_read_byte(&pBBFS->last); + } if (c == '\n') { _bbep.iCursorX = 0; - _bbep.iCursorY += pBBF->height; + if (pBBF) { + _bbep.iCursorY += pBBF->height; + } else { + _bbep.iCursorY += pBBFS->height; + } } else if (c != '\r') { - if (c >= pBBF->first && c <= pBBF->last) { - BB_GLYPH *pBBG = &pBBF->glyphs[c - pBBF->first]; - w = pBBG->width; - h = pBBG->height; - if (w > 0 && h > 0) { // Is there an associated bitmap? - w += pBBG->xOffset; + if (c >= first && c <= last) { + if (pBBF) { + pGlyph = &pBBF->glyphs[c - first]; + w = pgm_read_word(&pGlyph->width); + h = pgm_read_word(&pGlyph->height); + } else { // small font + pSmallGlyph = &pBBFS->glyphs[c - first]; + w = pgm_read_word(&pSmallGlyph->width); + h = pgm_read_word(&pSmallGlyph->height); + } + if (w > 0 && h > 0) { // Is there an associated bitmap? + if (pBBF) { + w += (int16_t)pgm_read_word(&pGlyph->xOffset); + } else { + w += (int8_t)pgm_read_byte(&pSmallGlyph->xOffset); + } if (_bbep.wrap && (_bbep.iCursorX + w) > _bbep.width) { _bbep.iCursorX = 0; _bbep.iCursorY += h; } - bbepWriteStringCustom(&_bbep, (BB_FONT *)_bbep.pFont, -1, -1, szTemp, _bbep.iFG, _bbep.iPlane); + bbepWriteStringCustom(&_bbep, _bbep.pFont, -1, -1, szTemp, _bbep.iFG, _bbep.iPlane); } } } @@ -455,21 +530,14 @@ int16_t BBEPAPER::getCursorY(void) { return _bbep.iCursorY; } -void BBEPAPER::getTextBounds(const char *string, int16_t x, int16_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h) +void BBEPAPER::getStringBox(const char *string, BB_RECT *pRect) { - if (_bbep.pFont) { - int width, top, bottom; - bbepGetStringBox(&_bbep, (BB_FONT *)_bbep.pFont, string, &width, &top, &bottom); - *x1 = x; - *w = width; - *y1 = y + top; - *h = (bottom - top + 1); - } + bbepGetStringBox(&_bbep, (char *)string, pRect); } #ifdef ARDUINO -void BBEPAPER::getTextBounds(const String &str, int16_t x, int16_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h) +void BBEPAPER::getStringBox(const String &str, BB_RECT *pRect) { - getTextBounds(str.c_str(), x, y, x1, y1, w, h); + bbepGetStringBox(&_bbep, str.c_str(), pRect); } #endif int BBEPAPER::dataTime(void) diff --git a/src/bb_epaper.h b/src/bb_epaper.h index 7a430fe..6844ca8 100644 --- a/src/bb_epaper.h +++ b/src/bb_epaper.h @@ -45,6 +45,15 @@ enum { BBEP_ERROR_COUNT }; +#ifndef __ONEBITDISPLAY__ +typedef struct { + int x; + int y; + int w; + int h; +} BB_RECT; +#endif + #define LIGHT_SLEEP 0 #define DEEP_SLEEP 1 @@ -74,7 +83,9 @@ enum { PLANE_0=0, PLANE_1, PLANE_BOTH, - PLANE_DUPLICATE // duplicate 0 to both 0 and 1 + PLANE_DUPLICATE, // duplicate 0 to both 0 and 1 + PLANE_0_TO_1, // send plane 0 to plane 1 memory + PLANE_FALSE_DIFF, // use 'partial' mode to force all pixels to update }; #ifndef __ONEBITDISPLAY__ // 5 possible font sizes: 8x8, 16x32, 6x8, 12x16 (stretched from 6x8 with smoothing), 16x16 (stretched from 8x8) @@ -103,6 +114,17 @@ typedef struct epd_panel { const uint8_t *pColorLookup; // color translation table } EPD_PANEL; +// Products with built-in SPI EPDs +enum { + EPD_PRODUCT_UNDEFINED=0, + EPD_BADGER2040, + EPD_LILYGO_S3_MINI, + EPD_TRMNL_OG, + EPD_CROWPANEL29, + EPD_CROWPANEL213, + EPD_PRODUCT_COUNT +}; + // Display types enum { EP_PANEL_UNDEFINED=0, @@ -124,7 +146,10 @@ enum { EP37_240x416, // GDEY037T03 EP213_104x212, // InkyPHAT 2.13 black and white EP75_800x480, // GDEY075T7 + EP75_800x480_4GRAY, // GDEW075T7 in 4 grayscale mode + EP75_800x480_4GRAY_OLD, // GDEY075T7 in 4 grayscale mode EP29_128x296, // Pimoroni Badger2040 + EP29_128x296_4GRAY, // Pimoroni Badger2040 EP213R_122x250, // Inky phat 2.13 B/W/R EP154_200x200, // waveshare EP154B_200x200, // DEPG01540BN @@ -140,10 +165,14 @@ enum { EP583R_600x448, // 4-bits per pixel needs different support EP75R_800x480, // Waveshare 800x480 3-color EP426_800x480, // Waveshare 4.26" B/W 800x480 + EP426_800x480_4GRAY, // Waveshare 4.26" 800x480 2-bit grayscale mode EP29R2_128x296, // Adafruit 2.9" 128x296 Tricolor FeatherWing EP41_640x400, // EInk ED040TC1 SPI UC81xx EP81_SPECTRA_1024x576, // Spectra 8.1" 1024x576 6-colors EP7_960x640, // ED070EC1 + EP213R2_122x250, // UC8151 3-color + EP29Z_128x296, // SSD1680 (CrowPanel 2.9") + EP213Z_122x250, // SSD1680 (CrowPanel 2.13") EP_PANEL_COUNT }; #ifdef FUTURE @@ -183,6 +212,7 @@ enum { #define BBEP_PARTIAL2 0x0080 #define BBEP_4BPP_DATA 0x0100 #define BBEP_SPLIT_BUFFER 0x0200 +#define BBEP_HAS_SECOND_PLANE 0x0400 #define BBEP_BLACK 0 #define BBEP_WHITE 1 @@ -410,30 +440,37 @@ class BBEPAPER #endif // __LINUX__ { public: + BBEPAPER(void) { memset(&_bbep, 0, sizeof(_bbep)); } BBEPAPER(int iPanel); int createVirtual(int iWidth, int iHeight, int iFlags); void setAddrWindow(int x, int y, int w, int h); int setPanelType(int iPanel); + int begin(int iProduct); void setCS2(uint8_t cs); bool hasFastRefresh(); bool hasPartialRefresh(); #ifndef __LINUX__ #ifdef ARDUINO +#ifdef SCK void initIO(int iDC, int iReset, int iBusy, int iCS = SS, int iMOSI = MOSI, int iSCLK = SCK, uint32_t u32Speed = 8000000); +#else // no SCK/MOSI default pins + void initIO(int iDC, int iReset, int iBusy, int iCS, int iMOSI, int iSCLK, uint32_t u32Speed = 8000000); +#endif #else // esp-idf? void initIO(int iDC, int iReset, int iBusy, int iCS, int iMOSI, int iSCLK, uint32_t u32Speed); #endif // ARDUINO #else // __LINUX__ void initIO(int iDC, int iReset, int iBusy, int iCS, int iSPIChannel, uint32_t u32Speed = 8000000); #endif - int writePlane(int iPlane = PLANE_BOTH); + int writePlane(int iPlane = PLANE_BOTH, bool bInvert = false); void startWrite(int iPlane); void writeData(uint8_t *pData, int iLen); void writeCmd(uint8_t u8Cmd); int refresh(int iMode, bool bWait = true); void setBuffer(uint8_t *pBuffer); - int allocBuffer(void); + int allocBuffer(bool bSecondPlane = true); void * getBuffer(void); + uint8_t * getCache(void); void freeBuffer(void); uint32_t capabilities(); void setRotation(int iAngle); @@ -460,9 +497,9 @@ class BBEPAPER int16_t getCursorX(void); int16_t getCursorY(void); int testPanelType(void); - void getTextBounds(const char *string, int16_t x, int16_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h); + void getStringBox(const char *string, BB_RECT *pRect); #ifdef ARDUINO - void getTextBounds(const String &str, int16_t x, int16_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h); + void getStringBox(const String &str, BB_RECT *pRect); #endif int dataTime(); int opTime(); diff --git a/src/g5dec.inl b/src/g5dec.inl index 76254b4..37b1f6a 100644 --- a/src/g5dec.inl +++ b/src/g5dec.inl @@ -5,13 +5,18 @@ // Written by Larry Bank // Copyright (c) 2024 BitBank Software, Inc. // -// Use of this software is governed by the Business Source License -// included in the file ./LICENSE. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//=========================================================================== // -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// ./APL.txt. + #include "Group5.h" /* diff --git a/src/g5enc.inl b/src/g5enc.inl index b97a5fa..44ada28 100644 --- a/src/g5enc.inl +++ b/src/g5enc.inl @@ -5,13 +5,18 @@ // Written by Larry Bank // Copyright (c) 2024 BitBank Software, Inc. // -// Use of this software is governed by the Business Source License -// included in the file ./LICENSE. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//=========================================================================== // -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// ./APL.txt. + #include "Group5.h" /* Number of consecutive 1 bits in a byte from MSB to LSB */ @@ -45,7 +50,7 @@ static const uint8_t vtable[14] = 2,7}; /* V(3) = 0000010 */ -static void G5ENCInsertCode(BUFFERED_BITS *bb, BIGUINT ulCode, int iLen) +static void G5ENCInsertCode(G5_BUFFERED_BITS *bb, BIGUINT ulCode, int iLen) { if ((bb->ulBitOff + iLen) > REGISTER_WIDTH) { // need to write data bb->ulBits |= (ulCode >> (bb->ulBitOff + iLen - REGISTER_WIDTH)); // partial bits on first word @@ -61,7 +66,7 @@ static void G5ENCInsertCode(BUFFERED_BITS *bb, BIGUINT ulCode, int iLen) // // Flush any buffered bits to the output // -static void G5ENCFlushBits(BUFFERED_BITS *bb) +static void G5ENCFlushBits(G5_BUFFERED_BITS *bb) { while (bb->ulBitOff >= 8) { @@ -69,9 +74,11 @@ static void G5ENCFlushBits(BUFFERED_BITS *bb) bb->ulBits <<= 8; bb->ulBitOff -= 8; } - *bb->pBuf++ = (unsigned char) (bb->ulBits >> (REGISTER_WIDTH - 8)); - bb->ulBitOff = 0; - bb->ulBits = 0; + if (bb->ulBitOff) { // partial byte? + *bb->pBuf++ = (unsigned char) (bb->ulBits >> (REGISTER_WIDTH - 8)); + } + bb->ulBitOff = 0; + bb->ulBits = 0; } /* G5ENCFlushBits() */ // // Initialize the compressor @@ -198,13 +205,13 @@ int xsize, iErr, iHighWater; int iCur, iRef, iLen; int iHLen; // number of bits for long horizontal codes int16_t *CurFlips, *RefFlips; -BUFFERED_BITS bb; +G5_BUFFERED_BITS bb; if (pImage == NULL || pPixels == NULL) return G5_INVALID_PARAMETER; iHighWater = pImage->iOutSize - 32; iHLen = 32 - __builtin_clz(pImage->iWidth); - memcpy(&bb, &pImage->bb, sizeof(BUFFERED_BITS)); // keep local copy + memcpy(&bb, &pImage->bb, sizeof(G5_BUFFERED_BITS)); // keep local copy CurFlips = pImage->pCur; RefFlips = pImage->pRef; xsize = pImage->iWidth; /* For performance reasons */ @@ -283,7 +290,7 @@ BUFFERED_BITS bb; if (pImage->y == pImage->iHeight-1) { // last line of image G5ENCFlushBits(&bb); // output the final buffered bits // wrap up final output - pImage->iDataSize = (int)(bb.pBuf-pImage->pOutBuf); + pImage->iDataSize = 1 + (int)(bb.pBuf-pImage->pOutBuf); iErr = G5_ENCODE_COMPLETE; } pImage->pCur = RefFlips; // swap current and reference lines