/* * 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_UC8173 #include "gdisp_lld_config.h" #include "../../../src/gdisp/gdisp_driver.h" #if defined(GDISP_SCREEN_HEIGHT) || defined(GDISP_SCREEN_HEIGHT) #if GFX_COMPILER_WARNING_TYPE == GFX_COMPILER_WARNING_DIRECT #warning "GDISP: This low level driver does not support setting a screen size. It is being ignored." #elif GFX_COMPILER_WARNING_TYPE == GFX_COMPILER_WARNING_MACRO COMPILER_WARNING("GDISP: This low level driver does not support setting a screen size. It is being ignored.") #endif #undef GDISP_SCREEN_WIDTH #undef GDISP_SCREEN_HEIGHT #endif /*------------------ Load the board file ------------------*/ typedef struct UC8173Lut { uint8_t const *lutVCOM; // 32 bytes uint8_t const *lutFW; // 512 bytes uint8_t const *lutFT; // 128 bytes gBool regal; } UC8173Lut; #include "board_UC8173.h" /*------------------ Default UC8173 parameters ------------------*/ #ifndef UC8173_REVERSEAXIS_Y #define UC8173_REVERSEAXIS_Y GFXOFF #endif #ifndef UC8173_REVERSEAXIS_X #define UC8173_REVERSEAXIS_X GFXOFF #endif #ifndef UC8173_DEFAULT_MODE #define UC8173_DEFAULT_MODE 0 #endif #ifndef UC8173_USE_OTP_LUT #define UC8173_USE_OTP_LUT GFXOFF #endif #ifndef UC8173_CAN_READ #define UC8173_CAN_READ GFXOFF #endif #ifdef UC8173_VCOM_VOLTAGE #define UC8173_VCOM_VOLTAGEBYTE (((UC8173_VCOM_VOLTAGE) + 0.1)/-0.05) #endif #ifndef UC8171_BORDER #define UC8171_BORDER 0 /* 0 = Hi-Z, 1 = Black, 2 = White */ #endif #ifndef UC8173_INIT_REAL_LUT #define UC8173_INIT_REAL_LUT GFXON #endif #define UC8173_HEIGHT 240 #define UC8173_WIDTH 240 /*------------------ Set FB parameters ------------------*/ #define FB_REVERSEAXIS_Y UC8173_REVERSEAXIS_Y #define FB_REVERSEAXIS_X UC8173_REVERSEAXIS_X #define FB_WIDTH UC8173_WIDTH #define FB_HEIGHT UC8173_HEIGHT #define FB_PAGES 1 #define FB_PIXELORDER_MSB GFXON /*------------------ Include Generic FB Code ------------------*/ // This FB is for 1,2 or 4 bits per pixel packed along the x-axis #define FB_TYPE_PIXELS (LLDCOLOR_TYPE_BITS/LLDCOLOR_BITS) #define FB_AXIS_MASK (FB_TYPE_PIXELS-1) #define FB_LINE_TYPES ((FB_WIDTH+FB_TYPE_PIXELS-1)/FB_TYPE_PIXELS) #define FB_PAGE_TYPES (FB_LINE_TYPES*FB_HEIGHT) #define FB_ADDR(fbp, pg, px, py) ((fbp)->fb + ((px)/FB_TYPE_PIXELS + (py)*FB_LINE_TYPES + (pg)*FB_PAGE_TYPES)) #if FB_PIXELORDER_MSB #define FB_COLOR(px, py, c) ((c) << ((LLDCOLOR_TYPE_BITS-LLDCOLOR_BITS)-(((px) & FB_AXIS_MASK)<<(LLDCOLOR_BITS-1)))) #else #define FB_COLOR(px, py, c) ((c) << (((px) & FB_AXIS_MASK)<<(LLDCOLOR_BITS-1))) #endif #define FB_FLUSH_REQUIRED(fbp) ((fbp)->fa0.x < (fbp)->fa1.x) #define FB_FLUSH_WIDTH(fbp) ((fbp)->fa1.x - (fbp)->fa0.x + 1) #define FB_FLUSH_HEIGHT(fbp) ((fbp)->fa1.y - (fbp)->fa0.y + 1) #define FB_FLUSH_ALL(fbp) { (fbp)->fa0.x = (fbp)->fa0.y = 0; (fbp)->fa1.x = FB_WIDTH-1; (fbp)->fa1.y = FB_HEIGHT-1; } #define FB_FLUSH_NONE(fbp) { (fbp)->fa0.x = FB_WIDTH; (fbp)->fa0.y = FB_HEIGHT; (fbp)->fa1.x = (fbp)->fa1.y = -1; } #define FB_FLUSH_POINT(fbp, px, py) { \ if ((px) < (fbp)->fa0.x) (fbp)->fa0.x = (px) & ~FB_AXIS_MASK; \ if ((px) > (fbp)->fa1.x) (fbp)->fa1.x = (px) | FB_AXIS_MASK; \ if ((py) < (fbp)->fa0.y) (fbp)->fa0.y = (py); \ if ((py) > (fbp)->fa1.y) (fbp)->fa1.y = (py); \ } #define FB_SETPIXEL(fbp, pg, px, py, c) { \ LLDCOLOR_TYPE *p, oc; \ p = FB_ADDR((fbp), (pg), (px), (py)); \ oc = (*p & ~FB_COLOR((px), (py), LLDCOLOR_MASK())) | FB_COLOR((px), (py), (c)); \ if (oc != *p) { *p = oc; FB_FLUSH_POINT((fbp), (px), (py)); } \ } typedef struct FBpriv { gPoint fa0, fa1; LLDCOLOR_TYPE fb[FB_PAGE_TYPES * FB_PAGES]; } FBpriv; /*------------------ UC8173 Driver Code ------------------*/ #include "UC8173.h" // UC8173 Inernal Macros & types typedef struct UC8173_Private { UC8173Lut *lut; FBpriv fb; } UC8173_Private; #if !UC8173_INIT_REAL_LUT static uint8_t const UC8173_LUT_None[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00 }; #endif LLDSPEC gBool gdisp_lld_init(GDisplay* g) { UC8173_Private *priv; // Allocate the private area g->priv = gfxAlloc(sizeof(UC8173_Private)); if (!g->priv) return gFalse; priv = (UC8173_Private *)g->priv; // Initialize the private area // As the display is non-volatile we need to ensure a full flush on the first draw priv->lut = &UC8173_ModeTable[UC8173_DEFAULT_MODE]; FB_FLUSH_ALL(&priv->fb); // Initialise the board interface if (!init_board(g)) return gFalse; // Hardware reset setpin_reset(g, gTrue); gfxSleepMilliseconds(100); setpin_reset(g, gFalse); gfxSleepMilliseconds(300); // Acquire the bus acquire_bus(g); // Booster soft-start - Values from example code write_cmd(g, BTST); write_data(g, 0x17); // data: PhaseA: Bits76=StartupTime [10ms+n*10ms def=0x00], Bits543=Strength, Bits210=MinGDROffTime write_data(g, 0x97); // data: PhaseB: Bits76=StartupTime [10ms+n*10ms def=0x00], Bits543=Strength, Bits210=MinGDROffTime write_data(g, 0x20); // data: PhaseC: Bits543=Strength, Bits210=MinGDROffTime [def=0x07] // Power settings - Values from datasheet default values write_cmd(g, PWR); write_data(g, 0x03); // data: 0x02=Internal VDH/VDL, 0x01=Internal VGH/VGL write_data(g, 0x00); // data: Always 0x00 write_data(g, 0x26); // data: 0x00->0x3F VSH Voltage 2.4V -> 11.0V step 0.2V - default=10V (0x26) write_data(g, 0x26); // data: 0x00->0x3F VSL Voltage 2.4V -> 11.0V step 0.2V - default=10V (0x26) write_data(g, 0x03); // data: 0x00->0x3F Red Voltage 2.4V -> 11.0V step 0.2V - default= 3V (0x03) // Power-on write_cmd(g, PON); while (!getpin_busy(g)); // Panel setting register - Values from datasheet (OTP untested) write_cmd(g, PSR); // data: 0x08 + ..., 0x04=Shift Right, 0x02=Booster On, 0x01=Reset Off write_data(g, 0x0F); #if UC8173_USE_OTP_LUT write_cmd(g, 0x86); // data: Always 0x86, write_data=Register LUT, write_cmd=OTP LUT #else write_data(g, 0x86); #endif // Power-off sequence - Values from example code write_cmd(g, PFS); // data: Shutdown frames - 0x00=1, 0x10=2, 0x20=3, 0x30=4 write_data(g, 0x00); // PLL control - Values from example code write_cmd(g, LPRD); // data: PLL Clock Freq = 1MHz/(n+1) Min=0x04 (=200kHz) write_data(g, 0x25); // Internal temperature sensor enable - Values from example code write_cmd(g, TSE); // data: 0x00=Use internal temperature sensor, 0x80=external, Bits0-4=signed degrees to add eg 0x0F = -1 write_data(g, 0x00); // VCOM settings - Values from datasheet write_cmd(g, CDI); // data: 0x80=Border Hi-Z, 0x40=Border Output DC enabled, 0x30=BorderColor, 0x01=Normal/Inverted #if UC8171_BORDER == 0 write_data(g, 0x81); #elif UC8171_BORDER == 1 write_data(g, 0x41); #elif UC8171_BORDER == 2 write_data(g, 0x71); #else #error "UC8173: UC8173_BORDER must be 0..2" #endif // More VCOM data - Values from example code write_data(g, 0x20); // data: 0xX0=Src->VCOM interval (n+1 HSync, Def=0011), 0b0000-00XX=VCOM->Src interval (top 2 bits) write_data(g, 0x10); // data: 0xXX=VCOM->Src interval (bottom 8 bits) Def=0x18 // Set display panel resolution - from datasheet: should always be 240x240 for this chip write_cmd(g, TRES); write_data(g, (UC8173_WIDTH-1)); write_data(g, ((UC8173_HEIGHT-1)>>8)); write_data(g, ((UC8173_HEIGHT-1) & 0xFF)); // Undocumented register - Values from example code write_cmd(g, GDS); write_data(g, 0xA9); write_data(g, 0xA9); write_data(g, 0xEB); write_data(g, 0xEB); write_data(g, 0x02); // Auto measure VCOM - Values from example code write_cmd(g, AMV); write_data(g, 0x11); while (!getpin_busy(g)); // Get current VCOM value and write it #if defined(UC8173_VCOM_VOLTAGEBYTE) // VCM_DC setting write_cmd(g, VDCS); write_data(g, UC8173_VCOM_VOLTAGEBYTE); // was 0x12 in example code = -1.0V #elif UC8173_CAN_READ { uint8_t vc; write_cmd(g, VV); vc = read_data(g); write_cmd(g, VDCS); write_data(g, vc); } #else #error "UC8173: Either UC8173_VCOM_VOLTAGE or UC8173_VCOM_VOLTAGEBYTE must be defined or UC8173_CAN_READ must be GFXON" #endif // Undocumented register - Values from example code write_cmd(g, VBDS); write_data(g, 0x22); // Undocumented register - Values from example code write_cmd(g, LVSEL); write_data(g, 0x02); // Undocumented register - Values from example code write_cmd(g, GBS); write_data(g, 0x02); write_data(g, 0x02); // Undocumented register - Values from example code write_cmd(g, GSS); write_data(g, 0x02); write_data(g, 0x02); // Undocumented register - Values from example code write_cmd(g, DF); // For REGAL (???) write_data(g, 0x1F); write_cmd(g, PON); while (!getpin_busy(g)); #if UC8173_INIT_REAL_LUT // Load the real LUT's write_cmd(g, LUT_KWVCOM); write_data_burst(g, priv->lut->lutVCOM, 32); write_cmd(g, LUT_KW); write_data_burst(g, priv->lut->lutFW, 512); write_cmd(g, LUT_FT); write_data_burst(g, priv->lut->lutFT, 128); #else // Clear the LUT's - Values from example code write_cmd(g, LUT_KW); write_data_burst(g, UC8173_LUT_None, sizeof(UC8173_LUT_None)); write_cmd(g, LUT_KWVCOM); write_data_burst(g, UC8173_LUT_None, sizeof(UC8173_LUT_None)); #endif write_cmd(g, POF); while (getpin_busy(g)); // Finish Init post_init_board(g); // Release the bus release_bus(g); // Initialise the GDISP structure g->g.Width = UC8173_WIDTH; g->g.Height = UC8173_HEIGHT; g->g.Orientation = GDISP_ROTATE_0; g->g.Powermode = powerOn; g->g.Backlight = 0; g->g.Contrast = 0; return gTrue; } #if GDISP_HARDWARE_FLUSH LLDSPEC void gdisp_lld_flush(GDisplay* g) { coord_t cy, cx, dx, dy; LLDCOLOR_TYPE *fb; UC8173_Private *priv; priv = (UC8173_Private *)g->priv; if (!FB_FLUSH_REQUIRED(&priv->fb)) return; #if 0 FB_FLUSH_ALL(&priv->fb); #endif // Acquire the bus to communicate with the display controller acquire_bus(g); // Upload the new temperature LUT write_cmd(g, LUT_KWVCOM); write_data_burst(g, priv->lut->lutVCOM, 32); write_cmd(g, LUT_KW); write_data_burst(g, priv->lut->lutFW, 512); write_cmd(g, LUT_FT); write_data_burst(g, priv->lut->lutFT, 128); // Calculate the width, height cx = FB_FLUSH_WIDTH(&priv->fb); cy = FB_FLUSH_HEIGHT(&priv->fb); // Setup the window // Datasheet says x,y,w,h but in practice it needs to be x,y,w-1,h-1 write_cmd(g, DTMW); write_data(g, (uint8_t)((priv->fb.fa0.x >> 0) & 0xFF)); write_data(g, (uint8_t)((priv->fb.fa0.y >> 8) & 0x03)); write_data(g, (uint8_t)((priv->fb.fa0.y >> 0) & 0xFF)); write_data(g, (uint8_t)(((cx-1) >> 0) & 0xFF)); write_data(g, (uint8_t)(((cy-1) >> 8) & 0x03)); write_data(g, (uint8_t)(((cy-1) >> 0) & 0xFF)); // Transfer the buffer #if GDISP_LLD_PIXELFORMAT == GDISP_PIXELFORMAT_MONO write_cmd(g, DTM4); #elif GDISP_LLD_PIXELFORMAT == GDISP_PIXELFORMAT_GRAY4 write_cmd(g, DTM2); #else #error "UC8173: Unsupported driver color format" #endif dx = (cx+FB_TYPE_PIXELS-1)/FB_TYPE_PIXELS * (LLDCOLOR_TYPE_BITS/8); for (fb = FB_ADDR(&priv->fb, 0, priv->fb.fa0.x, priv->fb.fa0.y), dy = cy; dy; dy--, fb += FB_LINE_TYPES) write_data_burst(g, (uint8_t *)fb, dx); // Power-up the DC/DC converter to update the display panel write_cmd(g, PON); while (!getpin_busy(g)); // Refresh the panel contents // Datasheet says x,y,w,h but in practice it needs to be x,y,w-1,h-1 write_cmd(g, DRF); // data: Partial Scan = 0x10, REGAL = 0x08, VCOM_DoNothing = 0x04 (GC4/A2 = 0x00, GU4 = 0x08) write_data(g, (priv->lut->regal ? 0x08 : 0x00)); write_data(g, (uint8_t)((priv->fb.fa0.x >> 0) & 0xFF)); write_data(g, (uint8_t)((priv->fb.fa0.y >> 8) & 0x03)); write_data(g, (uint8_t)((priv->fb.fa0.y >> 0) & 0xFF)); write_data(g, (uint8_t)(((cx-1) >> 0) & 0xFF)); write_data(g, (uint8_t)(((cy-1) >> 8) & 0x03)); write_data(g, (uint8_t)(((cy-1) >> 0) & 0xFF)); while (!getpin_busy(g)); // Power-down the DC/DC converter write_cmd(g, POF); while (getpin_busy(g)); // Release the bus release_bus(g); // Mark as flushed FB_FLUSH_NONE(&priv->fb); } #endif #if GDISP_HARDWARE_DRAWPIXEL LLDSPEC void gdisp_lld_draw_pixel(GDisplay* g) { coord_t x, y; UC8173_Private *priv; priv = (UC8173_Private *)g->priv; // Handle the different possible orientations #if GDISP_NEED_CONTROL && GDISP_HARDWARE_CONTROL switch(g->g.Orientation) { default: case GDISP_ROTATE_0: #if FB_REVERSEAXIS_X x = FB_WIDTH-1 - g->p.x; #else x = g->p.x; #endif #if FB_REVERSEAXIS_Y y = FB_HEIGHT-1 - g->p.y; #else y = g->p.y; #endif break; case GDISP_ROTATE_90: #if FB_REVERSEAXIS_X x = FB_WIDTH-1 - g->p.y; #else x = g->p.y; #endif #if FB_REVERSEAXIS_Y y = g->p.x; #else y = FB_HEIGHT-1 - g->p.x; #endif break; case GDISP_ROTATE_180: #if FB_REVERSEAXIS_X x = g->p.x; #else x = FB_WIDTH-1 - g->p.x; #endif #if FB_REVERSEAXIS_Y y = g->p.y; #else y = FB_HEIGHT-1 - g->p.y; #endif break; case GDISP_ROTATE_270: #if FB_REVERSEAXIS_X x = g->p.y; #else x = FB_WIDTH-1 - g->p.y; #endif #if FB_REVERSEAXIS_Y y = FB_HEIGHT-1 - g->p.x; #else y = g->p.x; #endif break; } #else #if FB_REVERSEAXIS_X x = FB_WIDTH-1 - g->p.x; #else x = g->p.x; #endif #if FB_REVERSEAXIS_Y y = FB_HEIGHT-1 - g->p.y; #else y = g->p.y; #endif #endif // Modify the framebuffer content FB_SETPIXEL(&priv->fb, 0, x, y, gdispColor2Native(g->p.color)); } #endif #if GDISP_NEED_CONTROL && GDISP_HARDWARE_CONTROL LLDSPEC void gdisp_lld_control(GDisplay* g) { UC8173_Private *priv; priv = (UC8173_Private *)g->priv; switch(g->p.x) { // Custom gdispControl() to invert the framebuffer case GDISP_CONTROL_INVERT: { unsigned i; LLDCOLOR_TYPE *fb; // Invert the framebuffer for (fb = priv->fb.fb, i = FB_PAGE_TYPES; i ; i--, fb++) fb[0] = ~fb[0]; // We should flush these changes to the display controller framebuffer at some point FB_FLUSH_ALL(&priv->fb); } break; // Custom gdispControl() to set which EINK Mode (waveform) to use case GDISP_CONTROL_SETMODE: if ((unsigned)g->p.ptr < sizeof(UC8173_ModeTable)/sizeof(UC8173_ModeTable[0])) priv->lut = &UC8173_ModeTable[(unsigned)g->p.ptr]; break; // Custom gdispControl() to set the EINK border case GDISP_CONTROL_SETBORDER: if ((unsigned)g->p.ptr > 2) break; acquire_bus(g); write_cmd(g, PON); while (!getpin_busy(g)); write_cmd(g, CDI); switch((unsigned)g->p.ptr) { case 0: write_data(g, 0x81); // Border Hi-Z break; case 1: write_data(g, 0x41); // Border Black break; case 2: write_data(g, 0x71); // Border White break; } write_cmd(g, POF); while (getpin_busy(g)); release_bus(g); break; default: break; } } #endif #endif // GFX_USE_GDISP