#include <Adafruit_GFX.h> // Core graphics library
#include <st7735_t3_font_Arial.h>
#include <ST7735_t3.h> // Hardware-specific library
#include <ST7789_t3.h> // Hardware-specific library
#include <SPI.h>
#include <Fonts/FreeMonoBoldOblique12pt7b.h>
#include <Fonts/FreeSerif12pt7b.h>
#define ROTATION 1
#include "SPI.h"
#if defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x
#define TFT_DC 9 // only CS pin
#define TFT_CS 10 // using standard pin
#define TFT_RST 8
ST7789_t3 tft = ST7789_t3(TFT_CS, TFT_DC, TFT_RST);
#define DBGSerial Serial
#else
//#define DEFAULT_PINS
//#define USE_SPI1
#define KURTS_FLEXI
//#define FRANKS_C64
#ifdef KURTS_FLEXI
#define TFT_DC 22
#define TFT_CS 15
#define TFT_RST -1
#define TFT_SCK 14
#define TFT_MISO 12
#define TFT_MOSI 7
#define DEBUG_PIN 13
#elif defined(FRANKS_C64)
#define SCK 14
#define MISO 39
#define MOSI 28
#define TFT_TOUCH_CS 38
#define TFT_TOUCH_INT 37
#define TFT_DC 20
#define TFT_CS 21
#define TFT_RST -1 // 255 = unused, connected to 3.3V
#define TFT_SCK SCK
#define TFT_MOSI MOSI
#define TFT_MISO MISO
#elif defined(DEFAULT_PINS)
#define TFT_DC 9
#define TFT_CS 10
#define TFT_RST 8
#define TFT_SCK 13
#define TFT_MISO 12
#define TFT_MOSI 11
#elif defined(USE_SPI1)
#define TFT_DC 31
#define TFT_CS 10 // any pin will work not hardware
#define TFT_RST 8
#define TFT_SCK 32
#define TFT_MISO 5
#define TFT_MOSI 21
#define DEBUG_PIN 13
#else
//#define TFT_DC 9
#define TFT_DC 45
#define TFT_CS 10
#define TFT_RST 7
#define TFT_SCK 13
#define TFT_MISO 12
#define TFT_MOSI 11
#endif
//ST7735_t3n tft = ST7735_t3n(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCK, TFT_MISO, &SPIN);
#endif
#define ST7735_PINK 0xF81F
#define ST7735_ORANGE 0xFD20 /* 255, 165, 0 */
#define ST7735_PURPLE 0x780F /* 128, 0, 128 */
#define ST7735_LIGHTGREY 0xC618 /* 192, 192, 192 */
#define ST7735_OLIVE 0x7BE0 /* 128, 128, 0 */
#define ST7735_MAROON 0x7800 /* 128, 0, 0 */
uint16_t our_pallet[] = {
ST7735_BLACK, ST7735_RED, ST7735_GREEN, ST7735_BLUE, ST7735_WHITE,
ST7735_YELLOW, ST7735_ORANGE, ST7735_CYAN, ST7735_PINK
};
ST7735_Button button;
// Let's allocate the frame buffer ourself.
DMAMEM uint16_t tft_frame_buffer[240 * 320];
uint8_t use_dma = 0;
uint8_t use_clip_rect = 0;
uint8_t use_set_origin = 0;
uint8_t use_fb = 0;
#define ORIGIN_TEST_X 50
#define ORIGIN_TEST_Y 50
void setup() {
while (!Serial && (millis() < 4000)) ;
DBGSerial.begin(115200);
//DBGSerial.printf("Begin: CS:%d, DC:%d, MOSI:%d, MISO: %d, SCK: %d, RST: %d\n", TFT_CS, TFT_DC, TFT_MOSI, TFT_MISO, TFT_SCK, TFT_RST);
tft.init(240, 320);
tft.setFrameBuffer(tft_frame_buffer);
tft.setRotation(ROTATION);
tft.fillScreen(ST7735_BLACK);
button.initButton(&tft, 200, 125, 100, 40, ST7735_GREEN, ST7735_YELLOW, ST7735_RED, "UP", 1, 1);
drawTestScreen();
}
void SetupOrClearClipRectAndOffsets() {
if (use_clip_rect) {
tft.setClipRect(); // make sure we clear the whole screen
tft.setOrigin(); // make sure none are set yet
tft.fillScreen(ST7735_LIGHTGREY);
// Now lets set origin.
if (use_set_origin)
tft.setOrigin(ORIGIN_TEST_X, ORIGIN_TEST_Y);
int x = tft.width() / 4;
int y = tft.height() / 4;
int w = tft.width() / 2;
int h = tft.height() / 2;
tft.drawRect(x, y, w, h, ST7735_ORANGE);
tft.updateScreen();
tft.setClipRect(x + 1, y + 1, w - 2, h - 2);
delay(250);
} else {
tft.setClipRect();
if (use_set_origin)
tft.setOrigin(ORIGIN_TEST_X, ORIGIN_TEST_Y);
else
tft.setOrigin();
}
}
uint16_t palette[256]; // Should probably be 256, but I don't use many colors...
uint16_t pixel_data[2500];
const uint8_t pict1bpp[] = {0xff, 0xff, 0xc0, 0x03, 0xa0, 0x05, 0x90, 0x9, 0x88, 0x11, 0x84, 0x21, 0x82, 0x41, 0x81, 0x81,
0x81, 0x81, 0x82, 0x41, 0x84, 0x21, 0x88, 0x11, 0x90, 0x09, 0xa0, 0x05, 0xc0, 0x03, 0xff, 0xff
};
const uint8_t pict2bpp[] = {
0x00, 0x00, 0x55, 0x55, 0xaa, 0xaa, 0xff, 0xff,
0x00, 0x00, 0x55, 0x55, 0xaa, 0xaa, 0xff, 0xff,
0x55, 0x55, 0xaa, 0xaa, 0xff, 0xff, 0x00, 0x00,
0x55, 0x55, 0xaa, 0xaa, 0xff, 0xff, 0x00, 0x00,
0xaa, 0xaa, 0xff, 0xff, 0x00, 0x00, 0x55, 0x55,
0xaa, 0xaa, 0xff, 0xff, 0x00, 0x00, 0x55, 0x55,
0xff, 0xff, 0x00, 0x00, 0x55, 0x55, 0xaa, 0xaa,
0xff, 0xff, 0x00, 0x00, 0x55, 0x55, 0xaa, 0xaa,
0x00, 0x00, 0x55, 0x55, 0xaa, 0xaa, 0xff, 0xff,
0x00, 0x00, 0x55, 0x55, 0xaa, 0xaa, 0xff, 0xff,
0x55, 0x55, 0xaa, 0xaa, 0xff, 0xff, 0x00, 0x00,
0x55, 0x55, 0xaa, 0xaa, 0xff, 0xff, 0x00, 0x00,
0xaa, 0xaa, 0xff, 0xff, 0x00, 0x00, 0x55, 0x55,
0xaa, 0xaa, 0xff, 0xff, 0x00, 0x00, 0x55, 0x55,
0xff, 0xff, 0x00, 0x00, 0x55, 0x55, 0xaa, 0xaa,
0xff, 0xff, 0x00, 0x00, 0x55, 0x55, 0xaa, 0xaa,
};
const uint8_t pict4bpp[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x00,
0x00, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x00,
0x00, 0x11, 0x22, 0x22, 0x22, 0x22, 0x11, 0x00,
0x00, 0x11, 0x22, 0x22, 0x22, 0x22, 0x11, 0x00,
0x00, 0x11, 0x22, 0x33, 0x33, 0x22, 0x11, 0x00,
0x00, 0x11, 0x22, 0x33, 0x33, 0x22, 0x11, 0x00,
0x00, 0x11, 0x22, 0x33, 0x33, 0x22, 0x11, 0x00,
0x00, 0x11, 0x22, 0x33, 0x33, 0x22, 0x11, 0x00,
0x00, 0x11, 0x22, 0x22, 0x22, 0x22, 0x11, 0x00,
0x00, 0x11, 0x22, 0x22, 0x22, 0x22, 0x11, 0x00,
0x00, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x00,
0x00, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
void drawTestScreen() {
DBGSerial.printf("Use FB: %d ", use_fb); DBGSerial.flush();
tft.useFrameBuffer(use_fb);
SetupOrClearClipRectAndOffsets();
uint32_t start_time = millis();
tft.fillScreen(use_fb ? ST7735_RED : ST7735_BLACK);
//tft.setFont(Inconsolata_60);
tft.setFont(Arial_24);
tft.setTextColor(ST7735_WHITE);
tft.setCursor(0, 0);
tft.println("Test");
tft.setTextColor(ST7735_WHITE, ST7735_RED);
tft.println("text");
tft.setCursor(85, 65);
tft.print("XYZ");
tft.setFontAdafruit();
tft.setTextSize(2);
tft.setTextColor(ST7735_WHITE);
tft.println("01234");
tft.setTextColor(ST7735_WHITE, ST7735_GREEN);
tft.println("56789!@#$%");
tft.drawRect(0, 150, 100, 50, ST7735_WHITE);
tft.drawLine(0, 150, 100, 50, ST7735_GREEN);
//tft.fillRectVGradient(125, 150, 50, 50, ST7735_GREEN, ST7735_YELLOW);
//tft.fillRectHGradient(200, 150, 50, 50, ST7735_YELLOW, ST7735_GREEN);
// Try a read rect and write rect
#ifdef DEBUG_PIN
digitalWrite(DEBUG_PIN, HIGH);
#endif
tft.readRect(0, 0, 50, 50, pixel_data);
// For heck of it lets make sure readPixel and ReadRect
// give us same data, maybe check along diagnal?
for (uint i=0; i < 50; i++) {
uint16_t pixel_color = tft.readPixel(i,i);
if (pixel_color != pixel_data[i*50+i]) {
DBGSerial.printf("Read rect/pixel mismatch: %d %x %x\n", i, pixel_color,pixel_data[i*50+i]);
}
}
#ifdef DEBUG_PIN
digitalWrite(DEBUG_PIN, LOW);
#endif
tft.writeRect(250, 0, 50, 50, pixel_data);
// Lets try to pack this rectangle of data into 8 byte
tft.readRect(85, 65, 50, 50, pixel_data);
uint16_t *ppd16 = pixel_data;
uint8_t *ppd8 = (uint8_t*)pixel_data;
uint8_t palette_cnt = 0;
int palette_index;
for (int i = 0; i < 2500; i++) {
for (palette_index = 0; palette_index < palette_cnt; palette_index++) {
if (*ppd16 == palette[palette_index])
break;
}
if (palette_index >= palette_cnt) {
palette[palette_cnt++] = *ppd16; // save away the color
}
*ppd8++ = palette_index;
ppd16++;
}
//tft.writeRect8BPP(200, 50, 50, 50, (uint8_t*)pixel_data, palette);
palette[0] = ST7735_CYAN;
palette[1] = ST7735_OLIVE;
//tft.writeRect1BPP(75, 100, 16, 16, pict1bpp, palette);
//tft.writeRect1BPP(320 - 90, 75, 16, 16, pict1bpp, palette);
palette[2] = ST7735_MAROON;
palette[3] = ST7735_PINK;
//tft.writeRect2BPP(75, 125, 32, 16, pict2bpp, palette);
//tft.writeRectNBPP(15, 125, 32, 16, 2, pict2bpp, palette);
//tft.writeRectNBPP(75, 150, 16, 16, 4, pict4bpp, palette);
// Try drawing button
tft.setFontAdafruit();
button.drawButton();
if (use_dma) {
tft.updateScreenAsync();
} else {
tft.updateScreen();
}
DBGSerial.println(millis() - start_time, DEC);
if (use_dma && use_fb) {
delay(500);
DBGSerial.printf("DMA error status: %x\n", DMA_ES);
}
use_fb = use_fb ? 0 : 1 ;
DBGSerial.println(use_fb, DEC);
}
void fillScreenTest() {
tft.useFrameBuffer(0);
SetupOrClearClipRectAndOffsets();
tft.fillScreen(ST7735_RED);
WaitForUserInput();
tft.fillScreen(ST7735_GREEN);
WaitForUserInput();
tft.fillScreen(ST7735_WHITE);
WaitForUserInput();
tft.fillScreen(ST7735_BLACK);
}
void printTextSizes(const char *sz) {
Serial.printf("%s(%d,%d): SPL:%u ", sz, tft.getCursorX(), tft.getCursorY(), tft.strPixelLen(sz));
int16_t x, y;
uint16_t w, h;
tft.getTextBounds(sz, tft.getCursorX(), tft.getCursorY(), &x, &y, &w, &h);
Serial.printf(" Rect(%d, %d, %u %u)\n", x, y, w, h);
tft.drawRect(x, y, w, h, ST7735_GREEN);
}
void drawTextScreen(bool fOpaque) {
SetupOrClearClipRectAndOffsets();
tft.setTextSize(1);
uint32_t start_time = millis();
tft.useFrameBuffer(use_fb);
tft.fillScreen(use_fb ? ST7735_RED : ST7735_BLACK);
tft.setFont(Arial_40);
if (fOpaque)
tft.setTextColor(ST7735_WHITE, use_fb ? ST7735_BLACK : ST7735_RED);
else
tft.setTextColor(ST7735_WHITE);
tft.setCursor(0, 5);
tft.println("AbCdEfGhIj");
tft.setFont(Arial_28);
tft.println("0123456789!@#$");
#if 0
tft.setFont(Arial_20);
tft.println("abcdefghijklmnopq");
tft.setFont(Arial_14);
tft.println("ABCDEFGHIJKLMNOPQRST");
tft.setFont(Arial_10);
tft.println("0123456789zyxwvutu");
#endif
tft.setFont(&FreeMonoBoldOblique12pt7b);
tft.setCursor(50, tft.getCursorY());
printTextSizes("AdaFruit");
tft.println("yAdaFruitq");
tft.setCursor(50, tft.getCursorY());
tft.setFont(&FreeSerif12pt7b);
tft.setTextSize(2);
printTextSizes("FreeSan12");
tft.println("FreeSan12q");
tft.setFont();
tft.setCursor(50, tft.getCursorY());
tft.setTextSize(1,2);
printTextSizes("Sys(1,2)");
tft.println("Sys(1,2)");
tft.setTextSize(1);
printTextSizes("System");
tft.println("System");
tft.setTextSize(1);
tft.updateScreen();
DBGSerial.printf("Use FB: %d OP: %d, DT: %d OR: %d\n", use_fb, fOpaque, use_set_origin, millis() - start_time);
}
//=============================================================================
// Wait for user input
//=============================================================================
void WaitForUserInput() {
DBGSerial.println("Hit Enter to continue");
DBGSerial.flush();
while (DBGSerial.read() == -1) ;
while (DBGSerial.read() != -1) ;
}
//=============================================================================
// Try continuous update
//=============================================================================
void WaitForFrame(bool fCont, uint32_t wait_frame_count) {
if (fCont) {
while (tft.frameCount() < wait_frame_count) yield();
} else {
tft.updateScreenAsync();
WaitForUserInput();
}
}
void testDMAContUpdate(bool fCont) {
// Force frame buffer on
DBGSerial.printf("continuous DMA udpate test - Frame mode on\n"); DBGSerial.flush();
if (!fCont) {
DBGSerial.println("Step Mode");
DBGSerial.flush();
}
use_fb = 1; //
tft.useFrameBuffer(use_fb);
tft.fillScreen(ST7735_GREEN);
// check to see if screen memory actually turned green.
if (use_fb) {
uint16_t *pw = tft.getFrameBuffer();
int error_count = 0;
for (int i = 0; i < (240 * 320); i++)
{
if (*pw != ST7735_GREEN) {
DBGSerial.printf("tft.fillScreen(ST7735_GREEN) not green? %d != %x\n", i, *pw);
error_count++;
}
pw++;
}
DBGSerial.printf("tft.fillScreen(ST7735_GREEN(%x)) error count = %d\n", ST7735_GREEN, error_count);
}
if (fCont)
tft.updateScreenAsync(fCont);
// Start the update
WaitForFrame(fCont, 10);
tft.fillScreen(ST7735_YELLOW);
tft.drawRect(5, 5, 310, 230, ST7735_GREEN);
tft.fillRect(140, 100, 40, 40, ST7735_BLUE);
WaitForFrame(fCont, 20);
tft.fillScreen(ST7735_RED);
tft.drawRect(5, 5, 310, 230, ST7735_WHITE);
WaitForFrame(fCont, 30);
tft.fillScreen(ST7735_BLACK);
tft.drawRect(5, 5, 310, 230, ST7735_GREEN);
tft.drawRect(25, 25, 270, 190, ST7735_RED);
WaitForFrame(fCont, 40);
digitalWrite(0, HIGH);
tft.drawRect(5, 5, 310, 230, ST7735_GREEN);
tft.setCursor(10, 100);
tft.setTextColor(ST7735_RED, ST7735_BLACK);
tft.setFont(Arial_20);
tft.println("DONE");
tft.setFont();
tft.setCursor(10, 200);
tft.setTextColor(ST7735_GREEN);
tft.print("Done");
tft.setTextSize(2);
tft.setCursor(10, 50);
tft.setTextColor(ST7735_WHITE, ST7735_RED);
tft.print("Done");
digitalWrite(0, LOW);
WaitForFrame(fCont, 45);
tft.fillRect(0, 0, 2, 2, ST7735_PURPLE);
if (!fCont) {
DBGSerial.println("Lets now try doing Continue for a few iterations to see if it changes");
tft.updateScreenAsync(true);
while (tft.frameCount() < 10) yield();
}
tft.endUpdateAsync();
DBGSerial.println("after endUpdateAsync");
tft.waitUpdateAsyncComplete();
DBGSerial.println("after waitUpdateAsyncComplete");
DBGSerial.println("Finished test");
delay(2000);
DBGSerial.println("Do normal update to see if data is there");
tft.updateScreen();
}
void loop(void) {
// See if any text entered
int ich;
if ((ich = DBGSerial.read()) != -1) {
while (DBGSerial.read() != -1) delay(1);
// See if We have a dma operation in progress?
if (tft.asyncUpdateActive()) {
DBGSerial.printf("Async Update active DMA error status: %x\n", DMA_ES);
//tft.dumpDMASettings();
}
if (ich == 'c') {
use_clip_rect = !use_clip_rect;
if (use_clip_rect) DBGSerial.println("Clip Rectangle Turned on");
else DBGSerial.println("Clip Rectangle turned off");
return;
}
if (ich == 'd') {
use_dma = !use_dma;
if (use_dma) DBGSerial.println("DMA Turned on");
else DBGSerial.println("DMA turned off");
return;
}
if (ich == 's') {
use_set_origin = !use_set_origin;
if (use_set_origin) DBGSerial.printf("Set origin to %d, %d\n", ORIGIN_TEST_X, ORIGIN_TEST_Y);
else DBGSerial.println("Clear origin");
return;
}
if (ich == 'o')
drawTextScreen(1);
else if (ich == 'f')
fillScreenTest();
else if (ich == 't')
drawTextScreen(0);
else if (ich == 'r') {
testDMAContUpdate(true);
DBGSerial.println("Returned from testDMAContUpdate");
}
else if (ich == 'a') {
testDMAContUpdate(false);
DBGSerial.println("Returned from testDMAContUpdate");
}
else
drawTestScreen();
}
}