Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
TFTLCD-Library/Adafruit_TFTLCD.cpp
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
1167 lines (1056 sloc)
28 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// IMPORTANT: LIBRARY MUST BE SPECIFICALLY CONFIGURED FOR EITHER TFT SHIELD | |
// OR BREAKOUT BOARD USAGE. SEE RELEVANT COMMENTS IN Adafruit_TFTLCD.h | |
// Graphics library by ladyada/adafruit with init code from Rossum | |
// MIT license | |
#if defined(__SAM3X8E__) | |
#include <include/pio.h> | |
#define PROGMEM | |
#define pgm_read_byte(addr) (*(const unsigned char *)(addr)) | |
#define pgm_read_word(addr) (*(const unsigned short *)(addr)) | |
#endif | |
#ifdef __AVR__ | |
#include <avr/pgmspace.h> | |
#endif | |
#include "Adafruit_TFTLCD.h" | |
#include "pin_magic.h" | |
#include "pins_arduino.h" | |
#include "wiring_private.h" | |
//#define TFTWIDTH 320 | |
//#define TFTHEIGHT 480 | |
#define TFTWIDTH 240 | |
#define TFTHEIGHT 320 | |
// LCD controller chip identifiers | |
#define ID_932X 0 | |
#define ID_7575 1 | |
#define ID_9341 2 | |
#define ID_HX8357D 3 | |
#define ID_UNKNOWN 0xFF | |
#include "registers.h" | |
// Constructor for breakout board (configurable LCD control lines). | |
// Can still use this w/shield, but parameters are ignored. | |
Adafruit_TFTLCD::Adafruit_TFTLCD(uint8_t cs, uint8_t cd, uint8_t wr, uint8_t rd, | |
uint8_t reset) | |
: Adafruit_GFX(TFTWIDTH, TFTHEIGHT) { | |
#ifndef USE_ADAFRUIT_SHIELD_PINOUT | |
// Convert pin numbers to registers and bitmasks | |
_reset = reset; | |
#ifdef __AVR__ | |
csPort = portOutputRegister(digitalPinToPort(cs)); | |
cdPort = portOutputRegister(digitalPinToPort(cd)); | |
wrPort = portOutputRegister(digitalPinToPort(wr)); | |
rdPort = portOutputRegister(digitalPinToPort(rd)); | |
#endif | |
#if defined(__SAM3X8E__) | |
csPort = digitalPinToPort(cs); | |
cdPort = digitalPinToPort(cd); | |
wrPort = digitalPinToPort(wr); | |
rdPort = digitalPinToPort(rd); | |
#endif | |
csPinSet = digitalPinToBitMask(cs); | |
cdPinSet = digitalPinToBitMask(cd); | |
wrPinSet = digitalPinToBitMask(wr); | |
rdPinSet = digitalPinToBitMask(rd); | |
csPinUnset = ~csPinSet; | |
cdPinUnset = ~cdPinSet; | |
wrPinUnset = ~wrPinSet; | |
rdPinUnset = ~rdPinSet; | |
#ifdef __AVR__ | |
*csPort |= csPinSet; // Set all control bits to HIGH (idle) | |
*cdPort |= cdPinSet; // Signals are ACTIVE LOW | |
*wrPort |= wrPinSet; | |
*rdPort |= rdPinSet; | |
#endif | |
#if defined(__SAM3X8E__) | |
csPort->PIO_SODR |= csPinSet; // Set all control bits to HIGH (idle) | |
cdPort->PIO_SODR |= cdPinSet; // Signals are ACTIVE LOW | |
wrPort->PIO_SODR |= wrPinSet; | |
rdPort->PIO_SODR |= rdPinSet; | |
#endif | |
pinMode(cs, OUTPUT); // Enable outputs | |
pinMode(cd, OUTPUT); | |
pinMode(wr, OUTPUT); | |
pinMode(rd, OUTPUT); | |
if (reset) { | |
digitalWrite(reset, HIGH); | |
pinMode(reset, OUTPUT); | |
} | |
#endif | |
init(); | |
} | |
// Constructor for shield (fixed LCD control lines) | |
Adafruit_TFTLCD::Adafruit_TFTLCD(void) : Adafruit_GFX(TFTWIDTH, TFTHEIGHT) { | |
init(); | |
} | |
// Initialization common to both shield & breakout configs | |
void Adafruit_TFTLCD::init(void) { | |
#ifdef USE_ADAFRUIT_SHIELD_PINOUT | |
CS_IDLE; // Set all control bits to idle state | |
WR_IDLE; | |
RD_IDLE; | |
CD_DATA; | |
digitalWrite(5, HIGH); // Reset line | |
pinMode(A3, OUTPUT); // Enable outputs | |
pinMode(A2, OUTPUT); | |
pinMode(A1, OUTPUT); | |
pinMode(A0, OUTPUT); | |
pinMode(5, OUTPUT); | |
#endif | |
setWriteDir(); // Set up LCD data port(s) for WRITE operations | |
rotation = 0; | |
cursor_y = cursor_x = 0; | |
textcolor = 0xFFFF; | |
_width = TFTWIDTH; | |
_height = TFTHEIGHT; | |
} | |
// Initialization command tables for different LCD controllers | |
#define TFTLCD_DELAY 0xFF | |
static const uint8_t HX8347G_regValues[] PROGMEM = { | |
0x2E, 0x89, 0x29, 0x8F, 0x2B, 0x02, 0xE2, 0x00, 0xE4, 0x01, 0xE5, 0x10, | |
0xE6, 0x01, 0xE7, 0x10, 0xE8, 0x70, 0xF2, 0x00, 0xEA, 0x00, 0xEB, 0x20, | |
0xEC, 0x3C, 0xED, 0xC8, 0xE9, 0x38, 0xF1, 0x01, | |
// skip gamma, do later | |
0x1B, 0x1A, 0x1A, 0x02, 0x24, 0x61, 0x25, 0x5C, | |
0x18, 0x36, 0x19, 0x01, 0x1F, 0x88, TFTLCD_DELAY, 5, // delay 5 ms | |
0x1F, 0x80, TFTLCD_DELAY, 5, 0x1F, 0x90, TFTLCD_DELAY, 5, 0x1F, 0xD4, | |
TFTLCD_DELAY, 5, 0x17, 0x05, | |
0x36, 0x09, 0x28, 0x38, TFTLCD_DELAY, 40, 0x28, 0x3C, | |
0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0xEF, 0x06, 0x00, 0x07, 0x00, | |
0x08, 0x01, 0x09, 0x3F}; | |
static const uint8_t HX8357D_regValues[] PROGMEM = { | |
HX8357_SWRESET, | |
0, | |
HX8357D_SETC, | |
3, | |
0xFF, | |
0x83, | |
0x57, | |
TFTLCD_DELAY, | |
250, | |
HX8357_SETRGB, | |
4, | |
0x00, | |
0x00, | |
0x06, | |
0x06, | |
HX8357D_SETCOM, | |
1, | |
0x25, // -1.52V | |
HX8357_SETOSC, | |
1, | |
0x68, // Normal mode 70Hz, Idle mode 55 Hz | |
HX8357_SETPANEL, | |
1, | |
0x05, // BGR, Gate direction swapped | |
HX8357_SETPWR1, | |
6, | |
0x00, | |
0x15, | |
0x1C, | |
0x1C, | |
0x83, | |
0xAA, | |
HX8357D_SETSTBA, | |
6, | |
0x50, | |
0x50, | |
0x01, | |
0x3C, | |
0x1E, | |
0x08, | |
// MEME GAMMA HERE | |
HX8357D_SETCYC, | |
7, | |
0x02, | |
0x40, | |
0x00, | |
0x2A, | |
0x2A, | |
0x0D, | |
0x78, | |
HX8357_COLMOD, | |
1, | |
0x55, | |
HX8357_MADCTL, | |
1, | |
0xC0, | |
HX8357_TEON, | |
1, | |
0x00, | |
HX8357_TEARLINE, | |
2, | |
0x00, | |
0x02, | |
HX8357_SLPOUT, | |
0, | |
TFTLCD_DELAY, | |
150, | |
HX8357_DISPON, | |
0, | |
TFTLCD_DELAY, | |
50, | |
}; | |
static const uint16_t ILI932x_regValues[] PROGMEM = { | |
ILI932X_START_OSC, | |
0x0001, // Start oscillator | |
TFTLCD_DELAY, | |
50, // 50 millisecond delay | |
ILI932X_DRIV_OUT_CTRL, | |
0x0100, | |
ILI932X_DRIV_WAV_CTRL, | |
0x0700, | |
ILI932X_ENTRY_MOD, | |
0x1030, | |
ILI932X_RESIZE_CTRL, | |
0x0000, | |
ILI932X_DISP_CTRL2, | |
0x0202, | |
ILI932X_DISP_CTRL3, | |
0x0000, | |
ILI932X_DISP_CTRL4, | |
0x0000, | |
ILI932X_RGB_DISP_IF_CTRL1, | |
0x0, | |
ILI932X_FRM_MARKER_POS, | |
0x0, | |
ILI932X_RGB_DISP_IF_CTRL2, | |
0x0, | |
ILI932X_POW_CTRL1, | |
0x0000, | |
ILI932X_POW_CTRL2, | |
0x0007, | |
ILI932X_POW_CTRL3, | |
0x0000, | |
ILI932X_POW_CTRL4, | |
0x0000, | |
TFTLCD_DELAY, | |
200, | |
ILI932X_POW_CTRL1, | |
0x1690, | |
ILI932X_POW_CTRL2, | |
0x0227, | |
TFTLCD_DELAY, | |
50, | |
ILI932X_POW_CTRL3, | |
0x001A, | |
TFTLCD_DELAY, | |
50, | |
ILI932X_POW_CTRL4, | |
0x1800, | |
ILI932X_POW_CTRL7, | |
0x002A, | |
TFTLCD_DELAY, | |
50, | |
ILI932X_GAMMA_CTRL1, | |
0x0000, | |
ILI932X_GAMMA_CTRL2, | |
0x0000, | |
ILI932X_GAMMA_CTRL3, | |
0x0000, | |
ILI932X_GAMMA_CTRL4, | |
0x0206, | |
ILI932X_GAMMA_CTRL5, | |
0x0808, | |
ILI932X_GAMMA_CTRL6, | |
0x0007, | |
ILI932X_GAMMA_CTRL7, | |
0x0201, | |
ILI932X_GAMMA_CTRL8, | |
0x0000, | |
ILI932X_GAMMA_CTRL9, | |
0x0000, | |
ILI932X_GAMMA_CTRL10, | |
0x0000, | |
ILI932X_GRAM_HOR_AD, | |
0x0000, | |
ILI932X_GRAM_VER_AD, | |
0x0000, | |
ILI932X_HOR_START_AD, | |
0x0000, | |
ILI932X_HOR_END_AD, | |
0x00EF, | |
ILI932X_VER_START_AD, | |
0X0000, | |
ILI932X_VER_END_AD, | |
0x013F, | |
ILI932X_GATE_SCAN_CTRL1, | |
0xA700, // Driver Output Control (R60h) | |
ILI932X_GATE_SCAN_CTRL2, | |
0x0003, // Driver Output Control (R61h) | |
ILI932X_GATE_SCAN_CTRL3, | |
0x0000, // Driver Output Control (R62h) | |
ILI932X_PANEL_IF_CTRL1, | |
0X0010, // Panel Interface Control 1 (R90h) | |
ILI932X_PANEL_IF_CTRL2, | |
0X0000, | |
ILI932X_PANEL_IF_CTRL3, | |
0X0003, | |
ILI932X_PANEL_IF_CTRL4, | |
0X1100, | |
ILI932X_PANEL_IF_CTRL5, | |
0X0000, | |
ILI932X_PANEL_IF_CTRL6, | |
0X0000, | |
ILI932X_DISP_CTRL1, | |
0x0133, // Main screen turn on | |
}; | |
void Adafruit_TFTLCD::begin(uint16_t id) { | |
uint8_t i = 0; | |
reset(); | |
delay(200); | |
if ((id == 0x9325) || (id == 0x9328)) { | |
uint16_t a, d; | |
driver = ID_932X; | |
CS_ACTIVE; | |
while (i < sizeof(ILI932x_regValues) / sizeof(uint16_t)) { | |
a = pgm_read_word(&ILI932x_regValues[i++]); | |
d = pgm_read_word(&ILI932x_regValues[i++]); | |
if (a == TFTLCD_DELAY) | |
delay(d); | |
else | |
writeRegister16(a, d); | |
} | |
setRotation(rotation); | |
setAddrWindow(0, 0, TFTWIDTH - 1, TFTHEIGHT - 1); | |
} else if (id == 0x9341) { | |
driver = ID_9341; | |
CS_ACTIVE; | |
writeRegister8(ILI9341_SOFTRESET, 0); | |
delay(50); | |
writeRegister8(ILI9341_DISPLAYOFF, 0); | |
writeRegister8(ILI9341_POWERCONTROL1, 0x23); | |
writeRegister8(ILI9341_POWERCONTROL2, 0x10); | |
writeRegister16(ILI9341_VCOMCONTROL1, 0x2B2B); | |
writeRegister8(ILI9341_VCOMCONTROL2, 0xC0); | |
writeRegister8(ILI9341_MEMCONTROL, ILI9341_MADCTL_MY | ILI9341_MADCTL_BGR); | |
writeRegister8(ILI9341_PIXELFORMAT, 0x55); | |
writeRegister16(ILI9341_FRAMECONTROL, 0x001B); | |
writeRegister8(ILI9341_ENTRYMODE, 0x07); | |
/* writeRegister32(ILI9341_DISPLAYFUNC, 0x0A822700);*/ | |
writeRegister8(ILI9341_SLEEPOUT, 0); | |
delay(150); | |
writeRegister8(ILI9341_DISPLAYON, 0); | |
delay(500); | |
setAddrWindow(0, 0, TFTWIDTH - 1, TFTHEIGHT - 1); | |
return; | |
} else if (id == 0x8357) { | |
// HX8357D | |
driver = ID_HX8357D; | |
CS_ACTIVE; | |
while (i < sizeof(HX8357D_regValues)) { | |
uint8_t r = pgm_read_byte(&HX8357D_regValues[i++]); | |
uint8_t len = pgm_read_byte(&HX8357D_regValues[i++]); | |
if (r == TFTLCD_DELAY) { | |
delay(len); | |
} else { | |
// Serial.print("Register $"); Serial.print(r, HEX); | |
// Serial.print(" datalen "); Serial.println(len); | |
CS_ACTIVE; | |
CD_COMMAND; | |
write8(r); | |
CD_DATA; | |
for (uint8_t d = 0; d < len; d++) { | |
uint8_t x = pgm_read_byte(&HX8357D_regValues[i++]); | |
write8(x); | |
} | |
CS_IDLE; | |
} | |
} | |
return; | |
} else if (id == 0x7575) { | |
uint8_t a, d; | |
driver = ID_7575; | |
CS_ACTIVE; | |
while (i < sizeof(HX8347G_regValues)) { | |
a = pgm_read_byte(&HX8347G_regValues[i++]); | |
d = pgm_read_byte(&HX8347G_regValues[i++]); | |
if (a == TFTLCD_DELAY) | |
delay(d); | |
else | |
writeRegister8(a, d); | |
} | |
setRotation(rotation); | |
setLR(); // Lower-right corner of address window | |
} else { | |
driver = ID_UNKNOWN; | |
return; | |
} | |
} | |
void Adafruit_TFTLCD::reset(void) { | |
CS_IDLE; | |
// CD_DATA; | |
WR_IDLE; | |
RD_IDLE; | |
#ifdef USE_ADAFRUIT_SHIELD_PINOUT | |
digitalWrite(5, LOW); | |
delay(2); | |
digitalWrite(5, HIGH); | |
#else | |
if (_reset) { | |
digitalWrite(_reset, LOW); | |
delay(2); | |
digitalWrite(_reset, HIGH); | |
} | |
#endif | |
// Data transfer sync | |
CS_ACTIVE; | |
CD_COMMAND; | |
write8(0x00); | |
for (uint8_t i = 0; i < 3; i++) | |
WR_STROBE; // Three extra 0x00s | |
CS_IDLE; | |
} | |
// Sets the LCD address window (and address counter, on 932X). | |
// Relevant to rect/screen fills and H/V lines. Input coordinates are | |
// assumed pre-sorted (e.g. x2 >= x1). | |
void Adafruit_TFTLCD::setAddrWindow(int x1, int y1, int x2, int y2) { | |
CS_ACTIVE; | |
if (driver == ID_932X) { | |
// Values passed are in current (possibly rotated) coordinate | |
// system. 932X requires hardware-native coords regardless of | |
// MADCTL, so rotate inputs as needed. The address counter is | |
// set to the top-left corner -- although fill operations can be | |
// done in any direction, the current screen rotation is applied | |
// because some users find it disconcerting when a fill does not | |
// occur top-to-bottom. | |
int x, y, t; | |
switch (rotation) { | |
default: | |
x = x1; | |
y = y1; | |
break; | |
case 1: | |
t = y1; | |
y1 = x1; | |
x1 = TFTWIDTH - 1 - y2; | |
y2 = x2; | |
x2 = TFTWIDTH - 1 - t; | |
x = x2; | |
y = y1; | |
break; | |
case 2: | |
t = x1; | |
x1 = TFTWIDTH - 1 - x2; | |
x2 = TFTWIDTH - 1 - t; | |
t = y1; | |
y1 = TFTHEIGHT - 1 - y2; | |
y2 = TFTHEIGHT - 1 - t; | |
x = x2; | |
y = y2; | |
break; | |
case 3: | |
t = x1; | |
x1 = y1; | |
y1 = TFTHEIGHT - 1 - x2; | |
x2 = y2; | |
y2 = TFTHEIGHT - 1 - t; | |
x = x1; | |
y = y2; | |
break; | |
} | |
writeRegister16(0x0050, x1); // Set address window | |
writeRegister16(0x0051, x2); | |
writeRegister16(0x0052, y1); | |
writeRegister16(0x0053, y2); | |
writeRegister16(0x0020, x); // Set address counter to top left | |
writeRegister16(0x0021, y); | |
} else if (driver == ID_7575) { | |
writeRegisterPair(HX8347G_COLADDRSTART_HI, HX8347G_COLADDRSTART_LO, x1); | |
writeRegisterPair(HX8347G_ROWADDRSTART_HI, HX8347G_ROWADDRSTART_LO, y1); | |
writeRegisterPair(HX8347G_COLADDREND_HI, HX8347G_COLADDREND_LO, x2); | |
writeRegisterPair(HX8347G_ROWADDREND_HI, HX8347G_ROWADDREND_LO, y2); | |
} else if ((driver == ID_9341) || (driver == ID_HX8357D)) { | |
uint32_t t; | |
t = x1; | |
t <<= 16; | |
t |= x2; | |
writeRegister32(ILI9341_COLADDRSET, t); // HX8357D uses same registers! | |
t = y1; | |
t <<= 16; | |
t |= y2; | |
writeRegister32(ILI9341_PAGEADDRSET, t); // HX8357D uses same registers! | |
} | |
CS_IDLE; | |
} | |
// Unlike the 932X drivers that set the address window to the full screen | |
// by default (using the address counter for drawPixel operations), the | |
// 7575 needs the address window set on all graphics operations. In order | |
// to save a few register writes on each pixel drawn, the lower-right | |
// corner of the address window is reset after most fill operations, so | |
// that drawPixel only needs to change the upper left each time. | |
void Adafruit_TFTLCD::setLR(void) { | |
CS_ACTIVE; | |
writeRegisterPair(HX8347G_COLADDREND_HI, HX8347G_COLADDREND_LO, _width - 1); | |
writeRegisterPair(HX8347G_ROWADDREND_HI, HX8347G_ROWADDREND_LO, _height - 1); | |
CS_IDLE; | |
} | |
// Fast block fill operation for fillScreen, fillRect, H/V line, etc. | |
// Requires setAddrWindow() has previously been called to set the fill | |
// bounds. 'len' is inclusive, MUST be >= 1. | |
void Adafruit_TFTLCD::flood(uint16_t color, uint32_t len) { | |
uint16_t blocks; | |
uint8_t i, hi = color >> 8, lo = color; | |
CS_ACTIVE; | |
CD_COMMAND; | |
if (driver == ID_9341) { | |
write8(0x2C); | |
} else if (driver == ID_932X) { | |
write8(0x00); // High byte of GRAM register... | |
write8(0x22); // Write data to GRAM | |
} else if (driver == ID_HX8357D) { | |
write8(HX8357_RAMWR); | |
} else { | |
write8(0x22); // Write data to GRAM | |
} | |
// Write first pixel normally, decrement counter by 1 | |
CD_DATA; | |
write8(hi); | |
write8(lo); | |
len--; | |
blocks = (uint16_t)(len / 64); // 64 pixels/block | |
if (hi == lo) { | |
// High and low bytes are identical. Leave prior data | |
// on the port(s) and just toggle the write strobe. | |
while (blocks--) { | |
i = 16; // 64 pixels/block / 4 pixels/pass | |
do { | |
WR_STROBE; | |
WR_STROBE; | |
WR_STROBE; | |
WR_STROBE; // 2 bytes/pixel | |
WR_STROBE; | |
WR_STROBE; | |
WR_STROBE; | |
WR_STROBE; // x 4 pixels | |
} while (--i); | |
} | |
// Fill any remaining pixels (1 to 64) | |
for (i = (uint8_t)len & 63; i--;) { | |
WR_STROBE; | |
WR_STROBE; | |
} | |
} else { | |
while (blocks--) { | |
i = 16; // 64 pixels/block / 4 pixels/pass | |
do { | |
write8(hi); | |
write8(lo); | |
write8(hi); | |
write8(lo); | |
write8(hi); | |
write8(lo); | |
write8(hi); | |
write8(lo); | |
} while (--i); | |
} | |
for (i = (uint8_t)len & 63; i--;) { | |
write8(hi); | |
write8(lo); | |
} | |
} | |
CS_IDLE; | |
} | |
void Adafruit_TFTLCD::drawFastHLine(int16_t x, int16_t y, int16_t length, | |
uint16_t color) { | |
int16_t x2; | |
// Initial off-screen clipping | |
if ((length <= 0) || (y < 0) || (y >= _height) || (x >= _width) || | |
((x2 = (x + length - 1)) < 0)) | |
return; | |
if (x < 0) { // Clip left | |
length += x; | |
x = 0; | |
} | |
if (x2 >= _width) { // Clip right | |
x2 = _width - 1; | |
length = x2 - x + 1; | |
} | |
setAddrWindow(x, y, x2, y); | |
flood(color, length); | |
if (driver == ID_932X) | |
setAddrWindow(0, 0, _width - 1, _height - 1); | |
else | |
setLR(); | |
} | |
void Adafruit_TFTLCD::drawFastVLine(int16_t x, int16_t y, int16_t length, | |
uint16_t color) { | |
int16_t y2; | |
// Initial off-screen clipping | |
if ((length <= 0) || (x < 0) || (x >= _width) || (y >= _height) || | |
((y2 = (y + length - 1)) < 0)) | |
return; | |
if (y < 0) { // Clip top | |
length += y; | |
y = 0; | |
} | |
if (y2 >= _height) { // Clip bottom | |
y2 = _height - 1; | |
length = y2 - y + 1; | |
} | |
setAddrWindow(x, y, x, y2); | |
flood(color, length); | |
if (driver == ID_932X) | |
setAddrWindow(0, 0, _width - 1, _height - 1); | |
else | |
setLR(); | |
} | |
void Adafruit_TFTLCD::fillRect(int16_t x1, int16_t y1, int16_t w, int16_t h, | |
uint16_t fillcolor) { | |
int16_t x2, y2; | |
// Initial off-screen clipping | |
if ((w <= 0) || (h <= 0) || (x1 >= _width) || (y1 >= _height) || | |
((x2 = x1 + w - 1) < 0) || ((y2 = y1 + h - 1) < 0)) | |
return; | |
if (x1 < 0) { // Clip left | |
w += x1; | |
x1 = 0; | |
} | |
if (y1 < 0) { // Clip top | |
h += y1; | |
y1 = 0; | |
} | |
if (x2 >= _width) { // Clip right | |
x2 = _width - 1; | |
w = x2 - x1 + 1; | |
} | |
if (y2 >= _height) { // Clip bottom | |
y2 = _height - 1; | |
h = y2 - y1 + 1; | |
} | |
setAddrWindow(x1, y1, x2, y2); | |
flood(fillcolor, (uint32_t)w * (uint32_t)h); | |
if (driver == ID_932X) | |
setAddrWindow(0, 0, _width - 1, _height - 1); | |
else | |
setLR(); | |
} | |
void Adafruit_TFTLCD::fillScreen(uint16_t color) { | |
if (driver == ID_932X) { | |
// For the 932X, a full-screen address window is already the default | |
// state, just need to set the address pointer to the top-left corner. | |
// Although we could fill in any direction, the code uses the current | |
// screen rotation because some users find it disconcerting when a | |
// fill does not occur top-to-bottom. | |
uint16_t x, y; | |
switch (rotation) { | |
default: | |
x = 0; | |
y = 0; | |
break; | |
case 1: | |
x = TFTWIDTH - 1; | |
y = 0; | |
break; | |
case 2: | |
x = TFTWIDTH - 1; | |
y = TFTHEIGHT - 1; | |
break; | |
case 3: | |
x = 0; | |
y = TFTHEIGHT - 1; | |
break; | |
} | |
CS_ACTIVE; | |
writeRegister16(0x0020, x); | |
writeRegister16(0x0021, y); | |
} else if ((driver == ID_9341) || (driver == ID_7575) || | |
(driver == ID_HX8357D)) { | |
// For these, there is no settable address pointer, instead the | |
// address window must be set for each drawing operation. However, | |
// this display takes rotation into account for the parameters, no | |
// need to do extra rotation math here. | |
setAddrWindow(0, 0, _width - 1, _height - 1); | |
} | |
flood(color, (long)TFTWIDTH * (long)TFTHEIGHT); | |
} | |
void Adafruit_TFTLCD::drawPixel(int16_t x, int16_t y, uint16_t color) { | |
// Clip | |
if ((x < 0) || (y < 0) || (x >= _width) || (y >= _height)) | |
return; | |
CS_ACTIVE; | |
if (driver == ID_932X) { | |
int16_t t; | |
switch (rotation) { | |
case 1: | |
t = x; | |
x = TFTWIDTH - 1 - y; | |
y = t; | |
break; | |
case 2: | |
x = TFTWIDTH - 1 - x; | |
y = TFTHEIGHT - 1 - y; | |
break; | |
case 3: | |
t = x; | |
x = y; | |
y = TFTHEIGHT - 1 - t; | |
break; | |
} | |
writeRegister16(0x0020, x); | |
writeRegister16(0x0021, y); | |
writeRegister16(0x0022, color); | |
} else if (driver == ID_7575) { | |
uint8_t hi, lo; | |
switch (rotation) { | |
default: | |
lo = 0; | |
break; | |
case 1: | |
lo = 0x60; | |
break; | |
case 2: | |
lo = 0xc0; | |
break; | |
case 3: | |
lo = 0xa0; | |
break; | |
} | |
writeRegister8(HX8347G_MEMACCESS, lo); | |
// Only upper-left is set -- bottom-right is full screen default | |
writeRegisterPair(HX8347G_COLADDRSTART_HI, HX8347G_COLADDRSTART_LO, x); | |
writeRegisterPair(HX8347G_ROWADDRSTART_HI, HX8347G_ROWADDRSTART_LO, y); | |
hi = color >> 8; | |
lo = color; | |
CD_COMMAND; | |
write8(0x22); | |
CD_DATA; | |
write8(hi); | |
write8(lo); | |
} else if ((driver == ID_9341) || (driver == ID_HX8357D)) { | |
setAddrWindow(x, y, _width - 1, _height - 1); | |
CS_ACTIVE; | |
CD_COMMAND; | |
write8(0x2C); | |
CD_DATA; | |
write8(color >> 8); | |
write8(color); | |
} | |
CS_IDLE; | |
} | |
// Issues 'raw' an array of 16-bit color values to the LCD; used | |
// externally by BMP examples. Assumes that setWindowAddr() has | |
// previously been set to define the bounds. Max 255 pixels at | |
// a time (BMP examples read in small chunks due to limited RAM). | |
void Adafruit_TFTLCD::pushColors(uint16_t *data, uint8_t len, boolean first) { | |
uint16_t color; | |
uint8_t hi, lo; | |
CS_ACTIVE; | |
if (first == true) { // Issue GRAM write command only on first call | |
CD_COMMAND; | |
if (driver == ID_932X) | |
write8(0x00); | |
if ((driver == ID_9341) || (driver == ID_HX8357D)) { | |
write8(0x2C); | |
} else { | |
write8(0x22); | |
} | |
} | |
CD_DATA; | |
while (len--) { | |
color = *data++; | |
hi = color >> 8; // Don't simplify or merge these | |
lo = color; // lines, there's macro shenanigans | |
write8(hi); // going on. | |
write8(lo); | |
} | |
CS_IDLE; | |
} | |
void Adafruit_TFTLCD::setRotation(uint8_t x) { | |
// Call parent rotation func first -- sets up rotation flags, etc. | |
Adafruit_GFX::setRotation(x); | |
// Then perform hardware-specific rotation operations... | |
CS_ACTIVE; | |
if (driver == ID_932X) { | |
uint16_t t; | |
switch (rotation) { | |
default: | |
t = 0x1030; | |
break; | |
case 1: | |
t = 0x1028; | |
break; | |
case 2: | |
t = 0x1000; | |
break; | |
case 3: | |
t = 0x1018; | |
break; | |
} | |
writeRegister16(0x0003, t); // MADCTL | |
// For 932X, init default full-screen address window: | |
setAddrWindow(0, 0, _width - 1, _height - 1); // CS_IDLE happens here | |
} | |
if (driver == ID_7575) { | |
uint8_t t; | |
switch (rotation) { | |
default: | |
t = 0; | |
break; | |
case 1: | |
t = 0x60; | |
break; | |
case 2: | |
t = 0xc0; | |
break; | |
case 3: | |
t = 0xa0; | |
break; | |
} | |
writeRegister8(HX8347G_MEMACCESS, t); | |
// 7575 has to set the address window on most drawing operations. | |
// drawPixel() cheats by setting only the top left...by default, | |
// the lower right is always reset to the corner. | |
setLR(); // CS_IDLE happens here | |
} | |
if (driver == ID_9341) { | |
// MEME, HX8357D uses same registers as 9341 but different values | |
uint16_t t = 0; | |
switch (rotation) { | |
case 2: | |
t = ILI9341_MADCTL_MX | ILI9341_MADCTL_BGR; | |
break; | |
case 3: | |
t = ILI9341_MADCTL_MV | ILI9341_MADCTL_BGR; | |
break; | |
case 0: | |
t = ILI9341_MADCTL_MY | ILI9341_MADCTL_BGR; | |
break; | |
case 1: | |
t = ILI9341_MADCTL_MX | ILI9341_MADCTL_MY | ILI9341_MADCTL_MV | | |
ILI9341_MADCTL_BGR; | |
break; | |
} | |
writeRegister8(ILI9341_MADCTL, t); // MADCTL | |
// For 9341, init default full-screen address window: | |
setAddrWindow(0, 0, _width - 1, _height - 1); // CS_IDLE happens here | |
} | |
if (driver == ID_HX8357D) { | |
// MEME, HX8357D uses same registers as 9341 but different values | |
uint16_t t = 0; | |
switch (rotation) { | |
case 2: | |
t = HX8357B_MADCTL_RGB; | |
break; | |
case 3: | |
t = HX8357B_MADCTL_MX | HX8357B_MADCTL_MV | HX8357B_MADCTL_RGB; | |
break; | |
case 0: | |
t = HX8357B_MADCTL_MX | HX8357B_MADCTL_MY | HX8357B_MADCTL_RGB; | |
break; | |
case 1: | |
t = HX8357B_MADCTL_MY | HX8357B_MADCTL_MV | HX8357B_MADCTL_RGB; | |
break; | |
} | |
writeRegister8(ILI9341_MADCTL, t); // MADCTL | |
// For 8357, init default full-screen address window: | |
setAddrWindow(0, 0, _width - 1, _height - 1); // CS_IDLE happens here | |
} | |
} | |
#ifdef read8isFunctionalized | |
#define read8(x) x = read8fn() | |
#endif | |
// Because this function is used infrequently, it configures the ports for | |
// the read operation, reads the data, then restores the ports to the write | |
// configuration. Write operations happen a LOT, so it's advantageous to | |
// leave the ports in that state as a default. | |
uint16_t Adafruit_TFTLCD::readPixel(int16_t x, int16_t y) { | |
if ((x < 0) || (y < 0) || (x >= _width) || (y >= _height)) | |
return 0; | |
CS_ACTIVE; | |
if (driver == ID_932X) { | |
uint8_t hi, lo; | |
int16_t t; | |
switch (rotation) { | |
case 1: | |
t = x; | |
x = TFTWIDTH - 1 - y; | |
y = t; | |
break; | |
case 2: | |
x = TFTWIDTH - 1 - x; | |
y = TFTHEIGHT - 1 - y; | |
break; | |
case 3: | |
t = x; | |
x = y; | |
y = TFTHEIGHT - 1 - t; | |
break; | |
} | |
writeRegister16(0x0020, x); | |
writeRegister16(0x0021, y); | |
// Inexplicable thing: sometimes pixel read has high/low bytes | |
// reversed. A second read fixes this. Unsure of reason. Have | |
// tried adjusting timing in read8() etc. to no avail. | |
for (uint8_t pass = 0; pass < 2; pass++) { | |
CD_COMMAND; | |
write8(0x00); | |
write8(0x22); // Read data from GRAM | |
CD_DATA; | |
setReadDir(); // Set up LCD data port(s) for READ operations | |
read8(hi); // First 2 bytes back are a dummy read | |
read8(hi); | |
read8(hi); // Bytes 3, 4 are actual pixel value | |
read8(lo); | |
setWriteDir(); // Restore LCD data port(s) to WRITE configuration | |
} | |
CS_IDLE; | |
return ((uint16_t)hi << 8) | lo; | |
} else if (driver == ID_7575) { | |
uint8_t r, g, b; | |
writeRegisterPair(HX8347G_COLADDRSTART_HI, HX8347G_COLADDRSTART_LO, x); | |
writeRegisterPair(HX8347G_ROWADDRSTART_HI, HX8347G_ROWADDRSTART_LO, y); | |
CD_COMMAND; | |
write8(0x22); // Read data from GRAM | |
setReadDir(); // Set up LCD data port(s) for READ operations | |
CD_DATA; | |
read8(r); // First byte back is a dummy read | |
read8(r); | |
read8(g); | |
read8(b); | |
setWriteDir(); // Restore LCD data port(s) to WRITE configuration | |
CS_IDLE; | |
return (((uint16_t)r & B11111000) << 8) | (((uint16_t)g & B11111100) << 3) | | |
(b >> 3); | |
} else | |
return 0; | |
} | |
// Ditto with the read/write port directions, as above. | |
uint16_t Adafruit_TFTLCD::readID(void) { | |
uint16_t id; | |
// retry a bunch! | |
for (int i = 0; i < 5; i++) { | |
id = (uint16_t)readReg(0xD3); | |
delayMicroseconds(50); | |
if (id == 0x9341) { | |
return id; | |
} | |
} | |
uint8_t hi, lo; | |
/* | |
for (uint8_t i=0; i<128; i++) { | |
Serial.print("$"); Serial.print(i, HEX); | |
Serial.print(" = 0x"); Serial.println(readReg(i), HEX); | |
} | |
*/ | |
if (readReg(0x04) == 0x8000) { // eh close enough | |
// setc! | |
/* | |
Serial.println("!"); | |
for (uint8_t i=0; i<254; i++) { | |
Serial.print("$"); Serial.print(i, HEX); | |
Serial.print(" = 0x"); Serial.println(readReg(i), HEX); | |
} | |
*/ | |
writeRegister24(HX8357D_SETC, 0xFF8357); | |
delay(300); | |
// Serial.println(readReg(0xD0), HEX); | |
if (readReg(0xD0) == 0x990000) { | |
return 0x8357; | |
} | |
} | |
CS_ACTIVE; | |
CD_COMMAND; | |
write8(0x00); | |
WR_STROBE; // Repeat prior byte (0x00) | |
setReadDir(); // Set up LCD data port(s) for READ operations | |
CD_DATA; | |
read8(hi); | |
read8(lo); | |
setWriteDir(); // Restore LCD data port(s) to WRITE configuration | |
CS_IDLE; | |
id = hi; | |
id <<= 8; | |
id |= lo; | |
return id; | |
} | |
uint32_t Adafruit_TFTLCD::readReg(uint8_t r) { | |
uint32_t id; | |
uint8_t x; | |
// try reading register #4 | |
CS_ACTIVE; | |
CD_COMMAND; | |
write8(r); | |
setReadDir(); // Set up LCD data port(s) for READ operations | |
CD_DATA; | |
delayMicroseconds(50); | |
read8(x); | |
id = x; // Do not merge or otherwise simplify | |
id <<= 8; // these lines. It's an unfortunate | |
read8(x); | |
id |= x; // shenanigans that are going on. | |
id <<= 8; // these lines. It's an unfortunate | |
read8(x); | |
id |= x; // shenanigans that are going on. | |
id <<= 8; // these lines. It's an unfortunate | |
read8(x); | |
id |= x; // shenanigans that are going on. | |
CS_IDLE; | |
setWriteDir(); // Restore LCD data port(s) to WRITE configuration | |
// Serial.print("Read $"); Serial.print(r, HEX); | |
// Serial.print(":\t0x"); Serial.println(id, HEX); | |
return id; | |
} | |
// Pass 8-bit (each) R,G,B, get back 16-bit packed color | |
uint16_t Adafruit_TFTLCD::color565(uint8_t r, uint8_t g, uint8_t b) { | |
return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3); | |
} | |
// For I/O macros that were left undefined, declare function | |
// versions that reference the inline macros just once: | |
#ifndef write8 | |
void Adafruit_TFTLCD::write8(uint8_t value) { write8inline(value); } | |
#endif | |
#ifdef read8isFunctionalized | |
uint8_t Adafruit_TFTLCD::read8fn(void) { | |
uint8_t result; | |
read8inline(result); | |
return result; | |
} | |
#endif | |
#ifndef setWriteDir | |
void Adafruit_TFTLCD::setWriteDir(void) { setWriteDirInline(); } | |
#endif | |
#ifndef setReadDir | |
void Adafruit_TFTLCD::setReadDir(void) { setReadDirInline(); } | |
#endif | |
#ifndef writeRegister8 | |
void Adafruit_TFTLCD::writeRegister8(uint8_t a, uint8_t d) { | |
writeRegister8inline(a, d); | |
} | |
#endif | |
#ifndef writeRegister16 | |
void Adafruit_TFTLCD::writeRegister16(uint16_t a, uint16_t d) { | |
writeRegister16inline(a, d); | |
} | |
#endif | |
#ifndef writeRegisterPair | |
void Adafruit_TFTLCD::writeRegisterPair(uint8_t aH, uint8_t aL, uint16_t d) { | |
writeRegisterPairInline(aH, aL, d); | |
} | |
#endif | |
void Adafruit_TFTLCD::writeRegister24(uint8_t r, uint32_t d) { | |
CS_ACTIVE; | |
CD_COMMAND; | |
write8(r); | |
CD_DATA; | |
delayMicroseconds(10); | |
write8(d >> 16); | |
delayMicroseconds(10); | |
write8(d >> 8); | |
delayMicroseconds(10); | |
write8(d); | |
CS_IDLE; | |
} | |
void Adafruit_TFTLCD::writeRegister32(uint8_t r, uint32_t d) { | |
CS_ACTIVE; | |
CD_COMMAND; | |
write8(r); | |
CD_DATA; | |
delayMicroseconds(10); | |
write8(d >> 24); | |
delayMicroseconds(10); | |
write8(d >> 16); | |
delayMicroseconds(10); | |
write8(d >> 8); | |
delayMicroseconds(10); | |
write8(d); | |
CS_IDLE; | |
} |