inital support for 7-color ACeP displays

This commit is contained in:
Laurence Bank
2024-12-16 12:21:13 +00:00
parent 99936d8ed2
commit 631e3c09e5
5 changed files with 429 additions and 158 deletions
+64 -28
View File
@@ -22,6 +22,7 @@
#include <SPI.h>
// foreward references
void bbepWakeUp(BBEPDISP *pBBEP);
void bbepSendCMDSequence(BBEPDISP *pBBEP, const uint8_t *pSeq);
//
// Initialize the GPIO pins and SPI for use by bb_eink
//
@@ -44,39 +45,57 @@ void bbepInitIO(BBEPDISP *pBBEP, uint8_t u8DC, uint8_t u8RST, uint8_t u8BUSY, ui
pinMode(pBBEP->iBUSYPin, INPUT);
}
pBBEP->iSpeed = u32Speed;
#ifdef ARDUINO_ARCH_ESP32
SPI.begin(pBBEP->iCLKPin, -1, pBBEP->iMOSIPin, pBBEP->iCSPin);
#else
pinMode(pBBEP->iCSPin, OUTPUT);
digitalWrite(pBBEP->iCSPin, HIGH); // we have to manually control the CS pin
digitalWrite(pBBEP->iCSPin, HIGH); // manually control the CS pin
#ifdef ARDUINO_ARCH_ESP32
SPI.begin(pBBEP->iCLKPin, -1, pBBEP->iMOSIPin, -1); //pBBEP->iCSPin);
#else
SPI.begin(); // other architectures have fixed SPI pins
#endif
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
} /* bbepInitIO() */
//
// Convenience function to write a command byte along with a data
// byte (it's single parameter)
//
void bbepCMD2(BBEPDISP *pBBEP, uint8_t cmd1, uint8_t cmd2)
{
if (!pBBEP->is_awake) {
// if it's asleep, it can't receive commands
bbepWakeUp(pBBEP);
pBBEP->is_awake = 1;
if (pBBEP->iFlags & BBEP_7COLOR) { // need to send before you can send it data
bbepSendCMDSequence(pBBEP, pBBEP->pInitFull);
}
digitalWrite(pBBEP->iDCPin, LOW);
#ifndef ARDUINO_ARCH_ESP32
} /* bbepInitIO() */
void bbepWriteIT8951Cmd(BBEPDISP *pBBEP, uint16_t cmd)
{
uint8_t ucTemp[4];
ucTemp[0] = 0x60; // command introducer = 0x6000
ucTemp[1] = 0;
ucTemp[2] = (uint8_t)(cmd >> 8);
ucTemp[3] = (uint8_t)cmd;
digitalWrite(pBBEP->iCSPin, LOW);
#ifdef ARDUINO_ARCH_ESP32
SPI.transferBytes(ucTemp, NULL, 4);
#else
SPI.transfer(ucTemp, 4);
#endif
SPI.transfer(cmd1);
digitalWrite(pBBEP->iDCPin, HIGH);
SPI.transfer(cmd2); // second byte is data
#ifndef ARDUINO_ARCH_ESP32
digitalWrite(pBBEP->iCSPin, HIGH);
} /* bbepWriteIT8951Cmd() */
void bbepWriteIT8951Data(BBEPDISP *pBBEP, uint8_t *pData, int iLen)
{
digitalWrite(pBBEP->iCSPin, LOW);
SPI.transfer(0);
SPI.transfer(0); // 0x0000 is the data introducer
#ifdef ARDUINO_ARCH_ESP32
SPI.transferBytes(pData, NULL, iLen);
#else
SPI.transfer(pData, iLen);
#endif
// digitalWrite(pBBEP->iDCPin, HIGH); // leave data mode as the default
} /* bbepCMD2() */
digitalWrite(pBBEP->iCSPin, HIGH);
} /* bbepWriteIT8951Data() */
void bbepWriteIT8951CmdArgs(BBEPDISP *pBBEP, uint16_t cmd, uint16_t *pArgs, int iCount)
{
bbepWriteIT8951Cmd(pBBEP, cmd);
for (int i=0; i<iCount; i++) {
pArgs[i] = __builtin_bswap16(pArgs[i]);
}
bbepWriteIT8951Data(pBBEP, (uint8_t *)pArgs, iCount * 2);
} /* bbepWriteIT8951CmdArgs() */
//
// Write a single byte as a COMMAND (D/C set low)
//
@@ -88,13 +107,10 @@ void bbepWriteCmd(BBEPDISP *pBBEP, uint8_t cmd)
pBBEP->is_awake = 1;
}
digitalWrite(pBBEP->iDCPin, LOW);
#ifndef ARDUINO_ARCH_ESP32
delay(1);
digitalWrite(pBBEP->iCSPin, LOW);
#endif
SPI.transfer(cmd);
#ifndef ARDUINO_ARCH_ESP32
digitalWrite(pBBEP->iCSPin, HIGH);
#endif
digitalWrite(pBBEP->iDCPin, HIGH); // leave data mode as the default
} /* bbepWriteCmd() */
//
@@ -104,7 +120,17 @@ void bbepWriteData(BBEPDISP *pBBEP, uint8_t *pData, int iLen)
{
// digitalWrite(pBBEP->iDCPin, HIGH);
#ifdef ARDUINO_ARCH_ESP32
SPI.transferBytes(pData, NULL, iLen);
if (pBBEP->iFlags & BBEP_CS_EVERY_BYTE) {
for (int i=0; i<iLen; i++) {
digitalWrite(pBBEP->iCSPin, LOW);
SPI.transfer(pData[i]);
digitalWrite(pBBEP->iCSPin, HIGH);
}
} else {
digitalWrite(pBBEP->iCSPin, LOW);
SPI.transferBytes(pData, NULL, iLen);
digitalWrite(pBBEP->iCSPin, HIGH);
}
#else
if (pBBEP->iFlags & BBEP_CS_EVERY_BYTE) {
for (int i=0; i<iLen; i++) { // Arduino clobbers the data (duplex)
@@ -122,4 +148,14 @@ void bbepWriteData(BBEPDISP *pBBEP, uint8_t *pData, int iLen)
#endif
} /* bbepWriteData() */
//
// Convenience function to write a command byte along with a data
// byte (it's single parameter)
//
void bbepCMD2(BBEPDISP *pBBEP, uint8_t cmd1, uint8_t cmd2)
{
bbepWriteCmd(pBBEP, cmd1);
bbepWriteData(pBBEP, &cmd2, 1);
} /* bbepCMD2() */
#endif // __BB_EP_IO__
+179 -15
View File
@@ -22,9 +22,11 @@ void InvertBytes(uint8_t *pData, uint8_t bLen);
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);
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);
int bbepSetPixel16Clr(void *pb, int x, int y, unsigned char ucColor);
const uint8_t ucMirror[256] PROGMEM =
{0, 128, 64, 192, 32, 160, 96, 224, 16, 144, 80, 208, 48, 176, 112, 240,
@@ -459,6 +461,13 @@ const uint8_t epd266yr_init_sequence_full[] PROGMEM =
0x00
};
// M5Paper
const uint16_t epd47_it8951_init[] PROGMEM = {
1, IT8951_TCON_SYS_RUN,
2, IT8951_I80CPCR, 0x0001, // enable packed write
3, 0x0039, 0x0001, 2300, // set VCOM to -2.30v
0
};
// 1.54" 200x200
const uint8_t epd154_init_sequence_full[] PROGMEM =
{
@@ -1196,7 +1205,63 @@ const uint8_t epd75_init_sequence_full[] PROGMEM = {
0
};
// GDEY073D46 800x480 7-color init sequence
const uint8_t epd73_init[] PROGMEM = {
7, 0xaa, 0x49, 0x55, 0x20, 0x08, 0x09, 0x18, // CMD H
7, UC8151_PWR, 0x3f, 0x00, 0x32, 0x2a, 0x0e, 0x2a, // power setting
3, UC8151_PSR, 0x5f, 0x69, // panel setting
5, UC8151_PFS, 0x00, 0x54, 0x00, 0x44,
5, 0x05, 0x40, 0x1f, 0x1f, 0x2c, // BTST1
5, 0x06, 0x6f, 0x1f, 0x1f, 0x25, // BTST2
5, 0x08, 0x6f, 0x1f, 0x1f, 0x22, // BTST3
3, 0x13, 0x00, 0x04, // IPC
2, 0x30, 0x3c,
// 2, UC8151_PLL, 0x02,
2, UC8151_TSE, 0x00,
2, UC8151_CDI, 0x3f,
3, UC8151_TCON, 0x02, 0x00,
5, UC8151_TRES, 0x03, 0x20, 0x01, 0xe0, // resolution 800x480
2, UC8151_VDCS, 0x1e,
2, 0x84, 0x00, // T_VCDS
2, 0x86, 0x00, // AGID
2, UC8151_PWS, 0x2f,
2, 0xe0, 0x00, // CCSET
2, 0xe6, 0x00, // TSSET
1, UC8151_PON, // power on
BUSY_WAIT,
0
};
// Spectra 6 (GDEP073E01) 800x480 7-color init sequence
const uint8_t epd73_spectra_init[] PROGMEM = {
7, 0xaa, 0x49, 0x55, 0x20, 0x08, 0x09, 0x18, // CMD H
7, UC8151_PWR, 0x3f, 0x00, 0x32, 0x2a, 0x0e, 0x2a, // power setting
3, UC8151_PSR, 0x5f, 0x69, // panel setting
5, UC8151_PFS, 0x00, 0x54, 0x00, 0x44,
5, 0x05, 0x40, 0x1f, 0x1f, 0x2c, // BTST1
5, 0x06, 0x6f, 0x1f, 0x16, 0x25, // BTST2
5, 0x08, 0x6f, 0x1f, 0x1f, 0x22, // BTST3
3, 0x13, 0x00, 0x04, // IPC
2, UC8151_PLL, 0x02,
// 2, 0x30, 0x3c,
2, UC8151_TSE, 0x00,
2, UC8151_CDI, 0x3f,
3, UC8151_TCON, 0x02, 0x00,
5, UC8151_TRES, 0x03, 0x20, 0x01, 0xe0, // resolution 800x480
2, UC8151_VDCS, 0x1e,
2, 0x84, 0x01, // T_VCDS
2, 0x86, 0x00, // AGID
2, UC8151_PWS, 0x2f,
2, 0xe0, 0x00, // CCSET
2, 0xe6, 0x00, // TSSET
1, UC8151_PON, // power on
BUSY_WAIT,
0
};
#ifdef NO_RAM
uint8_t u8Cache[128]; // buffer a single line of up to 1024 pixels
#else // we need a larger cache for 4-bit panels
uint8_t u8Cache[512];
#endif
//
// Definitions for each supported panel
// The order tracks that of the enumerated panel types
@@ -1230,7 +1295,11 @@ const EPD_PANEL panelDefs[] PROGMEM = {
{128, 296, 0, epd29yr_init_sequence_full, NULL, NULL, BBEP_4COLOR, BBEP_CHIP_UC81xx}, // EP29YR_128x296
{168, 384, 0, epd29yrh_init_sequence_full, NULL, NULL, BBEP_4COLOR, BBEP_CHIP_UC81xx}, // EP29YR_168x384
{648, 480, 0, epd583_init_sequence_full, NULL, epd583_init_sequence_part, 0, BBEP_CHIP_UC81xx}, // EP583_648x480
{128, 296, 0, epd293_init_sequence_full, epd293_init_sequence_fast, epd296_init_sequence_part, BBEP_PARTIAL2, BBEP_CHIP_SSD16xx}, // EP296_128x296 Waveshare 2.9" V2 B/W
{152, 296, 0, epd26r_init_sequence_full, NULL, NULL, BBEP_3COLOR, BBEP_CHIP_SSD16xx}, // EP26R_152x296 Solum ESL harvested 2.6" panel B/W/R
{540, 960, 0, (const uint8_t *)epd47_it8951_init, NULL, NULL, 0, BBEP_CHIP_IT8951}, // EP47_540x960 = M5Paper (original)
{800, 480, 0, epd73_init, NULL, NULL, BBEP_7COLOR, BBEP_CHIP_UC81xx}, // EP73_800x480 GEDY073D4 6 7-color 800x480
{800, 480, 0, epd73_spectra_init, NULL, NULL, BBEP_7COLOR, BBEP_CHIP_UC81xx}, // EP73_800x480 Spectra 6 7-color 800x480
};
//
// Set the e-paper panel type
@@ -1251,13 +1320,16 @@ int bbepSetPanelType(BBEPDISP *pBBEP, int iPanel)
pBBEP->pInitFast = panelDefs[iPanel].pInitFast;
pBBEP->pInitPart = panelDefs[iPanel].pInitPart;
pBBEP->type = iPanel;
// select the correct pixel drawing functions (2/3/4 color)
// select the correct pixel drawing functions (2/3/4/7 color)
if (pBBEP->iFlags & BBEP_4COLOR) {
pBBEP->pfnSetPixel = bbepSetPixel4Clr;
pBBEP->pfnSetPixelFast = bbepSetPixelFast4Clr;
} else if (pBBEP->iFlags & BBEP_3COLOR) {
pBBEP->pfnSetPixel = bbepSetPixel3Clr;
pBBEP->pfnSetPixelFast = bbepSetPixelFast3Clr;
} else if (pBBEP->iFlags & BBEP_7COLOR) {
pBBEP->pfnSetPixel = bbepSetPixel16Clr;
pBBEP->pfnSetPixelFast = bbepSetPixelFast16Clr;
} else { // must be B/W
pBBEP->pfnSetPixel = bbepSetPixel2Clr;
pBBEP->pfnSetPixelFast = bbepSetPixelFast2Clr;
@@ -1280,6 +1352,9 @@ int bbepCreateVirtual(BBEPDISP *pBBEP, int iWidth, int iHeight, int iFlags)
} else if (iFlags & BBEP_3COLOR) {
pBBEP->pfnSetPixel = bbepSetPixel3Clr;
pBBEP->pfnSetPixelFast = bbepSetPixelFast3Clr;
} else if (pBBEP->iFlags & BBEP_7COLOR) {
pBBEP->pfnSetPixel = bbepSetPixel16Clr;
pBBEP->pfnSetPixelFast = bbepSetPixelFast16Clr;
} else { // must be B/W
pBBEP->pfnSetPixel = bbepSetPixel2Clr;
pBBEP->pfnSetPixelFast = bbepSetPixelFast2Clr;
@@ -1300,6 +1375,7 @@ void bbepWaitBusy(BBEPDISP *pBBEP)
if (!pBBEP) return;
if (pBBEP->iBUSYPin == 0xff) return;
delay(10); // give time for the busy status to be valid
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 (iTimeout < 5000) {
@@ -1316,7 +1392,7 @@ void bbepWakeUp(BBEPDISP *pBBEP)
digitalWrite(pBBEP->iRSTPin, LOW);
delay(10);
digitalWrite(pBBEP->iRSTPin, HIGH);
delay(10);
delay(20);
bbepWaitBusy(pBBEP);
} /* bbepWakeUp() */
//
@@ -1328,8 +1404,18 @@ 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;
if (pBBEP->iFlags & (BBEP_4COLOR | BBEP_7COLOR)) return;
if (pBBEP->chip_type == BBEP_CHIP_IT8951) {
uint16_t u16Temp[6];
u16Temp[0] = 0; // DEBUG (_endian_type << 8 | _pix_bpp << 4 | _rotate);
u16Temp[1] = x;
u16Temp[2] = y;
u16Temp[3] = cx;
u16Temp[4] = cy;
bbepWriteIT8951CmdArgs(pBBEP, IT8951_TCON_LD_IMG_AREA, u16Temp, 5);
return;
}
tx = x/8; // round down to next lower byte
ty = y;
cx = (cx + 7) & 0xfff8; // make width an even number of bytes
@@ -1390,11 +1476,15 @@ void bbepSleep(BBEPDISP *pBBEP, int bDeep)
{
if (!pBBEP) return;
if (pBBEP->chip_type == BBEP_CHIP_UC81xx) {
bbepCMD2(pBBEP, UC8151_CDI, 0x17); // border floating
bbepWriteCmd(pBBEP, UC8151_POFF); // power off
bbepWaitBusy(pBBEP);
if (bDeep) {
bbepCMD2(pBBEP, UC8151_DSLP, 0xa5); // deep sleep
if (pBBEP->iFlags & BBEP_7COLOR) {
bbepCMD2(pBBEP, UC8151_POFF, 0x00); // power off
} else {
bbepCMD2(pBBEP, UC8151_CDI, 0x17); // border floating
bbepWriteCmd(pBBEP, UC8151_POFF); // power off
bbepWaitBusy(pBBEP);
if (bDeep) {
bbepCMD2(pBBEP, UC8151_DSLP, 0xa5); // deep sleep
}
}
} else {
bbepCMD2(pBBEP, SSD1608_DEEP_SLEEP, 0x01); // deep sleep mode 1 keeps RAM,only uses about 1uA
@@ -1487,7 +1577,11 @@ void bbepFill(BBEPDISP *pBBEP, unsigned char ucData, int iPlane)
pBBEP->iCursorX = pBBEP->iCursorY = 0;
iPitch = ((pBBEP->native_width+7)/8);
iSize = pBBEP->native_height * iPitch;
if (pBBEP->iFlags & BBEP_3COLOR) {
if (pBBEP->iFlags & BBEP_7COLOR) {
uc1 = ucData | (ucData << 4);
iPitch = pBBEP->native_width / 2;
iSize = pBBEP->native_height * iPitch;
} else if (pBBEP->iFlags & BBEP_3COLOR) {
if (ucData == BBEP_WHITE) {
uc1 = 0xff; uc2 = 0x00; // red plane has priority
} else if (ucData == BBEP_BLACK) {
@@ -1504,12 +1598,15 @@ void bbepFill(BBEPDISP *pBBEP, unsigned char ucData, int iPlane)
} else { // B/W
if (ucData == BBEP_WHITE) {
uc1 = uc2 = 0xff;
} else {
} else {
uc1 = uc2 = 0x00;
}
}
}
if (pBBEP->ucScreen) { // there's a local framebuffer, use it
if ((pBBEP->iFlags & BBEP_3COLOR) || iPlane == PLANE_BOTH) {
if (pBBEP->iFlags & BBEP_7COLOR) {
memset(pBBEP->ucScreen, uc1, iSize);
return;
} else if ((pBBEP->iFlags & BBEP_3COLOR) || iPlane == PLANE_BOTH) {
memset(pBBEP->ucScreen, uc1, iSize);
memset(&pBBEP->ucScreen[iSize], uc2, iSize);
} else if (iPlane == PLANE_DUPLICATE) {
@@ -1589,7 +1686,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) {
if (pBBEP->iFlags & (BBEP_4COLOR | BBEP_7COLOR)) {
bbepCMD2(pBBEP, 0x12, 0x00);
} else {
bbepWriteCmd(pBBEP, UC8151_PTOU); // partial out (update the entire panel, not just the last memory window)
@@ -1635,6 +1732,68 @@ void bbepSetRotation(BBEPDISP *pBBEP, int iRotation)
}
} /* bbepSetRotation() */
void bbepWriteImage4bpp(BBEPDISP *pBBEP, uint8_t ucCMD)
{
int tx, ty, iPitch;
uint8_t uc, *s, *d;
if (ucCMD) {
bbepWriteCmd(pBBEP, ucCMD); // start write
}
if (pBBEP->iOrientation == 0) {
iPitch = pBBEP->native_width / 2;
s = pBBEP->ucScreen;
for (ty=0; ty<pBBEP->height; ty++) {
bbepWriteData(pBBEP, s, iPitch);
s += iPitch; // 2 pixels per byte
} // for ty
} else if (pBBEP->iOrientation == 180) {
iPitch = pBBEP->native_width / 2;
for (ty=pBBEP->height-1; ty>=0; ty--) {
s = &pBBEP->ucScreen[(ty * iPitch) + iPitch - 1];
// reverse the pixel direction
for (tx=0; tx<pBBEP->native_width; tx+=2) {
uc = *s--;
uc = (uc >> 4) | (uc << 4); // swap nibbles
u8Cache[tx/2] = uc;
}
bbepWriteData(pBBEP, u8Cache, iPitch);
} // for ty
} else if (pBBEP->iOrientation == 90) {
iPitch = pBBEP->native_height / 2;
for (tx=0; tx<pBBEP->width; tx++) {
d = u8Cache;
for (ty=pBBEP->height-1; ty > 0; ty-=2) {
s = &pBBEP->ucScreen[(tx>>1) + (ty * iPitch)];
if (tx & 1) {
uc = (s[0] << 4) | (s[-iPitch] & 0x0f);
} else {
uc = (s[0] & 0xf0) | (s[-iPitch] >> 4);
}
s -= iPitch*2;
*d++ = uc; // store 4 pixels
} // for ty
bbepWriteData(pBBEP, u8Cache, pBBEP->height/2);
} // for tx
} else if (pBBEP->iOrientation == 270) {
iPitch = pBBEP->native_height / 2;
for (tx=pBBEP->width-1; tx>= 0; tx--) {
d = u8Cache;
for (ty=0; ty < pBBEP->height; ty+=2) {
s = &pBBEP->ucScreen[(tx>>1) + (ty * iPitch)];
if (tx & 1) {
uc = (s[0] << 4) | (s[iPitch] & 0x0f);
} else {
uc = (s[0] & 0xf0) | (s[iPitch] >> 4);
}
s += iPitch*2;
*d++ = uc; // store 4 pixels
} // for ty
bbepWriteData(pBBEP, u8Cache, pBBEP->height/2);
} // for tx
}
} /* bbepWriteImage4bpp() */
void bbepWriteImage2bpp(BBEPDISP *pBBEP, uint8_t ucCMD)
{
int tx, ty;
@@ -1795,10 +1954,15 @@ int bbepWritePlane(BBEPDISP *pBBEP, int iPlane)
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_DUPLICATE) {
return BBEP_ERROR_BAD_PARAMETER;
}
bbepSetAddrWindow(pBBEP, 0,0, pBBEP->native_width, pBBEP->native_height);
if (pBBEP->iFlags & BBEP_7COLOR) {
bbepWriteImage4bpp(pBBEP, 0x10);
return BBEP_SUCCESS;
}
if (pBBEP->iFlags & BBEP_4COLOR) { // 4-color only has 1 way to go
bbepWriteImage2bpp(pBBEP, 0x10);
return BBEP_SUCCESS;
+80 -104
View File
@@ -430,6 +430,56 @@ void bbepSetPixelFast2Clr(void *pb, int x, int y, unsigned char ucColor)
pBBEP->ucScreen[i] = u8;
} /* bbepSetPixelFast2Clr() */
int bbepSetPixel16Clr(void *pb, int x, int y, unsigned char ucColor)
{
int i;
int iPitch, iSize;
uint8_t u8;
BBEPDISP *pBBEP = (BBEPDISP *)pb;
// only available for local buffer operations
if (!pBBEP || !pBBEP->ucScreen) return BBEP_ERROR_BAD_PARAMETER;
iPitch = pBBEP->width >> 1;
iSize = (pBBEP->native_width >> 1) * pBBEP->native_height;
i = (x >> 1) + (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;
}
u8 = pBBEP->ucScreen[i];
if (x & 1) {
u8 &= 0xf0;
u8 |= ucColor;
} else {
u8 &= 0x0f;
u8 |= (ucColor << 4);
}
pBBEP->ucScreen[i] = u8;
return BBEP_SUCCESS;
} /* bbepSetPixel16Clr() */
void bbepSetPixelFast16Clr(void *pb, int x, int y, unsigned char ucColor)
{
int i;
int iPitch;
uint8_t u8;
BBEPDISP *pBBEP = (BBEPDISP *)pb;
iPitch = pBBEP->width >> 1;
i = (x >> 1) + (y * iPitch);
u8 = pBBEP->ucScreen[i];
if (x & 1) {
u8 &= 0xf0;
u8 |= ucColor;
} else {
u8 &= 0x0f;
u8 |= (ucColor << 4);
}
pBBEP->ucScreen[i] = u8;
} /* bbepSetPixelFast16Clr() */
//
// Invert font data
//
@@ -1390,13 +1440,34 @@ void bbepGetStringBox(BBEPDISP *pBBEP, BB_FONT *pFont, const char *szMsg, int *w
*bottom = maxy;
} /* bbepGetStringBox() */
void bbepAllocBuffer(BBEPDISP *pBBEP)
int bbepAllocBuffer(BBEPDISP *pBBEP)
{
#ifndef NO_RAM
pBBEP->ucScreen = (uint8_t *)malloc(pBBEP->width * ((pBBEP->height+7)>>3));
pBBEP->iScreenOffset = 0;
int iSize;
if (pBBEP->iFlags & (BBEP_7COLOR | BBEP_16GRAY)) { // 4-bpp
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 defined (HAL_ESP32_HAL_H_) && !defined(__riscv)
if (iSize > 90000) { // need to use PSRAM
#ifndef BOARD_HAS_PSRAM
#error "Please enable PSRAM!"
#endif
} /* obdAllocBuffer() */
pBBEP->ucScreen = (uint8_t *)ps_malloc(iSize);
} else {
pBBEP->ucScreen = (uint8_t *)malloc(iSize);
}
#else // not ESP32 or no PSRAM
pBBEP->ucScreen = (uint8_t *)malloc(iSize);
#endif // ESP32
if (pBBEP->ucScreen != NULL) {
return BBEP_SUCCESS;
}
#endif
return BBEP_ERROR_NO_MEMORY; // failed
} /* bbepAllocBuffer() */
//
// Draw a line from x1,y1 to x2,y2 in the given color
// This function supports both buffered and bufferless drawing
@@ -1421,18 +1492,6 @@ void bbepDrawLine(BBEPDISP *pBBEP, int x1, int y1, int x2, int y2, uint8_t ucCol
pBBEP->last_error = BBEP_ERROR_BAD_PARAMETER;
return;
}
iPitch = (pBBEP->width + 7)>>3;
if (ucColor >= BBEP_YELLOW && pBBEP->iFlags & (BBEP_3COLOR | BBEP_4COLOR)) {
// use the second half of the image buffer
iRedOffset = iPitch * pBBEP->height;
}
if (!pBBEP->ucScreen) { // no back buffer, draw in local buffer
if (ucColor == BBEP_BLACK) // fill with opposite color
ucFill = 0xff;
else
ucFill = 0;
memset(u8Cache, ucFill, sizeof(u8Cache));
}
if(abs(dx) > abs(dy)) {
// X major case
if(x2 < x1) {
@@ -1454,46 +1513,14 @@ void bbepDrawLine(BBEPDISP *pBBEP, int x1, int y1, int x2, int y2, uint8_t ucCol
dy = -dy;
yinc = -1;
}
if (pBBEP->ucScreen) {
p = pStart = &pBBEP->ucScreen[iRedOffset + (x1>>3) + (y * iPitch)]; // point to current spot in back buffer
} else { // no back buffer, draw directly to the display RAM
p = pStart = u8Cache;
}
mask = 0x80 >> (x1 & 7); // current bit offset
for(x=x1; x1 <= x2; x1++) {
if (ucColor == BBEP_BLACK) {
*p &= ~mask; // set pixel and increment x pointer
} else {
*p |= mask;
}
mask >>= 1;
if (mask == 0) {
p++;
mask = 0x80; // next byte
}
for(; x1 <= x2; x1++) {
(*pBBEP->pfnSetPixelFast)(pBBEP, x1, y1, ucColor);
error -= dy;
if (error < 0) {
error += dx;
if (pBBEP->ucScreen) {
p += (yinc > 0) ? iPitch : -iPitch;
pStart = p;
} else { // no buffer
// y is changing, dump the pixels we modified
bbepSetAddrWindow(pBBEP, x, y1, 16+x1-x, 1);
bbepStartWrite(pBBEP, pBBEP->iPlane);
bbepWriteData(pBBEP, pStart, (16+x1-x)>>3); // write the row we changed
memset(pStart, ucFill, (16+x1-x)>>3);
p = pStart;
}
x = x1+1; // we've already written the byte at x1
y1 += yinc;
}
} // for x1
if (x != x1 && !pBBEP->ucScreen) { // some data needs to be written
bbepSetAddrWindow(pBBEP, x, y1, 16+x1-x, 1);
bbepStartWrite(pBBEP, pBBEP->iPlane);
bbepWriteData(pBBEP, pStart, (16+x1-x)>>3);
}
} else {
// Y major case
if(y1 > y2) {
@@ -1513,31 +1540,8 @@ void bbepDrawLine(BBEPDISP *pBBEP, int x1, int y1, int x2, int y2, uint8_t ucCol
xinc = -1;
}
for(; y1 <= y2; y1++) {
mask = 0x80 >> (x1 & 7); // current bit offset
if (pBBEP->ucScreen) {
p = &pBBEP->ucScreen[iRedOffset + (x1>>3) + (y1 * iPitch)]; // point
bOld = bNew = p[0]; // current pixels
} else {
bOld = bNew = ucFill;
}
if (ucColor == BBEP_BLACK) {
bNew &= ~mask; // set the pixel
} else {
bNew |= mask;
}
(*pBBEP->pfnSetPixelFast)(pBBEP, x1, y1, ucColor);
error -= dx;
if (bOld != bNew) {
if (pBBEP->ucScreen) {
p[0] = bNew; // save to RAM
p += iPitch; // next line
bOld = bNew = p[0];
} else {
bbepSetAddrWindow(pBBEP, x1, y1, 8, 1);
bbepStartWrite(pBBEP, pBBEP->iPlane);
bbepWriteData(pBBEP, &bNew, 1);
bOld = bNew = ucFill;
}
}
if (error < 0) {
error += dy;
x1 += xinc;
@@ -1563,35 +1567,7 @@ 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) {
// use the second half of the image buffer
iRedOffset = iPitch * pBBEP->height;
if (ucColor == BBEP_RED) {
d[iRedOffset] |= ucMask; // red has priority
} else {
d[iRedOffset] &= ~ucMask; // must remove red first
if (ucColor == BBEP_WHITE)
*d |= ucMask;
else
*d &= ~ucMask;
}
} else {
if (ucColor == BBEP_WHITE)
*d |= ucMask;
else
*d &= ~ucMask;
}
(*pBBEP->pfnSetPixelFast)(pBBEP, x, y, ucColor);
} /* DrawScaledPixel() */
//
// For drawing filled ellipses
@@ -1698,7 +1674,7 @@ void bbepRectangle(BBEPDISP *pBBEP, int x1, int y1, int x2, int y2, uint8_t ucCo
int iPitch;
int iRedOffset = 0;
if (ucColor >= BBEP_YELLOW) {
if (!(pBBEP->iFlags & BBEP_7COLOR) && ucColor >= BBEP_YELLOW) {
if (pBBEP->iFlags & (BBEP_3COLOR | BBEP_4COLOR)) {
// use the second half of the image buffer
iRedOffset = pBBEP->width * ((pBBEP->height+7)/8);
+2 -8
View File
@@ -141,13 +141,7 @@ void BBEPAPER::backupPlane(void)
}
int BBEPAPER::allocBuffer(void)
{
int iSize = ((_bbep.native_width+7)>>3) * _bbep.native_height;
iSize *= 2; // 2 bit planes
_bbep.ucScreen = (uint8_t *)malloc(iSize);
if (_bbep.ucScreen != NULL) {
return BBEP_SUCCESS;
}
return BBEP_ERROR_NO_MEMORY; // failed
return bbepAllocBuffer(&_bbep);
} /* allocBuffer() */
void * BBEPAPER::getBuffer(void)
@@ -212,7 +206,7 @@ bool BBEPAPER::hasPartialRefresh()
void BBEPAPER::setTextColor(int iFG, int iBG)
{
if ((_bbep.iFlags & (BBEP_3COLOR | BBEP_4COLOR)) == 0) {
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;
}
+104 -3
View File
@@ -64,6 +64,7 @@ enum {
BBEP_CHIP_NOT_DEFINED = 0,
BBEP_CHIP_SSD16xx,
BBEP_CHIP_UC81xx,
BBEP_CHIP_IT8951,
BBEP_CHIP_NONE, // for virtual display
BBEP_CHIP_COUNT
};
@@ -97,7 +98,7 @@ typedef struct epd_panel {
const uint8_t *pInitFull;
const uint8_t *pInitFast;
const uint8_t *pInitPart;
uint8_t flags;
uint16_t flags;
uint8_t chip_type;
} EPD_PANEL;
@@ -132,6 +133,9 @@ enum {
EP583_648x480, // DEPG0583BN
EP296_128x296, // Waveshare 2.9" 128x296 B/W V2
EP26R_152x296, // Solum 2.6" B/W/R harvested panel
EP47_540x960, // M5Paper
EP73_800x480, // GEDY073D46 (slower, EOL 7-color)
EP73_SPECTRA_800x480, // Spectra 6 7-color 800x480
// EP583R_600x448, // 4-bits per pixel needs different support
EP_PANEL_COUNT
};
@@ -166,8 +170,10 @@ enum {
#define BBEP_3COLOR 0x0004
#define BBEP_4COLOR 0x0008
#define BBEP_4GRAY 0x0010
#define BBEP_CS_EVERY_BYTE 0x0020
#define BBEP_PARTIAL2 0x0040
#define BBEP_7COLOR 0x0020
#define BBEP_16GRAY 0x0040
#define BBEP_CS_EVERY_BYTE 0x0080
#define BBEP_PARTIAL2 0x0100
#define BBEP_BLACK 0
#define BBEP_WHITE 1
@@ -175,6 +181,16 @@ enum {
#define BBEP_RED 3
#define BBEP_TRANSPARENT 255
// colors for Spectra 6
#define BBEP_SPECTRA_BLACK 0x00
#define BBEP_SPECTRA_WHITE 0x01
#define BBEP_SPECTRA_GREEN 0x02
#define BBEP_SPECTRA_BLUE 0x03
#define BBEP_SPECTRA_RED 0x04
#define BBEP_SPECTRA_YELLOW 0x05
#define BBEP_SPECTRA_ORANGE 0x06
#define BBEP_SPECTRA_CLEAN 0x07
// 4 gray levels
#define BBEP_GRAY0 0
#define BBEP_GRAY1 1
@@ -182,6 +198,88 @@ enum {
#define BBEP_GRAY3 3
#ifndef __ONEBITDISPLAY__
// (IT8951 commands)
// Built in I80 Command Code
#define IT8951_TCON_SYS_RUN 0x0001
#define IT8951_TCON_STANDBY 0x0002
#define IT8951_TCON_SLEEP 0x0003
#define IT8951_TCON_REG_RD 0x0010
#define IT8951_TCON_REG_WR 0x0011
#define IT8951_TCON_MEM_BST_RD_T 0x0012
#define IT8951_TCON_MEM_BST_RD_S 0x0013
#define IT8951_TCON_MEM_BST_WR 0x0014
#define IT8951_TCON_MEM_BST_END 0x0015
#define IT8951_TCON_LD_IMG 0x0020
#define IT8951_TCON_LD_IMG_AREA 0x0021
#define IT8951_TCON_LD_IMG_END 0x0022
// I80 User defined command code
#define IT8951_I80_CMD_DPY_AREA 0x0034
#define IT8951_I80_CMD_GET_DEV_INFO 0x0302
#define IT8951_I80_CMD_DPY_BUF_AREA 0x0037
#define IT8951_I80_CMD_VCOM 0x0039
// Rotate mode
#define IT8951_ROTATE_0 0
#define IT8951_ROTATE_90 1
#define IT8951_ROTATE_180 2
#define IT8951_ROTATE_270 3
// Pixel mode (Bit per Pixel)
#define IT8951_2BPP 0
#define IT8951_3BPP 1
#define IT8951_4BPP 2
#define IT8951_8BPP 3
// Endian Type
#define IT8951_LDIMG_L_ENDIAN 0
#define IT8951_LDIMG_B_ENDIAN 1
/*-----------------------------------------------------------------------
IT8951 Registers defines
------------------------------------------------------------------------*/
// Register Base Address
#define IT8951_DISPLAY_REG_BASE 0x1000 // Register RW access
// Base Address of Basic LUT Registers
#define IT8951_LUT0EWHR \
(IT8951_DISPLAY_REG_BASE + 0x00) // LUT0 Engine Width Height Reg
#define IT8951_LUT0XYR (IT8951_DISPLAY_REG_BASE + 0x40) // LUT0 XY Reg
#define IT8951_LUT0BADDR \
(IT8951_DISPLAY_REG_BASE + 0x80) // LUT0 Base Address Reg
#define IT8951_LUT0MFN \
(IT8951_DISPLAY_REG_BASE + 0xC0) // LUT0 Mode and Frame number Reg
#define IT8951_LUT01AF \
(IT8951_DISPLAY_REG_BASE + 0x114) // LUT0 and LUT1 Active Flag Reg
// Update Parameter Setting Register
#define IT8951_UP0SR \
(IT8951_DISPLAY_REG_BASE + 0x134) // Update Parameter0 Setting Reg
#define IT8951_UP1SR \
(IT8951_DISPLAY_REG_BASE + 0x138) // Update Parameter1 Setting Reg
#define IT8951_LUT0ABFRV \
(IT8951_DISPLAY_REG_BASE + \
0x13C) // LUT0 Alpha blend and Fill rectangle Value
#define IT8951_UPBBADDR \
(IT8951_DISPLAY_REG_BASE + 0x17C) // Update Buffer Base Address
#define IT8951_LUT0IMXY \
(IT8951_DISPLAY_REG_BASE + 0x180) // LUT0 Image buffer X/Y offset Reg
#define IT8951_LUTAFSR \
(IT8951_DISPLAY_REG_BASE + \
0x224) // LUT Status Reg (status of All LUT Engines)
#define IT8951_BGVR \
(IT8951_DISPLAY_REG_BASE + 0x250) // Bitmap (1bpp) image color table
// System Registers
#define IT8951_SYS_REG_BASE 0x0000
// Address of System Registers
#define IT8951_I80CPCR (IT8951_SYS_REG_BASE + 0x04)
// Memory Converter Registers
#define IT8951_MCSR_BASE_ADDR 0x0200
#define IT8951_MCSR (IT8951_MCSR_BASE_ADDR + 0x0000)
#define IT8951_LISAR (IT8951_MCSR_BASE_ADDR + 0x0008)
// (UC81xx commands)
enum {
UC8151_PSR = 0x00,
@@ -391,6 +489,9 @@ class BBEPAPER
private:
BBEPDISP _bbep;
uint32_t _tar_memaddr = 0x001236E0;
uint16_t _dev_memaddr_l = 0x36E0;
uint16_t _dev_memaddr_h = 0x0012;
}; // class BBEPAPER
#endif // __cplusplus