-
-
Notifications
You must be signed in to change notification settings - Fork 5
Home
The FASTEPD class exposes the methods detailed below.
This is the entire class definition for FASTEPD:
class FASTEPD {
public:
FASTEPD() {memset(&_state, 0, sizeof(_state)); _state.iFont = FONT_8x8; _state.iFG = BBEP_BLACK;}
int initPanel(int iPanelType);
int initCustomPanel(BBPANELDEF *pPanel, BBPANELPROCS *pProcs);
int setPanelSize(int width, int height);
int setMode(int iMode); // set graphics mode
uint8_t *previousBuffer(void) { return _state.pPrevious;}
uint8_t *currentBuffer(void) { return _state.pCurrent;}
int einkPower(int bOn);
int fullUpdate(bool bFast = false, bool bKeepOn = false, BBEPRECT *pRect = NULL);
int partialUpdate(bool bKeepOn, int iStartRow = 0, int iEndRow = 2047);
int setRotation(int iAngle);
int getRotation(void) { return _state.rotation;}
void backupPlane(void);
void drawRoundRect(int x, int y, int w, int h, int r, uint8_t color);
void fillRoundRect(int x, int y, int w, int h, int r, uint8_t color);
void fillScreen(uint8_t iColor);
void drawRect(int x, int y, int w, int h, uint8_t color);
void fillRect(int x, int y, int w, int h, uint8_t color);
void setTextWrap(bool bWrap);
void setTextColor(int iFG, int iBG = BBEP_TRANSPARENT);
void setCursor(int x, int y) {_state.iCursorX = x; _state.iCursorY = y;}
int loadBMP(const uint8_t *pBMP, int x, int y, int iFG, int iBG);
int loadG5Image(const uint8_t *pG5, int x, int y, int iFG, int iBG);
void setFont(int iFont);
void setFont(const void *pFont);
int getStringBox(const char *text, BBEPRECT *pRect);
void drawLine(int x1, int y1, int x2, int y2, int iColor);
void drawPixel(int16_t x, int16_t y, uint8_t color);
void drawPixelFast(int16_t x, int16_t y, uint8_t color);
int16_t height(void) { return _state.height;}
int16_t width(void) {return _state.width;}
void drawCircle(int32_t x, int32_t y, int32_t r, uint32_t color);
void fillCircle(int32_t x, int32_t y, int32_t r, uint32_t color);
void drawEllipse(int16_t x, int16_t y, int32_t rx, int32_t ry, uint16_t color);
void fillEllipse(int16_t x, int16_t y, int32_t rx, int32_t ry, uint16_t color);
void drawString(const char *pText, int x, int y);
void drawSprite(const uint8_t *pSprite, int cx, int cy, int iPitch, int x, int y, uint8_t iColor);
#ifdef ARDUINO
using Print::write;
virtual size_t write(uint8_t);
#endif
protected:
FASTEPDSTATE _state;
};
The class is a C++ wrapper for the C code which does all of the actual work. The BBEPDIYSTATE structure is kept private, but is passed as a pointer to the 'worker' functions. This makes it easy to lift out the C code if you want to use it in a pure C project. Here are details of each method:
initPanel(int iPanel)
The integer panel value passed to this function will be one of the pre-defined names (e.g. BB_PANEL_M5PAPERS3). This will set up the I/O and allocate the memory to prepare the panel for use. Normally a panel definition has all of the information needed, but in the case of the EPDiy V7 PCB, you would need to define the panel size separately (see setPanelSize below).
initCustomPanel(BBPANELDEF *, BBPANELPROCS *)
This method is initializing a custom PCB and panel. Pass it the panel definition structure and callback functions (see below for info on the callback functions).
setPanelSize(int w, int h)
This method allows you to define the type of eink panel you're using separately from the hardware platform. Specifically, this is for the EPDiy V7 PCB where you can connect a variety of panels to it.
setMode(int mode)
This method allows you to switch pixel modes. The only accepted values are BB_MODE_1BPP and BB_MODE_4BPP. The mode can be switched at any time after initializing the panel
previousBuffer()
Returns a pointer to the internal buffer used to hold the previous image.
currentBuffer()
Returns a pointer to the internal buffer used to hold the current image.
einkPower(int bOn)
This method allows you to enable or disable the DC/DC circuit which powers the eink display.
fullUpdate(bool bFast, bool bKeepOn, BBEPRECT *pRect)
Perform a full (flashing) update with various options. The first parameter (bFast) indicates if you would like a fast (single flash of B/W) or normal (double flash). If your display has accumulated ghosting from previous updates, a doube-flash might be needed to clear it completely. The second parameter (bKeepOn) indicates if you would like the DC/DC circuit to stay powered on after the update is complete. If you're going to immediately do more updates, then it can be worth keeping the power on to save time. A power on sequence normally takes 10-40ms. The third parameter allows you to define an update region such that only those pixels are affected by the update.
partialUpdate(int bKeepOn, int iStartRow, int iEndRow)
Perform a partial (non-flashing) update. This update method looks at the current image buffer and compares it to the previous image buffer and changes the pixels that have changed (one direction: B->W or W->B). As with the fullUpdate, the bKeepOn parameter allows you to indicate that you would like the DC/DC boost power to stay on after the update is finished. The next two parameters indicate the starting and ending row numbers that need to be updated. If you don't specify them, the entire display will be updated. If you know that you're only changing a few lines, this can increase the speed of the update. The row values are for the native orientation of the display panel. If you're working at a rotation angle of 90 or 270 degrees, then these values will refer to the columns.
setRotation(int iAngle)
Set the drawing rotation angle to 0,90,180 or 270 degrees. This affects all drawing, but does not have any effect on the existing data in the current and previous buffers. For example, if you want to display text in multiple orientations at the same time, simply call this method and continue drawing on the display.
getRotation(void)
Returns the current drawing orientation.
backupPlane(void)
This method copies the current image buffer into the previous image buffer. This can be useful for controlling partial updates after the power is lost. If you know the content that you previously drew, you can re-create it, draw the new content and then do a non-flashing update after the memory contents were lost (e.g. after ESP32 deep sleep).
void drawRoundRect(int x, int y, int w, int h, int r, uint8_t color);
This method draws a rectangle (outline) with rounded corners. The fifth parameter is the radius of the 90 degree arc used to draw each corner.
void fillRoundRect(int x, int y, int w, int h, int r, uint8_t color);
This method draws a filled rectangle with rounded corners. The fifth parameter is the radius of the 90 degree arc used to draw each corner.
void fillScreen(uint8_t color);
This method fills the current image buffer with the given color. The previous image buffer is unaffected.
void drawRect(int x, int y, int w, int h, uint8_t color);
Draw a rectangle (outline) in the given color.
void fillRect(int x, int y, int w, int h, uint8_t color);
Draw a filled rectangle in the given color.
void setTextWrap(bool bWrap);
Enable or disable the automatic wrapping of text when it reaches the end of the current line.
void setTextColor(int iFG, int iBG = BBEP_TRANSPARENT);
Sets the color for text to be drawn. The first parameter is the foreground color and the second is the background. Either one can be set to BBEP_TRANSPARENT to not draw any color.
void setCursor(int x, int y);
Sets the cursor position for drawing text. Internal fonts use 0,0 as the upper left corner of the character box. TrueType converted fonts use the baseline of the font as 0,0 with negative offsets for the top.
int loadBMP(const uint8_t *pBMP, int x, int y, int iFG, int iBG);
Load a 1-bit Windows BMP image and draw it at the given x,y. The foreground and background colors determine how 1's and 0's of the image will be drawn onto the image buffer.
int loadG5Image(const uint8_t *pG5, int x, int y, int iFG, int iBG);
Decompress and draw a Group5 compressed image at the given x,y with the given colors. See the section below about compressed fonts and images.
void setFont(int iFont);
This version of the overloaded function sets the current font to one of the internal, fixed width fonts. Valid values are: FONT_6x8, FONT_8x8, FONT_12x16, FONT_16x16.
void setFont(const void *pFont);
This version of the overloaded function takes a void pointer to a compressed BB_FONT. This data can be compiled into the code or loaded dynamically at run time.
int getStringBox(const char *text, BBEPRECT *pRect);
Returns the rectangle which completely surrounds the given string. The coordinates are relative to the current cursor position. For example, if using a BB_FONT (converted TrueType font) and the cursor is at 0,0, then the starting Y value will be negative since TrueType fonts measure the Y=0 point from the baseline of the font.
void drawLine(int x1, int y1, int x2, int y2, int iColor);
Draw a Bresenham line from x1,y1 to x2,y2 with the given color.
void drawPixel(int16_t x, int16_t y, uint8_t color);
Draw a pixel at x,y of the given color. The boundary conditions will be checked to ensure that the pixel isn't drawn beyond the edges of the current display area.
void drawPixelFast(int16_t x, int16_t y, uint8_t color);
This pixel draw function is faster because it doesn't do boundary checking. If you're sure that you won't ask it to draw outside of the current display area, then this will use fewer CPU cycles.
int16_t height(void) { return _state.height;}
Returns the current height of the display in pixels. This will change to be the physical width of the display if the rotation is set to 90 or 270 degrees.
int16_t width(void) {return _state.width;}
Returns the current width of the display in pixels. This will return the physical height of the display if the rotation angle is set to 90 or 270 degrees.
void drawCircle(int32_t x, int32_t y, int32_t r, uint32_t color);
Draw a Bresenham circle (outline) with the given color.
void fillCircle(int32_t x, int32_t y, int32_t r, uint32_t color);
Draw a filled Bresenham circle with the given color.
void drawEllipse(int16_t x, int16_t y, int32_t rx, int32_t ry, uint16_t color);
Draw an ellipse (outline) with the given color. The radius in the X and Y directions are specified separately.
void fillEllipse(int16_t x, int16_t y, int32_t rx, int32_t ry, uint16_t color);
Draw a filled ellipse with the given color. The radius of the X and Y directions are specified separately.
void drawString(const char *pText, int x, int y);
Draw a text string using the currently selected font and the currently selected colors starting at the x,y position provided.
void drawSprite(const uint8_t *pSprite, int cx, int cy, int iPitch, int x, int y, uint8_t iColor);
Draw a sprite (1-bit uncompressed graphics) at the given position and color. The iPitch value is the number of bytes per line of the image data. For example, if your sprite is 32 pixels wide with no padding, the iPitch value will be 4 (32/8).
There are 3 callback functions which allow you to support your own custom parallel eink hardware. Inside bb_epdiy there are multiple versions of these functions for the included hardware support. If you've got a board which isn't yet supported, it's fairly easy to implement your own.
typedef int (BB_EINK_POWER)(void *pBBEP, int bOn);
This function is for turning on and off the power supply to the eink panel. The void pointer points to the BBEPDIYSTATE structure within the class instance.
typedef int (BB_IO_INIT)(void *pBBEP);
This function initializes the I/O of the board (not including the ESP32 LCD peripheral). This is for initializing any GPIOs or I/O expanders needed to talk to the display panel.
typedef void (BB_ROW_CONTROL)(void *pBBEP, int iMode);
This function is for controlling the row counter of the display. Two possible values are sent in the iMode parameter: ROW_START and ROW_STEP. This function must control the signals needed to reset and step the row counter.
It is probably easiest to see how these functions work by looking at the M5PaperS3 implementation. It doesn't use any I2C expanders or power control chips, it simply uses the ESP32's GPIO to directly control all operations of the eink panel.
For people familiar with SPI epaper displays like the small ones found in supermarket shelf labels, the larger parallel ones work on the same principal. Charged pigment particles are suspended in a clear oil between two conductors and pushed back and forth with an electric field. The smaller displays normally have a dedicated controller built onto the display glass (e.g. SSD16xx or UC81xx) which is optimized for low power. You send it commands and data over a 1-bit serial bus (SPI) and it manages the framebuffer and updating of the pixels. Parallel eink displays are very similar except that there is no low power controller on the glass and instead, a simple state machine is exposed. This state machine allows you to write data a row at a time and step to the next row. The row data consists of 2-bits per pixel which control the two transistors of each pixel. The 2 bits normally mean the following: 00 = floating/no-change, 01=make darker, 10=make lighter, 11=skip. The display only has memory for a single row, so you need to manage the framebuffer on the MCU side. Since the pixel control bits are sent 8 or 16 at a time (parallel data bus) and at high speed (~20MHz), you can push a whole frame's worth of data quite quickly. One catch - it takes more than one "push" to get the pixels to be fully positioned into a black or white state. For the average panel, it may need 5 to 10 pushes to get the pixel to fully go from one state to another. This may seem impractical - why not just do the full push in one shot. There are two reasons - one is that the temperature affects the viscosity of the oil. The colder the ambient temperature, the slower the granules move. This means that it will take more 'pushes' to get the color you want. The other reason is that by allowing an incremental push, the display can support shades of gray between black and white. Now we've hit the really complex subject of getting 16 good looking gray levels from black and white eink! Besides the temperature, the mobility of the granules increases if they're thrust back and forth between their black and white states. This is why a 'full' update will set all pixels to black and then white and then the final color - to ensure that they're maximally mobile and will be fully set into their extreme positions. To get more than a few unique gray levels, it requires a long set of moves (->B->W->B->B->W) to tease them into middle positions. The manufacturer (EInk) publishes a matrix of values for each temperature and for transitioning from one gray level to another. Sometimes these tables stretch for more than 40 steps per color change! There are more simplistic efforts to get gray levels that take fewer steps, but result in poorly distributed grays. This set of steps has mistakenly been called a "waveform". There's no waveform involved in eink displays - it's a purely digital device with several fixed states.
This library uses the same data compression as my bb_epaper library. I modified CCITT Group4 2D compression to be more compact for embedded systems. I call it "Group5" compression. It is an excellent fit for font data and 1-bit graphics with lossless compression ratios approaching 20:1 in some cases. For the case of TrueType fonts, a set of 256 characters of large dimensions (> 100pt) can usually fit in less than 10K of space after being compressed. Since this compression is fast and efficient, all converted fonts are stored compressed. Images can optionally be compressed with the provided tool too.