2017-02-02 16:07:59 +00:00
|
|
|
/*
|
|
|
|
* 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"
|
|
|
|
#include "UC8173.h"
|
|
|
|
#include "board_UC8173.h"
|
|
|
|
|
2017-06-30 09:43:51 +00:00
|
|
|
#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
|
2017-02-02 16:07:59 +00:00
|
|
|
#endif
|
|
|
|
|
2017-06-30 09:43:51 +00:00
|
|
|
#define GDISP_SCREEN_HEIGHT 240
|
|
|
|
#define GDISP_SCREEN_WIDTH 240
|
2017-02-02 16:07:59 +00:00
|
|
|
|
|
|
|
#define PRIV(g) ((UC8173_Private*)((g)->priv))
|
2017-03-04 05:06:30 +00:00
|
|
|
#define FRAMEBUFFER(g) ((uint8_t *)(PRIV(g)+1))
|
2017-02-02 16:07:59 +00:00
|
|
|
#define GDISP_FLG_NEEDFLUSH (GDISP_FLG_DRIVER << 0)
|
|
|
|
|
|
|
|
#if GDISP_LLD_PIXELFORMAT == GDISP_PIXELFORMAT_MONO
|
|
|
|
#define LINE_BYTES (GDISP_SCREEN_WIDTH/8)
|
|
|
|
#define WRITEBUFCMD DTM4
|
|
|
|
#define xyaddr(x, y) (((x)>>3) + ((y) * LINE_BYTES))
|
|
|
|
//#define xybit(x, c) ((c) << ((x) & 7)) // This one has the wrong order of the pixels inside the byte
|
|
|
|
#define xybit(x, c) ((c) << (7-((x) & 7)))
|
|
|
|
#elif GDISP_LLD_PIXELFORMAT == GDISP_PIXELFORMAT_GRAY4
|
|
|
|
#define LINE_BYTES (GDISP_SCREEN_WIDTH/4)
|
|
|
|
#define WRITEBUFCMD DTM2 // NOT SURE THIS IS RIGHT - MAY NEED TO USE DTM0 and then send a refresh???
|
|
|
|
#define xyaddr(x, y) (((x)>>2) + ((y) * LINE_BYTES))
|
|
|
|
//#define xybit(x, c) ((c) << (((x) & 3)<<1)) // This one has the wrong order of the pixels inside the byte
|
|
|
|
#define xybit(x, c) ((c) << (6-((x) & 3)<<1))
|
|
|
|
#else
|
|
|
|
#error "UC8173: Unsupported driver color format"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
typedef struct UC8173_Private {
|
|
|
|
coord_t flushWindowX;
|
|
|
|
coord_t flushWindowY;
|
|
|
|
coord_t flushWindowWidth;
|
|
|
|
coord_t flushWindowHeight;
|
|
|
|
} UC8173_Private;
|
|
|
|
|
|
|
|
// This function rounds a given integer up to a specified multiple. Note, multiple must be a power of 2!
|
|
|
|
static GFXINLINE void _roundUp(coord_t* numToRound, uint8_t multiple)
|
|
|
|
{
|
|
|
|
*numToRound = (*numToRound + multiple - 1) & ~(multiple - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static GFXINLINE void _wait_for_busy_high(GDisplay* g)
|
|
|
|
{
|
|
|
|
while (!getpin_busy(g));
|
|
|
|
}
|
|
|
|
|
|
|
|
static GFXINLINE void _wait_for_busy_low(GDisplay* g)
|
|
|
|
{
|
|
|
|
while (getpin_busy(g));
|
|
|
|
}
|
|
|
|
|
2017-03-02 06:10:14 +00:00
|
|
|
void _load_lut(GDisplay* g, uint32_t lutRegister, const uint8_t* lut, uint32_t lutCounter)
|
2017-02-02 16:07:59 +00:00
|
|
|
{
|
|
|
|
uint32_t i;
|
|
|
|
|
2017-03-02 06:10:14 +00:00
|
|
|
write_cmd(g, lutRegister);
|
|
|
|
for (i = 0; i < lutCounter; i++) {
|
|
|
|
write_data(g, *lut);
|
|
|
|
lut++;
|
2017-02-02 16:07:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-02 06:10:14 +00:00
|
|
|
static void _upload_lut(GDisplay* g)
|
|
|
|
{
|
|
|
|
_load_lut(g, LUT_KWVCOM, _lut_KWvcom_DC_A2_240ms, 32);
|
|
|
|
_load_lut(g, LUT_KW, _lut_kw_A2_240ms, 512);
|
|
|
|
_load_lut(g, LUT_FT, _lut_ft, 128);
|
2017-02-02 16:07:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void _clear_lut(GDisplay* g)
|
|
|
|
{
|
|
|
|
write_cmd(g, PON);
|
|
|
|
_wait_for_busy_high(g);
|
|
|
|
|
2017-03-02 06:10:14 +00:00
|
|
|
_load_lut(g, LUT_KW, _lut_None, 42);
|
|
|
|
_load_lut(g, LUT_KWVCOM, _lut_None, 42);
|
2017-02-02 16:07:59 +00:00
|
|
|
|
|
|
|
write_cmd(g, POF);
|
|
|
|
_wait_for_busy_low(g);
|
|
|
|
}
|
|
|
|
|
2017-03-02 06:10:14 +00:00
|
|
|
#if GDISP_NEED_CONTROL && GDISP_HARDWARE_CONTROL
|
|
|
|
static void _invertFramebuffer(GDisplay* g)
|
|
|
|
{
|
|
|
|
uint32_t i;
|
2017-02-02 16:07:59 +00:00
|
|
|
|
2017-03-02 06:10:14 +00:00
|
|
|
for (i = 0; i < LINE_BYTES*GDISP_SCREEN_HEIGHT; i++) {
|
|
|
|
FRAMEBUFFER(g)[i] = ~(FRAMEBUFFER(g)[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// We should flush these changes to the display controller framebuffer at some point
|
|
|
|
g->flags |= GDISP_FLG_NEEDFLUSH;
|
2017-02-02 16:07:59 +00:00
|
|
|
}
|
2017-03-02 06:10:14 +00:00
|
|
|
#endif
|
2017-02-02 16:07:59 +00:00
|
|
|
|
|
|
|
LLDSPEC bool_t gdisp_lld_init(GDisplay* g)
|
|
|
|
{
|
|
|
|
// Allocate the private area plus the framebuffer
|
|
|
|
g->priv = gfxAlloc(sizeof(UC8173_Private) + LINE_BYTES*GDISP_SCREEN_HEIGHT);
|
|
|
|
if (!g->priv) {
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initialize the private area
|
|
|
|
PRIV(g)->flushWindowX = 0;
|
|
|
|
PRIV(g)->flushWindowY = 0;
|
|
|
|
PRIV(g)->flushWindowWidth = GDISP_SCREEN_WIDTH;
|
|
|
|
PRIV(g)->flushWindowHeight = GDISP_SCREEN_HEIGHT;
|
|
|
|
|
|
|
|
// Initialise the board interface
|
|
|
|
if (!init_board(g)) {
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Hardware reset
|
|
|
|
setpin_reset(g, FALSE);
|
|
|
|
gfxSleepMilliseconds(100);
|
|
|
|
setpin_reset(g, TRUE);
|
|
|
|
gfxSleepMilliseconds(1000);
|
|
|
|
|
|
|
|
// Acquire the bus
|
|
|
|
acquire_bus(g);
|
|
|
|
|
|
|
|
// Booster soft-start
|
|
|
|
write_cmd(g, BTST);
|
|
|
|
write_data(g, 0x17); //0x17
|
|
|
|
write_data(g, 0x97); //0x97
|
|
|
|
write_data(g, 0x20); //0x20
|
|
|
|
|
|
|
|
// Power settings
|
|
|
|
write_cmd(g, PWR);
|
|
|
|
write_data(g, 0x03);
|
|
|
|
write_data(g, 0x00);
|
|
|
|
write_data(g, 0x2B); //1C 2B
|
|
|
|
write_data(g, 0x2B); //1C 2B
|
|
|
|
write_data(g, 0x00);
|
|
|
|
|
|
|
|
// Power-on
|
|
|
|
write_cmd(g, PON);
|
|
|
|
_wait_for_busy_high(g);
|
|
|
|
|
|
|
|
// Panel setting register
|
|
|
|
write_cmd(g, PSR);
|
|
|
|
write_data(g, 0x0F); //0x0B
|
|
|
|
write_data(g, 0x86); //0x86
|
|
|
|
|
|
|
|
// Power-off sequence
|
|
|
|
write_cmd(g, PFS);
|
|
|
|
write_data(g, 0x00);
|
|
|
|
|
|
|
|
// PLL control
|
|
|
|
write_cmd(g, LPRD);
|
|
|
|
write_data(g, 0x25);
|
|
|
|
|
|
|
|
// Internal temperature sensor enable
|
|
|
|
write_cmd(g, TSE);
|
|
|
|
write_data(g, 0x00); // Use internal temperature sensor
|
|
|
|
|
|
|
|
// VCOM and data interval settings
|
|
|
|
write_cmd(g, CDI);
|
|
|
|
write_data(g, 0xE1);
|
|
|
|
write_data(g, 0x20);
|
|
|
|
write_data(g, 0x10);
|
|
|
|
|
|
|
|
// Set display panel resolution
|
|
|
|
write_cmd(g, TRES);
|
|
|
|
write_data(g, 0xEF);
|
|
|
|
write_data(g, 0x00);
|
|
|
|
write_data(g, 0xEF);
|
|
|
|
|
|
|
|
// Undocumented register, taken from sample 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
|
|
|
|
write_cmd(g, AMV);
|
|
|
|
write_data(g, 0x11); // 5 seconds, enabled
|
|
|
|
_wait_for_busy_high(g);
|
|
|
|
|
|
|
|
// Get current VCOM value
|
|
|
|
// write_cmd(g, VV);
|
|
|
|
// unsigned char vcom_temp = spi_9b_get();
|
|
|
|
// vcom_temp = vcom_temp + 4;
|
|
|
|
// Auto_VCOM = vcom_temp;
|
|
|
|
|
|
|
|
// VCM_DC setting
|
|
|
|
write_cmd(g, VDCS);
|
|
|
|
write_data(g, 0x12); // Write vcom_temp here
|
|
|
|
|
|
|
|
// Undocumented register, taken from sample code
|
|
|
|
write_cmd(g, VBDS);
|
|
|
|
write_data(g, 0x22);
|
|
|
|
|
|
|
|
// Undocumented register, taken from sample code
|
|
|
|
write_cmd(g, LVSEL);
|
|
|
|
write_data(g, 0x02);
|
|
|
|
|
|
|
|
// Undocumented register, taken from sample code
|
|
|
|
write_cmd(g, GBS);
|
|
|
|
write_data(g, 0x02);
|
|
|
|
write_data(g, 0x02);
|
|
|
|
|
|
|
|
// Undocumented register, taken from sample code
|
|
|
|
write_cmd(g, GSS);
|
|
|
|
write_data(g, 0x02);
|
|
|
|
write_data(g, 0x02);
|
|
|
|
|
|
|
|
// Undocumented register, taken from sample code
|
|
|
|
write_cmd(g, DF); // For REGAL (???)
|
|
|
|
write_data(g, 0x1F);
|
|
|
|
|
|
|
|
// Clear the look-up table
|
|
|
|
_clear_lut(g);
|
|
|
|
|
|
|
|
// Finish Init
|
|
|
|
post_init_board(g);
|
|
|
|
|
|
|
|
// Release the bus
|
|
|
|
release_bus(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;
|
|
|
|
g->g.Backlight = 0;
|
|
|
|
g->g.Contrast = 0;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if GDISP_HARDWARE_FLUSH
|
|
|
|
LLDSPEC void gdisp_lld_flush(GDisplay* g)
|
|
|
|
{
|
|
|
|
coord_t y;
|
|
|
|
|
|
|
|
// Don't flush unless we really need to
|
|
|
|
if (!(g->flags & GDISP_FLG_NEEDFLUSH)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Round the flushing window width and height up to the next multiple of four
|
|
|
|
_roundUp(&(PRIV(g)->flushWindowWidth), 4);
|
|
|
|
_roundUp(&(PRIV(g)->flushWindowWidth), 4);
|
|
|
|
|
|
|
|
// Acquire the bus to communicate with the display controller
|
|
|
|
acquire_bus(g);
|
|
|
|
|
|
|
|
// Upload the new temperature LUT
|
2017-03-02 06:10:14 +00:00
|
|
|
_upload_lut(g);
|
2017-02-02 16:07:59 +00:00
|
|
|
|
|
|
|
// Setup the window
|
|
|
|
write_cmd(g, DTMW);
|
|
|
|
write_data(g, (uint8_t)((PRIV(g)->flushWindowX >> 0) & 0xFF));
|
|
|
|
write_data(g, (uint8_t)((PRIV(g)->flushWindowY >> 8) & 0x03));
|
|
|
|
write_data(g, (uint8_t)((PRIV(g)->flushWindowY >> 0) & 0xFF));
|
|
|
|
write_data(g, (uint8_t)((((PRIV(g)->flushWindowWidth)-1) >> 0) & 0xFF));
|
|
|
|
write_data(g, (uint8_t)((((PRIV(g)->flushWindowHeight)-1) >> 8) & 0x03));
|
|
|
|
write_data(g, (uint8_t)((((PRIV(g)->flushWindowHeight)-1) >> 0) & 0xFF));
|
|
|
|
|
|
|
|
// Dump our framebuffer
|
|
|
|
// Note: The display controller doesn't allow changing the vertical scanning direction
|
|
|
|
// so we have to manually send the lines "the other way around" here.
|
|
|
|
write_cmd(g, WRITEBUFCMD);
|
|
|
|
for (y = GDISP_SCREEN_HEIGHT-1; y >= 0; y--) {
|
|
|
|
write_data_burst(g, FRAMEBUFFER(g)+y*LINE_BYTES, LINE_BYTES);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Power-up the DC/DC converter to update the display panel
|
|
|
|
write_cmd(g, PON);
|
|
|
|
_wait_for_busy_high(g);
|
|
|
|
|
|
|
|
// Refresh the panel contents
|
|
|
|
write_cmd(g, DRF);
|
2017-03-02 06:10:14 +00:00
|
|
|
write_data(g, 0x00); // Enable REGAL function
|
2017-02-02 16:07:59 +00:00
|
|
|
write_data(g, 0x00);
|
|
|
|
write_data(g, 0x00);
|
|
|
|
write_data(g, 0x00);
|
|
|
|
write_data(g, 0xEF);
|
|
|
|
write_data(g, 0x00);
|
|
|
|
write_data(g, 0xEF);
|
|
|
|
_wait_for_busy_high(g);
|
|
|
|
|
|
|
|
// Power-down the DC/DC converter to make all the low-power pussys happy
|
|
|
|
write_cmd(g, POF);
|
|
|
|
_wait_for_busy_low(g);
|
|
|
|
|
|
|
|
// Release the bus again
|
|
|
|
release_bus(g);
|
|
|
|
|
|
|
|
// Clear the 'need-flushing' flag
|
|
|
|
g->flags &=~ GDISP_FLG_NEEDFLUSH;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if GDISP_HARDWARE_DRAWPIXEL
|
|
|
|
LLDSPEC void gdisp_lld_draw_pixel(GDisplay* g)
|
|
|
|
{
|
|
|
|
coord_t x, y;
|
|
|
|
LLDCOLOR_TYPE *p;
|
|
|
|
|
|
|
|
// Handle the different possible orientations
|
|
|
|
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_WIDTH-1 - g->p.y;
|
|
|
|
y = g->p.x;
|
|
|
|
break;
|
|
|
|
}
|
2017-03-02 06:10:14 +00:00
|
|
|
|
2017-02-02 16:07:59 +00:00
|
|
|
// Modify the framebuffer content
|
|
|
|
p = &FRAMEBUFFER(g)[xyaddr(x,y)];
|
|
|
|
*p &=~ xybit(x, LLDCOLOR_MASK());
|
|
|
|
*p |= xybit(x, gdispColor2Native(g->p.color));
|
|
|
|
|
2017-06-30 09:43:51 +00:00
|
|
|
// ToDo
|
2017-02-02 16:07:59 +00:00
|
|
|
// There appears to be an issue in the silicone, still talking to the manufacturer about this one. Update will follow!
|
|
|
|
#if 0
|
|
|
|
// Update the flush window region
|
|
|
|
if (g->flags & GDISP_FLG_NEEDFLUSH) {
|
|
|
|
if (x < PRIV(g)->flushWindowX)
|
|
|
|
PRIV(g)->flushWindowX = x;
|
|
|
|
if (y < PRIV(g)->flushWindowY)
|
|
|
|
PRIV(g)->flushWindowY = y;
|
|
|
|
if (x > PRIV(g)->flushWindowX + PRIV(g)->flushWindowWidth)
|
|
|
|
PRIV(g)->flushWindowWidth =
|
|
|
|
} else {
|
|
|
|
PRIV(g)->flushWindowX = x;
|
|
|
|
PRIV(g)->flushWindowY = y;
|
|
|
|
PRIV(g)->flushWindowWidth = 1;
|
|
|
|
PRIV(g)->flushWindowHeight = 1;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
PRIV(g)->flushWindowX = 0;
|
|
|
|
PRIV(g)->flushWindowY = 0;
|
|
|
|
PRIV(g)->flushWindowWidth = GDISP_SCREEN_WIDTH;
|
|
|
|
PRIV(g)->flushWindowHeight = GDISP_SCREEN_HEIGHT;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// We should flush these changes to the display controller framebuffer at some point
|
|
|
|
g->flags |= GDISP_FLG_NEEDFLUSH;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if GDISP_NEED_CONTROL && GDISP_HARDWARE_CONTROL
|
|
|
|
LLDSPEC void gdisp_lld_control(GDisplay* g) {
|
|
|
|
switch(g->p.x) {
|
|
|
|
case GDISP_CONTROL_INVERT:
|
|
|
|
_invertFramebuffer(g);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#endif // GFX_USE_GDISP
|