Looking at adding PJRC optimized display code to the PSDR. But his chip is rather different (no hardware FIFO) so I'll need to implement the changes with DMA I think, I'm sure it will work well, but it's going to be some work.... Update to block diagram. Also, finally getting to work on next rev of hardware!!

This commit is contained in:
Michael Colton 2014-08-23 14:08:17 -06:00
parent 03b0cfa0d4
commit 3d31149c20
5 changed files with 1164 additions and 0 deletions

Binary file not shown.

Binary file not shown.

252
Source/include/ILI9341_t3.h Normal file
View file

@ -0,0 +1,252 @@
// https://github.com/PaulStoffregen/ILI9341_t3
// http://forum.pjrc.com/threads/26305-Highly-optimized-ILI9341-(320x240-TFT-color-display)-library
/***************************************************
This is our library for the Adafruit ILI9341 Breakout and Shield
----> http://www.adafruit.com/products/1651
Check out the links above for our tutorials and wiring diagrams
These displays use SPI to communicate, 4 or 5 pins are required to
interface (RST is optional)
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
MIT license, all text above must be included in any redistribution
****************************************************/
#ifndef _ILI9341_t3H_
#define _ILI9341_t3H_
//#include "Arduino.h"
#include "stdbool.h"
#define ILI9341_TFTWIDTH 240
#define ILI9341_TFTHEIGHT 320
#define ILI9341_NOP 0x00
#define ILI9341_SWRESET 0x01
#define ILI9341_RDDID 0x04
#define ILI9341_RDDST 0x09
#define ILI9341_SLPIN 0x10
#define ILI9341_SLPOUT 0x11
#define ILI9341_PTLON 0x12
#define ILI9341_NORON 0x13
#define ILI9341_RDMODE 0x0A
#define ILI9341_RDMADCTL 0x0B
#define ILI9341_RDPIXFMT 0x0C
#define ILI9341_RDIMGFMT 0x0A
#define ILI9341_RDSELFDIAG 0x0F
#define ILI9341_INVOFF 0x20
#define ILI9341_INVON 0x21
#define ILI9341_GAMMASET 0x26
#define ILI9341_DISPOFF 0x28
#define ILI9341_DISPON 0x29
#define ILI9341_CASET 0x2A
#define ILI9341_PASET 0x2B
#define ILI9341_RAMWR 0x2C
#define ILI9341_RAMRD 0x2E
#define ILI9341_PTLAR 0x30
#define ILI9341_MADCTL 0x36
#define ILI9341_PIXFMT 0x3A
#define ILI9341_FRMCTR1 0xB1
#define ILI9341_FRMCTR2 0xB2
#define ILI9341_FRMCTR3 0xB3
#define ILI9341_INVCTR 0xB4
#define ILI9341_DFUNCTR 0xB6
#define ILI9341_PWCTR1 0xC0
#define ILI9341_PWCTR2 0xC1
#define ILI9341_PWCTR3 0xC2
#define ILI9341_PWCTR4 0xC3
#define ILI9341_PWCTR5 0xC4
#define ILI9341_VMCTR1 0xC5
#define ILI9341_VMCTR2 0xC7
#define ILI9341_RDID1 0xDA
#define ILI9341_RDID2 0xDB
#define ILI9341_RDID3 0xDC
#define ILI9341_RDID4 0xDD
#define ILI9341_GMCTRP1 0xE0
#define ILI9341_GMCTRN1 0xE1
/*
#define ILI9341_PWCTR6 0xFC
*/
// Color definitions
#define ILI9341_BLACK 0x0000
#define ILI9341_BLUE 0x001F
#define ILI9341_RED 0xF800
#define ILI9341_GREEN 0x07E0
#define ILI9341_CYAN 0x07FF
#define ILI9341_MAGENTA 0xF81F
#define ILI9341_YELLOW 0xFFE0
#define ILI9341_WHITE 0xFFFF
//class ILI9341_t3 : public Print
//{
// public:
ILI9341_t3(uint8_t _CS, uint8_t _DC, uint8_t _RST = 255);
void begin(void);
void pushColor(uint16_t color);
void fillScreen(uint16_t color);
void drawPixel(int16_t x, int16_t y, uint16_t color);
void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
void setRotation(uint8_t r);
void invertDisplay(bool i);
void setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
// Pass 8-bit (each) R,G,B, get back 16-bit packed color
static uint16_t color565(uint8_t r, uint8_t g, uint8_t b) {
return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
}
//uint8_t readdata(void);
uint8_t readcommand8(uint8_t reg, uint8_t index = 0);
// KJE Added functions to read pixel data...
uint16_t readPixel(int16_t x, int16_t y);
// from Adafruit_GFX.h
void drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color);
void drawCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, uint16_t color);
void fillCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color);
void fillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, int16_t delta, uint16_t color);
void drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color);
void fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color);
void drawRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h, int16_t radius, uint16_t color);
void fillRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h, int16_t radius, uint16_t color);
void drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color);
void drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t bg, uint8_t size);
void setCursor(int16_t x, int16_t y);
void setTextColor(uint16_t c);
void setTextColor(uint16_t c, uint16_t bg);
void setTextSize(uint8_t s);
void setTextWrap(bool w);
virtual size_t write(uint8_t);
int16_t width(void) { return _width; }
int16_t height(void) { return _height; }
uint8_t getRotation(void);
void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color);
void drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
// protected:
int16_t
_width, _height, // Display w/h as modified by current rotation
cursor_x, cursor_y;
uint16_t
textcolor, textbgcolor;
uint8_t
textsize,
rotation;
bool
wrap; // If set, 'wrap' text at right edge of display
// private:
uint8_t _rst;
uint8_t _cs, _dc;
uint8_t pcs_data, pcs_command;
void setAddr(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
__attribute__((always_inline)) {
writecommand_cont(ILI9341_CASET); // Column addr set
writedata16_cont(x0); // XSTART
writedata16_cont(x1); // XEND
writecommand_cont(ILI9341_PASET); // Row addr set
writedata16_cont(y0); // YSTART
writedata16_cont(y1); // YEND
}
//void waitFifoNotFull(void) __attribute__((always_inline)) {
void waitFifoNotFull(void) {
uint32_t sr;
uint32_t tmp __attribute__((unused));
do {
sr = SPI0.SR;
if (sr & 0xF0) tmp = SPI0_POPR; // drain RX FIFO
} while ((sr & (15 << 12)) > (3 << 12));
}
//void waitFifoEmpty(void) __attribute__((always_inline)) {
void waitFifoEmpty(void) {
uint32_t sr;
uint32_t tmp __attribute__((unused));
do {
sr = SPI0.SR;
if (sr & 0xF0) tmp = SPI0_POPR; // drain RX FIFO
} while ((sr & 0xF0F0) > 0); // wait both RX & TX empty
}
void waitTransmitComplete(void) __attribute__((always_inline)) {
uint32_t tmp __attribute__((unused));
while (!(SPI0.SR & SPI_SR_TCF)) ; // wait until final output done
tmp = SPI0_POPR; // drain the final RX FIFO word
}
void writecommand_cont(uint8_t c) __attribute__((always_inline)) {
SPI0.PUSHR = c | (pcs_command << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT;
waitFifoNotFull();
}
void writedata8_cont(uint8_t c) __attribute__((always_inline)) {
SPI0.PUSHR = c | (pcs_data << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT;
waitFifoNotFull();
}
void writedata16_cont(uint16_t d) __attribute__((always_inline)) {
SPI0.PUSHR = d | (pcs_data << 16) | SPI_PUSHR_CTAS(1) | SPI_PUSHR_CONT;
waitFifoNotFull();
}
void writecommand_last(uint8_t c) __attribute__((always_inline)) {
waitFifoEmpty();
SPI0.SR = SPI_SR_TCF;
SPI0.PUSHR = c | (pcs_command << 16) | SPI_PUSHR_CTAS(0);
waitTransmitComplete();
}
void writedata8_last(uint8_t c) __attribute__((always_inline)) {
waitFifoEmpty();
SPI0.SR = SPI_SR_TCF;
SPI0.PUSHR = c | (pcs_data << 16) | SPI_PUSHR_CTAS(0);
waitTransmitComplete();
}
void writedata16_last(uint16_t d) __attribute__((always_inline)) {
waitFifoEmpty();
SPI0.SR = SPI_SR_TCF;
SPI0.PUSHR = d | (pcs_data << 16) | SPI_PUSHR_CTAS(1);
waitTransmitComplete();
}
void HLine(int16_t x, int16_t y, int16_t w, uint16_t color)
__attribute__((always_inline)) {
setAddr(x, y, x+w-1, y);
writecommand_cont(ILI9341_RAMWR);
do { writedata16_cont(color); } while (--w > 0);
}
void VLine(int16_t x, int16_t y, int16_t h, uint16_t color)
__attribute__((always_inline)) {
setAddr(x, y, x, y+h-1);
writecommand_cont(ILI9341_RAMWR);
do { writedata16_cont(color); } while (--h > 0);
}
void Pixel(int16_t x, int16_t y, uint16_t color)
__attribute__((always_inline)) {
setAddr(x, y, x, y);
writecommand_cont(ILI9341_RAMWR);
writedata16_cont(color);
}
//};
#ifndef swap
#define swap(a, b) { typeof(a) t = a; a = b; b = t; }
#endif
#endif

912
Source/src/ILI9341_t3.c Normal file
View file

@ -0,0 +1,912 @@
// https://github.com/PaulStoffregen/ILI9341_t3
// http://forum.pjrc.com/threads/26305-Highly-optimized-ILI9341-(320x240-TFT-color-display)-library
/***************************************************
This is our library for the Adafruit ILI9341 Breakout and Shield
----> http://www.adafruit.com/products/1651
Check out the links above for our tutorials and wiring diagrams
These displays use SPI to communicate, 4 or 5 pins are required to
interface (RST is optional)
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
MIT license, all text above must be included in any redistribution
****************************************************/
#include "ILI9341_t3.h"
#include <SPI.h>
// Teensy 3.1 can only generate 30 MHz SPI when running at 120 MHz (overclock)
// At all other speeds, SPI.beginTransaction() will use the fastest available clock
#define SPICLOCK 30000000
#define WIDTH ILI9341_TFTWIDTH
#define HEIGHT ILI9341_TFTHEIGHT
// Constructor when using hardware SPI. Faster, but must use SPI pins
// specific to each board type (e.g. 11,13 for Uno, 51,52 for Mega, etc.)
ILI9341_t3(uint8_t cs, uint8_t dc, uint8_t rst)
{
_cs = cs;
_dc = dc;
_rst = rst;
_width = WIDTH;
_height = HEIGHT;
rotation = 0;
cursor_y = cursor_x = 0;
textsize = 1;
textcolor = textbgcolor = 0xFFFF;
wrap = true;
}
void ILI9341_t3_setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
{
SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));
setAddr(x0, y0, x1, y1);
writecommand_last(ILI9341_RAMWR); // write to RAM
SPI.endTransaction();
}
void ILI9341_t3_pushColor(uint16_t color)
{
SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));
writedata16_last(color);
SPI.endTransaction();
}
void ILI9341_t3_drawPixel(int16_t x, int16_t y, uint16_t color) {
if((x < 0) ||(x >= _width) || (y < 0) || (y >= _height)) return;
SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));
setAddr(x, y, x, y);
writecommand_cont(ILI9341_RAMWR);
writedata16_last(color);
SPI.endTransaction();
}
void ILI9341_t3_drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color)
{
// Rudimentary clipping
if((x >= _width) || (y >= _height)) return;
if((y+h-1) >= _height) h = _height-y;
SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));
setAddr(x, y, x, y+h-1);
writecommand_cont(ILI9341_RAMWR);
while (h-- > 1) {
writedata16_cont(color);
}
writedata16_last(color);
SPI.endTransaction();
}
void ILI9341_t3_drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color)
{
// Rudimentary clipping
if((x >= _width) || (y >= _height)) return;
if((x+w-1) >= _width) w = _width-x;
SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));
setAddr(x, y, x+w-1, y);
writecommand_cont(ILI9341_RAMWR);
while (w-- > 1) {
writedata16_cont(color);
}
writedata16_last(color);
SPI.endTransaction();
}
void ILI9341_t3_fillScreen(uint16_t color)
{
fillRect(0, 0, _width, _height, color);
}
// fill a rectangle
void ILI9341_t3_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 >= _width) || (y >= _height)) return;
if((x + w - 1) >= _width) w = _width - x;
if((y + h - 1) >= _height) h = _height - y;
// TODO: this can result in a very long transaction time
// should break this into multiple transactions, even though
// it'll cost more overhead, so we don't stall other SPI libs
SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));
setAddr(x, y, x+w-1, y+h-1);
writecommand_cont(ILI9341_RAMWR);
for(y=h; y>0; y--) {
for(x=w; x>1; x--) {
writedata16_cont(color);
}
writedata16_last(color);
}
SPI.endTransaction();
}
#define MADCTL_MY 0x80
#define MADCTL_MX 0x40
#define MADCTL_MV 0x20
#define MADCTL_ML 0x10
#define MADCTL_RGB 0x00
#define MADCTL_BGR 0x08
#define MADCTL_MH 0x04
void ILI9341_t3_setRotation(uint8_t m)
{
writecommand_cont(ILI9341_MADCTL);
rotation = m % 4; // can't be higher than 3
switch (rotation) {
case 0:
writedata8_last(MADCTL_MX | MADCTL_BGR);
_width = ILI9341_TFTWIDTH;
_height = ILI9341_TFTHEIGHT;
break;
case 1:
writedata8_last(MADCTL_MV | MADCTL_BGR);
_width = ILI9341_TFTHEIGHT;
_height = ILI9341_TFTWIDTH;
break;
case 2:
writedata8_last(MADCTL_MY | MADCTL_BGR);
_width = ILI9341_TFTWIDTH;
_height = ILI9341_TFTHEIGHT;
break;
case 3:
writedata8_last(MADCTL_MX | MADCTL_MY | MADCTL_MV | MADCTL_BGR);
_width = ILI9341_TFTHEIGHT;
_height = ILI9341_TFTWIDTH;
break;
}
}
void ILI9341_t3_invertDisplay(bool i)
{
writecommand_last(i ? ILI9341_INVON : ILI9341_INVOFF);
}
/*
uint8_t ILI9341_t3::readdata(void)
{
uint8_t r;
// Try to work directly with SPI registers...
// First wait until output queue is empty
uint16_t wTimeout = 0xffff;
while (((SPI0.SR) & (15 << 12)) && (--wTimeout)) ; // wait until empty
// SPI0_MCR |= SPI_MCR_CLR_RXF; // discard any received data
// SPI0_SR = SPI_SR_TCF;
// Transfer a 0 out...
writedata8_cont(0);
// Now wait until completed.
wTimeout = 0xffff;
while (((SPI0.SR) & (15 << 12)) && (--wTimeout)) ; // wait until empty
r = SPI0.POPR; // get the received byte... should check for it first...
return r;
}
*/
uint8_t ILI9341_t3_readcommand8(uint8_t c, uint8_t index)
{
uint16_t wTimeout = 0xffff;
uint8_t r=0;
SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));
while (((SPI0.SR) & (15 << 12)) && (--wTimeout)) ; // wait until empty
// Make sure the last frame has been sent...
SPI0.SR = SPI_SR_TCF; // dlear it out;
wTimeout = 0xffff;
while (!((SPI0.SR) & SPI_SR_TCF) && (--wTimeout)) ; // wait until it says the last frame completed
// clear out any current received bytes
wTimeout = 0x10; // should not go more than 4...
while ((((SPI0.SR) >> 4) & 0xf) && (--wTimeout)) {
r = SPI0.POPR;
}
//writecommand(0xD9); // sekret command
SPI0.PUSHR = 0xD9 | (pcs_command << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT;
// while (((SPI0.SR) & (15 << 12)) > (3 << 12)) ; // wait if FIFO full
// writedata(0x10 + index);
SPI0.PUSHR = (0x10 + index) | (pcs_data << 16) | SPI_PUSHR_CTAS(0);
// while (((SPI0.SR) & (15 << 12)) > (3 << 12)) ; // wait if FIFO full
// writecommand(c);
SPI0.PUSHR = c | (pcs_command << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT;
// while (((SPI0.SR) & (15 << 12)) > (3 << 12)) ; // wait if FIFO full
// readdata
SPI0.PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0);
// while (((SPI0.SR) & (15 << 12)) > (3 << 12)) ; // wait if FIFO full
// Now wait until completed.
wTimeout = 0xffff;
while (((SPI0.SR) & (15 << 12)) && (--wTimeout)) ; // wait until empty
// Make sure the last frame has been sent...
SPI0.SR = SPI_SR_TCF; // dlear it out;
wTimeout = 0xffff;
while (!((SPI0.SR) & SPI_SR_TCF) && (--wTimeout)) ; // wait until it says the last frame completed
wTimeout = 0x10; // should not go more than 4...
// lets get all of the values on the FIFO
while ((((SPI0.SR) >> 4) & 0xf) && (--wTimeout)) {
r = SPI0.POPR;
}
SPI.endTransaction();
return r; // get the received byte... should check for it first...
}
// KJE Added functions to read pixel data...
uint16_t ILI9341_t3_readPixel(int16_t x, int16_t y)
{
SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));
writecommand_cont(ILI9341_CASET); // Column addr set
writedata16_cont(x); // XSTART
x++;
writedata16_cont(x); // XEND
writecommand_cont(ILI9341_PASET); // Row addr set
writedata16_cont(y); // YSTART
y++;
writedata16_cont(y); // YEND
writecommand_cont(ILI9341_RAMRD); // write to RAM
digitalWrite(_dc, HIGH);
digitalWrite(_cs, LOW);
uint16_t r = SPI.transfer(0x00);
r <<= 8;
r |= SPI.transfer(0x00);
digitalWrite(_cs, HIGH);
SPI.endTransaction();
return r;
}
static const uint8_t init_commands[] = {
4, 0xEF, 0x03, 0x80, 0x02,
4, 0xCF, 0x00, 0XC1, 0X30,
5, 0xED, 0x64, 0x03, 0X12, 0X81,
4, 0xE8, 0x85, 0x00, 0x78,
6, 0xCB, 0x39, 0x2C, 0x00, 0x34, 0x02,
2, 0xF7, 0x20,
3, 0xEA, 0x00, 0x00,
2, ILI9341_PWCTR1, 0x23, // Power control
2, ILI9341_PWCTR2, 0x10, // Power control
3, ILI9341_VMCTR1, 0x3e, 0x28, // VCM control
2, ILI9341_VMCTR2, 0x86, // VCM control2
2, ILI9341_MADCTL, 0x48, // Memory Access Control
2, ILI9341_PIXFMT, 0x55,
3, ILI9341_FRMCTR1, 0x00, 0x18,
4, ILI9341_DFUNCTR, 0x08, 0x82, 0x27, // Display Function Control
2, 0xF2, 0x00, // Gamma Function Disable
2, ILI9341_GAMMASET, 0x01, // Gamma curve selected
16, ILI9341_GMCTRP1, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08,
0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00, // Set Gamma
16, ILI9341_GMCTRN1, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07,
0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F, // Set Gamma
0
};
void ILI9341_t3_begin(void)
{
SPI.begin();
if (SPI.pinIsChipSelect(_cs, _dc)) {
pcs_data = SPI.setCS(_cs);
pcs_command = pcs_data | SPI.setCS(_dc);
} else {
pcs_data = 0;
pcs_command = 0;
return;
}
// toggle RST low to reset
if (_rst < 255) {
pinMode(_rst, OUTPUT);
digitalWrite(_rst, HIGH);
delay(5);
digitalWrite(_rst, LOW);
delay(20);
digitalWrite(_rst, HIGH);
delay(150);
}
/*
uint8_t x = readcommand8(ILI9341_RDMODE);
Serial.print("\nDisplay Power Mode: 0x"); Serial.println(x, HEX);
x = readcommand8(ILI9341_RDMADCTL);
Serial.print("\nMADCTL Mode: 0x"); Serial.println(x, HEX);
x = readcommand8(ILI9341_RDPIXFMT);
Serial.print("\nPixel Format: 0x"); Serial.println(x, HEX);
x = readcommand8(ILI9341_RDIMGFMT);
Serial.print("\nImage Format: 0x"); Serial.println(x, HEX);
x = readcommand8(ILI9341_RDSELFDIAG);
Serial.print("\nSelf Diagnostic: 0x"); Serial.println(x, HEX);
*/
SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));
const uint8_t *addr = init_commands;
while (1) {
uint8_t count = *addr++;
if (count-- == 0) break;
writecommand_cont(*addr++);
while (count-- > 0) {
writedata8_cont(*addr++);
}
}
writecommand_last(ILI9341_SLPOUT); // Exit Sleep
SPI.endTransaction();
delay(120);
SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));
writecommand_last(ILI9341_DISPON); // Display on
SPI.endTransaction();
}
/*
This is the core graphics library for all our displays, providing a common
set of graphics primitives (points, lines, circles, etc.). It needs to be
paired with a hardware-specific library for each display device we carry
(to handle the lower-level functions).
Adafruit invests time and resources providing this open source code, please
support Adafruit & open-source hardware by purchasing products from Adafruit!
Copyright (c) 2013 Adafruit Industries. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include "glcdfont.c"
// Draw a circle outline
void ILI9341_t3_drawCircle(int16_t x0, int16_t y0, int16_t r,
uint16_t color) {
int16_t f = 1 - r;
int16_t ddF_x = 1;
int16_t ddF_y = -2 * r;
int16_t x = 0;
int16_t y = r;
drawPixel(x0 , y0+r, color);
drawPixel(x0 , y0-r, color);
drawPixel(x0+r, y0 , color);
drawPixel(x0-r, y0 , color);
while (x<y) {
if (f >= 0) {
y--;
ddF_y += 2;
f += ddF_y;
}
x++;
ddF_x += 2;
f += ddF_x;
drawPixel(x0 + x, y0 + y, color);
drawPixel(x0 - x, y0 + y, color);
drawPixel(x0 + x, y0 - y, color);
drawPixel(x0 - x, y0 - y, color);
drawPixel(x0 + y, y0 + x, color);
drawPixel(x0 - y, y0 + x, color);
drawPixel(x0 + y, y0 - x, color);
drawPixel(x0 - y, y0 - x, color);
}
}
void ILI9341_t3_drawCircleHelper( int16_t x0, int16_t y0,
int16_t r, uint8_t cornername, uint16_t color) {
int16_t f = 1 - r;
int16_t ddF_x = 1;
int16_t ddF_y = -2 * r;
int16_t x = 0;
int16_t y = r;
while (x<y) {
if (f >= 0) {
y--;
ddF_y += 2;
f += ddF_y;
}
x++;
ddF_x += 2;
f += ddF_x;
if (cornername & 0x4) {
drawPixel(x0 + x, y0 + y, color);
drawPixel(x0 + y, y0 + x, color);
}
if (cornername & 0x2) {
drawPixel(x0 + x, y0 - y, color);
drawPixel(x0 + y, y0 - x, color);
}
if (cornername & 0x8) {
drawPixel(x0 - y, y0 + x, color);
drawPixel(x0 - x, y0 + y, color);
}
if (cornername & 0x1) {
drawPixel(x0 - y, y0 - x, color);
drawPixel(x0 - x, y0 - y, color);
}
}
}
void ILI9341_t3_fillCircle(int16_t x0, int16_t y0, int16_t r,
uint16_t color) {
drawFastVLine(x0, y0-r, 2*r+1, color);
fillCircleHelper(x0, y0, r, 3, 0, color);
}
// Used to do circles and roundrects
void ILI9341_t3_fillCircleHelper(int16_t x0, int16_t y0, int16_t r,
uint8_t cornername, int16_t delta, uint16_t color) {
int16_t f = 1 - r;
int16_t ddF_x = 1;
int16_t ddF_y = -2 * r;
int16_t x = 0;
int16_t y = r;
while (x<y) {
if (f >= 0) {
y--;
ddF_y += 2;
f += ddF_y;
}
x++;
ddF_x += 2;
f += ddF_x;
if (cornername & 0x1) {
drawFastVLine(x0+x, y0-y, 2*y+1+delta, color);
drawFastVLine(x0+y, y0-x, 2*x+1+delta, color);
}
if (cornername & 0x2) {
drawFastVLine(x0-x, y0-y, 2*y+1+delta, color);
drawFastVLine(x0-y, y0-x, 2*x+1+delta, color);
}
}
}
// Bresenham's algorithm - thx wikpedia
void ILI9341_t3_drawLine(int16_t x0, int16_t y0,
int16_t x1, int16_t y1, uint16_t color)
{
if (y0 == y1) {
if (x1 > x0) {
drawFastHLine(x0, y0, x1 - x0 + 1, color);
} else if (x1 < x0) {
drawFastHLine(x1, y0, x0 - x1 + 1, color);
} else {
drawPixel(x0, y0, color);
}
return;
} else if (x0 == x1) {
if (y1 > y0) {
drawFastVLine(x0, y0, y1 - y0 + 1, color);
} else {
drawFastVLine(x0, y1, y0 - y1 + 1, color);
}
return;
}
bool steep = abs(y1 - y0) > abs(x1 - x0);
if (steep) {
swap(x0, y0);
swap(x1, y1);
}
if (x0 > x1) {
swap(x0, x1);
swap(y0, y1);
}
int16_t dx, dy;
dx = x1 - x0;
dy = abs(y1 - y0);
int16_t err = dx / 2;
int16_t ystep;
if (y0 < y1) {
ystep = 1;
} else {
ystep = -1;
}
SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));
int16_t xbegin = x0;
if (steep) {
for (; x0<=x1; x0++) {
err -= dy;
if (err < 0) {
int16_t len = x0 - xbegin;
if (len) {
VLine(y0, xbegin, len + 1, color);
} else {
Pixel(y0, x0, color);
}
xbegin = x0 + 1;
y0 += ystep;
err += dx;
}
}
if (x0 > xbegin + 1) {
VLine(y0, xbegin, x0 - xbegin, color);
}
} else {
for (; x0<=x1; x0++) {
err -= dy;
if (err < 0) {
int16_t len = x0 - xbegin;
if (len) {
HLine(xbegin, y0, len + 1, color);
} else {
Pixel(x0, y0, color);
}
xbegin = x0 + 1;
y0 += ystep;
err += dx;
}
}
if (x0 > xbegin + 1) {
HLine(xbegin, y0, x0 - xbegin, color);
}
}
writecommand_last(ILI9341_NOP);
SPI.endTransaction();
}
// Draw a rectangle
void ILI9341_t3_drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color)
{
SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));
HLine(x, y, w, color);
HLine(x, y+h-1, w, color);
VLine(x, y, h, color);
VLine(x+w-1, y, h, color);
writecommand_last(ILI9341_NOP);
SPI.endTransaction();
}
// Draw a rounded rectangle
void ILI9341_t3_drawRoundRect(int16_t x, int16_t y, int16_t w,
int16_t h, int16_t r, uint16_t color) {
// smarter version
drawFastHLine(x+r , y , w-2*r, color); // Top
drawFastHLine(x+r , y+h-1, w-2*r, color); // Bottom
drawFastVLine(x , y+r , h-2*r, color); // Left
drawFastVLine(x+w-1, y+r , h-2*r, color); // Right
// draw four corners
drawCircleHelper(x+r , y+r , r, 1, color);
drawCircleHelper(x+w-r-1, y+r , r, 2, color);
drawCircleHelper(x+w-r-1, y+h-r-1, r, 4, color);
drawCircleHelper(x+r , y+h-r-1, r, 8, color);
}
// Fill a rounded rectangle
void ILI9341_t3_fillRoundRect(int16_t x, int16_t y, int16_t w,
int16_t h, int16_t r, uint16_t color) {
// smarter version
fillRect(x+r, y, w-2*r, h, color);
// draw four corners
fillCircleHelper(x+w-r-1, y+r, r, 1, h-2*r-1, color);
fillCircleHelper(x+r , y+r, r, 2, h-2*r-1, color);
}
// Draw a triangle
void ILI9341_t3_drawTriangle(int16_t x0, int16_t y0,
int16_t x1, int16_t y1,
int16_t x2, int16_t y2, uint16_t color) {
drawLine(x0, y0, x1, y1, color);
drawLine(x1, y1, x2, y2, color);
drawLine(x2, y2, x0, y0, color);
}
// Fill a triangle
void ILI9341_t3_fillTriangle ( int16_t x0, int16_t y0,
int16_t x1, int16_t y1,
int16_t x2, int16_t y2, uint16_t color) {
int16_t a, b, y, last;
// Sort coordinates by Y order (y2 >= y1 >= y0)
if (y0 > y1) {
swap(y0, y1); swap(x0, x1);
}
if (y1 > y2) {
swap(y2, y1); swap(x2, x1);
}
if (y0 > y1) {
swap(y0, y1); swap(x0, x1);
}
if(y0 == y2) { // Handle awkward all-on-same-line case as its own thing
a = b = x0;
if(x1 < a) a = x1;
else if(x1 > b) b = x1;
if(x2 < a) a = x2;
else if(x2 > b) b = x2;
drawFastHLine(a, y0, b-a+1, color);
return;
}
int16_t
dx01 = x1 - x0,
dy01 = y1 - y0,
dx02 = x2 - x0,
dy02 = y2 - y0,
dx12 = x2 - x1,
dy12 = y2 - y1,
sa = 0,
sb = 0;
// For upper part of triangle, find scanline crossings for segments
// 0-1 and 0-2. If y1=y2 (flat-bottomed triangle), the scanline y1
// is included here (and second loop will be skipped, avoiding a /0
// error there), otherwise scanline y1 is skipped here and handled
// in the second loop...which also avoids a /0 error here if y0=y1
// (flat-topped triangle).
if(y1 == y2) last = y1; // Include y1 scanline
else last = y1-1; // Skip it
for(y=y0; y<=last; y++) {
a = x0 + sa / dy01;
b = x0 + sb / dy02;
sa += dx01;
sb += dx02;
/* longhand:
a = x0 + (x1 - x0) * (y - y0) / (y1 - y0);
b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
*/
if(a > b) swap(a,b);
drawFastHLine(a, y, b-a+1, color);
}
// For lower part of triangle, find scanline crossings for segments
// 0-2 and 1-2. This loop is skipped if y1=y2.
sa = dx12 * (y - y1);
sb = dx02 * (y - y0);
for(; y<=y2; y++) {
a = x1 + sa / dy12;
b = x0 + sb / dy02;
sa += dx12;
sb += dx02;
/* longhand:
a = x1 + (x2 - x1) * (y - y1) / (y2 - y1);
b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
*/
if(a > b) swap(a,b);
drawFastHLine(a, y, b-a+1, color);
}
}
void ILI9341_t3_drawBitmap(int16_t x, int16_t y,
const uint8_t *bitmap, int16_t w, int16_t h,
uint16_t color) {
int16_t i, j, byteWidth = (w + 7) / 8;
for(j=0; j<h; j++) {
for(i=0; i<w; i++ ) {
if(pgm_read_byte(bitmap + j * byteWidth + i / 8) & (128 >> (i & 7))) {
drawPixel(x+i, y+j, color);
}
}
}
}
size_t ILI9341_t3_write(uint8_t c) {
if (c == '\n') {
cursor_y += textsize*8;
cursor_x = 0;
} else if (c == '\r') {
// skip em
} else {
drawChar(cursor_x, cursor_y, c, textcolor, textbgcolor, textsize);
cursor_x += textsize*6;
if (wrap && (cursor_x > (_width - textsize*6))) {
cursor_y += textsize*8;
cursor_x = 0;
}
}
return 1;
}
// Draw a character
void ILI9341_t3_drawChar(int16_t x, int16_t y, unsigned char c,
uint16_t fgcolor, uint16_t bgcolor, uint8_t size)
{
if((x >= _width) || // Clip right
(y >= _height) || // Clip bottom
((x + 6 * size - 1) < 0) || // Clip left TODO: is this correct?
((y + 8 * size - 1) < 0)) // Clip top TODO: is this correct?
return;
if (fgcolor == bgcolor) {
// This transparent approach is only about 20% faster
if (size == 1) {
uint8_t mask = 0x01;
int16_t xoff, yoff;
for (yoff=0; yoff < 8; yoff++) {
uint8_t line = 0;
for (xoff=0; xoff < 5; xoff++) {
if (font[c * 5 + xoff] & mask) line |= 1;
line <<= 1;
}
line >>= 1;
xoff = 0;
while (line) {
if (line == 0x1F) {
drawFastHLine(x + xoff, y + yoff, 5, fgcolor);
break;
} else if (line == 0x1E) {
drawFastHLine(x + xoff, y + yoff, 4, fgcolor);
break;
} else if ((line & 0x1C) == 0x1C) {
drawFastHLine(x + xoff, y + yoff, 3, fgcolor);
line <<= 4;
xoff += 4;
} else if ((line & 0x18) == 0x18) {
drawFastHLine(x + xoff, y + yoff, 2, fgcolor);
line <<= 3;
xoff += 3;
} else if ((line & 0x10) == 0x10) {
drawPixel(x + xoff, y + yoff, fgcolor);
line <<= 2;
xoff += 2;
} else {
line <<= 1;
xoff += 1;
}
}
mask = mask << 1;
}
} else {
uint8_t mask = 0x01;
int16_t xoff, yoff;
for (yoff=0; yoff < 8; yoff++) {
uint8_t line = 0;
for (xoff=0; xoff < 5; xoff++) {
if (font[c * 5 + xoff] & mask) line |= 1;
line <<= 1;
}
line >>= 1;
xoff = 0;
while (line) {
if (line == 0x1F) {
fillRect(x + xoff * size, y + yoff * size,
5 * size, size, fgcolor);
break;
} else if (line == 0x1E) {
fillRect(x + xoff * size, y + yoff * size,
4 * size, size, fgcolor);
break;
} else if ((line & 0x1C) == 0x1C) {
fillRect(x + xoff * size, y + yoff * size,
3 * size, size, fgcolor);
line <<= 4;
xoff += 4;
} else if ((line & 0x18) == 0x18) {
fillRect(x + xoff * size, y + yoff * size,
2 * size, size, fgcolor);
line <<= 3;
xoff += 3;
} else if ((line & 0x10) == 0x10) {
fillRect(x + xoff * size, y + yoff * size,
size, size, fgcolor);
line <<= 2;
xoff += 2;
} else {
line <<= 1;
xoff += 1;
}
}
mask = mask << 1;
}
}
} else {
// This solid background approach is about 5 time faster
SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));
setAddr(x, y, x + 6 * size - 1, y + 8 * size);
writecommand_cont(ILI9341_RAMWR);
uint8_t xr, yr;
uint8_t mask = 0x01;
uint16_t color;
for (y=0; y < 8; y++) {
for (yr=0; yr < size; yr++) {
for (x=0; x < 5; x++) {
if (font[c * 5 + x] & mask) {
color = fgcolor;
} else {
color = bgcolor;
}
for (xr=0; xr < size; xr++) {
writedata16_cont(color);
}
}
for (xr=0; xr < size; xr++) {
writedata16_cont(bgcolor);
}
}
mask = mask << 1;
}
uint32_t n = 6 * size * size;
do {
writedata16_cont(bgcolor);
n--;
} while (n > 1);
writedata16_last(bgcolor);
SPI.endTransaction();
}
}
void ILI9341_t3_setCursor(int16_t x, int16_t y) {
cursor_x = x;
cursor_y = y;
}
void ILI9341_t3_setTextSize(uint8_t s) {
textsize = (s > 0) ? s : 1;
}
void ILI9341_t3_setTextColor(uint16_t c) {
// For 'transparent' background, we'll set the bg
// to the same as fg instead of using a flag
textcolor = textbgcolor = c;
}
void ILI9341_t3_setTextColor(uint16_t c, uint16_t b) {
textcolor = c;
textbgcolor = b;
}
void ILI9341_t3_setTextWrap(bool w) {
wrap = w;
}
uint8_t ILI9341_t3_getRotation(void) {
return rotation;
}