mirror of
https://github.com/usetrmnl/bb_epaper.git
synced 2026-04-29 13:43:26 -07:00
Fixed 2-byte command bug
This commit is contained in:
@@ -8,3 +8,4 @@ category=Display
|
||||
url=https://github.com/bitbank2/bb_eink
|
||||
architectures=*
|
||||
includes=bb_eink.h
|
||||
depends=Group5
|
||||
|
||||
+72
-9
@@ -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
|
||||
}
|
||||
|
||||
+165
-120
@@ -18,7 +18,9 @@
|
||||
//
|
||||
#ifndef __BB_EI_GFX__
|
||||
#define __BB_EI_GFX__
|
||||
|
||||
#include <Group5.h>
|
||||
#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; ty<end_y && ty < pBBEI->native_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<w+7; j+= 8) {
|
||||
uc1 = *s;
|
||||
uc0 |= (uc1 >> 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;
|
||||
|
||||
+34
-13
@@ -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 <Arduino.h>
|
||||
#include <SPI.h>
|
||||
|
||||
//
|
||||
// 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
|
||||
|
||||
+23
-5
@@ -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
|
||||
|
||||
+9
-7
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user