Library for Waveshare epd1in54b (1.54 inch ePaper black/red)
e-Paper Module
Description
Waveshare 1.54 e-paper module imported from offiicial code https://www.waveshare.com/wiki/1.54inch_e-Paper_Module_(B) Codes are referenced from official Arduino code and Raspberry pi code.
SPI is used for communication between ePaper module and mbed.
Following shapes can be used:
- primitive shape (square, circle, line)
- font (variable sizes)
Pull request are appreciated (Note that the original code is copyrighted by (C) Waveshare )
Example Output
Display output with example code will be like following image:
Example Code
include the mbed library with this snippet
#include "mbed.h" #include "epd1in54b.h" // Control PinName rst; PinName dc; PinName busy; // SPI communication PinName mosi; PinName miso; PinName sclk; PinName cs; DigitalOut myled(LED1); unsigned char frame_black[EPD_HEIGHT*EPD_WIDTH/8]; unsigned char frame_red[EPD_HEIGHT*EPD_WIDTH/8]; int main() { mosi = p5; miso = p6; sclk = p7; cs = p8; rst = p9; dc = p10; busy = p11; memset(frame_black, 0xFF, sizeof(unsigned char)*EPD_HEIGHT*EPD_WIDTH/8); memset(frame_red, 0xFF, sizeof(unsigned char)*EPD_HEIGHT*EPD_WIDTH/8); Epd epd = Epd(mosi, miso, sclk, cs, dc, rst, busy); if (epd.Init() != 0){ return -1; } /* Draw something to the frame buffer */ // For simplicity, the arguments are explicit numerical coordinates epd.DrawRectangle(frame_black, 10, 60, 50, 110, COLORED); epd.DrawLine(frame_black, 10, 60, 50, 110, COLORED); epd.DrawLine(frame_black, 50, 60, 10, 110, COLORED); epd.DrawCircle(frame_black, 120, 80, 30, COLORED); epd.DrawFilledRectangle(frame_red, 10, 130, 50, 180, COLORED); epd.DrawFilledRectangle(frame_red, 0, 6, 200, 26, COLORED); epd.DrawFilledCircle(frame_red, 120, 150, 30, COLORED); /*Write strings to the buffer */ epd.DrawStringAt(frame_black, 30, 30, "e-Paper Demo", &Font16, COLORED); epd.DrawStringAt(frame_red, 28, 10, "Hello world!", &Font16, UNCOLORED); // display images epd.DisplayFrame(frame_black, frame_red); //epd.DisplayFrame(IMAGE_BLACK, IMAGE_RED); epd.Sleep(); while(1) { myled = 1; wait(0.5); myled = 0; wait(0.5); } }
epd1in54b.cpp
- Committer:
- imachooon
- Date:
- 2018-02-11
- Revision:
- 0:cba3c1061231
File content as of revision 0:cba3c1061231:
/** * @filename : epd1in54b.cpp * @brief : Implements for e-paper library * @author : Yehui from Waveshare * * Copyright (C) Waveshare August 10 2017 * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documnetation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include <stdlib.h> #include "epd1in54b.h" #define pgm_read_byte(x) (*(const char*)x) const unsigned char lut_vcom0[] = { 0x0E, 0x14, 0x01, 0x0A, 0x06, 0x04, 0x0A, 0x0A, 0x0F, 0x03, 0x03, 0x0C, 0x06, 0x0A, 0x00 }; const unsigned char lut_w[] = { 0x0E, 0x14, 0x01, 0x0A, 0x46, 0x04, 0x8A, 0x4A, 0x0F, 0x83, 0x43, 0x0C, 0x86, 0x0A, 0x04 }; const unsigned char lut_b[] = { 0x0E, 0x14, 0x01, 0x8A, 0x06, 0x04, 0x8A, 0x4A, 0x0F, 0x83, 0x43, 0x0C, 0x06, 0x4A, 0x04 }; const unsigned char lut_g1[] = { 0x8E, 0x94, 0x01, 0x8A, 0x06, 0x04, 0x8A, 0x4A, 0x0F, 0x83, 0x43, 0x0C, 0x06, 0x0A, 0x04 }; const unsigned char lut_g2[] = { 0x8E, 0x94, 0x01, 0x8A, 0x06, 0x04, 0x8A, 0x4A, 0x0F, 0x83, 0x43, 0x0C, 0x06, 0x0A, 0x04 }; const unsigned char lut_vcom1[] = { 0x03, 0x1D, 0x01, 0x01, 0x08, 0x23, 0x37, 0x37, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; const unsigned char lut_red0[] = { 0x83, 0x5D, 0x01, 0x81, 0x48, 0x23, 0x77, 0x77, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; const unsigned char lut_red1[] = { 0x03, 0x1D, 0x01, 0x01, 0x08, 0x23, 0x37, 0x37, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; Epd::~Epd() { }; Epd::Epd(PinName mosi, PinName miso, PinName sclk, PinName cs, PinName dc, PinName rst, PinName busy ):EpdIf(mosi, miso, sclk, cs, dc, rst, busy){ width = EPD_WIDTH; height= EPD_HEIGHT; rotate = ROTATE_0; } int Epd::Init(void) { if(IfInit() != 0){ return -1; } /* EPD hardware init start */ Reset(); SendCommand(POWER_SETTING); SendData(0x07); SendData(0x00); SendData(0x08); SendData(0x00); SendCommand(BOOSTER_SOFT_START); SendData(0x07); SendData(0x07); SendData(0x07); SendCommand(POWER_ON); WaitUntilIdle(); SendCommand(PANEL_SETTING); SendData(0xcf); SendCommand(VCOM_AND_DATA_INTERVAL_SETTING); SendData(0x17); SendCommand(PLL_CONTROL); SendData(0x39); SendCommand(TCON_RESOLUTION); SendData(0xC8); SendData(0x00); SendData(0xC8); SendCommand(VCM_DC_SETTING_REGISTER); SendData(0x0E); SetLutBw(); SetLutRed(); /* EPD hardware init end */ return 0; } /** * @brief: basic function for sending commands */ void Epd::SendCommand(unsigned char command) { DigitalWrite(m_dc, LOW); SpiTransfer(command); } /** * @brief: basic function for sending data */ void Epd::SendData(unsigned char data) { DigitalWrite(m_dc, HIGH); SpiTransfer(data); } /** * @brief: Wait until the m_busy goes HIGH */ void Epd::WaitUntilIdle(void) { while(DigitalRead(m_busy) == 0) { //0: busy, 1: idle DelayMs(100); } } /** * @brief: module reset. * often used to awaken the module in deep sleep, * see Epd::Sleep(); */ void Epd::Reset(void) { DigitalWrite(m_rst, LOW); //module reset DelayMs(200); DigitalWrite(m_rst, HIGH); DelayMs(200); } /** * @brief: set the look-up tables */ void Epd::SetLutBw(void) { unsigned int count; SendCommand(0x20); //g vcom for(count = 0; count < 15; count++) { SendData(lut_vcom0[count]); } SendCommand(0x21); //g ww -- for(count = 0; count < 15; count++) { SendData(lut_w[count]); } SendCommand(0x22); //g bw r for(count = 0; count < 15; count++) { SendData(lut_b[count]); } SendCommand(0x23); //g wb w for(count = 0; count < 15; count++) { SendData(lut_g1[count]); } SendCommand(0x24); //g bb b for(count = 0; count < 15; count++) { SendData(lut_g2[count]); } } void Epd::SetLutRed(void) { unsigned int count; SendCommand(0x25); for(count = 0; count < 15; count++) { SendData(lut_vcom1[count]); } SendCommand(0x26); for(count = 0; count < 15; count++) { SendData(lut_red0[count]); } SendCommand(0x27); for(count = 0; count < 15; count++) { SendData(lut_red1[count]); } } void Epd::DisplayFrame(const unsigned char* frame_buffer_black, const unsigned char* frame_buffer_red) { unsigned char temp; if (frame_buffer_black != NULL) { SendCommand(DATA_START_TRANSMISSION_1); DelayMs(2); for (int i = 0; i < 200 * 200 / 8; i++) { temp = 0x00; for (int bit = 0; bit < 4; bit++) { if ((frame_buffer_black[i] & (0x80 >> bit)) != 0) { temp |= 0xC0 >> (bit * 2); } } SendData(temp); temp = 0x00; for (int bit = 4; bit < 8; bit++) { if ((frame_buffer_black[i] & (0x80 >> bit)) != 0) { temp |= 0xC0 >> ((bit - 4) * 2); } } SendData(temp); } DelayMs(2); } if (frame_buffer_red != NULL) { SendCommand(DATA_START_TRANSMISSION_2); DelayMs(2); for (int i = 0; i < this->width * this->height / 8; i++) { SendData(frame_buffer_red[i]); } DelayMs(2); } SendCommand(DISPLAY_REFRESH); WaitUntilIdle(); } /** * @brief: After this command is transmitted, the chip would enter the * deep-sleep mode to save power. * The deep sleep mode would return to standby by hardware reset. * The only one parameter is a check code, the command would be * executed if check code = 0xA5. * You can use Epd::Init() to awaken */ void Epd::Sleep() { SendCommand(VCOM_AND_DATA_INTERVAL_SETTING); SendData(0x17); SendCommand(VCM_DC_SETTING_REGISTER); //to solve Vcom drop SendData(0x00); SendCommand(POWER_SETTING); //power setting SendData(0x02); //gate switch to external SendData(0x00); SendData(0x00); SendData(0x00); WaitUntilIdle(); SendCommand(POWER_OFF); //power off } void Epd::SetRotate(int rotate){ if (rotate == ROTATE_0){ rotate = ROTATE_0; width = EPD_WIDTH; height = EPD_HEIGHT; } else if (rotate == ROTATE_90){ rotate = ROTATE_90; width = EPD_HEIGHT; height = EPD_WIDTH; } else if (rotate == ROTATE_180){ rotate = ROTATE_180; width = EPD_WIDTH; height = EPD_HEIGHT; } else if (rotate == ROTATE_270){ rotate = ROTATE_270; width = EPD_HEIGHT; height = EPD_WIDTH; } } void Epd::SetPixel(unsigned char* frame_buffer, int x, int y, int colored){ if (x < 0 || x >= width || y < 0 || y >= height){ return; } if (rotate == ROTATE_0){ SetAbsolutePixel(frame_buffer, x, y, colored); } else if (rotate == ROTATE_90){ int point_temp = x; x = EPD_WIDTH - y; y = point_temp; SetAbsolutePixel(frame_buffer, x, y, colored); } else if (rotate == ROTATE_180){ x = EPD_WIDTH - x; y = EPD_HEIGHT- y; SetAbsolutePixel(frame_buffer, x, y, colored); } else if (rotate == ROTATE_270){ int point_temp = x; x = y; y = EPD_HEIGHT - point_temp; SetAbsolutePixel(frame_buffer, x, y, colored); } } void Epd::SetAbsolutePixel(unsigned char *frame_buffer, int x, int y, int colored){ // To avoid display orientation effects // use EPD_WIDTH instead of self.width // use EPD_HEIGHT instead of self.height if (x < 0 || x >= EPD_WIDTH || y < 0 || y >= EPD_HEIGHT){ return; } if (colored){ frame_buffer[(x + y * EPD_WIDTH) / 8] &= ~(0x80 >> (x % 8)); } else{ frame_buffer[(x + y * EPD_WIDTH) / 8] |= 0x80 >> (x % 8); } } void Epd::DrawLine(unsigned char*frame_buffer, int x0, int y0, int x1, int y1, int colored){ // Bresenham algorithm int dx = x1 - x0 >= 0 ? x1 - x0 : x0 - x1; int sx = x0 < x1 ? 1 : -1; int dy = y1 - y0 <= 0 ? y1 - y0 : y0 - y1; int sy = y0 < y1 ? 1 : -1; int err = dx + dy; while((x0 != x1) && (y0 != y1)){ SetPixel(frame_buffer, x0, y0 , colored); if (2 * err >= dy){ err += dy; x0 += sx; } if (2 * err <= dx){ err += dx; y0 += sy; } } } void Epd::DrawHorizontalLine(unsigned char *frame_buffer, int x, int y, int width, int colored){ for (int i=x; i<x + width; i++){ SetPixel(frame_buffer, i, y, colored); } } void Epd::DrawVerticalLine(unsigned char *frame_buffer, int x, int y, int height, int colored){ for (int i=y; i<y + height; i++){ SetPixel(frame_buffer, x, i, colored); } } void Epd::DrawRectangle(unsigned char *frame_buffer, int x0, int y0, int x1, int y1, int colored){ int min_x = x1 > x0 ? x0 : x1; int max_x = x1 > x0 ? x1 : x0; int min_y = y1 > y0 ? y0 : y1; int max_y = y1 > y0 ? y1 : y0; DrawHorizontalLine(frame_buffer, min_x, min_y, max_x - min_x + 1, colored); DrawHorizontalLine(frame_buffer, min_x, max_y, max_x - min_x + 1, colored); DrawVerticalLine(frame_buffer, min_x, min_y, max_y - min_y + 1, colored); DrawVerticalLine(frame_buffer, max_x, min_y, max_y - min_y + 1, colored); } void Epd::DrawFilledRectangle(unsigned char *frame_buffer, int x0, int y0, int x1, int y1, int colored){ int min_x = x1 > x0 ? x0 : x1; int max_x = x1 > x0 ? x1 : x0; int min_y = y1 > y0 ? y0 : y1; int max_y = y1 > y0 ? y1 : y0; for (int i=min_x; i < max_x+1; i++){ DrawVerticalLine(frame_buffer, i, min_y, max_y - min_y + 1, colored); } } void Epd::DrawCircle(unsigned char *frame_buffer, int x, int y, int radius, int colored){ // Bresenham algorithm int x_pos = -radius; int y_pos = 0; int err = 2 - 2 * radius; if (x >= width || y >= height){ return; } while ( 1 ){ SetPixel(frame_buffer, x - x_pos, y + y_pos, colored); SetPixel(frame_buffer, x + x_pos, y + y_pos, colored); SetPixel(frame_buffer, x + x_pos, y - y_pos, colored); SetPixel(frame_buffer, x - x_pos, y - y_pos, colored); int e2 = err; if (e2 <= y_pos){ y_pos += 1; err += y_pos * 2 + 1; if(-x_pos == y_pos && e2 <= x_pos){ e2 = 0; } } if (e2 > x_pos){ x_pos += 1; err += x_pos * 2 + 1; } if (x_pos > 0){ break; } } } void Epd::DrawFilledCircle(unsigned char* frame_buffer, int x, int y, int radius, int colored){ // Bresenham algorithm int x_pos = -radius; int y_pos = 0; int err = 2 - 2 * radius; if (x >= width || y >= height){ return; } while ( 1 ){ SetPixel(frame_buffer, x - x_pos, y + y_pos, colored); SetPixel(frame_buffer, x + x_pos, y + y_pos, colored); SetPixel(frame_buffer, x + x_pos, y - y_pos, colored); SetPixel(frame_buffer, x - x_pos, y - y_pos, colored); DrawHorizontalLine(frame_buffer, x + x_pos, y + y_pos, 2 * (-x_pos) + 1, colored); DrawHorizontalLine(frame_buffer, x + x_pos, y - y_pos, 2 * (-x_pos) + 1, colored); int e2 = err; if (e2 <= y_pos){ y_pos += 1; err += y_pos * 2 + 1; if(-x_pos == y_pos && e2 <= x_pos){ e2 = 0; } } if (e2 > x_pos){ x_pos += 1; err += x_pos * 2 + 1; } if (x_pos > 0){ break; } } } /** * @brief: this draws a charactor on the frame buffer but not refresh */ void Epd::DrawCharAt(unsigned char *frame_buffer, int x, int y, char ascii_char, sFONT* font, int colored) { int i, j; unsigned int char_offset = (ascii_char - ' ') * font->Height * (font->Width / 8 + (font->Width % 8 ? 1 : 0)); const unsigned char* ptr = &font->table[char_offset]; for (j = 0; j < font->Height; j++) { for (i = 0; i < font->Width; i++) { if (*ptr & (0x80 >> (i % 8))) { SetPixel(frame_buffer, x + i, y + j, colored); } if (i % 8 == 7) { ptr++; } } if (font->Width % 8 != 0) { ptr++; } } } /** * @brief: this displays a string on the frame buffer but not refresh */ void Epd::DrawStringAt(unsigned char *frame_buffer, int x, int y, const char* text, sFONT* font, int colored) { const char* p_text = text; unsigned int counter = 0; int refcolumn = x; /* Send the string character by character on EPD */ while (*p_text != 0) { /* Display one character on EPD */ DrawCharAt(frame_buffer, refcolumn, y, *p_text, font, colored); /* Decrement the column position by 16 */ refcolumn += font->Width; /* Point on the next character */ p_text++; counter++; } }