You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.
ndg/display/GC9A01.c

598 lines
12 KiB
C

/**
* @file GC9A01.c
*
**/
/*********************
* INCLUDES
*********************/
#include "GC9A01.h"
#if USE_GC9A01
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#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