Added initial support for 4-color panels (b/w/y/r)

This commit is contained in:
Laurence Bank
2024-11-12 18:28:25 +00:00
parent 94c3fb9248
commit 485ea36d48
7 changed files with 220 additions and 37 deletions
+12 -5
View File
@@ -75,6 +75,9 @@ const char *szPanelNames[] = {
"EP213R_122x250 ",
"EP154_200x200 ",
"EP154B_200x200 ",
"EP266YR_184x360", // GDEY0266F51
"EP29YR_128x296 ", // GDEY029F51
"EP29YR_168x384 ", // GDEY029F51H
NULL
};
// names of the operating modes
@@ -90,7 +93,7 @@ enum {
// List of supported colors for each panel type
// 2 = Black/White, 3 = Black/White/Red
const uint8_t u8PanelColors[] = {2,2,2,2,2,2,2,2,2,2,3,2,3,3,3,2,2,2,2,3,2,2};
const uint8_t u8PanelColors[] = {2,2,2,2,2,2,2,2,2,2,3,2,3,3,3,2,2,2,2,3,2,2,4,4,4};
void epdBegin()
{
@@ -162,8 +165,7 @@ void EPDTest(int iMode)
oled.setFont(FONT_8x8);
oled.println(szMode[iMode]);
if (iMode == MODE_PARTIAL) {
int i, x, y;
char szTemp[16];
int i;
if (!epd.hasPartialRefresh()) {
oled.setFont(FONT_8x8);
@@ -230,11 +232,16 @@ void EPDTest(int iMode)
} else {
epd.println("No backbuffer (0 RAM)");
}
if (u8PanelColors[iPanel] != 3)
if (u8PanelColors[iPanel] == 2)
epd.println("Two colors (B/W)");
else {
else if (u8PanelColors[iPanel] == 3) {
epd.setTextColor(BBEP_RED, BBEP_WHITE);
epd.println("Three colors (B/W/R)");
} else {
epd.setTextColor(BBEP_RED, BBEP_WHITE);
epd.print("Four ");
epd.setTextColor(BBEP_YELLOW, BBEP_WHITE);
epd.println("colors (B/W/Y/R)");
}
if (epd.getBuffer()) {
epd.writePlane();
+2 -1
View File
@@ -2,7 +2,8 @@
// 3 color (Black/White/Red) example
//
#include <bb_epaper.h>
BBEPAPER bbep(EPD154R_152x152); // 1.54" 152x152 B/W/R
//BBEPAPER bbep(EP154R_152x152); // 1.54" 152x152 B/W/R
BBEPAPER bbep(EP266YR_184x360); // 1.54" 152x152 B/W/R
// My Arduino Nano 33 BLE e-paper adapter
#define DC_PIN 16
#define BUSY_PIN 15
+12 -4
View File
@@ -106,11 +106,19 @@ void bbepWriteData(BBEPDISP *pBBEP, uint8_t *pData, int iLen)
#ifdef ARDUINO_ARCH_ESP32
SPI.transferBytes(pData, NULL, iLen);
#else
digitalWrite(pBBEP->iCSPin, LOW);
for (int i=0; i<iLen; i++) { // Arduino clobbers the data (duplex)
SPI.transfer(pData[i]);
if (pBBEP->iFlags & BBEP_CS_EVERY_BYTE) {
for (int i=0; i<iLen; i++) { // Arduino clobbers the data (duplex)
digitalWrite(pBBEP->iCSPin, LOW);
SPI.transfer(pData[i]);
digitalWrite(pBBEP->iCSPin, HIGH);
}
} else {
digitalWrite(pBBEP->iCSPin, LOW);
for (int i=0; i<iLen; i++) { // Arduino clobbers the data (duplex)
SPI.transfer(pData[i]);
}
digitalWrite(pBBEP->iCSPin, HIGH);
}
digitalWrite(pBBEP->iCSPin, HIGH);
#endif
} /* bbepWriteData() */
+167 -19
View File
@@ -372,6 +372,65 @@ const uint8_t epd154a_init_sequence_part[] PROGMEM =
0
};
const uint8_t epd29yr_init_sequence_full[] PROGMEM =
{
0x02, 0x4d, 0x78,
0x03, 0x00, 0x0f, 0x29, // PSR
0x03, 0x01, 0x07, 0x00, // PWRR
0x04, 0x03, 0x10, 0x54, 0x44, // POFS
0x08, 0x06, 0x05, 0x00, 0x3f, 0x0a, 0x25, 0x12, 0x1a, // BTST_P
0x02, 0x50, 0x37, // CDI
0x03, 0x60, 0x02, 0x02, // TCON
0x05, 0x61, 0x00, 128, 0x01, 296-256, // TRES
0x02, 0xe7, 0x1c,
0x02, 0xe3, 0x22,
0x04, 0xb4, 0xd0, 0xb5, 0x03,
0x02, 0xe9, 0x01,
0x02, 0x30, 0x08,
0x01, 0x04, // PON
BUSY_WAIT,
0x00
};
const uint8_t epd29yrh_init_sequence_full[] PROGMEM =
{
0x02, 0x4d, 0x78,
0x03, 0x00, 0x0f, 0x29, // PSR
0x03, 0x01, 0x07, 0x00, // PWRR
0x04, 0x03, 0x10, 0x54, 0x44, // POFS
0x08, 0x06, 0x05, 0x00, 0x3f, 0x0a, 0x25, 0x12, 0x1a, // BTST_P
0x02, 0x50, 0x37, // CDI
0x03, 0x60, 0x02, 0x02, // TCON
0x05, 0x61, 0x00, 168, 0x01, 384-256, // TRES
0x02, 0xe7, 0x1c,
0x02, 0xe3, 0x22,
0x04, 0xb4, 0xd0, 0xb5, 0x03,
0x02, 0xe9, 0x01,
0x02, 0x30, 0x08,
0x01, 0x04, // PON
BUSY_WAIT,
0x00
};
const uint8_t epd266yr_init_sequence_full[] PROGMEM =
{
0x02, 0x4d, 0x78,
0x03, 0x00, 0x0f, 0x29, // PSR
0x03, 0x01, 0x07, 0x00, // PWRR
0x04, 0x03, 0x10, 0x54, 0x44, // POFS
0x08, 0x06, 0x05, 0x00, 0x3f, 0x0a, 0x25, 0x12, 0x1a, // BTST_P
0x02, 0x50, 0x37, // CDI
0x03, 0x60, 0x02, 0x02, // TCON
0x05, 0x61, 0x00, 184, 0x01, 360-256, // TRES
0x02, 0xe7, 0x1c,
0x02, 0xe3, 0x22,
0x04, 0xb4, 0xd0, 0xb5, 0x03,
0x02, 0xe9, 0x01,
0x02, 0x30, 0x08,
0x01, 0x04, // PON
BUSY_WAIT,
0x00
};
// 1.54" 200x200
const uint8_t epd154_init_sequence_full[] PROGMEM =
{
@@ -1107,6 +1166,9 @@ const EPD_PANEL panelDefs[] PROGMEM = {
{122, 250, 1, epd213r_inky_init_sequence_full, NULL, NULL, BBEP_3COLOR, BBEP_CHIP_SSD16xx}, // 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}, // EP154_200x200
{200, 200, 0, epd154_init_sequence_full, epd154_init_sequence_fast, epd154b_init_sequence_part, 0, BBEP_CHIP_SSD16xx}, // EP154B_200x200
{184, 360, 0, epd266yr_init_sequence_full, NULL, NULL, BBEP_4COLOR, BBEP_CHIP_UC81xx}, // EP266YR_184x360
{128, 296, 0, epd29yr_init_sequence_full, NULL, NULL, BBEP_4COLOR, BBEP_CHIP_UC81xx}, // EP29YR_129x296
{168, 384, 0, epd29yrh_init_sequence_full, NULL, NULL, BBEP_4COLOR, BBEP_CHIP_UC81xx}, // EP29YR_168x384
};
//
// Set the e-paper panel type
@@ -1143,7 +1205,21 @@ int bbepCreateVirtual(BBEPDISP *pBBEP, int iWidth, int iHeight, int iFlags)
return BBEP_ERROR_BAD_PARAMETER;
}
}
//
// 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 bbepWaitBusy(BBEPDISP *pBBEP)
{
if (!pBBEP) return;
if (pBBEP->iBUSYPin == 0xff) return;
uint8_t busy_idle = (pBBEP->chip_type == BBEP_CHIP_UC81xx) ? HIGH : LOW;
delay(1); // some panels need a short delay before testing the BUSY line
while (1) {
if (digitalRead(pBBEP->iBUSYPin) == busy_idle) break;
}
} /* bbepWaitBusy() */
//
// Toggle the reset line to wake up the eink from deep sleep
//
@@ -1154,22 +1230,9 @@ void bbepWakeUp(BBEPDISP *pBBEP)
delay(10);
digitalWrite(pBBEP->iRSTPin, HIGH);
delay(10);
bbepWaitBusy(pBBEP);
} /* bbepWakeUp() */
//
// 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 bbepWaitBusy(BBEPDISP *pBBEP)
{
if (!pBBEP) return;
uint8_t busy_idle = (pBBEP->chip_type == BBEP_CHIP_UC81xx) ? HIGH : LOW;
delay(1); // some panels need a short delay before testing the BUSY line
while (1) {
if (digitalRead(pBBEP->iBUSYPin) == busy_idle) break;
}
} /* bbepWaitBusy() */
//
// Set the memory window for future writes into panel memory
//
void bbepSetAddrWindow(BBEPDISP *pBBEP, int x, int y, int cx, int cy)
@@ -1178,6 +1241,8 @@ void bbepSetAddrWindow(BBEPDISP *pBBEP, int x, int y, int cx, int cy)
int i, tx, ty;
if (!pBBEP) return;
if (pBBEP->iFlags & BBEP_4COLOR) return;
tx = x/8; // round down to next lower byte
ty = y;
cx = (cx + 7) & 0xfff8; // make width an even number of bytes
@@ -1327,6 +1392,12 @@ void bbepFill(BBEPDISP *pBBEP, unsigned char ucData, int iPlane)
} else if (ucData == BBEP_RED) {
uc1 = 0x00; uc2 = 0xff;
}
} else if (pBBEP->iFlags & BBEP_4COLOR) {
iPitch = ((pBBEP->native_width+3)/4);
iSize = pBBEP->native_height * iPitch;
iPlane = PLANE_0; // only 1 2-bit memory plane
uc1 = ucData | (ucData << 2);
uc1 |= (uc1 << 4); // set color in all 4 pixels of the byte
} else { // B/W
if (ucData == BBEP_WHITE) {
uc1 = uc2 = 0xff;
@@ -1355,13 +1426,13 @@ void bbepFill(BBEPDISP *pBBEP, unsigned char ucData, int iPlane)
} else if (ucData == BBEP_RED) {
uc1 = 0x00; uc2 = 0xff;
}
} else { // for B/W, both planes get the same data
} else if (!(pBBEP->iFlags & BBEP_4COLOR)) { // for B/W, both planes get the same data
if (ucData == BBEP_WHITE) ucData = 0xff;
else if (ucData == BBEP_BLACK) ucData = 0;
uc1 = uc2 = ucData;
}
if (pBBEP->chip_type == BBEP_CHIP_UC81xx) {
if (pBBEP->iFlags & BBEP_RED_SWAPPED) {
if (pBBEP->iFlags & (BBEP_RED_SWAPPED | BBEP_4COLOR)) {
ucCMD1 = UC8151_DTM1;
ucCMD2 = UC8151_DTM2;
} else {
@@ -1415,8 +1486,12 @@ int bbepRefresh(BBEPDISP *pBBEP, int iMode)
return BBEP_ERROR_BAD_PARAMETER;
} // switch on mode
if (pBBEP->chip_type == BBEP_CHIP_UC81xx) {
bbepWriteCmd(pBBEP, UC8151_PTOU); // partial out (update the entire panel, not just the last memory window)
bbepWriteCmd(pBBEP, UC8151_DRF);
if (pBBEP->iFlags & BBEP_4COLOR) {
bbepCMD2(pBBEP, 0x12, 0x00);
} else {
bbepWriteCmd(pBBEP, UC8151_PTOU); // partial out (update the entire panel, not just the last memory window)
bbepWriteCmd(pBBEP, UC8151_DRF);
}
} else {
const uint8_t u8CMD[4] = {0xf7, 0xc7, 0xff, 0}; // normal, fast, partial
if (pBBEP->iFlags & BBEP_3COLOR) {
@@ -1454,6 +1529,75 @@ void bbepSetRotation(BBEPDISP *pBBEP, int iRotation)
}
} /* bbepSetRotation() */
void bbepWriteImage2bpp(BBEPDISP *pBBEP, uint8_t ucCMD)
{
int tx, ty;
uint8_t *s, *d, uc, uc1, ucMask;
uint8_t *pBuffer;
pBuffer = pBBEP->ucScreen;
if (ucCMD) {
bbepWriteCmd(pBBEP, ucCMD); // start write
}
// Convert the bit direction and write the data to the EPD
if (pBBEP->iOrientation == 180) {
for (ty=pBBEP->height-1; ty>=0; ty--) {
d = u8Cache;
s = &pBuffer[ty * pBBEP->width/4];
for (tx=pBBEP->width-4; tx>=0; tx-=4) {
uc = 0;
ucMask = 0x03;
uc1 = s[tx>>2];
for (int pix=0; pix<8; pix +=2) { // reverse the direction of the 4 pixels
uc <<= 2; // shift down 1 pixel
uc |= ((uc1 & ucMask) >> pix);
ucMask <<= 2;
}
*d++ = uc; // store 4 pixels
} // for tx
bbepWriteData(pBBEP, u8Cache, pBBEP->width/4);
} // for ty
} else if (pBBEP->iOrientation == 0) {
s = pBBEP->ucScreen;
for (ty=0; ty<pBBEP->height; ty++) {
bbepWriteData(pBBEP, s, pBBEP->width/4);
s += pBBEP->width/4; // 4 pixels per byte
} // for ty
} else if (pBBEP->iOrientation == 90) {
for (tx=0; tx<pBBEP->width; tx++) {
d = u8Cache;
for (ty=pBBEP->height-1; ty > 0; ty-=4) {
s = &pBuffer[(tx>>2) + (ty * (pBBEP->width/4))];
uc = 0;
ucMask = 0xc0 >> ((tx & 3) * 2);
for (int pix=0; pix<4; pix++) {
uc <<= 2; // shift down 1 pixel
uc |= ((s[0] & ucMask) >> ((3-(tx&3))*2)); // inverted plane 0
s -= (pBBEP->width/4);
}
*d++ = uc; // store 4 pixels
} // for ty
bbepWriteData(pBBEP, u8Cache, pBBEP->height/4);
} // for tx
} else if (pBBEP->iOrientation == 270) {
for (tx=pBBEP->width-1; tx>=0; tx--) {
d = u8Cache;
for (ty=3; ty<pBBEP->height; ty+=4) {
s = &pBuffer[(tx>>2) + (ty * pBBEP->width/4)];
ucMask = 0xc0 >> ((tx & 3) * 2);
uc = 0;
for (int pix=0; pix<4; pix++) {
uc >>= 2;
uc |= ((s[0] & ucMask) << ((tx&3)*2)); // inverted plane 0
s -= (pBBEP->width/4);
} // for pix
*d++ = uc; // store 2 pixels
} // for ty
bbepWriteData(pBBEP, u8Cache, pBBEP->height/4);
} // for x
} // 270
} /* bbepWriteImage2bpp() */
//
// Write Image data (entire plane) from RAM to the e-paper
// Rotate the pixels if necessary
@@ -1549,6 +1693,10 @@ int bbepWritePlane(BBEPDISP *pBBEP, int iPlane)
return BBEP_ERROR_BAD_PARAMETER;
bbepSetAddrWindow(pBBEP, 0,0, pBBEP->native_width, pBBEP->native_height);
if (pBBEP->iFlags & BBEP_4COLOR) { // 4-color only has 1 way to go
bbepWriteImage2bpp(pBBEP, 0x10);
return BBEP_SUCCESS;
}
if (pBBEP->chip_type == BBEP_CHIP_UC81xx) {
if (pBBEP->iFlags & BBEP_RED_SWAPPED) {
ucCMD1 = UC8151_DTM1;
+19 -2
View File
@@ -321,6 +321,15 @@ int bbepSetPixel(BBEPDISP *pBBEP, int x, int y, unsigned char ucColor)
// only available for local buffer operations
if (!pBBEP || !pBBEP->ucScreen) return BBEP_ERROR_BAD_PARAMETER;
if (pBBEP->iFlags & BBEP_4COLOR) { // 2-bits per pixel
uint8_t ucMask = 0xc0 >> ((x & 3)*2);
iPitch = (pBBEP->width+3)>>2;
i = (x >> 2) + (y * iPitch);
if (i < 0 || i >= (iPitch * pBBEP->height)) return BBEP_ERROR_BAD_PARAMETER;
pBBEP->ucScreen[i] &= ~ucMask;
pBBEP->ucScreen[i] |= ucColor << ((3-(x & 3))*2);
return BBEP_SUCCESS;
}
iPitch = (pBBEP->width+7)>>3;
iSize = ((pBBEP->native_width+7)>>3) * pBBEP->native_height;
@@ -1367,7 +1376,6 @@ static void DrawScaledPixel(BBEPDISP *pBBEP, int iCX, int iCY, int x, int y, int
int iPitch;
int iRedOffset = 0;
iPitch = (pBBEP->width + 7) >> 3;
if (iXFrac != 0x10000) x = ((x * iXFrac) >> 16);
if (iYFrac != 0x10000) y = ((y * iYFrac) >> 16);
x += iCX; y += iCY;
@@ -1375,9 +1383,18 @@ static void DrawScaledPixel(BBEPDISP *pBBEP, int iCX, int iCY, int x, int y, int
pBBEP->last_error = BBEP_ERROR_BAD_PARAMETER;
return; // off the screen
}
if (pBBEP->iFlags & BBEP_4COLOR) {
iPitch = (pBBEP->width + 3) >> 2;
ucMask = 0xc0 >> ((x & 3)*2);
d = &pBBEP->ucScreen[(x>>2) + (y * iPitch)];
d[0] &= ~ucMask;
d[0] |= (ucColor << ((3-(x & 3))*2));
return;
}
iPitch = (pBBEP->width + 7) >> 3;
ucMask = 0x80 >> (x & 7);
d = &pBBEP->ucScreen[(x>>3) + (y * iPitch)];
if (pBBEP->iFlags & (BBEP_3COLOR | BBEP_4COLOR)) {
if (pBBEP->iFlags & BBEP_3COLOR) {
// use the second half of the image buffer
iRedOffset = iPitch * pBBEP->height;
if (ucColor == BBEP_RED) {
+3 -4
View File
@@ -138,8 +138,7 @@ void BBEPAPER::backupPlane(void)
int BBEPAPER::allocBuffer(void)
{
int iSize = ((_bbep.native_width+7)>>3) * _bbep.native_height;
if (_bbep.iFlags & (BBEP_3COLOR | BBEP_4COLOR) || _bbep.chip_type == BBEP_CHIP_UC81xx)
iSize *= 2; // 2 bit planes
iSize *= 2; // 2 bit planes
_bbep.ucScreen = (uint8_t *)malloc(iSize);
if (_bbep.ucScreen != NULL) {
return BBEP_SUCCESS;
@@ -212,8 +211,8 @@ void BBEPAPER::setTextColor(int iFG, int iBG)
if (iFG > BBEP_RED) iFG = BBEP_BLACK;
if (iBG > BBEP_RED) iBG = BBEP_BLACK;
if ((_bbep.iFlags & (BBEP_3COLOR | BBEP_4COLOR)) == 0) {
if (iFG == BBEP_RED) iFG = BBEP_BLACK; // can't set red color
if (iBG == BBEP_RED) iBG = BBEP_BLACK;
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;
+5 -2
View File
@@ -120,6 +120,9 @@ enum {
EP213R_122x250, // Inky phat 2.13 B/W/R
EP154_200x200, // waveshare
EP154B_200x200, // DEPG01540BN
EP266YR_184x360, // GDEY0266F51
EP29YR_128x296, // GDEY029F51
EP29YR_168x384, // GDEY029F51H
EP_PANEL_COUNT
};
#ifdef FUTURE
@@ -160,8 +163,8 @@ enum {
#define BBEP_4GRAY 0x0010
#define BBEP_CS_EVERY_BYTE 0x0020
#define BBEP_WHITE 0
#define BBEP_BLACK 1
#define BBEP_BLACK 0
#define BBEP_WHITE 1
#define BBEP_YELLOW 2
#define BBEP_RED 3