From 9618d7917bea1cb9f753569f0ddef91714aae49f Mon Sep 17 00:00:00 2001 From: ergosys Date: Sat, 13 Dec 2014 14:34:34 -0800 Subject: [PATCH 1/3] Partially handle screen rotation using remapping Use row and column remapping to change either row or column addressing for each orientation. This removes the need to reverse the drawing direction of either x or y in the drawing routines. However in the 90 and 270 case x and y must still be swapped. Also, rename the SSD1306 remapping command names so they are more descriptive. --- drivers/gdisp/SSD1306/SSD1306.h | 7 +- drivers/gdisp/SSD1306/gdisp_lld_SSD1306.c | 86 +++++++++++------------ 2 files changed, 45 insertions(+), 48 deletions(-) diff --git a/drivers/gdisp/SSD1306/SSD1306.h b/drivers/gdisp/SSD1306/SSD1306.h index d1c59fe4..7d7010e8 100644 --- a/drivers/gdisp/SSD1306/SSD1306.h +++ b/drivers/gdisp/SSD1306/SSD1306.h @@ -33,10 +33,11 @@ #define SSD1306_HV_PAGE_ADDRESS 0x22 #define SSD1306_PAM_PAGE_START 0xB0 -#define SSD1306_COMSCANINC 0xC0 -#define SSD1306_COMSCANDEC 0xC8 +#define SSD1306_ROWSCANINC 0xC0 +#define SSD1306_ROWSCANDEC 0xC8 -#define SSD1306_SEGREMAP 0xA0 +#define SSD1306_COLSCANINC 0xA0 +#define SSD1306_COLSCANDEC 0xA1 #define SSD1306_CHARGEPUMP 0x8D diff --git a/drivers/gdisp/SSD1306/gdisp_lld_SSD1306.c b/drivers/gdisp/SSD1306/gdisp_lld_SSD1306.c index 2386c2d4..713c52bb 100644 --- a/drivers/gdisp/SSD1306/gdisp_lld_SSD1306.c +++ b/drivers/gdisp/SSD1306/gdisp_lld_SSD1306.c @@ -105,8 +105,8 @@ LLDSPEC bool_t gdisp_lld_init(GDisplay *g) { write_cmd(g, SSD1306_SETSTARTLINE | 0); write_cmd2(g, SSD1306_ENABLE_CHARGE_PUMP, 0x14); write_cmd2(g, SSD1306_MEMORYMODE, 0); - write_cmd(g, SSD1306_SEGREMAP+1); - write_cmd(g, SSD1306_COMSCANDEC); + write_cmd(g, SSD1306_COLSCANDEC); + write_cmd(g, SSD1306_ROWSCANDEC); #if GDISP_SCREEN_HEIGHT == 64 write_cmd2(g, SSD1306_SETCOMPINS, 0x12); #else @@ -168,26 +168,11 @@ LLDSPEC bool_t gdisp_lld_init(GDisplay *g) { #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_WIDTH-1 - g->p.y; - y = g->p.x; - break; + coord_t x = g->p.x; + coord_t y = g->p.y; + if (g->g.Orientation == GDISP_ROTATE_90 || g->g.Orientation == GDISP_ROTATE_270) { + coord_t tmp; + tmp = x; x = y; y = tmp; } if (gdispColor2Native(g->p.color) != Black) RAM(g)[xyaddr(x, y)] |= xybit(y); @@ -199,26 +184,11 @@ LLDSPEC bool_t gdisp_lld_init(GDisplay *g) { #if GDISP_HARDWARE_PIXELREAD LLDSPEC color_t gdisp_lld_get_pixel_color(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_WIDTH-1 - g->p.y; - y = g->p.x; - break; + coord_t x = g->p.x; + coord_t y = g->p.y; + if (g->g.Orientation == GDISP_ROTATE_90 || g->g.Orientation == GDISP_ROTATE_270) { + coord_t tmp; + tmp = x; x = y; y = tmp; } return (RAM(g)[xyaddr(x, y)] & xybit(y)) ? White : Black; } @@ -252,8 +222,8 @@ LLDSPEC bool_t gdisp_lld_init(GDisplay *g) { case GDISP_CONTROL_ORIENTATION: if (g->g.Orientation == (orientation_t)g->p.ptr) return; - switch((orientation_t)g->p.ptr) { - /* Rotation is handled by the drawing routines */ + orientation_t orient = (orientation_t)g->p.ptr; + switch(orient) { case GDISP_ROTATE_0: case GDISP_ROTATE_180: g->g.Height = GDISP_SCREEN_HEIGHT; @@ -267,7 +237,33 @@ LLDSPEC bool_t gdisp_lld_init(GDisplay *g) { default: return; } - g->g.Orientation = (orientation_t)g->p.ptr; + // Remap the rows and columns according to orientation. This just + // eliminates the need to reverse x or y directions in the drawing + // routines. There is still the need to swap x and y for 90 and 270. + // However, without these, the hardware fill routine would be much + // more complicated. + acquire_bus(g); + switch(orient) { + default: + case GDISP_ROTATE_0: + write_cmd(g, SSD1306_COLSCANDEC); + write_cmd(g, SSD1306_ROWSCANDEC); + break; + case GDISP_ROTATE_180: + write_cmd(g, SSD1306_COLSCANINC); + write_cmd(g, SSD1306_ROWSCANINC); + break; + case GDISP_ROTATE_90: + write_cmd(g, SSD1306_COLSCANDEC); + write_cmd(g, SSD1306_ROWSCANINC); + break; + case GDISP_ROTATE_270: + write_cmd(g, SSD1306_COLSCANINC); + write_cmd(g, SSD1306_ROWSCANDEC); + break; + } + release_bus(g); + g->g.Orientation = orient; return; case GDISP_CONTROL_CONTRAST: From d3b3020a078fff9607fd169c32210888ee6b0c5c Mon Sep 17 00:00:00 2001 From: ergosys Date: Sat, 13 Dec 2014 14:44:01 -0800 Subject: [PATCH 2/3] use gdispColor2Native to get fill color --- drivers/gdisp/SSD1306/gdisp_lld_SSD1306.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gdisp/SSD1306/gdisp_lld_SSD1306.c b/drivers/gdisp/SSD1306/gdisp_lld_SSD1306.c index 713c52bb..1885ffac 100644 --- a/drivers/gdisp/SSD1306/gdisp_lld_SSD1306.c +++ b/drivers/gdisp/SSD1306/gdisp_lld_SSD1306.c @@ -159,7 +159,7 @@ LLDSPEC bool_t gdisp_lld_init(GDisplay *g) { #if GDISP_HARDWARE_CLEARS LLDSPEC void gdisp_lld_clear(GDisplay *g) { - uint8_t fill = (g->p.color == Black) ? 0 : 0xff; + uint8_t fill = (gdispColor2Native(g->p.color) == Black) ? 0 : 0xff; int bytes = GDISP_SCREEN_WIDTH * GDISP_SCREEN_HEIGHT/8; memset(RAM(g), fill, bytes); g->flags |= GDISP_FLG_NEEDFLUSH; From 555fda21708519de00dd10a213e346d084c50023 Mon Sep 17 00:00:00 2001 From: ergosys Date: Sat, 13 Dec 2014 14:44:53 -0800 Subject: [PATCH 3/3] Implement "hardware" fills Implement hardware fills by drawing on the framebuffer. This provides a significant performance boost for filled geometric primitives and a small one for font drawing. Tested at all orientations. --- drivers/gdisp/SSD1306/gdisp_lld_SSD1306.c | 40 +++++++++++++++++++++++ drivers/gdisp/SSD1306/gdisp_lld_config.h | 1 + 2 files changed, 41 insertions(+) diff --git a/drivers/gdisp/SSD1306/gdisp_lld_SSD1306.c b/drivers/gdisp/SSD1306/gdisp_lld_SSD1306.c index 1885ffac..a13b82a8 100644 --- a/drivers/gdisp/SSD1306/gdisp_lld_SSD1306.c +++ b/drivers/gdisp/SSD1306/gdisp_lld_SSD1306.c @@ -166,6 +166,46 @@ LLDSPEC bool_t gdisp_lld_init(GDisplay *g) { } #endif +#if GDISP_HARDWARE_FILLS + LLDSPEC void gdisp_lld_fill_area(GDisplay *g) { + coord_t sy = g->p.y; + coord_t ey = sy + g->p.cy - 1; + coord_t sx = g->p.x; + coord_t ex = g->p.x + g->p.cx - 1; + if (g->g.Orientation == GDISP_ROTATE_90 || g->g.Orientation == GDISP_ROTATE_270) { + coord_t tmp; + tmp = sx; sx = sy; sy = tmp; + tmp = ex; ex = ey; ey = tmp; + } + unsigned spage = sy / 8; + uint8_t * base = RAM(g) + GDISP_SCREEN_WIDTH * spage; + uint8_t mask = 0xff << (sy&7); + unsigned zpages = (ey / 8) - spage; + coord_t col; + if (gdispColor2Native(g->p.color)==Black) { + while (zpages--) { + for (col = sx; col <= ex; col++) + base[col] &= ~mask; + mask = 0xff; + base += GDISP_SCREEN_WIDTH; + } + mask &= (0xff >> (7 - (ey&7))); + for (col = sx; col <= ex; col++) + base[col] &= ~mask; + } else { + while (zpages--) { + for (col = sx; col <= ex; col++) + base[col] |= mask; + mask = 0xff; + base += GDISP_SCREEN_WIDTH; + } + mask &= (0xff >> (7 - (ey&7))); + for (col = sx; col <= ex; col++) + base[col] |= mask; + } + } +#endif + #if GDISP_HARDWARE_DRAWPIXEL LLDSPEC void gdisp_lld_draw_pixel(GDisplay *g) { coord_t x = g->p.x; diff --git a/drivers/gdisp/SSD1306/gdisp_lld_config.h b/drivers/gdisp/SSD1306/gdisp_lld_config.h index 9c502157..a0961dcf 100644 --- a/drivers/gdisp/SSD1306/gdisp_lld_config.h +++ b/drivers/gdisp/SSD1306/gdisp_lld_config.h @@ -19,6 +19,7 @@ #define GDISP_HARDWARE_PIXELREAD TRUE #define GDISP_HARDWARE_CONTROL TRUE #define GDISP_HARDWARE_CLEARS TRUE +#define GDISP_HARDWARE_FILLS TRUE #define GDISP_LLD_PIXELFORMAT GDISP_PIXELFORMAT_MONO