diff --git a/library.properties b/library.properties index 9098531..5ee3bae 100644 --- a/library.properties +++ b/library.properties @@ -8,3 +8,4 @@ category=Display url=https://github.com/bitbank2/bb_eink architectures=* includes=bb_eink.h +depends=Group5 diff --git a/src/bb_ei.inl b/src/bb_ei.inl index c5febec..9499fd7 100644 --- a/src/bb_ei.inl +++ b/src/bb_ei.inl @@ -17,8 +17,6 @@ // bb_ei.inl // display interfacing/control code for bb_eink library // -// EPD command and LUT tables -// 2.7" 176x264 LUTs #ifndef __BB_EI__ #define __BB_EI__ @@ -131,7 +129,7 @@ const uint8_t epd42r_init_sequence[] PROGMEM = { 0x03, 0x4f, 0x2b, 0x01, // RAM Y counter 0x00 }; -const uint8_t epd29r_init_sequence[] PROGMEM = { +const uint8_t epd29r_init_sequence_full[] PROGMEM = { 0x01, 0x12, // soft reset BUSY_WAIT, 0x02, 0x74, 0x54, // set analog block control @@ -141,7 +139,7 @@ const uint8_t epd29r_init_sequence[] PROGMEM = { 0x04, 0x01, 0x27, 0x01, 0x00, // output control 0x02, 0x11, 0x03, // data entry mode 0x03, 0x44, 0x00, 0x0f, // RAM X start/end - 0x05, 0x45, 0,0,0x27, 0x01, // RAM Y start/end + 0x05, 0x45, 0x00, 0x00, 0x27, 0x01, // RAM Y start/end 0x02, 0x3c, 0x01, // border (0=bk,1=wh,2=red) 0x02, 0x18, 0x80, // temp sensor = internal 0x02, 0x21, 0x00, // display update ctrl 1 @@ -154,7 +152,7 @@ const uint8_t epd29r_init_sequence[] PROGMEM = { }; // for 152x152 BWR -const uint8_t epd29r_init_sequence_152[] PROGMEM = { +const uint8_t epd154r_init_sequence_full[] PROGMEM = { 0x01, 0x12, // soft reset BUSY_WAIT, 0x02, 0x74, 0x54, // set analog block control @@ -164,7 +162,7 @@ const uint8_t epd29r_init_sequence_152[] PROGMEM = { 0x04, 0x01, 0x97, 0x00, 0x00, // output control 0x02, 0x11, 0x03, // data entry mode 0x03, 0x44, 0x00, 0x12, // RAM X start/end - 0x05, 0x45, 0,0,0x97, 0x00, // RAM Y start/end + 0x05, 0x45, 0x00, 0x00, 0x97, 0x00, // RAM Y start/end 0x02, 0x3c, 0x01, // border (0=bk,1=wh,2=red) 0x02, 0x18, 0x80, // temp sensor = internal 0x02, 0x21, 0x00, // display update ctrl 1 @@ -441,6 +439,57 @@ const uint8_t epd102_init_sequence_part[] PROGMEM = 0 }; +const uint8_t epd122_init_sequence_full[] PROGMEM = +{ + 0x01, SSD1608_SW_RESET, + BUSY_WAIT, + + 0x04, 0x01, 0xaf, 0x00, 0x00, // driver output control + 0x02, 0x11, 0x03, // data entry mode + 0x03, 0x44, 0x00, 0x17, // ram start/end + 0x05, 0x45, 0x00, 0x00, 0xbf, 0x00, + 0x02, 0x3c, 0x05, // border waveform + 0x02, 0x18, 0x80, // read built-in temp sensor + + 0x02, 0x4e, 0x00, + 0x03, 0x4f, 0x00, 0x00, + BUSY_WAIT, + 0x00 // end of table +}; + +const uint8_t epd122_init_sequence_fast[] PROGMEM = +{ + 0x01, SSD1608_SW_RESET, + BUSY_WAIT, + + 0x02, 0x11, 0x03, // data entry mode + 0x02, 0x18, 0x80, // read built-in temp sensor + 0x02, 0x22, 0xb1, // load temp value + 0x01, 0x20, // execute + BUSY_WAIT, + 0x03, 0x1a, 0x64, 0x00, // write temp value + 0x02, 0x22, 0x91, // load temp + 0x01, 0x20, // execute + BUSY_WAIT, + 0x03, 0x44, 0x00, 0x17, // ram start/end + 0x05, 0x45, 0x00, 0x00, 0xaf, 0x00, + 0x02, 0x4e, 0x00, + 0x03, 0x4f, 0x00, 0x00, + BUSY_WAIT, + 0x00 // end of table +}; + +const uint8_t epd122_init_sequence_part[] PROGMEM = +{ + 0x02, 0x3c, 0x80, // border waveform + 0x02, 0x11, 0x03, // data entry mode + 0x03, 0x44, 0x00, 0x17, // ram start/end + 0x05, 0x45, 0x00, 0x00, 0xaf, 0x00, + 0x02, 0x4e, 0x00, + 0x03, 0x4f, 0x00, 0x00, + BUSY_WAIT, + 0x00 // end of table +}; const uint8_t epd293_init_sequence_full[] PROGMEM = { 0x01, SSD1608_SW_RESET, @@ -890,11 +939,12 @@ const uint8_t epd294_init_sequence_part[] = }; /* epd294_init_sequence_part */ uint8_t u8Cache[128]; // buffer a single line of up to 1024 pixels +// // Definitions for each supported panel // The order tracks that of the enumerated panel types // ** ONLY ADD NEW PANELS TO THE END OF THE LIST ** // -const EPD_PANEL panelDefs[] = { +const EPD_PANEL panelDefs[] PROGMEM = { {0}, // undefined panel {400, 300, epd42_init_sequence_full, NULL, epd42_init_sequence_part, 0, BBEI_CHIP_UC81xx}, // EPD42_400x300 {400, 300, epd42b_init_sequence_full, epd42b_init_sequence_fast, epd42b_init_sequence_part, 0, BBEI_CHIP_SSD16xx}, // EPD42B_400x300 @@ -905,6 +955,9 @@ const EPD_PANEL panelDefs[] = { {152, 296, epd266_init_sequence_full, NULL, epd266_init_sequence_part, 0, BBEI_CHIP_SSD16xx}, // EPD266_152x296 {80, 128, epd102_init_sequence_full, NULL, epd102_init_sequence_part, 0, BBEI_CHIP_UC81xx}, // EPD102_80x128 {176, 264, epd27_init_sequence_full, NULL, epd27_init_sequence_part, 0, BBEI_CHIP_SSD16xx}, // EPD27B_176x264 + {128, 296, epd29r_init_sequence_full, NULL, NULL, BBEI_3COLOR, BBEI_CHIP_SSD16xx}, // EPD29R_128x296 + {192, 176, epd122_init_sequence_full, epd122_init_sequence_fast, epd122_init_sequence_part, 0, BBEI_CHIP_SSD16xx}, // EPD122_192x176 + {152, 152, epd154r_init_sequence_full, NULL, NULL, BBEI_3COLOR, BBEI_CHIP_SSD16xx}, // EPD154R_152x152 }; int bbeiSetPanelType(BBEIDISP *pBBEI, int iPanel) { @@ -919,6 +972,7 @@ int bbeiSetPanelType(BBEIDISP *pBBEI, int iPanel) pBBEI->pInitFull = panelDefs[iPanel].pInitFull; pBBEI->pInitFast = panelDefs[iPanel].pInitFast; pBBEI->pInitPart = panelDefs[iPanel].pInitPart; + pBBEI->type = iPanel; return BBEI_SUCCESS; } /* bbeiSetPanelType() */ @@ -926,7 +980,7 @@ void bbeiSetPosition(BBEIDISP *pBBEI, int x, int y, int cx, int cy) { uint8_t uc[12]; int i, tx, ty; - + tx = x/8; // round down to next lower byte ty = y; cx = (cx + 7) & 0xfff8; // make width an even number of bytes @@ -957,23 +1011,26 @@ void bbeiSetPosition(BBEIDISP *pBBEI, int x, int y, int cx, int cy) bbeiWriteData(pBBEI, uc, i); // EPDWriteCmd(UC8151_PTOU); // partial out } else { // SSD16xx +// bbeiCMD2(pBBEI, SSD1608_DATA_MODE, 0x3); bbeiWriteCmd(pBBEI, SSD1608_SET_RAMXPOS); uc[0] = tx; // start x (byte boundary) uc[1] = tx+((cx-1)>>3); // end x bbeiWriteData(pBBEI, uc, 2); + bbeiWriteCmd(pBBEI, SSD1608_SET_RAMYPOS); uc[0] = (uint8_t)ty; // start y uc[1] = (uint8_t)(ty>>8); uc[2] = (uint8_t)(ty+cy-1); // end y uc[3] = (uint8_t)((ty+cy-1)>>8); bbeiWriteData(pBBEI, uc, 4); + // set ram counter to start of this region bbeiCMD2(pBBEI, SSD1608_SET_RAMXCOUNT, tx); uc[0] = ty; uc[1] = (ty>>8); bbeiWriteCmd(pBBEI, SSD1608_SET_RAMYCOUNT); bbeiWriteData(pBBEI, uc, 2); - // EPD_CMD2(SSD1608_DATA_MODE, 0x3); +// bbeiCMD2(pBBEI, SSD1608_DATA_MODE, 0x3); } } /* bbeiSetPosition() */ // @@ -984,6 +1041,8 @@ void bbeiSendCMDSequence(BBEIDISP *pBBEI, const uint8_t *pSeq) int iLen; uint8_t *s; + if (pBBEI == NULL || pSeq == NULL) return; + s = (uint8_t *)pSeq; while (s[0] != 0) { iLen = *s++; @@ -1004,6 +1063,7 @@ int bbeiRefresh(BBEIDISP *pBBEI, int iMode) { if (iMode != REFRESH_FULL && iMode != REFRESH_FAST && iMode != REFRESH_PARTIAL) return BBEI_ERROR_BAD_PARAMETER; + bbeiWakeUp(pBBEI); // tickle the reset line switch (iMode) { case REFRESH_FULL: @@ -1027,6 +1087,9 @@ int bbeiRefresh(BBEIDISP *pBBEI, int iMode) bbeiWriteCmd(pBBEI, UC8151_DRF); } else { const uint8_t u8CMD[3] = {0xf7, 0xc7, 0xff}; // normal, fast, partial + if (pBBEI->iFlags & BBEI_3COLOR) { + iMode = REFRESH_FAST; + } // 3-color = 0xc7 bbeiCMD2(pBBEI, SSD1608_DISP_CTRL2, u8CMD[iMode]); bbeiWriteCmd(pBBEI, SSD1608_MASTER_ACTIVATE); // refresh } diff --git a/src/bb_ei_gfx.inl b/src/bb_ei_gfx.inl index 05e3cb1..d99d090 100644 --- a/src/bb_ei_gfx.inl +++ b/src/bb_ei_gfx.inl @@ -18,7 +18,9 @@ // #ifndef __BB_EI_GFX__ #define __BB_EI_GFX__ - +#include +#include "bb_font.h" +G5DECIMAGE g5dec; // forward declarations void InvertBytes(uint8_t *pData, uint8_t bLen); @@ -566,21 +568,129 @@ void bbeiSetTextWrap(BBEIDISP *pBBEI, int bWrap) pBBEI->wrap = bWrap; } /* bbeiSetTextWrap() */ // +// Draw a string of BB_FONT characters directly into the EPD framebuffer +// +void bbeiWriteStringCustom(BBEIDISP *pBBEI, BB_FONT *pFont, int x, int y, char *szMsg, int iColor, uint8_t iPlane) +{ +int rc, i, h, w, end_y, dx, dy, ty, iSrcPitch, iPitch; +unsigned int c; +uint8_t *s; +BB_GLYPH *pGlyph; +uint8_t *pBits, u8CMD, u8EndMask; + + if (x == -1) + x = pBBEI->iCursorX; + if (y == -1) + y = pBBEI->iCursorY; + if (x == CENTER_X) { // center the string on the e-paper + dx = i = 0; + while (szMsg[i]) { + c = szMsg[i++]; + if (c < pFont->first || c > pFont->last) // undefined character + continue; // skip it + c -= pFont->first; // first char of font defined + pGlyph = &pFont->glyphs[c]; // glyph info for this character + dx += pGlyph->xAdvance; + } + x = (pBBEI->native_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 += (pFont->last - pFont->first + 1) * sizeof(BB_GLYPH); + i = 0; + while (szMsg[i] && x < pBBEI->native_width && y < pBBEI->native_height) { + c = szMsg[i++]; + if (c < pFont->first || c > pFont->last) // undefined character + continue; // skip it + c -= pFont->first; // first char of font defined + pGlyph = &pFont->glyphs[c]; // glyph info for this character + if (pGlyph->width > 1) { // skip this if drawing a space + dx = x + pGlyph->xOffset; // offset from character UL to start drawing + dy = y + pGlyph->yOffset; + s = pBits + pGlyph->bitmapOffset; // start of compressed bitmap data + if (pFont->rotation == 0 || pFont->rotation == 180) { + h = pGlyph->height; + w = pGlyph->width; + } else { // rotated + w = pGlyph->height; + h = pGlyph->width; + } + if ((dy + h) > pBBEI->native_height) { // trim it + h = pBBEI->native_height - dy; + } + u8EndMask = 0xff; + if (w & 7) { // width ends on a partial byte + u8EndMask <<= (8-(w & 7)); + } + end_y = dy + h; + ty = (pGlyph[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) return; // corrupt data? + // set the memory window for this character + iSrcPitch = (w+7)/8; + w += (dx & 7); // add parital byte + bbeiSetPosition(pBBEI, dx, dy, w, h); + iPitch = (w+7)/8; + // start writing into the correct plane + if (pBBEI->chip_type == BBEI_CHIP_UC81xx) { + u8CMD = /*(iPlane) ? UC8151_DTM1 : */UC8151_DTM2; + } else { + u8CMD = /*(iPlane) ? SSD1608_WRITE_ALTRAM :*/ SSD1608_WRITE_RAM; + } + bbeiWriteCmd(pBBEI, u8CMD); // memory write command + for (ty=dy; tynative_height; ty++) { + g5_decode_line(&g5dec, u8Cache); + u8Cache[iSrcPitch-1] &= u8EndMask; // clean pixels beyond character width + u8Cache[iSrcPitch] = 0; + if (dx & 7) { // need to shift it over by 1-7 bits + uint8_t *s = u8Cache, uc1, uc0 = 0; // last shifted byte + uint8_t n = dx & 7; // shift amount + for (int j=0; j> n); + *s++ = uc0; + uc0 = uc1 << (8-n); + } + *s++ = uc0; // store final byte + *s++ = 0; // and a zero for good measure + } + if (iColor == BBEI_BLACK) InvertBytes(u8Cache, iPitch); + bbeiWriteData(pBBEI, u8Cache, iPitch); // write each row into the EPD framebuffer + } // for y + } // if not drawing a space + if (pFont->rotation == 0 || pFont->rotation == 180) { + x += pGlyph->xAdvance; // width of this character + } else { + y += pGlyph->xAdvance; + } + } // while drawing characters + pBBEI->iCursorX = x; + pBBEI->iCursorY = y; +} /* EPDWriteStringDirect() */// // Draw a string of normal (8x8), small (6x8) or large (16x32) characters // At the given col+row // -int bbeiWriteString(BBEIDISP *pBBEI, int iScroll, int x, int y, char *szMsg, int iSize, int iColor, int bRender) +int bbeiWriteString(BBEIDISP *pBBEI, int x, int y, char *szMsg, int iSize, int iColor) { -int i, iFontOff, iLen, iFontSkip; -unsigned char c, *s; +int i, iFontOff, iLen; +uint8_t c, *s, ucCMD1, ucCMD2; int iOldFG; // old fg color to make sure red works - uint8_t u8Temp[128]; +uint8_t u8Temp[128]; if (pBBEI == NULL) return BBEI_ERROR_BAD_PARAMETER; + if (pBBEI->chip_type == BBEI_CHIP_UC81xx) { + ucCMD1 = UC8151_DTM2; + ucCMD2 = UC8151_DTM1; + } else { + ucCMD1 = SSD1608_WRITE_RAM; + ucCMD2 = SSD1608_WRITE_ALTRAM; + } - // e-paper color is inverted compared to OLED/LCD. If we're in bufferless mode, we'll need to - // invert the requested color - if (pBBEI->type >= EPD42_400x300 && !pBBEI->ucScreen) { + // If we're in bufferless mode, we'll need to invert the requested color + if (!pBBEI->ucScreen) { iColor = 1 - iColor; // invert the color } if (x == -1 || y == -1) // use the cursor position @@ -592,7 +702,6 @@ int iOldFG; // old fg color to make sure red works if (pBBEI->iCursorX >= pBBEI->width || pBBEI->iCursorY >= pBBEI->height) return BBEI_ERROR_BAD_PARAMETER; // can't draw off the display - bbeiSetPosition(pBBEI, pBBEI->iCursorX, pBBEI->iCursorY, 8, 16); iOldFG = pBBEI->iFG; // save old fg color if (iColor >= BBEI_YELLOW) { pBBEI->iFG = iColor; @@ -600,106 +709,42 @@ int iOldFG; // old fg color to make sure red works if (iSize == FONT_8x8) // 8x8 font { i = 0; - iFontSkip = iScroll & 7; // number of columns to initially skip + if (!pBBEI->ucScreen) { // bufferless mode, rotate the coordinate system to fit the situation + // bbeiSetPosition(pBBEI, √, pBBEI->iCursorX, 8, pBBEI->native_height-pBBEI->iCursorX); +// bbeiWriteCmd(pBBEI, ucCMD1); // write to "new" plane + } while (pBBEI->iCursorX < pBBEI->width && szMsg[i] != 0 && pBBEI->iCursorY < pBBEI->height) { - if (iScroll < 8) // only display visible characters - { - c = (unsigned char)szMsg[i]; - iFontOff = (int)(c-32) * 7; - // we can't directly use the pointer to FLASH memory, so copy to a local buffer - u8Temp[0] = 0; // first column is blank - memcpy_P(&u8Temp[1], &ucFont[iFontOff], 7); - if (iColor == BBEI_WHITE) InvertBytes(u8Temp, 8); - iLen = 8 - iFontSkip; - if (pBBEI->iCursorX + iLen > pBBEI->width) // clip right edge - iLen = pBBEI->width - pBBEI->iCursorX; - bbeiWriteData(pBBEI, &u8Temp[iFontSkip], iLen); -// obdWriteDataBlock(pBBEI, &ucTemp[iFontSkip], iLen, bRender); // write character pattern - pBBEI->iCursorX += iLen; - if (pBBEI->iCursorX >= pBBEI->width-7 && pBBEI->wrap) // word wrap enabled? - { - pBBEI->iCursorX = 0; // start at the beginning of the next line - pBBEI->iCursorY+=8; - bbeiSetPosition(pBBEI, pBBEI->iCursorX, pBBEI->iCursorY, 8, 8); - } - iFontSkip = 0; + c = (unsigned char)szMsg[i]; + iFontOff = (int)(c-32) * 7; + // we can't directly use the pointer to FLASH memory, so copy to a local buffer + u8Temp[0] = 0; // first column is blank + memcpy_P(&u8Temp[1], &ucFont[iFontOff], 7); // only needed on AVR + if (iColor == BBEI_WHITE) InvertBytes(u8Temp, 8); + iLen = 8; + if (pBBEI->iCursorX + iLen > pBBEI->width) { // clip right edge + iLen = pBBEI->width - pBBEI->iCursorX; + } + if (!pBBEI->ucScreen) { // bufferless mode, rotate the coordinate system to fit the situation + bbeiSetPosition(pBBEI, pBBEI->native_width-8-pBBEI->iCursorY, pBBEI->iCursorX, 8, pBBEI->native_height-pBBEI->iCursorX); + bbeiWriteCmd(pBBEI, ucCMD1); // write to "new" plane + bbeiWriteData(pBBEI, u8Temp, iLen); + } else { // draw in memory + // DEBUG + } + pBBEI->iCursorX += iLen; + if (pBBEI->iCursorX >= pBBEI->width-7 && pBBEI->wrap) { // word wrap enabled? + pBBEI->iCursorX = 0; // start at the beginning of the next line + pBBEI->iCursorY+=8; } - iScroll -= 8; i++; } // while pBBEI->iFG = iOldFG; // restore color return BBEI_SUCCESS; - } // 8x8 - else if (iSize == FONT_16x16) // 8x8 stretched to 16x16 - { + } else if (iSize == FONT_12x16) { // 6x8 stretched to 12x16 i = 0; - iFontSkip = iScroll & 15; // number of columns to initially skip - while (pBBEI->iCursorX < pBBEI->width && pBBEI->iCursorY < pBBEI->height && szMsg[i] != 0) - { + while (pBBEI->iCursorX < pBBEI->width && pBBEI->iCursorY < pBBEI->height && szMsg[i] != 0) { // stretch the 'normal' font instead of using the big font - if (iScroll < 16) // if characters are visible - { - int tx, ty; - c = szMsg[i] - 32; - unsigned char uc1, uc2, ucMask, *pDest; - s = (unsigned char *)&ucFont[(int)c*7]; - u8Temp[0] = 0; // first column is blank - memcpy_P(&u8Temp[1], s, 7); - if (iColor == BBEI_WHITE) - InvertBytes(u8Temp, 8); - // Stretch the font to double width + double height - memset(&u8Temp[8], 0, 32); // write 32 new bytes - for (tx=0; tx<8; tx++) - { - ucMask = 3; - pDest = &u8Temp[8+tx*2]; - uc1 = uc2 = 0; - c = u8Temp[tx]; - for (ty=0; ty<4; ty++) - { - if (c & (1 << ty)) // a bit is set - uc1 |= ucMask; - if (c & (1 << (ty + 4))) - uc2 |= ucMask; - ucMask <<= 2; - } - pDest[0] = uc1; - pDest[1] = uc1; // double width - pDest[16] = uc2; - pDest[17] = uc2; - } - iLen = 16 - iFontSkip; - if (pBBEI->iCursorX + iLen > pBBEI->width) // clip right edge - iLen = pBBEI->width - pBBEI->iCursorX; - bbeiSetPosition(pBBEI, pBBEI->iCursorX, pBBEI->iCursorY, 8, iLen); - bbeiWriteData(pBBEI, &u8Temp[8+iFontSkip], iLen); - bbeiSetPosition(pBBEI, pBBEI->iCursorX, pBBEI->iCursorY+8, 8, iLen); - bbeiWriteData(pBBEI, &u8Temp[24+iFontSkip], iLen); - pBBEI->iCursorX += iLen; - if (pBBEI->iCursorX >= pBBEI->width-15 && pBBEI->wrap) // word wrap enabled? - { - pBBEI->iCursorX = 0; // start at the beginning of the next line - pBBEI->iCursorY += 16; - bbeiSetPosition(pBBEI, pBBEI->iCursorX, pBBEI->iCursorY, 8, 16); - } - iFontSkip = 0; - } // if characters are visible - iScroll -= 16; - i++; - } // while - pBBEI->iFG = iOldFG; // restore color - return BBEI_SUCCESS; - } // 16x16 - else if (iSize == FONT_12x16) // 6x8 stretched to 12x16 - { - i = 0; - iFontSkip = iScroll % 12; // number of columns to initially skip - while (pBBEI->iCursorX < pBBEI->width && pBBEI->iCursorY < pBBEI->height && szMsg[i] != 0) - { -// stretch the 'normal' font instead of using the big font - if (iScroll < 12) // if characters are visible - { int tx, ty; c = szMsg[i] - 32; unsigned char uc1, uc2, ucMask, *pDest; @@ -807,23 +852,25 @@ int iOldFG; // old fg color to make sure red works ucMask <<= 1; ucMask2 <<= 1; } } - iLen = 12 - iFontSkip; + iLen = 12; if (pBBEI->iCursorX + iLen > pBBEI->width) // clip right edge iLen = pBBEI->width - pBBEI->iCursorX; - bbeiSetPosition(pBBEI, pBBEI->iCursorX, pBBEI->iCursorY, 8, iLen); - bbeiWriteData(pBBEI, &u8Temp[6+iFontSkip], iLen); - bbeiSetPosition(pBBEI, pBBEI->iCursorX, pBBEI->iCursorY+8, 8, iLen); - bbeiWriteData(pBBEI, &u8Temp[18+iFontSkip], iLen); + if (!pBBEI->ucScreen) { // bufferless mode + bbeiSetPosition(pBBEI, pBBEI->native_width-8-pBBEI->iCursorY, pBBEI->iCursorX, 8, iLen); + bbeiWriteCmd(pBBEI, ucCMD1); // write to "new" plane + bbeiWriteData(pBBEI, &u8Temp[6], iLen); + bbeiSetPosition(pBBEI, pBBEI->native_width-16-pBBEI->iCursorY, pBBEI->iCursorX, 8, iLen); + bbeiWriteCmd(pBBEI, ucCMD1); // write to "new" plane + bbeiWriteData(pBBEI, &u8Temp[18], iLen); + } else { // write to RAM + // DEBUG + } pBBEI->iCursorX += iLen; if (pBBEI->iCursorX >= pBBEI->width-11 && pBBEI->wrap) // word wrap enabled? { pBBEI->iCursorX = 0; // start at the beginning of the next line pBBEI->iCursorY += 16; - bbeiSetPosition(pBBEI, pBBEI->iCursorX, pBBEI->iCursorY, 8, 16); } - iFontSkip = 0; - } // if characters are visible - iScroll -= 12; i++; } // while pBBEI->iFG = iOldFG; // restore color @@ -832,31 +879,29 @@ int iOldFG; // old fg color to make sure red works else if (iSize == FONT_6x8) { i = 0; - iFontSkip = iScroll % 6; while (pBBEI->iCursorX < pBBEI->width && pBBEI->iCursorY < pBBEI->height && szMsg[i] != 0) { - if (iScroll < 6) // if characters are visible - { c = szMsg[i] - 32; // we can't directly use the pointer to FLASH memory, so copy to a local buffer u8Temp[0] = 0; // first column is blank memcpy_P(&u8Temp[1], &ucSmallFont[(int)c*5], 5); if (iColor == BBEI_WHITE) InvertBytes(u8Temp, 6); - iLen = 6 - iFontSkip; + iLen = 6; if (pBBEI->iCursorX + iLen > pBBEI->width) // clip right edge iLen = pBBEI->width - pBBEI->iCursorX; - bbeiWriteData(pBBEI, &u8Temp[iFontSkip], iLen); -// obdWriteDataBlock(pBBEI, &ucTemp[iFontSkip], iLen, bRender); // write character pattern + if (!pBBEI->ucScreen) { // bufferless mode + bbeiSetPosition(pBBEI, pBBEI->iCursorX, pBBEI->iCursorY, 8, iLen); + bbeiWriteCmd(pBBEI, ucCMD1); // write to "new" plane + bbeiWriteData(pBBEI, u8Temp, iLen); + } else { // write to RAM + // DEBUG + } pBBEI->iCursorX += iLen; - iFontSkip = 0; if (pBBEI->iCursorX >= pBBEI->width-5 && pBBEI->wrap) // word wrap enabled? { pBBEI->iCursorX = 0; // start at the beginning of the next line pBBEI->iCursorY +=8; - bbeiSetPosition(pBBEI, pBBEI->iCursorX, pBBEI->iCursorY, 8, 16); } - } // if characters are visible - iScroll -= 6; i++; } pBBEI->iFG = iOldFG; // restore color @@ -869,7 +914,7 @@ int iOldFG; // old fg color to make sure red works // // Get the width of text in a custom font // -void bbeiGetStringBox(GFXfont *pFont, char *szMsg, int *width, int *top, int *bottom) +void bbeiGetStringBox(BB_FONT *pFont, char *szMsg, int *width, int *top, int *bottom) { int cx = 0; unsigned int c, i = 0; diff --git a/src/bb_ei_io.inl b/src/bb_ei_io.inl index bf5a785..63d70e3 100644 --- a/src/bb_ei_io.inl +++ b/src/bb_ei_io.inl @@ -1,5 +1,5 @@ // -// bb_eink I/O functions +// bb_eink I/O wrapper functions for Arduino // Copyright (c) 2024 BitBank Software, Inc. // Written by Larry Bank (bitbank@pobox.com) // Project started 9/11/2024 @@ -14,12 +14,17 @@ // See the License for the specific language governing permissions and // limitations under the License. // +// Adapt these functions to whatever target platform you're using +// and the rest of the code can remain unchanged +// #ifndef __BB_EI_IO__ #define __BB_EI_IO__ #ifdef ARDUINO #include #include - +// +// Initialize the GPIO pins and SPI for use by bb_eink +// void bbeiInitIO(BBEIDISP *pBBEI, uint32_t u32Speed) { pinMode(pBBEI->iDCPin, OUTPUT); @@ -42,7 +47,9 @@ void bbeiInitIO(BBEIDISP *pBBEI, uint32_t u32Speed) SPI.beginTransaction(SPISettings(u32Speed, MSBFIRST, SPI_MODE0)); SPI.endTransaction(); // N.B. - if you call beginTransaction() again without a matching endTransaction(), it will hang on ESP32 } /* bbeiInitIO() */ - +// +// Toggle the reset line to wake up the eink from deep sleep +// void bbeiWakeUp(BBEIDISP *pBBEI) { digitalWrite(pBBEI->iRSTPin, LOW); @@ -50,15 +57,23 @@ void bbeiWakeUp(BBEIDISP *pBBEI) digitalWrite(pBBEI->iRSTPin, HIGH); delay(10); } /* bbeiWakeUp() */ - +// +// Wait for the busy status line to show idle +// The polarity of the busy signal is reversed on the UC81xx compared +// to the SSD16xx controllers +// void bbeiWaitBusy(BBEIDISP *pBBEI) { uint8_t busy_idle = (pBBEI->chip_type == BBEI_CHIP_UC81xx) ? HIGH : LOW; + delay(1); // some panels need a short delay before testing the BUSY line while (1) { if (digitalRead(pBBEI->iBUSYPin) == busy_idle) break; } } /* bbeiWaitBusy() */ - +// +// Convenience function to write a command byte along with a data +// byte (it's single parameter) +// void bbeiCMD2(BBEIDISP *pBBEI, uint8_t cmd1, uint8_t cmd2) { digitalWrite(pBBEI->iDCPin, LOW); @@ -66,14 +81,16 @@ void bbeiCMD2(BBEIDISP *pBBEI, uint8_t cmd1, uint8_t cmd2) digitalWrite(pBBEI->iCSPin, LOW); #endif SPI.transfer(cmd1); - SPI.transfer(cmd2); - digitalWrite(pBBEI->iDCPin, HIGH); // leave data mode as the default + digitalWrite(pBBEI->iDCPin, HIGH); + SPI.transfer(cmd2); // second byte is data #ifndef ARDUINO_ARCH_ESP32 digitalWrite(pBBEI->iCSPin, HIGH); #endif - +// digitalWrite(pBBEI->iDCPin, HIGH); // leave data mode as the default } /* bbeiCMD2() */ - +// +// Write a single byte as a COMMAND (D/C set low) +// void bbeiWriteCmd(BBEIDISP *pBBEI, uint8_t cmd) { digitalWrite(pBBEI->iDCPin, LOW); @@ -81,12 +98,14 @@ void bbeiWriteCmd(BBEIDISP *pBBEI, uint8_t cmd) digitalWrite(pBBEI->iCSPin, LOW); #endif SPI.transfer(cmd); - digitalWrite(pBBEI->iDCPin, HIGH); // leave data mode as the default #ifndef ARDUINO_ARCH_ESP32 digitalWrite(pBBEI->iCSPin, HIGH); #endif + digitalWrite(pBBEI->iDCPin, HIGH); // leave data mode as the default } /* bbeiWriteCmd() */ - +// +// Put the eink into light or deep sleep +// void bbeiSleep(BBEIDISP *pBBEI, int bDeep) { if (pBBEI->chip_type == BBEI_CHIP_UC81xx) { @@ -101,10 +120,12 @@ void bbeiSleep(BBEIDISP *pBBEI, int bDeep) return; } } /* bbeiSleep() */ - - +// +// Write 1 or more bytes as DATA (D/C set high) +// void bbeiWriteData(BBEIDISP *pBBEI, uint8_t *pData, int iLen) { +// digitalWrite(pBBEI->iDCPin, HIGH); #ifdef ARDUINO_ARCH_ESP32 SPI.transferBytes(pData, NULL, iLen); #else diff --git a/src/bb_eink.cpp b/src/bb_eink.cpp index c45d38e..02eef1f 100644 --- a/src/bb_eink.cpp +++ b/src/bb_eink.cpp @@ -37,7 +37,8 @@ void delay(int); #include "bb_ei_io.inl" // I/O (non-portable) code is in here #include "bb_ei.inl" // All of the display interface code is in here #include "bb_ei_gfx.inl" // drawing code - +#include "Group5.h" // Group5 data compression library +#include "bb_font.h" // bitbank compressed font info #ifdef __cplusplus // // C++ Class implementation @@ -64,6 +65,8 @@ void BBEINK::initIO(int iDC, int iReset, int iBusy, int iCS, int iMOSI, int iSCL _bbei.iRSTPin = iReset; _bbei.iBUSYPin = iBusy; bbeiInitIO(&_bbei, u32Speed); + bbeiWakeUp(&_bbei); + bbeiSendCMDSequence(&_bbei, _bbei.pInitFull); } /* initIO() */ int BBEINK::writePlane(int iPlane) @@ -98,9 +101,9 @@ void BBEINK::setBuffer(uint8_t *pBuffer) int BBEINK::allocBuffer(void) { - int iSize = ((_bbei.width+7)>>3) * _bbei.height; -// if (_bbei.iFlags & (BBEI_3COLOR | BBEI_4COLOR) || _bbei.chip_type == BBEI_CHIP_UC81xx) -// iSize *= 2; // 2 bit planes + int iSize = ((_bbei.native_width+7)>>3) * _bbei.native_height; + if (_bbei.iFlags & (BBEI_3COLOR | BBEI_4COLOR) || _bbei.chip_type == BBEI_CHIP_UC81xx) + iSize *= 2; // 2 bit planes _bbei.ucScreen = (uint8_t *)malloc(iSize); if (_bbei.ucScreen != NULL) { return BBEI_SUCCESS; @@ -189,7 +192,13 @@ int BBEINK::loadBMP3(const uint8_t *pBMP, int x, int y) void BBEINK::setFont(int iFont) { _bbei.iFont = iFont; - // _bbei.pFreeFont = NULL; + _bbei.pFont = NULL; +} /* setFont() */ + +void BBEINK::setFont(const void *pFont) +{ + _bbei.iFont = -1; + _bbei.pFont = (void *)pFont; } /* setFont() */ //void BBEINK::setFreeFont(const GFXfont *pFont) @@ -449,4 +458,13 @@ void BBEINK::wait(bool bQuick) { bbeiWaitBusy(&_bbei); } +void BBEINK::drawString(const char *pText, int x, int y) +{ + if (_bbei.pFont) { + bbeiWriteStringCustom(&_bbei, (BB_FONT *)_bbei.pFont, x, y, (char *)pText, _bbei.iFG, 0); // iPlane); + } else if (_bbei.iFont >= FONT_6x8 && _bbei.iFont < FONT_COUNT) { + bbeiWriteString(&_bbei, x, y, (char *)pText, _bbei.iFont, _bbei.iFG); + } +} /* drawString() */ + #endif // __cplusplus diff --git a/src/bb_eink.h b/src/bb_eink.h index bfc0309..e52e1bf 100644 --- a/src/bb_eink.h +++ b/src/bb_eink.h @@ -69,13 +69,11 @@ enum { FONT_6x8 = 0, FONT_8x8, FONT_12x16, - FONT_16x16, - FONT_16x32, - FONT_CUSTOM0, - FONT_CUSTOM1, - FONT_CUSTOM2, FONT_COUNT }; +// Centering coordinates to pass to the character drawing functions +#define CENTER_X 9998 +#define CENTER_Y 9999 typedef struct epd_panel { uint16_t width; @@ -99,6 +97,9 @@ enum { EPD266_152x296, // GDEY0266T90 EPD102_80x128, // GDEW0102T4 EPD27B_176x264, // GDEY027T91 + EPD29R_128x296, + EPD122_192x176, // GDEM0122T61 + EPD154R_152x152, EPD_PANEL_COUNT }; #ifdef FUTURE @@ -106,7 +107,6 @@ enum { EPD42Y_400x300, // DEPG0420YN EPD29_128x296, EPD29B_128x296, - EPD29R_128x296, EPD29Y_128x296, // DEPG0290YN EPD42R_400x300, EPD42R2_400x300, // GDEQ042Z21 @@ -254,6 +254,7 @@ int width, height, native_width, native_height; int iScreenOffset, iOrientation; int iFG, iBG; //current color int iFont, iFlags; +void *pFont; int iDataTime, iOpTime; // time in milliseconds for data transmission and operation uint32_t iSpeed; uint32_t iTimeout; // for e-ink panels @@ -301,6 +302,7 @@ class BBEINK int loadBMP(const uint8_t *pBMP, int x, int y, int iFG, int iBG); int loadBMP3(const uint8_t *pBMP, int x, int y); void setFont(int iFont); + void setFont(const void *pFont); void drawLine(int x1, int y1, int x2, int y2, int iColor); void drawPixel(int16_t x, int16_t y, uint8_t color); int16_t getCursorX(void); @@ -317,6 +319,7 @@ class BBEINK void fillEllipse(int16_t x, int16_t y, int32_t rx, int32_t ry, uint16_t color); void sleep(int bDeep); void wait(bool bQuick = false); + void drawString(const char *pText, int x, int y); #ifdef FUTURE void setPlane(int iPlane); @@ -328,7 +331,6 @@ class BBEINK void writeCommand(uint8_t ucCMD); void writeRaw(uint8_t *pData, int iLen); void pushImage(int x, int y, int w, int h, uint16_t *pixels); - void drawString(const char *pText, int x, int y); void drawString(String text, int x, int y); #endif // FUTURE