From 853863254f1636ac7fa1fc03a26c8b3cb5c64718 Mon Sep 17 00:00:00 2001 From: inmarket Date: Sat, 7 Jul 2018 23:14:28 +1000 Subject: [PATCH] Added WS29EPD driver by cpu20 for the WaveShare E-Paper display --- changelog.txt | 1 + drivers/gdisp/WS29EPD/WS29EPD.h | 39 +++ .../gdisp/WS29EPD/board_WS29EPD_template.h | 57 ++++ drivers/gdisp/WS29EPD/driver.mk | 2 + drivers/gdisp/WS29EPD/gdisp_lld_WS29EPD.c | 272 ++++++++++++++++++ drivers/gdisp/WS29EPD/gdisp_lld_config.h | 22 ++ drivers/gdisp/readme.txt | 2 + 7 files changed, 395 insertions(+) create mode 100644 drivers/gdisp/WS29EPD/WS29EPD.h create mode 100644 drivers/gdisp/WS29EPD/board_WS29EPD_template.h create mode 100644 drivers/gdisp/WS29EPD/driver.mk create mode 100644 drivers/gdisp/WS29EPD/gdisp_lld_WS29EPD.c create mode 100644 drivers/gdisp/WS29EPD/gdisp_lld_config.h diff --git a/changelog.txt b/changelog.txt index e782b7c7..1e595752 100644 --- a/changelog.txt +++ b/changelog.txt @@ -26,6 +26,7 @@ FIX: Fixed ST7735 driver and added kapacuk changes FEATURE: Added keyboard support to radio buttons (by Steffan) FEATURE: Added internal use only GFX_COMPILESTAGE (used to control compilation) FEATURE: Added support for ChibiOS Kernel V5 +FEATURE: Added WS29EPD WaveShare E-Paper display *** Release 2.8 *** diff --git a/drivers/gdisp/WS29EPD/WS29EPD.h b/drivers/gdisp/WS29EPD/WS29EPD.h new file mode 100644 index 00000000..2233cc6a --- /dev/null +++ b/drivers/gdisp/WS29EPD/WS29EPD.h @@ -0,0 +1,39 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +#ifndef _WS29EPD_H_ +#define _WS29EPD_H_ + +#include "gfx.h" + +/* Register definitions. */ +#define DRIVER_OUTPUT_CTRL 0x01 +#define BOOSTER_SOFT_START_CTRL 0x0C +#define DEEP_SLEEP_MODE 0x10 +#define DATA_ENTRY_MODE_SETTING 0x11 + +#define SWRESET 0x12 +#define TEMP_SENSOR_CTRL 0x1A +#define MASTER_ACTIVATION 0x20 +#define DISPLAY_UPDATE_CTRL 0x21 + +#define DISPLAY_UPDATE_CTRL2 0x22 +#define WRITE_RAM 0x24 +#define WRITE_VCOM_REG 0x2C +#define WRITE_LUT_REG 0x32 + +#define SET_DUMMY_LINE_PERIOD 0x3A +#define SET_GATE_LINE_WIDTH 0x3B +#define BORDER_WAVEFORM_CTRL 0x3C +#define SET_RAM_X_ADR 0x44 + +#define SET_RAM_Y_ADR 0x45 +#define SET_RAM_X_CNT 0x4E +#define SET_RAM_Y_CNT 0x4F +#define NOP 0xFF + +#endif diff --git a/drivers/gdisp/WS29EPD/board_WS29EPD_template.h b/drivers/gdisp/WS29EPD/board_WS29EPD_template.h new file mode 100644 index 00000000..4d966124 --- /dev/null +++ b/drivers/gdisp/WS29EPD/board_WS29EPD_template.h @@ -0,0 +1,57 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +#ifndef GDISP_LLD_BOARD_H +#define GDISP_LLD_BOARD_H + +#include "WS29EPD.h" + +static GFXINLINE void init_board(GDisplay *g) { + (void) g; +} + +static GFXINLINE void post_init_board(GDisplay *g) { + (void) g; +} + +static GFXINLINE void setpin_reset(GDisplay *g, bool_t state) { + (void) g; + (void) state; +} + +static GFXINLINE void acquire_bus(GDisplay *g) { + (void) g; +} + +static GFXINLINE void release_bus(GDisplay *g) { + (void) g; +} + +static GFXINLINE void write_data(GDisplay *g, uint8_t data) { + (void) g; + (void) data; +} + +static GFXINLINE void write_reg(GDisplay *g, uint8_t reg, uint8_t data){ + (void) g; + (void) reg; + (void) data; +} + +static GFXINLINE void write_cmd(GDisplay *g, uint8_t reg){ + (void) g; + (void) reg; +} + +static GFXINLINE void write_reg_data(GDisplay *g, uint8_t reg, uint8_t *data, uint8_t len) { + (void) g; + (void) reg; + (void) data; + (void) len; +} + +#endif /* GDISP_LLD_BOARD_H */ diff --git a/drivers/gdisp/WS29EPD/driver.mk b/drivers/gdisp/WS29EPD/driver.mk new file mode 100644 index 00000000..b611addc --- /dev/null +++ b/drivers/gdisp/WS29EPD/driver.mk @@ -0,0 +1,2 @@ +GFXINC += $(GFXLIB)/drivers/gdisp/WS29EPD +GFXSRC += $(GFXLIB)/drivers/gdisp/WS29EPD/gdisp_lld_WS29EPD.c diff --git a/drivers/gdisp/WS29EPD/gdisp_lld_WS29EPD.c b/drivers/gdisp/WS29EPD/gdisp_lld_WS29EPD.c new file mode 100644 index 00000000..ab700b0a --- /dev/null +++ b/drivers/gdisp/WS29EPD/gdisp_lld_WS29EPD.c @@ -0,0 +1,272 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +#include "gfx.h" + +#if GFX_USE_GDISP + +#define GDISP_DRIVER_VMT GDISPVMT_WS29EPD +#include "gdisp_lld_config.h" +#include "../../../src/gdisp/gdisp_driver.h" + +#include "board_WS29EPD.h" +#include "WS29EPD.h" + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#ifndef GDISP_SCREEN_HEIGHT + #define GDISP_SCREEN_HEIGHT 296 +#endif +#ifndef GDISP_SCREEN_WIDTH + #define GDISP_SCREEN_WIDTH 128 +#endif + +/* Every data byte determines 8 pixels. */ +#ifndef WS29EPD_PPB + #define WS29EPD_PPB 8 +#endif + +/*===========================================================================*/ +/* Driver local variables. */ +/*===========================================================================*/ + +/* initialization variables according to WaveShare. */ +uint8_t GDOControl[] = {(GDISP_SCREEN_HEIGHT-1)%256,(GDISP_SCREEN_HEIGHT-1)/256,0x00}; +uint8_t softstart[] = {0xd7,0xd6,0x9d}; +uint8_t LUTDefault_full[] = {0x02,0x02,0x01,0x11,0x12,0x12,0x22,0x22,0x66,0x69,0x69,0x59,0x58,0x99,0x99,0x88,0x00,0x00,0x00,0x00,0xF8,0xB4,0x13,0x51,0x35,0x51,0x51,0x19,0x01,0x00}; // Initialize the full display + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +LLDSPEC gBool gdisp_lld_init(GDisplay *g) { + /* Use the private area as a frame buffer. + * + * The frame buffer will be one big array of bytes storing all the pixels with WS29EPD_PPB pixel per byte. + * For the layout, the frame will be stored line by line in the x-direction. + * So: [Line x=0][Line x=1][Line x=2] ... [Line x=GDISP_SCREEN_WIDTH/WS29EPD_PPB] + * And every x-line contains GDISP_SCREEN_HEIGHT y-values: + * [y=0; y=1; y=2; y=3; ...; y=GDISP_SCREEN_HEIGHT][y=0; y=1; y=2; y=3; ...; y=GDISP_SCREEN_HEIGHT]... + * + */ + g->priv = gfxAlloc((GDISP_SCREEN_WIDTH / WS29EPD_PPB) * GDISP_SCREEN_HEIGHT * sizeof(uint8_t)); + if (!g->priv) + return gFalse; + + /* Initialize the LL hardware. */ + init_board(g); + + /* Hardware reset. */ + setpin_reset(g, gFalse); + gfxSleepMilliseconds(100); + setpin_reset(g, gTrue); + gfxSleepMilliseconds(100); + + /* Acquire the bus for the whole initialization. */ + acquire_bus(g); + + /* Send the initialization commands. (according to WaveShare) */ + write_reg_data(g, DRIVER_OUTPUT_CTRL, GDOControl, sizeof(GDOControl)/sizeof(GDOControl[0])); + write_reg_data(g, BOOSTER_SOFT_START_CTRL, softstart, sizeof(softstart)/sizeof(softstart[0])); + write_reg(g, WRITE_VCOM_REG, 0xA8); // VCOM 7c + write_reg(g, SET_DUMMY_LINE_PERIOD, 0x1A); // 4 dummy lines per gate + write_reg(g, SET_GATE_LINE_WIDTH, 0x08); // 2us per line -> Lower this value for faster screen refresh + write_reg(g, DATA_ENTRY_MODE_SETTING, 0x03); // Ram data entry mode -> X and Y increase + + write_reg_data(g, WRITE_LUT_REG, LUTDefault_full, sizeof(LUTDefault_full)/sizeof(LUTDefault_full[0])); // Initialize the LUT full + + write_reg(g, DISPLAY_UPDATE_CTRL2, 0xC0); // Power ON the display + write_cmd(g, MASTER_ACTIVATION); + write_reg(g, DEEP_SLEEP_MODE, 0x00); + + uint8_t zeros[2] = { 0, 0 }; + write_reg(g, SET_RAM_X_CNT, 0x00); // Set cursor at origin + write_reg_data(g, SET_RAM_Y_CNT, zeros, 2); + + uint8_t dataX[2] = { 0, (GDISP_SCREEN_WIDTH-1)/8 }; + uint8_t dataY[4] = { 0, 0, (GDISP_SCREEN_HEIGHT-1)%256, (GDISP_SCREEN_HEIGHT-1)/256 }; + write_reg_data(g, SET_RAM_X_ADR, dataX, 2); // Set viewport for the whole screen + write_reg_data(g, SET_RAM_Y_ADR, dataY, 4); + + release_bus(g); + + gfxSleepMilliseconds(1500); // Wait for the display to initialize + + /* Finish board initialization. */ + post_init_board(g); + + /* Initialise the GDISP structure */ + g->g.Width = GDISP_SCREEN_WIDTH; + g->g.Height = GDISP_SCREEN_HEIGHT; + g->g.Orientation = GDISP_ROTATE_0; + g->g.Powermode = powerOn; + return gTrue; +} + +#if 0 // Not needed yet + static void set_cursor(GDisplay *g) { + uint8_t dataY[2]; + + dataY[0] = g->p.y % 256; // Y-data is send in two bytes + dataY[1] = g->p.y / 256; + + switch(g->g.Orientation) { + default: + case GDISP_ROTATE_0: + case GDISP_ROTATE_180: + write_reg(g, SET_RAM_X_CNT, g->p.x / WS29EPD_PPB); + write_reg_data(g, SET_RAM_Y_CNT, dataY, 2); + break; + + case GDISP_ROTATE_90: + case GDISP_ROTATE_270: + write_reg(g, SET_RAM_Y_CNT, g->p.x / WS29EPD_PPB); + write_reg_data(g, SET_RAM_X_CNT, dataY, 2); + break; + } + } + + static void set_viewport(GDisplay *g) { + uint8_t dataX[2]; + uint8_t dataY[4]; + + // Fill up the X and Y position buffers. + dataX[0] = g->p.x / WS29EPD_PPB; + dataX[1] = (g->p.x + g->p.cx - 1) / WS29EPD_PPB; + + dataY[0] = g->p.y % 256; // Y-data is 9-bits so send in two bytes + dataY[1] = g->p.y / 256; + dataY[2] = (g->p.y + g->p.cy - 1) % 256; + dataY[3] = (g->p.y + g->p.cy - 1) / 256; + + switch(g->g.Orientation) { + default: + case GDISP_ROTATE_0: + case GDISP_ROTATE_180: + write_reg_data(g, SET_RAM_X_ADR, dataX, 2); + write_reg_data(g, SET_RAM_Y_ADR, dataY, 4); + break; + case GDISP_ROTATE_90: + case GDISP_ROTATE_270: + write_reg_data(g, SET_RAM_X_ADR, dataY, 4); + write_reg_data(g, SET_RAM_Y_ADR, dataX, 2); + break; + } + } +#endif + +#if GDISP_HARDWARE_DRAWPIXEL +LLDSPEC void gdisp_lld_draw_pixel(GDisplay *g) { + coord_t x, y; + + switch(g->g.Orientation) { + default: + case GDISP_ROTATE_0: + x = g->p.x; + y = g->p.y; + break; + case GDISP_ROTATE_90: + x = g->p.y; + y = GDISP_SCREEN_HEIGHT-1 - g->p.x; + break; + case GDISP_ROTATE_180: + x = GDISP_SCREEN_WIDTH-1 - g->p.x; + y = GDISP_SCREEN_HEIGHT-1 - g->p.y; + break; + case GDISP_ROTATE_270: + x = GDISP_SCREEN_HEIGHT-1 - g->p.y; + y = g->p.x; + break; + } + /* There is only black and no black (white). */ + if (gdispColor2Native(g->p.color) != Black) // Indexing in the array is done as described in the init routine + ((uint8_t *)g->priv)[(GDISP_SCREEN_HEIGHT*(x/WS29EPD_PPB)) + y] |= (1 << (WS29EPD_PPB-1 - (x % WS29EPD_PPB))); + else + ((uint8_t *)g->priv)[(GDISP_SCREEN_HEIGHT*(x/WS29EPD_PPB)) + y] &= ~(1 << (WS29EPD_PPB-1 - (x % WS29EPD_PPB))); +} +#endif + +#if GDISP_HARDWARE_FLUSH +LLDSPEC void gdisp_lld_flush(GDisplay *g) { + acquire_bus(g); + + /* Start writing frame buffer to ram. */ + write_cmd(g, WRITE_RAM); + + for(int i=0; ipriv)[(GDISP_SCREEN_HEIGHT*j) + i]); + + /* Update the screen. */ + write_reg(g, DISPLAY_UPDATE_CTRL2, 0xc7); // Full update (partial hasn't been implemented yet) + write_cmd(g, MASTER_ACTIVATION); + write_cmd(g, NOP); + release_bus(g); +} +#endif + +#if GDISP_NEED_CONTROL && GDISP_HARDWARE_CONTROL +LLDSPEC void gdisp_lld_control(GDisplay *g) { + switch(g->p.x) { + case GDISP_CONTROL_POWER: + if (g->g.Powermode == (powermode_t)g->p.ptr) + return; + switch((powermode_t)g->p.ptr) { + case powerOff: + case powerSleep: + case powerDeepSleep: + acquire_bus(g); // Put de display in deep sleep + write_reg(g, DISPLAY_UPDATE_CTRL2, 0x03); + write_reg(g, DEEP_SLEEP_MODE, 0x01); + release_bus(g); + break; + case powerOn: + acquire_bus(g); // Awake the display again + write_reg(g, DISPLAY_UPDATE_CTRL2, 0xC0); + write_reg(g, DEEP_SLEEP_MODE, 0x00); + release_bus(g); + break; + default: + return; + } + g->g.Powermode = (powermode_t)g->p.ptr; + return; + + case GDISP_CONTROL_ORIENTATION: + if (g->g.Orientation == (orientation_t)g->p.ptr) + return; + switch((orientation_t)g->p.ptr) { + case GDISP_ROTATE_0: + g->g.Height = GDISP_SCREEN_HEIGHT; + g->g.Width = GDISP_SCREEN_WIDTH; + break; + case GDISP_ROTATE_90: + g->g.Height = GDISP_SCREEN_WIDTH; + g->g.Width = GDISP_SCREEN_HEIGHT; + break; + case GDISP_ROTATE_180: + g->g.Height = GDISP_SCREEN_HEIGHT; + g->g.Width = GDISP_SCREEN_WIDTH; + break; + case GDISP_ROTATE_270: + g->g.Height = GDISP_SCREEN_WIDTH; + g->g.Width = GDISP_SCREEN_HEIGHT; + break; + default: + return; + } + g->g.Orientation = (orientation_t)g->p.ptr; + return; + default: + return; + } +} +#endif + +#endif // GFX_USE_GDISP diff --git a/drivers/gdisp/WS29EPD/gdisp_lld_config.h b/drivers/gdisp/WS29EPD/gdisp_lld_config.h new file mode 100644 index 00000000..0892a53c --- /dev/null +++ b/drivers/gdisp/WS29EPD/gdisp_lld_config.h @@ -0,0 +1,22 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +#ifndef _GDISP_LLD_CONFIG_H +#define _GDISP_LLD_CONFIG_H + +#if GFX_USE_GDISP + +#define GDISP_HARDWARE_FLUSH TRUE // This controller requires flushing +#define GDISP_HARDWARE_DRAWPIXEL TRUE +#define GDISP_HARDWARE_STREAM_WRITE FALSE +#define GDISP_HARDWARE_CONTROL TRUE + +#define GDISP_LLD_PIXELFORMAT GDISP_PIXELFORMAT_MONO + +#endif + +#endif diff --git a/drivers/gdisp/readme.txt b/drivers/gdisp/readme.txt index 3860d282..ecc2ba85 100644 --- a/drivers/gdisp/readme.txt +++ b/drivers/gdisp/readme.txt @@ -37,6 +37,8 @@ TLS8204 - Small monochrome LCD UC8173 - E-Ink display driver UC1601s - Small (64x132) monochrome LCD UC1610 - Small (78x64 or 160x104) 4 level grayscale LCD +UC8175 - Small E-Ink display +WS29EPD - Small E-Ink display by WaveShare QImage - Driver that allows rendering into a QImage object (of the Qt framework) uGFXnet - Remote Network display (in drivers/multiple/uGFXnet directory) Win32 - Microsoft Windows (in drivers/multiple/Win32 directory)