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/ILI9341.c

433 lines
15 KiB
C

/**
* @file ILI9341.c
*
* ILI9341.pdf [ILI9341_DS_V1.13_20110805]
*
* [references]
* - https://www.newhavendisplay.com/app_notes/ILI9341.pdf
* - Linux Source [v5.9-rc4] "drivers/staging/fbtft/fb_ili9341.c"
* - https://github.com/adafruit/Adafruit_ILI9341/blob/master/Adafruit_ILI9341.cpp
* - https://os.mbed.com/users/dreschpe/code/SPI_TFT_ILI9341
*
*/
/*********************
* INCLUDES
*********************/
#include "ILI9341.h"
#if USE_ILI9341 != 0
#include <stdio.h>
#include <stdbool.h>
#include LV_DRV_DISP_INCLUDE
#include LV_DRV_DELAY_INCLUDE
/*********************
* DEFINES
*********************/
#define ILI9341_CMD_MODE 0
#define ILI9341_DATA_MODE 1
#define ILI9341_TFTWIDTH 240
#define ILI9341_TFTHEIGHT 320
/* Level 1 Commands -------------- [section] Description */
#define ILI9341_NOP 0x00 /* [8.2.1 ] No Operation / Terminate Frame Memory Write */
#define ILI9341_SWRESET 0x01 /* [8.2.2 ] Software Reset */
#define ILI9341_RDDIDIF 0x04 /* [8.2.3 ] Read Display Identification Information */
#define ILI9341_RDDST 0x09 /* [8.2.4 ] Read Display Status */
#define ILI9341_RDDPM 0x0A /* [8.2.5 ] Read Display Power Mode */
#define ILI9341_RDDMADCTL 0x0B /* [8.2.6 ] Read Display MADCTL */
#define ILI9341_RDDCOLMOD 0x0C /* [8.2.7 ] Read Display Pixel Format */
#define ILI9341_RDDIM 0x0D /* [8.2.8 ] Read Display Image Mode */
#define ILI9341_RDDSM 0x0E /* [8.2.9 ] Read Display Signal Mode */
#define ILI9341_RDDSDR 0x0F /* [8.2.10] Read Display Self-Diagnostic Result */
#define ILI9341_SLPIN 0x10 /* [8.2.11] Enter Sleep Mode */
#define ILI9341_SLPOUT 0x11 /* [8.2.12] Leave Sleep Mode */
#define ILI9341_PTLON 0x12 /* [8.2.13] Partial Display Mode ON */
#define ILI9341_NORON 0x13 /* [8.2.14] Normal Display Mode ON */
#define ILI9341_DINVOFF 0x20 /* [8.2.15] Display Inversion OFF */
#define ILI9341_DINVON 0x21 /* [8.2.16] Display Inversion ON */
#define ILI9341_GAMSET 0x26 /* [8.2.17] Gamma Set */
#define ILI9341_DISPOFF 0x28 /* [8.2.18] Display OFF*/
#define ILI9341_DISPON 0x29 /* [8.2.19] Display ON*/
#define ILI9341_CASET 0x2A /* [8.2.20] Column Address Set */
#define ILI9341_PASET 0x2B /* [8.2.21] Page Address Set */
#define ILI9341_RAMWR 0x2C /* [8.2.22] Memory Write */
#define ILI9341_RGBSET 0x2D /* [8.2.23] Color Set (LUT for 16-bit to 18-bit color depth conversion) */
#define ILI9341_RAMRD 0x2E /* [8.2.24] Memory Read */
#define ILI9341_PTLAR 0x30 /* [8.2.25] Partial Area */
#define ILI9341_VSCRDEF 0x33 /* [8.2.26] Veritcal Scrolling Definition */
#define ILI9341_TEOFF 0x34 /* [8.2.27] Tearing Effect Line OFF */
#define ILI9341_TEON 0x35 /* [8.2.28] Tearing Effect Line ON */
#define ILI9341_MADCTL 0x36 /* [8.2.29] Memory Access Control */
#define MADCTL_MY 0x80 /* MY row address order */
#define MADCTL_MX 0x40 /* MX column address order */
#define MADCTL_MV 0x20 /* MV row / column exchange */
#define MADCTL_ML 0x10 /* ML vertical refresh order */
#define MADCTL_MH 0x04 /* MH horizontal refresh order */
#define MADCTL_RGB 0x00 /* RGB Order [default] */
#define MADCTL_BGR 0x08 /* BGR Order */
#define ILI9341_VSCRSADD 0x37 /* [8.2.30] Vertical Scrolling Start Address */
#define ILI9341_IDMOFF 0x38 /* [8.2.31] Idle Mode OFF */
#define ILI9341_IDMON 0x39 /* [8.2.32] Idle Mode ON */
#define ILI9341_PIXSET 0x3A /* [8.2.33] Pixel Format Set */
#define ILI9341_WRMEMCONT 0x3C /* [8.2.34] Write Memory Continue */
#define ILI9341_RDMEMCONT 0x3E /* [8.2.35] Read Memory Continue */
#define ILI9341_SETSCANTE 0x44 /* [8.2.36] Set Tear Scanline */
#define ILI9341_GETSCAN 0x45 /* [8.2.37] Get Scanline */
#define ILI9341_WRDISBV 0x51 /* [8.2.38] Write Display Brightness Value */
#define ILI9341_RDDISBV 0x52 /* [8.2.39] Read Display Brightness Value */
#define ILI9341_WRCTRLD 0x53 /* [8.2.40] Write Control Display */
#define ILI9341_RDCTRLD 0x54 /* [8.2.41] Read Control Display */
#define ILI9341_WRCABC 0x55 /* [8.2.42] Write Content Adaptive Brightness Control Value */
#define ILI9341_RDCABC 0x56 /* [8.2.43] Read Content Adaptive Brightness Control Value */
#define ILI9341_WRCABCMIN 0x5E /* [8.2.44] Write CABC Minimum Brightness */
#define ILI9341_RDCABCMIN 0x5F /* [8.2.45] Read CABC Minimum Brightness */
#define ILI9341_RDID1 0xDA /* [8.2.46] Read ID1 - Manufacturer ID (user) */
#define ILI9341_RDID2 0xDB /* [8.2.47] Read ID2 - Module/Driver version (supplier) */
#define ILI9341_RDID3 0xDC /* [8.2.48] Read ID3 - Module/Driver version (user) */
/* Level 2 Commands -------------- [section] Description */
#define ILI9341_IFMODE 0xB0 /* [8.3.1 ] Interface Mode Control */
#define ILI9341_FRMCTR1 0xB1 /* [8.3.2 ] Frame Rate Control (In Normal Mode/Full Colors) */
#define ILI9341_FRMCTR2 0xB2 /* [8.3.3 ] Frame Rate Control (In Idle Mode/8 colors) */
#define ILI9341_FRMCTR3 0xB3 /* [8.3.4 ] Frame Rate control (In Partial Mode/Full Colors) */
#define ILI9341_INVTR 0xB4 /* [8.3.5 ] Display Inversion Control */
#define ILI9341_PRCTR 0xB5 /* [8.3.6 ] Blanking Porch Control */
#define ILI9341_DISCTRL 0xB6 /* [8.3.7 ] Display Function Control */
#define ILI9341_ETMOD 0xB7 /* [8.3.8 ] Entry Mode Set */
#define ILI9341_BLCTRL1 0xB8 /* [8.3.9 ] Backlight Control 1 - Grayscale Histogram UI mode */
#define ILI9341_BLCTRL2 0xB9 /* [8.3.10] Backlight Control 2 - Grayscale Histogram still picture mode */
#define ILI9341_BLCTRL3 0xBA /* [8.3.11] Backlight Control 3 - Grayscale Thresholds UI mode */
#define ILI9341_BLCTRL4 0xBB /* [8.3.12] Backlight Control 4 - Grayscale Thresholds still picture mode */
#define ILI9341_BLCTRL5 0xBC /* [8.3.13] Backlight Control 5 - Brightness Transition time */
#define ILI9341_BLCTRL7 0xBE /* [8.3.14] Backlight Control 7 - PWM Frequency */
#define ILI9341_BLCTRL8 0xBF /* [8.3.15] Backlight Control 8 - ON/OFF + PWM Polarity*/
#define ILI9341_PWCTRL1 0xC0 /* [8.3.16] Power Control 1 - GVDD */
#define ILI9341_PWCTRL2 0xC1 /* [8.3.17] Power Control 2 - step-up factor for operating voltage */
#define ILI9341_VMCTRL1 0xC5 /* [8.3.18] VCOM Control 1 - Set VCOMH and VCOML */
#define ILI9341_VMCTRL2 0xC7 /* [8.3.19] VCOM Control 2 - VCOM offset voltage */
#define ILI9341_NVMWR 0xD0 /* [8.3.20] NV Memory Write */
#define ILI9341_NVMPKEY 0xD1 /* [8.3.21] NV Memory Protection Key */
#define ILI9341_RDNVM 0xD2 /* [8.3.22] NV Memory Status Read */
#define ILI9341_RDID4 0xD3 /* [8.3.23] Read ID4 - IC Device Code */
#define ILI9341_PGAMCTRL 0xE0 /* [8.3.24] Positive Gamma Control */
#define ILI9341_NGAMCTRL 0xE1 /* [8.3.25] Negative Gamma Correction */
#define ILI9341_DGAMCTRL1 0xE2 /* [8.3.26] Digital Gamma Control 1 */
#define ILI9341_DGAMCTRL2 0xE3 /* [8.3.27] Digital Gamma Control 2 */
#define ILI9341_IFCTL 0xF6 /* [8.3.28] 16bits Data Format Selection */
/* Extended Commands --------------- [section] Description*/
#define ILI9341_PWCTRLA 0xCB /* [8.4.1] Power control A */
#define ILI9341_PWCTRLB 0xCF /* [8.4.2] Power control B */
#define ILI9341_TIMECTRLA_INT 0xE8 /* [8.4.3] Internal Clock Driver timing control A */
#define ILI9341_TIMECTRLA_EXT 0xE9 /* [8.4.4] External Clock Driver timing control A */
#define ILI9341_TIMECTRLB 0xEA /* [8.4.5] Driver timing control B (gate driver timing control) */
#define ILI9341_PWSEQCTRL 0xED /* [8.4.6] Power on sequence control */
#define ILI9341_GAM3CTRL 0xF2 /* [8.4.7] Enable 3 gamma control */
#define ILI9341_PUMPRATIO 0xF7 /* [8.4.8] Pump ratio control */
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static inline void ili9341_write(int mode, uint8_t data);
static inline void ili9341_write_array(int mode, uint8_t *data, uint16_t len);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Initialize the ILI9341 display controller
*/
void ili9341_init(void)
{
uint8_t data[15];
/* hardware reset */
LV_DRV_DISP_SPI_CS(1);
LV_DRV_DISP_CMD_DATA(ILI9341_DATA_MODE);
LV_DRV_DISP_RST(0);
LV_DRV_DELAY_US(50);
LV_DRV_DISP_RST(1);
LV_DRV_DELAY_MS(5);
/* software reset */
ili9341_write(ILI9341_CMD_MODE, ILI9341_SWRESET);
LV_DRV_DELAY_MS(5);
ili9341_write(ILI9341_CMD_MODE, ILI9341_DISPOFF);
/* startup sequence */
ili9341_write(ILI9341_CMD_MODE, ILI9341_PWCTRLB);
data[0] = 0x00;
data[1] = 0x83;
data[2] = 0x30;
ili9341_write_array(ILI9341_DATA_MODE, data, 3);
ili9341_write(ILI9341_CMD_MODE, ILI9341_PWSEQCTRL);
data[0] = 0x64;
data[1] = 0x03;
data[2] = 0x12;
data[3] = 0x81;
ili9341_write_array(ILI9341_DATA_MODE, data, 4);
ili9341_write(ILI9341_CMD_MODE, ILI9341_TIMECTRLA_INT);
data[0] = 0x85;
data[1] = 0x01;
data[2] = 0x79;
ili9341_write_array(ILI9341_DATA_MODE, data, 3);
ili9341_write(ILI9341_CMD_MODE, ILI9341_PWCTRLA);
data[0] = 0x39;
data[1] = 0x2c;
data[2] = 0x00;
data[3] = 0x34;
data[4] = 0x02;
ili9341_write_array(ILI9341_DATA_MODE, data, 5);
ili9341_write(ILI9341_CMD_MODE, ILI9341_PUMPRATIO);
ili9341_write(ILI9341_DATA_MODE, 0x20);
ili9341_write(ILI9341_CMD_MODE, ILI9341_TIMECTRLB);
data[0] = 0x00;
data[1] = 0x00;
ili9341_write_array(ILI9341_DATA_MODE, data, 2);
/* power control */
ili9341_write(ILI9341_CMD_MODE, ILI9341_PWCTRL1);
ili9341_write(ILI9341_DATA_MODE, 0x26);
ili9341_write(ILI9341_CMD_MODE, ILI9341_PWCTRL2);
ili9341_write(ILI9341_DATA_MODE, 0x11);
/* VCOM */
ili9341_write(ILI9341_CMD_MODE, ILI9341_VMCTRL1);
data[0] = 0x35;
data[1] = 0x3e;
ili9341_write_array(ILI9341_DATA_MODE, data, 2);
ili9341_write(ILI9341_CMD_MODE, ILI9341_VMCTRL2);
ili9341_write(ILI9341_DATA_MODE, 0xbe);
/* set orientation */
ili9341_rotate(0, ILI9341_BGR);
/* 16 bit pixel */
ili9341_write(ILI9341_CMD_MODE, ILI9341_PIXSET);
ili9341_write(ILI9341_DATA_MODE, 0x55);
/* frame rate */
ili9341_write(ILI9341_CMD_MODE, ILI9341_FRMCTR1);
data[0] = 0x00;
data[1] = 0x1b;
ili9341_write_array(ILI9341_DATA_MODE, data, 2);
#if ILI9341_GAMMA
/* gamma curve set */
ili9341_write(ILI9341_CMD_MODE, ILI9341_GAMSET);
ili9341_write(ILI9341_DATA_MODE, 0x01);
/* positive gamma correction */
ili9341_write(ILI9341_CMD_MODE, ILI9341_PGAMCTRL);
data[0] = 0x1f;
data[1] = 0x1a;
data[2] = 0x18;
data[3] = 0x0a;
data[4] = 0x0f;
data[5] = 0x06;
data[6] = 0x45;
data[7] = 0x87;
data[8] = 0x32;
data[9] = 0x0a;
data[10] = 0x07;
data[11] = 0x02;
data[12] = 0x07;
data[13] = 0x05;
data[14] = 0x00;
ili9341_write_array(ILI9341_DATA_MODE, data, 15);
/* negative gamma correction */
ili9341_write(ILI9341_CMD_MODE, ILI9341_NGAMCTRL);
data[0] = 0x00;
data[1] = 0x25;
data[2] = 0x27;
data[3] = 0x05;
data[4] = 0x10;
data[5] = 0x09;
data[6] = 0x3a;
data[7] = 0x78;
data[8] = 0x4d;
data[9] = 0x05;
data[10] = 0x18;
data[11] = 0x0d;
data[12] = 0x38;
data[13] = 0x3a;
data[14] = 0x1f;
ili9341_write_array(ILI9341_DATA_MODE, data, 15);
#endif
/* window horizontal */
ili9341_write(ILI9341_CMD_MODE, ILI9341_CASET);
data[0] = 0;
data[1] = 0;
data[2] = (ILI9341_HOR_RES - 1) >> 8;
data[3] = (ILI9341_HOR_RES - 1);
ili9341_write_array(ILI9341_DATA_MODE, data, 4);
/* window vertical */
ili9341_write(ILI9341_CMD_MODE, ILI9341_PASET);
data[0] = 0;
data[1] = 0;
data[2] = (ILI9341_VER_RES - 1) >> 8;
data[3] = (ILI9341_VER_RES - 1);
ili9341_write_array(ILI9341_DATA_MODE, data, 4);
ili9341_write(ILI9341_CMD_MODE, ILI9341_RAMWR);
#if ILI9341_TEARING
/* tearing effect off */
ili9341_write(ILI9341_CMD_MODE, ILI9341_TEOFF);
/* tearing effect on */
ili9341_write(ILI9341_CMD_MODE, ILI9341_TEON);
#endif
/* entry mode set */
ili9341_write(ILI9341_CMD_MODE, ILI9341_ETMOD);
ili9341_write(ILI9341_DATA_MODE, 0x07);
/* display function control */
ili9341_write(ILI9341_CMD_MODE, ILI9341_DISCTRL);
data[0] = 0x0a;
data[1] = 0x82;
data[2] = 0x27;
data[3] = 0x00;
ili9341_write_array(ILI9341_DATA_MODE, data, 4);
/* exit sleep mode */
ili9341_write(ILI9341_CMD_MODE, ILI9341_SLPOUT);
LV_DRV_DELAY_MS(100);
/* display on */
ili9341_write(ILI9341_CMD_MODE, ILI9341_DISPON);
LV_DRV_DELAY_MS(20);
}
void ili9341_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_p)
{
if(area->x2 < 0 || area->y2 < 0 || area->x1 > (ILI9341_HOR_RES - 1) || area->y1 > (ILI9341_VER_RES - 1)) {
lv_disp_flush_ready(drv);
return;
}
/* Truncate the area to the screen */
int32_t act_x1 = area->x1 < 0 ? 0 : area->x1;
int32_t act_y1 = area->y1 < 0 ? 0 : area->y1;
int32_t act_x2 = area->x2 > ILI9341_HOR_RES - 1 ? ILI9341_HOR_RES - 1 : area->x2;
int32_t act_y2 = area->y2 > ILI9341_VER_RES - 1 ? ILI9341_VER_RES - 1 : area->y2;
int32_t y;
uint8_t data[4];
int32_t len = len = (act_x2 - act_x1 + 1) * 2;
lv_coord_t w = (area->x2 - area->x1) + 1;
/* window horizontal */
ili9341_write(ILI9341_CMD_MODE, ILI9341_CASET);
data[0] = act_x1 >> 8;
data[1] = act_x1;
data[2] = act_x2 >> 8;
data[3] = act_x2;
ili9341_write_array(ILI9341_DATA_MODE, data, 4);
/* window vertical */
ili9341_write(ILI9341_CMD_MODE, ILI9341_PASET);
data[0] = act_y1 >> 8;
data[1] = act_y1;
data[2] = act_y2 >> 8;
data[3] = act_y2;
ili9341_write_array(ILI9341_DATA_MODE, data, 4);
ili9341_write(ILI9341_CMD_MODE, ILI9341_RAMWR);
for(y = act_y1; y <= act_y2; y++) {
ili9341_write_array(ILI9341_DATA_MODE, (uint8_t *)color_p, len);
color_p += w;
}
lv_disp_flush_ready(drv);
}
void ili9341_rotate(int degrees, bool bgr)
{
uint8_t color_order = MADCTL_RGB;
if(bgr)
color_order = MADCTL_BGR;
ili9341_write(ILI9341_CMD_MODE, ILI9341_MADCTL);
switch(degrees) {
case 270:
ili9341_write(ILI9341_DATA_MODE, MADCTL_MV | color_order);
break;
case 180:
ili9341_write(ILI9341_DATA_MODE, MADCTL_MY | color_order);
break;
case 90:
ili9341_write(ILI9341_DATA_MODE, MADCTL_MX | MADCTL_MY | MADCTL_MV | color_order);
break;
case 0:
/* fall-through */
default:
ili9341_write(ILI9341_DATA_MODE, MADCTL_MX | color_order);
break;
}
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Write byte
* @param mode sets command or data mode for write
* @param byte the byte to write
*/
static inline void ili9341_write(int mode, uint8_t data)
{
LV_DRV_DISP_CMD_DATA(mode);
LV_DRV_DISP_SPI_WR_BYTE(data);
}
/**
* Write byte array
* @param mode sets command or data mode for write
* @param data the byte array to write
* @param len the length of the byte array
*/
static inline void ili9341_write_array(int mode, uint8_t *data, uint16_t len)
{
LV_DRV_DISP_CMD_DATA(mode);
LV_DRV_DISP_SPI_WR_ARRAY(data, len);
}
#endif