Library for Waveshare epd1in54b (1.54 inch ePaper black/red)

Dependents:   epd

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: /media/uploads/imachooon/img_20180211_022046.jpg

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++;
    }
}