From c2f2ab63e1542116c6e3eb2cd34c5901b04d6e42 Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Wed, 25 Oct 2023 18:18:47 +0200 Subject: [PATCH] SSD1312: wip --- .../gdisp/SSD1312/.gdisp_lld_SSD1312.c.swp | Bin 0 -> 20480 bytes drivers/gdisp/SSD1312/SSD1312.h | 28 ++ .../gdisp/SSD1312/board_SSD1312_template.h | 51 +++ drivers/gdisp/SSD1312/driver.mk | 3 + drivers/gdisp/SSD1312/gdisp_lld_SSD1312.c | 366 ++++++++++++++++++ drivers/gdisp/SSD1312/gdisp_lld_config.h | 27 ++ 6 files changed, 475 insertions(+) create mode 100644 drivers/gdisp/SSD1312/.gdisp_lld_SSD1312.c.swp create mode 100644 drivers/gdisp/SSD1312/SSD1312.h create mode 100644 drivers/gdisp/SSD1312/board_SSD1312_template.h create mode 100644 drivers/gdisp/SSD1312/driver.mk create mode 100644 drivers/gdisp/SSD1312/gdisp_lld_SSD1312.c create mode 100644 drivers/gdisp/SSD1312/gdisp_lld_config.h diff --git a/drivers/gdisp/SSD1312/.gdisp_lld_SSD1312.c.swp b/drivers/gdisp/SSD1312/.gdisp_lld_SSD1312.c.swp new file mode 100644 index 0000000000000000000000000000000000000000..4de69047d3089980c1d4800d16c65e7acf7c9aab GIT binary patch literal 20480 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 +#define xyaddr(x, y) (SSD1312_PAGE_OFFSET + (x) + ((y)>>3)*SSD1312_PAGE_WIDTH) +#define xybit(y) (1<<((y)&7)) + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * As this controller can't update on a pixel boundary we need to maintain the + * the entire display surface in memory so that we can do the necessary bit + * operations. Fortunately it is a small display in 4 bit grayscale. + * 64 * 128 / 2 = 4096 bytes. + */ + +LLDSPEC gBool gdisp_lld_init(GDisplay *g) +{ + // The private area is the display surface. + g->priv = gfxAlloc(GDISP_SCREEN_HEIGHT/8 * SSD1312_PAGE_WIDTH); + + // Fill in the prefix command byte on each page line of the display buffer + // We can do it during initialisation as this byte is never overwritten. + #ifdef SSD1312_PAGE_PREFIX + for (unsigned i = 0; i < GDISP_SCREEN_HEIGHT/8 * SSD1312_PAGE_WIDTH; i += SSD1312_PAGE_WIDTH) + RAM(g)[i] = SSD1312_PAGE_PREFIX; + #endif + + // Initialise the board interface + init_board(g); + + // Hardware reset + setpin_reset(g, gTrue); + gfxSleepMilliseconds(20); + setpin_reset(g, gFalse); + gfxSleepMilliseconds(200); + + // Display off + write_cmd(g, 0xAE); + + // Clock divider + write_cmd2(g, 0xD5, 0x80); + + // Multiplex ratio + write_cmd2(g, 0xA8, 0x1F); + + // Display offset + write_cmd2(g, 0xD3, 0x30); + + // Display start line + write_cmd(g, 0x40); + + // Charge pump + write_cmd2(g, 0x8D, 0x72); // 0x10 if Vcc externally supplied + + // Segment re-map + write_cmd(g, 0xA1); + + // COM output scan direction + write_cmd(g, 0xC0); + + // COM pin hardware configuration + write_cmd2(g, 0xDA, 0x10); + + // Set internal/external current reference + write_cmd2(g, 0xAD, 0x50); + + // Set contract + // ToDo: Also add to GDISP control interface below + write_cmd2(g, 0x81, 0x17); + + // Set pre-charge period + write_cmd2(g, 0xD9, 0xF1); + + // Set VCOMH select level + write_cmd2(g, 0xDB, 0x30); + + // Set entire display on/off + write_cmd(g, 0xA4); + + // Set normal/inverse display + write_cmd(g, 0xA6); + + // Display on + write_cmd(g, 0xAF); + + // Finish Init + post_init_board(g); + + // Initialise the GDISP structure + g->g.Width = GDISP_SCREEN_WIDTH; + g->g.Height = GDISP_SCREEN_HEIGHT; + g->g.Orientation = gOrientation0; + g->g.Powermode = gPowerOn; + g->g.Backlight = GDISP_INITIAL_BACKLIGHT; + g->g.Contrast = GDISP_INITIAL_CONTRAST; + + return gTrue; +} + +#if GDISP_HARDWARE_FLUSH + LLDSPEC void gdisp_lld_flush(GDisplay *g) { + gU8 * ram; + unsigned pages; + + // Don't flush if we don't need it. + if (!(g->flags & GDISP_FLG_NEEDFLUSH)) + return; + + ram = RAM(g); + pages = GDISP_SCREEN_HEIGHT/8; + + acquire_bus(g); + write_cmd(g, CMD_DISPLAY_START_LINE); + while (pages--) { + write_data(g, ram, SSD1312_PAGE_WIDTH); + ram += SSD1312_PAGE_WIDTH; + } + release_bus(g); + + g->flags &= ~GDISP_FLG_NEEDFLUSH; + } +#endif + +#if GDISP_HARDWARE_FILLS + LLDSPEC void gdisp_lld_fill_area(GDisplay *g) { + gCoord sy, ey; + gCoord sx, ex; + gCoord col; + unsigned spage, zpages; + gU8 * base; + gU8 mask; + + switch(g->g.Orientation) { + default: + case gOrientation0: + sx = g->p.x; + ex = g->p.x + g->p.cx - 1; + sy = g->p.y; + ey = sy + g->p.cy - 1; + break; + case gOrientation90: + sx = g->p.y; + ex = g->p.y + g->p.cy - 1; + sy = GDISP_SCREEN_HEIGHT - g->p.x - g->p.cx; + ey = GDISP_SCREEN_HEIGHT-1 - g->p.x; + break; + case gOrientation180: + sx = GDISP_SCREEN_WIDTH - g->p.x - g->p.cx; + ex = GDISP_SCREEN_WIDTH-1 - g->p.x; + sy = GDISP_SCREEN_HEIGHT - g->p.y - g->p.cy; + ey = GDISP_SCREEN_HEIGHT-1 - g->p.y; + break; + case gOrientation270: + sx = GDISP_SCREEN_WIDTH - g->p.y - g->p.cy; + ex = GDISP_SCREEN_WIDTH-1 - g->p.y; + sy = g->p.x; + ey = g->p.x + g->p.cx - 1; + break; + } + + spage = sy / 8; + base = RAM(g) + SSD1312_PAGE_OFFSET + SSD1312_PAGE_WIDTH * spage; + mask = 0xff << (sy&7); + zpages = (ey / 8) - spage; + + if (gdispColor2Native(g->p.color) == gdispColor2Native(GFX_BLACK)) { + while (zpages--) { + for (col = sx; col <= ex; col++) + base[col] &= ~mask; + mask = 0xff; + base += SSD1312_PAGE_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 += SSD1312_PAGE_WIDTH; + } + mask &= (0xff >> (7 - (ey&7))); + for (col = sx; col <= ex; col++) + base[col] |= mask; + } + g->flags |= GDISP_FLG_NEEDFLUSH; + } +#endif + +#if GDISP_HARDWARE_DRAWPIXEL + LLDSPEC void gdisp_lld_draw_pixel(GDisplay *g) { + gCoord x, y; + + switch(g->g.Orientation) { + default: + case gOrientation0: + x = g->p.x; + y = g->p.y; + break; + case gOrientation90: + x = g->p.y; + y = GDISP_SCREEN_HEIGHT-1 - g->p.x; + break; + case gOrientation180: + x = GDISP_SCREEN_WIDTH-1 - g->p.x; + y = GDISP_SCREEN_HEIGHT-1 - g->p.y; + break; + case gOrientation270: + x = GDISP_SCREEN_WIDTH-1 - g->p.y; + y = g->p.x; + break; + } + if (gdispColor2Native(g->p.color) != gdispColor2Native(GFX_BLACK)) + RAM(g)[xyaddr(x, y)] |= xybit(y); + else + RAM(g)[xyaddr(x, y)] &= ~xybit(y); + g->flags |= GDISP_FLG_NEEDFLUSH; + } +#endif + +#if GDISP_HARDWARE_PIXELREAD + LLDSPEC gColor gdisp_lld_get_pixel_color(GDisplay *g) { + gCoord x, y; + + switch(g->g.Orientation) { + default: + case gOrientation0: + x = g->p.x; + y = g->p.y; + break; + case gOrientation90: + x = g->p.y; + y = GDISP_SCREEN_HEIGHT-1 - g->p.x; + break; + case gOrientation180: + x = GDISP_SCREEN_WIDTH-1 - g->p.x; + y = GDISP_SCREEN_HEIGHT-1 - g->p.y; + break; + case gOrientation270: + x = GDISP_SCREEN_WIDTH-1 - g->p.y; + y = g->p.x; + break; + } + return (RAM(g)[xyaddr(x, y)] & xybit(y)) ? GFX_WHITE : GFX_BLACK; + } +#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 == (gPowermode)g->p.ptr) + return; + switch((gPowermode)g->p.ptr) { + case gPowerOff: + case gPowerSleep: + case gPowerDeepSleep: + acquire_bus(g); + write_cmd(g, SSD1312_DISPLAYOFF); + release_bus(g); + break; + case gPowerOn: + acquire_bus(g); + write_cmd(g, SSD1312_DISPLAYON); + release_bus(g); + break; + default: + return; + } + g->g.Powermode = (gPowermode)g->p.ptr; + return; + + case GDISP_CONTROL_ORIENTATION: + if (g->g.Orientation == (gOrientation)g->p.ptr) + return; + switch((gOrientation)g->p.ptr) { + /* Rotation is handled by the drawing routines */ + case gOrientation0: + case gOrientation180: + g->g.Height = GDISP_SCREEN_HEIGHT; + g->g.Width = GDISP_SCREEN_WIDTH; + break; + case gOrientation90: + case gOrientation270: + g->g.Height = GDISP_SCREEN_WIDTH; + g->g.Width = GDISP_SCREEN_HEIGHT; + break; + default: + return; + } + g->g.Orientation = (gOrientation)g->p.ptr; + return; + + case GDISP_CONTROL_CONTRAST: + if ((unsigned)g->p.ptr > 100) + g->p.ptr = (void *)100; + acquire_bus(g); + write_cmd2(g, SSD1312_SETCONTRAST, (((unsigned)g->p.ptr)<<8)/101); + release_bus(g); + g->g.Contrast = (unsigned)g->p.ptr; + return; + + // Our own special controller code to inverse the display + // 0 = normal, 1 = inverse + case GDISP_CONTROL_INVERSE: + acquire_bus(g); + write_cmd(g, g->p.ptr ? SSD1312_INVERTDISPLAY : SSD1312_NORMALDISPLAY); + release_bus(g); + return; + } + } +#endif // GDISP_NEED_CONTROL + +#endif // GFX_USE_GDISP + diff --git a/drivers/gdisp/SSD1312/gdisp_lld_config.h b/drivers/gdisp/SSD1312/gdisp_lld_config.h new file mode 100644 index 00000000..2e9bfed3 --- /dev/null +++ b/drivers/gdisp/SSD1312/gdisp_lld_config.h @@ -0,0 +1,27 @@ +/* + * 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.io/license.html + */ + +#ifndef _GDISP_LLD_CONFIG_H +#define _GDISP_LLD_CONFIG_H + +#if GFX_USE_GDISP + +/*===========================================================================*/ +/* Driver hardware support. */ +/*===========================================================================*/ + +#define GDISP_HARDWARE_FLUSH GFXON // This controller requires flushing +#define GDISP_HARDWARE_DRAWPIXEL GFXON +#define GDISP_HARDWARE_PIXELREAD GFXON +#define GDISP_HARDWARE_CONTROL GFXON +#define GDISP_HARDWARE_FILLS GFXON + +#define GDISP_LLD_PIXELFORMAT GDISP_PIXELFORMAT_MONO + +#endif /* GFX_USE_GDISP */ + +#endif /* _GDISP_LLD_CONFIG_H */