/* * 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_ST7565_QMK #include "gdisp_lld_config.h" #include "src/gdisp/gdisp_driver.h" #include "board_ST7565.h" /*===========================================================================*/ /* Driver local definitions. */ /*===========================================================================*/ #ifndef GDISP_SCREEN_HEIGHT #define GDISP_SCREEN_HEIGHT LCD_HEIGHT #endif #ifndef GDISP_SCREEN_WIDTH #define GDISP_SCREEN_WIDTH LCD_WIDTH #endif #ifndef GDISP_INITIAL_CONTRAST #define GDISP_INITIAL_CONTRAST 35 #endif #ifndef GDISP_INITIAL_BACKLIGHT #define GDISP_INITIAL_BACKLIGHT 100 #endif #define GDISP_FLG_NEEDFLUSH (GDISP_FLG_DRIVER<<0) #include "st7565.h" /*===========================================================================*/ /* Driver config defaults for backward compatibility. */ /*===========================================================================*/ #ifndef ST7565_LCD_BIAS #define ST7565_LCD_BIAS ST7565_LCD_BIAS_7 #endif #ifndef ST7565_ADC #define ST7565_ADC ST7565_ADC_NORMAL #endif #ifndef ST7565_COM_SCAN #define ST7565_COM_SCAN ST7565_COM_SCAN_INC #endif #ifndef ST7565_PAGE_ORDER #define ST7565_PAGE_ORDER 0,1,2,3 #endif /*===========================================================================*/ /* Driver local functions. */ /*===========================================================================*/ typedef struct{ bool_t buffer2; uint8_t data_pos; uint8_t data[16]; uint8_t ram[GDISP_SCREEN_HEIGHT * GDISP_SCREEN_WIDTH / 8]; }PrivData; // Some common routines and macros #define PRIV(g) ((PrivData*)g->priv) #define RAM(g) (PRIV(g)->ram) static GFXINLINE void write_cmd(GDisplay* g, uint8_t cmd) { PRIV(g)->data[PRIV(g)->data_pos++] = cmd; } static GFXINLINE void flush_cmd(GDisplay* g) { write_data(g, PRIV(g)->data, PRIV(g)->data_pos); PRIV(g)->data_pos = 0; } #define write_cmd2(g, cmd1, cmd2) { write_cmd(g, cmd1); write_cmd(g, cmd2); } #define write_cmd3(g, cmd1, cmd2, cmd3) { write_cmd(g, cmd1); write_cmd(g, cmd2); write_cmd(g, cmd3); } // Some common routines and macros #define delay(us) gfxSleepMicroseconds(us) #define delay_ms(ms) gfxSleepMilliseconds(ms) #define xyaddr(x, y) ((x) + ((y)>>3)*GDISP_SCREEN_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 monochrome. * 64 * 128 / 8 = 1024 bytes. */ LLDSPEC bool_t gdisp_lld_init(GDisplay *g) { // The private area is the display surface. g->priv = gfxAlloc(sizeof(PrivData)); PRIV(g)->buffer2 = false; PRIV(g)->data_pos = 0; // Initialise the board interface init_board(g); // Hardware reset setpin_reset(g, TRUE); gfxSleepMilliseconds(20); setpin_reset(g, FALSE); gfxSleepMilliseconds(20); acquire_bus(g); enter_cmd_mode(g); write_cmd(g, ST7565_RESET); write_cmd(g, ST7565_LCD_BIAS); write_cmd(g, ST7565_ADC); write_cmd(g, ST7565_COM_SCAN); write_cmd(g, ST7565_RESISTOR_RATIO | 0x1); write_cmd2(g, ST7565_CONTRAST, GDISP_INITIAL_CONTRAST); // turn on internal power supply (VC=1, VR=1, VF=1) write_cmd(g, ST7565_POWER_CONTROL | 0x07); write_cmd(g, ST7565_INVERT_DISPLAY); write_cmd(g, ST7565_ALLON_NORMAL); write_cmd(g, ST7565_START_LINE | 0); write_cmd(g, ST7565_RMW); flush_cmd(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 = powerOff; g->g.Backlight = GDISP_INITIAL_BACKLIGHT; g->g.Contrast = GDISP_INITIAL_CONTRAST; return TRUE; } #if GDISP_HARDWARE_FLUSH LLDSPEC void gdisp_lld_flush(GDisplay *g) { unsigned p; // Don't flush if we don't need it. if (!(g->flags & GDISP_FLG_NEEDFLUSH)) return; acquire_bus(g); enter_cmd_mode(g); unsigned dstOffset = (PRIV(g)->buffer2 ? 4 : 0); for (p = 0; p < 4; p++) { write_cmd(g, ST7565_PAGE | (p + dstOffset)); write_cmd(g, ST7565_COLUMN_MSB | 0); write_cmd(g, ST7565_COLUMN_LSB | 0); write_cmd(g, ST7565_RMW); flush_cmd(g); enter_data_mode(g); write_data(g, RAM(g) + (p*GDISP_SCREEN_WIDTH), GDISP_SCREEN_WIDTH); enter_cmd_mode(g); } unsigned line = (PRIV(g)->buffer2 ? 32 : 0); write_cmd(g, ST7565_START_LINE | line); flush_cmd(g); PRIV(g)->buffer2 = !PRIV(g)->buffer2; release_bus(g); g->flags &= ~GDISP_FLG_NEEDFLUSH; } #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; } if (gdispColor2Native(g->p.color) != 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 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_HEIGHT-1 - g->p.y; y = g->p.x; break; } return (RAM(g)[xyaddr(x, y)] & xybit(y)) ? White : Black; } #endif LLDSPEC void gdisp_lld_blit_area(GDisplay *g) { uint8_t* buffer = (uint8_t*)g->p.ptr; int linelength = g->p.cx; for (int i = 0; i < g->p.cy; i++) { unsigned dstx = g->p.x; unsigned dsty = g->p.y + i; unsigned srcx = g->p.x1; unsigned srcy = g->p.y1 + i; unsigned srcbit = srcy * g->p.x2 + srcx; for(int j=0; j < linelength; j++) { uint8_t src = buffer[srcbit / 8]; uint8_t bit = 7-(srcbit % 8); uint8_t bitset = (src >> bit) & 1; uint8_t* dst = &(RAM(g)[xyaddr(dstx, dsty)]); if (bitset) { *dst |= xybit(dsty); } else { *dst &= ~xybit(dsty); } dstx++; srcbit++; } } g->flags |= GDISP_FLG_NEEDFLUSH; } #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); enter_cmd_mode(g); write_cmd(g, ST7565_DISPLAY_OFF); flush_cmd(g); release_bus(g); break; case powerOn: acquire_bus(g); enter_cmd_mode(g); write_cmd(g, ST7565_DISPLAY_ON); flush_cmd(g); 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) { /* Rotation is handled by the drawing routines */ case GDISP_ROTATE_0: case GDISP_ROTATE_180: g->g.Height = GDISP_SCREEN_HEIGHT; g->g.Width = GDISP_SCREEN_WIDTH; break; case GDISP_ROTATE_90: 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; case GDISP_CONTROL_CONTRAST: g->g.Contrast = (unsigned)g->p.ptr & 63; acquire_bus(g); enter_cmd_mode(g); write_cmd2(g, ST7565_CONTRAST, g->g.Contrast); flush_cmd(g); release_bus(g); return; } } #endif // GDISP_NEED_CONTROL #endif // GFX_USE_GDISP