/** * @file GC9A01.c * **/ /********************* * INCLUDES *********************/ #include "GC9A01.h" #if USE_GC9A01 #include #include #include #include LV_DRV_DISP_INCLUDE #include LV_DRV_DELAY_INCLUDE /********************* * DEFINES *********************/ #ifndef GC9A01_XSTART #define GC9A01_XSTART 0 #endif #ifndef GC9A01_YSTART #define GC9A01_YSTART 0 #endif #define GC9A01_CMD_MODE 0 #define GC9A01_DATA_MODE 1 #define GC9A01_HOR_RES 240 #define GC9A01_VER_RES 240 /* GC9A01 Commands that we know of. Limited documentation */ #define GC9A01_INVOFF 0x20 #define GC9A01_INVON 0x21 #define GC9A01_DISPON 0x29 #define GC9A01_CASET 0x2A #define GC9A01_RASET 0x2B #define GC9A01_RAMWR 0x2C #define GC9A01_COLMOD 0x3A #define GC9A01_MADCTL 0x36 #define GC9A01_MADCTL_MY 0x80 #define GC9A01_MADCTL_MX 0x40 #define GC9A01_MADCTL_MV 0x20 #define GC9A01_MADCTL_RGB 0x00 #define GC9A01_DISFNCTRL 0xB6 /********************** * TYPEDEFS **********************/ /* Init script function */ struct GC9A01_function { uint16_t cmd; uint16_t data; }; /* Init script commands */ enum GC9A01_cmd { GC9A01_START, GC9A01_END, GC9A01_CMD, GC9A01_DATA, GC9A01_DELAY }; /********************** * STATIC PROTOTYPES **********************/ static void GC9A01_command(uint8_t cmd); static void GC9A01_data(uint8_t data); static void GC9A01_set_addr_win(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1); /********************** * STATIC VARIABLES **********************/ // Documentation on op codes for GC9A01 are very hard to find. // Will document should they be found. static struct GC9A01_function GC9A01_cfg_script[] = { { GC9A01_START, GC9A01_START}, { GC9A01_CMD, 0xEF}, { GC9A01_CMD, 0xEB}, { GC9A01_DATA, 0x14}, { GC9A01_CMD, 0xFE}, // Inter Register Enable1 { GC9A01_CMD, 0xEF}, // Inter Register Enable2 { GC9A01_CMD, 0xEB}, { GC9A01_DATA, 0x14}, { GC9A01_CMD, 0x84}, { GC9A01_DATA, 0x40}, { GC9A01_CMD, 0x85}, { GC9A01_DATA, 0xFF}, { GC9A01_CMD, 0x86}, { GC9A01_DATA, 0xFF}, { GC9A01_CMD, 0x87}, { GC9A01_DATA, 0xFF}, { GC9A01_CMD, 0x88}, { GC9A01_DATA, 0x0A}, { GC9A01_CMD, 0x89}, { GC9A01_DATA, 0x21}, { GC9A01_CMD, 0x8A}, { GC9A01_DATA, 0x00}, { GC9A01_CMD, 0x8B}, { GC9A01_DATA, 0x80}, { GC9A01_CMD, 0x8C}, { GC9A01_DATA, 0x01}, { GC9A01_CMD, 0x8D}, { GC9A01_DATA, 0x01}, { GC9A01_CMD, 0x8E}, { GC9A01_DATA, 0xFF}, { GC9A01_CMD, 0x8F}, { GC9A01_DATA, 0xFF}, { GC9A01_CMD, GC9A01_DISFNCTRL}, // Display Function Control { GC9A01_DATA, 0x00}, { GC9A01_DATA, 0x00}, { GC9A01_CMD, GC9A01_MADCTL}, // Memory Access Control { GC9A01_DATA, 0x48}, // Set the display direction 0,1,2,3 four directions { GC9A01_CMD, GC9A01_COLMOD}, // COLMOD: Pixel Format Set { GC9A01_DATA, 0x05}, // 16 Bits per pixel { GC9A01_CMD, 0x90}, { GC9A01_DATA, 0x08}, { GC9A01_DATA, 0x08}, { GC9A01_DATA, 0x08}, { GC9A01_DATA, 0x08}, { GC9A01_CMD, 0xBD}, { GC9A01_DATA, 0x06}, { GC9A01_CMD, 0xBC}, { GC9A01_DATA, 0x00}, { GC9A01_CMD, 0xFF}, { GC9A01_DATA, 0x60}, { GC9A01_DATA, 0x01}, { GC9A01_DATA, 0x04}, { GC9A01_CMD, 0xC3}, // Power Control 2 { GC9A01_DATA, 0x13}, { GC9A01_CMD, 0xC4}, // Power Control 3 { GC9A01_DATA, 0x13}, { GC9A01_CMD, 0xC9}, // Power Control 4 { GC9A01_DATA, 0x22}, { GC9A01_CMD, 0xBE}, { GC9A01_DATA, 0x11}, { GC9A01_CMD, 0xE1}, { GC9A01_DATA, 0x10}, { GC9A01_DATA, 0x0E}, { GC9A01_CMD, 0xDF}, { GC9A01_DATA, 0x21}, { GC9A01_DATA, 0x0C}, { GC9A01_DATA, 0x02}, { GC9A01_CMD, 0xF0}, // SET_GAMMA1 { GC9A01_DATA, 0x45}, { GC9A01_DATA, 0x09}, { GC9A01_DATA, 0x08}, { GC9A01_DATA, 0x08}, { GC9A01_DATA, 0x26}, { GC9A01_DATA, 0x2A}, { GC9A01_CMD, 0xF1}, // SET_GAMMA2 { GC9A01_DATA, 0x43}, { GC9A01_DATA, 0x70}, { GC9A01_DATA, 0x72}, { GC9A01_DATA, 0x36}, { GC9A01_DATA, 0x37}, { GC9A01_DATA, 0x6F}, { GC9A01_CMD, 0xF2}, // SET_GAMMA3 { GC9A01_DATA, 0x45}, { GC9A01_DATA, 0x09}, { GC9A01_DATA, 0x08}, { GC9A01_DATA, 0x08}, { GC9A01_DATA, 0x26}, { GC9A01_DATA, 0x2A}, { GC9A01_CMD, 0xF3}, // SET_GAMMA4 { GC9A01_DATA, 0x43}, { GC9A01_DATA, 0x70}, { GC9A01_DATA, 0x72}, { GC9A01_DATA, 0x36}, { GC9A01_DATA, 0x37}, { GC9A01_DATA, 0x6F}, { GC9A01_CMD, 0xED}, { GC9A01_DATA, 0x1B}, { GC9A01_DATA, 0x0B}, { GC9A01_CMD, 0xAE}, { GC9A01_DATA, 0x77}, { GC9A01_CMD, 0xCD}, { GC9A01_DATA, 0x63}, { GC9A01_CMD, 0x70}, { GC9A01_DATA, 0x07}, { GC9A01_DATA, 0x07}, { GC9A01_DATA, 0x04}, { GC9A01_DATA, 0x0E}, { GC9A01_DATA, 0x0F}, { GC9A01_DATA, 0x09}, { GC9A01_DATA, 0x07}, { GC9A01_DATA, 0x08}, { GC9A01_DATA, 0x03}, { GC9A01_CMD, 0xE8}, { GC9A01_DATA, 0x34}, { GC9A01_CMD, 0x62}, { GC9A01_DATA, 0x18}, { GC9A01_DATA, 0x0D}, { GC9A01_DATA, 0x71}, { GC9A01_DATA, 0xED}, { GC9A01_DATA, 0x70}, { GC9A01_DATA, 0x70}, { GC9A01_DATA, 0x18}, { GC9A01_DATA, 0x0F}, { GC9A01_DATA, 0x71}, { GC9A01_DATA, 0xEF}, { GC9A01_DATA, 0x70}, { GC9A01_DATA, 0x70}, { GC9A01_CMD, 0x63}, { GC9A01_DATA, 0x18}, { GC9A01_DATA, 0x11}, { GC9A01_DATA, 0x71}, { GC9A01_DATA, 0xF1}, { GC9A01_DATA, 0x70}, { GC9A01_DATA, 0x70}, { GC9A01_DATA, 0x18}, { GC9A01_DATA, 0x13}, { GC9A01_DATA, 0x71}, { GC9A01_DATA, 0xF3}, { GC9A01_DATA, 0x70}, { GC9A01_DATA, 0x70}, { GC9A01_CMD, 0x64}, { GC9A01_DATA, 0x28}, { GC9A01_DATA, 0x29}, { GC9A01_DATA, 0xF1}, { GC9A01_DATA, 0x01}, { GC9A01_DATA, 0xF1}, { GC9A01_DATA, 0x00}, { GC9A01_DATA, 0x07}, { GC9A01_CMD, 0x66}, { GC9A01_DATA, 0x3C}, { GC9A01_DATA, 0x00}, { GC9A01_DATA, 0xCD}, { GC9A01_DATA, 0x67}, { GC9A01_DATA, 0x45}, { GC9A01_DATA, 0x45}, { GC9A01_DATA, 0x10}, { GC9A01_DATA, 0x00}, { GC9A01_DATA, 0x00}, { GC9A01_DATA, 0x00}, { GC9A01_CMD, 0x67}, { GC9A01_DATA, 0x00}, { GC9A01_DATA, 0x3C}, { GC9A01_DATA, 0x00}, { GC9A01_DATA, 0x00}, { GC9A01_DATA, 0x00}, { GC9A01_DATA, 0x01}, { GC9A01_DATA, 0x54}, { GC9A01_DATA, 0x10}, { GC9A01_DATA, 0x32}, { GC9A01_DATA, 0x98}, { GC9A01_CMD, 0x74}, { GC9A01_DATA, 0x10}, { GC9A01_DATA, 0x85}, { GC9A01_DATA, 0x80}, { GC9A01_DATA, 0x00}, { GC9A01_DATA, 0x00}, { GC9A01_DATA, 0x4E}, { GC9A01_DATA, 0x00}, { GC9A01_CMD, 0x98}, { GC9A01_DATA, 0x3E}, { GC9A01_DATA, 0x07}, { GC9A01_CMD, 0x35}, // Tearing Effect Line ON { GC9A01_CMD, 0x21}, // Display Inversion ON { GC9A01_CMD, 0x11}, // Sleep Out Mode { GC9A01_DELAY, 120}, { GC9A01_CMD, GC9A01_DISPON}, // Display ON { GC9A01_DELAY, 255}, { GC9A01_END, GC9A01_END}, }; /********************** * MACROS **********************/ /********************** * GLOBAL FUNCTIONS **********************/ /** * Write a command to the GC9A01 * @param cmd the command */ static void GC9A01_command(uint8_t cmd) { LV_DRV_DISP_CMD_DATA(GC9A01_CMD_MODE); LV_DRV_DISP_SPI_WR_BYTE(cmd); } /** * Write data to the GC9A01 * @param data the data */ static void GC9A01_data(uint8_t data) { LV_DRV_DISP_CMD_DATA(GC9A01_DATA_MODE); LV_DRV_DISP_SPI_WR_BYTE(data); } static int GC9A01_data_array(uint8_t *buf, uint32_t len) { uint8_t *pt = buf; for (uint32_t lp = 0; lp < len; lp++, pt++) { LV_DRV_DISP_SPI_WR_BYTE(*pt); } return 0; } static int GC9A01_databuf(uint32_t len, uint8_t *buf) { uint32_t byte_left = len; uint8_t *pt = buf; while (byte_left) { if (byte_left > 64) { LV_DRV_DISP_SPI_WR_ARRAY((char*)pt, 64); byte_left = byte_left - 64; pt = pt + 64; } else { LV_DRV_DISP_SPI_WR_ARRAY((char*)pt, byte_left); byte_left=0; } } return 0; } // hard reset of the tft controller // ---------------------------------------------------------- static void GC9A01_hard_reset( void ) { LV_DRV_DISP_SPI_CS(0); // Low to listen to us LV_DRV_DISP_RST(1); LV_DRV_DELAY_MS(50); LV_DRV_DISP_RST(0); LV_DRV_DELAY_MS(50); LV_DRV_DISP_RST(1); LV_DRV_DELAY_MS(50); } // Configuration of the tft controller // ---------------------------------------------------------- static void GC9A01_run_cfg_script(void) { int i = 0; int end_script = 0; do { switch (GC9A01_cfg_script[i].cmd) { case GC9A01_START: break; case GC9A01_CMD: GC9A01_command( GC9A01_cfg_script[i].data & 0xFF ); break; case GC9A01_DATA: GC9A01_data( GC9A01_cfg_script[i].data & 0xFF ); break; case GC9A01_DELAY: LV_DRV_DELAY_MS(GC9A01_cfg_script[i].data); break; case GC9A01_END: end_script = 1; } i++; } while (!end_script); } void GC9A01_drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color) { // Rudimentary clipping if((x >= GC9A01_HOR_RES) || (y >= GC9A01_VER_RES)) return; if((y+h-1) >= GC9A01_VER_RES) h = GC9A01_VER_RES - y; LV_DRV_DISP_SPI_CS(0); // Listen to us GC9A01_set_addr_win(x, y, x, y + h - 1); uint8_t hi = color >> 8, lo = color; while (h--) { GC9A01_data(hi); GC9A01_data(lo); } LV_DRV_DISP_SPI_CS(1); } void GC9A01_drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color) { // Rudimentary clipping if((x >= GC9A01_HOR_RES) || (y >= GC9A01_VER_RES)) return; if((x+w-1) >= GC9A01_HOR_RES) w = GC9A01_HOR_RES - x; LV_DRV_DISP_SPI_CS(0); // Listen to us GC9A01_set_addr_win(x, y, x + w - 1, y); uint8_t hi = color >> 8, lo = color; while (w--) { GC9A01_data(hi); GC9A01_data(lo); } LV_DRV_DISP_SPI_CS(1); } // Pass 8-bit (each) R,G,B, get back 16-bit packed color uint16_t GC9A01_Color565(uint8_t r, uint8_t g, uint8_t b) { return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3); } void GC9A01_invertDisplay(bool i) { GC9A01_command(i ? GC9A01_INVON : GC9A01_INVOFF); } void GC9A01_drawPixel(int16_t x, int16_t y, uint16_t color) { if((x < 0) ||(x >= GC9A01_HOR_RES) || (y < 0) || (y >= GC9A01_VER_RES)) return; LV_DRV_DISP_SPI_CS(0); // Listen to us GC9A01_set_addr_win(x, y, x, y); uint8_t hi = color >> 8, lo = color; GC9A01_data(hi); GC9A01_data(lo); LV_DRV_DISP_SPI_CS(1); } void GC9A01_fillScreen(uint16_t color) { GC9A01_fillRect(0, 0, GC9A01_HOR_RES, GC9A01_VER_RES, color); } // fill a rectangle void GC9A01_fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) { // rudimentary clipping (drawChar w/big text requires this) if((x >= GC9A01_HOR_RES) || (y >= GC9A01_VER_RES)) return; if((x + w - 1) >= GC9A01_HOR_RES) w = GC9A01_HOR_RES - x; if((y + h - 1) >= GC9A01_VER_RES) h = GC9A01_VER_RES - y; LV_DRV_DISP_SPI_CS(0); // Listen to us GC9A01_set_addr_win(x, y, x + w - 1, y + h - 1); uint8_t hi = color >> 8, lo = color; for (y = h; y > 0; y--) { for (x = w; x > 0; x--) { GC9A01_data(hi); GC9A01_data(lo); } } LV_DRV_DISP_SPI_CS(1); } void GC9A01_setRotation(uint8_t m) { GC9A01_command(GC9A01_MADCTL); m %= 4; // can't be higher than 3 switch (m) { case 0: GC9A01_data(GC9A01_MADCTL_MX | GC9A01_MADCTL_MY | GC9A01_MADCTL_RGB); // _xstart = _colstart; // _ystart = _rowstart; break; case 1: GC9A01_data(GC9A01_MADCTL_MY | GC9A01_MADCTL_MV | GC9A01_MADCTL_RGB); // _ystart = _colstart; // _xstart = _rowstart; break; case 2: GC9A01_data(GC9A01_MADCTL_RGB); // _xstart = _colstart; // _ystart = _rowstart; break; case 3: GC9A01_data(GC9A01_MADCTL_MX | GC9A01_MADCTL_MV | GC9A01_MADCTL_RGB); // _ystart = _colstart; // _xstart = _rowstart; break; } } /** * Initialize the GC9A01 */ int GC9A01_init(void) { GC9A01_hard_reset(); GC9A01_run_cfg_script(); // GC9A01_fillScreen(0x0000); // Black // GC9A01_fillScreen(0xFFFF); // White GC9A01_fillScreen(0xAAAA); // ? return 0; } static void GC9A01_set_addr_win(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) { uint16_t x_start = x0 + GC9A01_XSTART, x_end = x1 + GC9A01_XSTART; uint16_t y_start = y0 + GC9A01_YSTART, y_end = y1 + GC9A01_YSTART; GC9A01_command(GC9A01_CASET); // Column addr set GC9A01_data(x_start >> 8); GC9A01_data(x_start & 0xFF); // XSTART GC9A01_data(x_end >> 8); GC9A01_data(x_end & 0xFF); // XEND GC9A01_command(GC9A01_RASET); // Row addr set GC9A01_data(y_start >> 8); GC9A01_data(y_start & 0xFF); // YSTART GC9A01_data(y_end >> 8); GC9A01_data(y_end & 0xFF); // YEND GC9A01_command(GC9A01_RAMWR); } void GC9A01_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t *color_p) { LV_DRV_DISP_SPI_CS(0); // Listen to us GC9A01_set_addr_win(area->x1, area->y1, area->x2, area->y2); int32_t len = (area->x2 - area->x1 + 1) * (area->y2 - area->y1 + 1) * 2; LV_DRV_DISP_CMD_DATA(GC9A01_DATA_MODE); LV_DRV_DISP_SPI_WR_ARRAY((char*)color_p, len); LV_DRV_DISP_SPI_CS(1); lv_disp_flush_ready(disp_drv); /* Indicate you are ready with the flushing*/ } #endif