Hi
I am working on a MIDI controller project with a Teensy 4.1. I upgraded the SPI display screen to a 8- bit 320x240 3.5" TFT CFAF320240F-035T. I am having trouble getting the MIDI controllers UI to display. I was able to incorporate a test sketch with blue, red and green color bars to move down the screen, but after the color bar sequence it goes black and then fades to white. So the screen is working but I can't get the controller’s UI with 8 encoders titled Control Mode to display on startup as it did before new screen. Here is the pin layout
Data pins on TFT D10– D11 D-12 D13- D14 D15- D16- D17:
ON Teensy 14, 15, 16, 17, 18, 19, 20 , 21
WR: 22
RD: 23
DC/RS: 2
CS: 3
RESET: 4
Backlight: 5
PS3 → GND
PS2 → 3.3 V
PS1 → GND
PS0 → 3.3V.
Here is the sketch
### **CODE BLOCK 1: Pin Definitions & Low-Level Functions**
```cpp
// ========================================
// 8-BIT TFT PIN DEFINITIONS (SSD2119)
// ========================================
constexpr int PIN_DC = 2; // RS / D-C
constexpr int PIN_CS = 3; // CS
constexpr int PIN_RST = 4; // RESET
constexpr int PIN_BL = 5; // Backlight
constexpr int PIN_WR = 22; // WR (active low)
constexpr int PIN_RD = 23; // RD (keep HIGH)
constexpr uint16_t TFT_W = 320;
constexpr uint16_t TFT_H = 240;
// Color constants
const uint16_t COLOR_BLACK = 0x0000;
const uint16_t COLOR_WHITE = 0xFFFF;
const uint16_t COLOR_RED = 0xF800;
const uint16_t COLOR_GREEN = 0x07E0;
const uint16_t COLOR_BLUE = 0x001F;
// ========================================
// 8-BIT TFT LOW-LEVEL FUNCTIONS
// ========================================
inline void wr_pulse() {
digitalWriteFast(PIN_WR, LOW);
delayMicroseconds(1);
digitalWriteFast(PIN_WR, HIGH);
delayMicroseconds(1);
}
inline void busWrite8(uint8_t v) {
for (int i = 0; i < 8; ++i) {
digitalWriteFast(DATA_PINS, (v >> i) & 1);
}
wr_pulse();
}
inline void writeIndex(uint16_t idx) {
digitalWriteFast(PIN_DC, LOW);
busWrite8(uint8_t(idx >> 8));
busWrite8(uint8_t(idx & 0xFF));
}
inline void writeData16(uint16_t val) {
digitalWriteFast(PIN_DC, HIGH);
busWrite8(uint8_t(val >> 8));
busWrite8(uint8_t(val & 0xFF));
}
inline void regWrite(uint16_t idx, uint16_t val) {
writeIndex(idx);
writeData16(val);
}
void tftReset() {
digitalWriteFast(PIN_RST, LOW);
delay(50);
digitalWriteFast(PIN_RST, HIGH);
delay(150);
}
```
---
### **CODE BLOCK 2: SSD2119 Initialization**
```cpp
void tftInit() {
digitalWriteFast(PIN_CS, LOW);
digitalWriteFast(PIN_RD, HIGH);
tftReset();
regWrite(0x0028, 0x0006);
regWrite(0x0000, 0x0001); // Start oscillator
delay(5);
regWrite(0x0010, 0x0000); // Sleep out
regWrite(0x0001, 0x72EF); // Driver output control
regWrite(0x0002, 0x0600); // LCD driving waveform
regWrite(0x0003, 0x6A38); // Power control
regWrite(0x000C, 0x0003);
regWrite(0x000D, 0x000A);
regWrite(0x000E, 0x2E00);
regWrite(0x001E, 0x00BE);
regWrite(0x000B, 0x5308);
regWrite(0x000F, 0x0000); // Gate scan start
regWrite(0x0025, 0x8000);
regWrite(0x0026, 0x7800);
regWrite(0x0011, 0x6870); // Entry mode
regWrite(0x0044, (239 << 8) | 0); // Vertical RAM address
regWrite(0x0045, 0); // Horizontal start
regWrite(0x0046, 319); // Horizontal end
regWrite(0x004E, 0); // Set GDDRAM X
regWrite(0x004F, 0); // Set GDDRAM Y
// Gamma settings
regWrite(0x0030, 0x0000);
regWrite(0x0031, 0x0104);
regWrite(0x0032, 0x0100);
regWrite(0x0033, 0x0305);
regWrite(0x0034, 0x0505);
regWrite(0x0035, 0x0305);
regWrite(0x0036, 0x0707);
regWrite(0x0037, 0x0300);
regWrite(0x003A, 0x1200);
regWrite(0x003B, 0x0800);
regWrite(0x0007, 0x0033); // Display on
delay(120);
}
inline void setXY(uint16_t x, uint16_t y) {
regWrite(0x004E, x);
regWrite(0x004F, y);
writeIndex(0x0022); // Prepare to write GRAM
}
void fillScreen_rowwise(uint16_t color) {
for (uint16_t y = 0; y < TFT_H; ++y) {
setXY(0, y);
uint8_t hi = color >> 8, lo = color & 0xFF;
digitalWriteFast(PIN_DC, HIGH);
for (uint16_t x = 0; x < TFT_W; ++x) {
busWrite8(hi);
busWrite8(lo);
}
}
}
```
---
### **CODE BLOCK 3: Hardware Initialization (in setup)**
```cpp
void initializeHardware() {
// Initialize 8-bit TFT pins
pinMode(PIN_DC, OUTPUT);
pinMode(PIN_CS, OUTPUT);
pinMode(PIN_RST, OUTPUT);
pinMode(PIN_BL, OUTPUT);
pinMode(PIN_WR, OUTPUT);
pinMode(PIN_RD, OUTPUT);
digitalWriteFast(PIN_BL, HIGH); // Backlight on
digitalWriteFast(PIN_WR, HIGH);
digitalWriteFast(PIN_RD, HIGH);
digitalWriteFast(PIN_CS, LOW); // CS stays LOW
for (int i = 0; i < 8; ++i) {
pinMode(DATA_PINS, OUTPUT);
digitalWriteFast(DATA_PINS, LOW);
}
// Initialize TFT
Serial.println("Initializing TFT...");
tft.begin(); // Calls tftInit()
// Color bar test - THIS WORKS FINE
Serial.println("Quick display test...");
tft.fillScreen(COLOR_BLACK);
delay(100);
tft.fillScreen(COLOR_RED);
delay(100);
tft.fillScreen(COLOR_GREEN);
delay(100);
tft.fillScreen(COLOR_BLUE);
delay(100);
tft.fillScreen(COLOR_BLACK);
Serial.println("Display test complete!");
tft.setRotation(0); // Landscape mode
}
```
---
### **CODE BLOCK 4: TFT Wrapper Class (Adafruit_GFX compatible)**
```cpp
class TFT_8bit : public Adafruit_GFX {
public:
TFT_8bit() : Adafruit_GFX(TFT_W, TFT_H) {}
void begin() {
tftInit();
}
void fillScreen(uint16_t color) {
fillScreen_rowwise(color);
}
void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
if((x >= TFT_W) || (y >= TFT_H)) return;
if((x+w-1) >= TFT_W) w = TFT_W-x;
if((y+h-1) >= TFT_H) h = TFT_H-y;
for(int16_t i=0; i<h; i++) {
setXY(x, y+i);
uint8_t hi = color >> 8, lo = color & 0xFF;
digitalWriteFast(PIN_DC, HIGH);
for(int16_t j=0; j<w; j++) {
busWrite8(hi);
busWrite8(lo);
}
}
}
void drawPixel(int16_t x, int16_t y, uint16_t color) {
if((x < 0) ||(x >= TFT_W) || (y < 0) || (y >= TFT_H)) return;
setXY(x, y);
writeData16(color);
}
void drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color) {
// Standard Bresenham circle algorithm
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);
}
}
};
TFT_8bit tft;
```
---
### **CODE BLOCK 5: The Problem - UI Initialization**
void initializeDisplay() {
Serial.println("Starting display initialization...");
lastDisplayedMode = (ControlMode)(-1);
tft.fillScreen(COLOR_BLACK);
Serial.println("Screen filled with black");
tft.setTextSize(1);
calculateEncoderPositions();
Serial.println("Encoder positions calculated");
drawStaticElements();
Serial.println("Static elements drawn");
if (controlMode == NOTE_MODE) {
Serial.println("Initializing NOTE_MODE");
pianoRollInitialized = false;
lastDrawnPlayhead = 255;
drawPianoRoll();
drawModeTitle();
drawMaxMinVisual();
displayNeedsInit = false;
return;
}
Serial.print("Initializing mode: ");
Serial.println(controlMode);
Serial.println("Drawing full Control Mode display...");
Serial.println(" - Drawing track display...");
updateTrackDisplay();
Serial.println(" - Drawing encoder knobs...");
for (int i = 0; i < NUM_ENCODERS; i++) {
drawEncoderKnob(i, true);
}
Serial.print(" - Drew ");
Serial.print(NUM_ENCODERS);
Serial.println(" encoder knobs");
Serial.println(" - Drawing mode title...");
drawModeTitle();
Serial.println(" - Drawing Max/Min visual...");
drawMaxMinVisual();
if (controlMode == MACRO_MODE) {
const char* title = "MACRO";
const char* subLabel = "";
int activeCount = 0;
for (int i = 0; i < NUM_ENCODERS; i++) {
if (macroEncodersActive) activeCount++;
}
if (activeCount > 0) {
char macroLabel[24];
snprintf(macroLabel, sizeof(macroLabel), " (%d CCs)", activeCount);
subLabel = macroLabel;
}
tft.setFont(NULL);
tft.setTextColor(COLOR_WHITE);
tft.setTextSize(1);
tft.setCursor(10, 220);
tft.print(title);
if (strlen(subLabel) > 0) {
tft.setCursor(10, 235);
tft.setTextColor(COLOR_LIGHTGREY);
tft.print(subLabel);
}
}
Serial.println("Full Control Mode display complete!");
displayNeedsInit = false;
}
void drawEncoderKnob(int encoderIndex, bool forceUpdate) {
if (!forceUpdate && !encoderNeedsUpdate[encoderIndex]) return;
int cx = encoderPositions[encoderIndex].x;
int cy = encoderPositions[encoderIndex].y;
drawEncoderCircle(encoderIndex);
float currentValue;
if (controlMode == DURATION_MODE) {
currentValue = map(currentSequenceDuration[encoderIndex], MIN_DURATION_STEPS, MAX_DURATION_STEPS, 0, 127);
} else if (controlMode == SEQUENCER_MODE) {
currentValue = currentSequence[encoderIndex];
} else if (controlMode == MACRO_MODE) {
if (macroEncodersActive[encoderIndex]) {
currentValue = allTracks[currentChannelIndex].macroSequences[encoderIndex][0];
} else {
currentValue = midiValues[encoderIndex];
}
} else {
currentValue = midiValues[encoderIndex];
}
float startAngle = 140.0;
float sweep = 270.0;
float valueRatio = currentValue / 127.0;
float angle = startAngle + valueRatio * sweep;
if (angle >= 360.0) angle -= 360.0;
float angleRad = angle * DEG_TO_RAD;
int x2 = cx + 15 * cos(angleRad);
int y2 = cy + 15 * sin(angleRad);
float perpAngleRad = angleRad + PI / 2;
int offsetX = round(cos(perpAngleRad));
int offsetY = round(sin(perpAngleRad));
uint16_t pointerColor = ((controlMode == SEQUENCER_MODE || controlMode == MACRO_MODE) && sequencerRunning && (encoderIndex == sequencerStep)) ? COLOR_GREEN : COLOR_WHITE;
tft.drawLine(cx, cy, x2, y2, pointerColor);
tft.drawLine(cx + offsetX, cy + offsetY, x2 + offsetX, y2 + offsetY, pointerColor);
encoderNeedsUpdate[encoderIndex] = false;
}
void drawEncoderCircle(int encoderIndex) {
int cx = encoderPositions[encoderIndex].x;
int cy = encoderPositions[encoderIndex].y;
int radius = encoderPositions[encoderIndex].radius;
uint16_t trackColor;
float currentValue;
if (controlMode == SEQUENCER_MODE) {
trackColor = (sequencerRunning && (encoderIndex == sequencerStep)) ? COLOR_GREEN : COLOR_WHITE;
currentValue = currentSequence[encoderIndex];
} else if (controlMode == DURATION_MODE) {
trackColor = (sequencerRunning && (encoderIndex == sequencerStep)) ? COLOR_GREEN : COLOR_WHITE;
currentValue = map(currentSequenceDuration[encoderIndex], MIN_DURATION_STEPS, MAX_DURATION_STEPS, 0, 127);
} else if (controlMode == MACRO_MODE) {
if (macroEncodersActive[encoderIndex]) {
trackColor = (currentChannel == 1) ? COLOR_GREEN : COLOR_BLUE;
currentValue = allTracks[currentChannelIndex].macroSequences[encoderIndex][0];
} else {
trackColor = COLOR_DARKGREY;
currentValue = midiValues[encoderIndex];
}
} else {
trackColor = (currentChannel == 1) ? COLOR_GREEN : COLOR_BLUE;
currentValue = midiValues[encoderIndex];
}
tft.fillCircle(cx, cy, radius + 5, COLOR_BLACK);
tft.drawCircle(cx, cy, radius, COLOR_DARKGREY);
tft.drawCircle(cx, cy, radius + 1, COLOR_DARKGREY);
if (currentValue > 0) {
float startAngle = 140.0;
float sweep = 270.0;
float progress = currentValue / 127.0;
float endAngle = startAngle + (progress * sweep);
if (endAngle >= 360.0) endAngle -= 360.0;
if (endAngle < startAngle) {
for (float angle = startAngle; angle < 360.0; angle += 2) {
float radians = angle * DEG_TO_RAD;
for (int thickness = 0; thickness < 2; thickness++) {
int x = cx + (radius + thickness - 1) * cos(radians);
int y = cy + (radius + thickness - 1) * sin(radians);
tft.drawPixel(x, y, trackColor);
}
}
for (float angle = 0; angle <= endAngle; angle += 2) {
float radians = angle * DEG_TO_RAD;
for (int thickness = 0; thickness < 2; thickness++) {
int x = cx + (radius + thickness - 1) * cos(radians);
int y = cy + (radius + thickness - 1) * sin(radians);
tft.drawPixel(x, y, trackColor);
}
}
} else {
for (float angle = startAngle; angle <= endAngle; angle += 2) {
float radians = angle * DEG_TO_RAD;
for (int thickness = 0; thickness < 2; thickness++) {
int x = cx + (radius + thickness - 1) * cos(radians);
int y = cy + (radius + thickness - 1) * sin(radians);
tft.drawPixel(x, y, trackColor);
}
}
}
}
}
---
Any help would be greatly appreciated!
I am working on a MIDI controller project with a Teensy 4.1. I upgraded the SPI display screen to a 8- bit 320x240 3.5" TFT CFAF320240F-035T. I am having trouble getting the MIDI controllers UI to display. I was able to incorporate a test sketch with blue, red and green color bars to move down the screen, but after the color bar sequence it goes black and then fades to white. So the screen is working but I can't get the controller’s UI with 8 encoders titled Control Mode to display on startup as it did before new screen. Here is the pin layout
Data pins on TFT D10– D11 D-12 D13- D14 D15- D16- D17:
ON Teensy 14, 15, 16, 17, 18, 19, 20 , 21
WR: 22
RD: 23
DC/RS: 2
CS: 3
RESET: 4
Backlight: 5
PS3 → GND
PS2 → 3.3 V
PS1 → GND
PS0 → 3.3V.
Here is the sketch
### **CODE BLOCK 1: Pin Definitions & Low-Level Functions**
```cpp
// ========================================
// 8-BIT TFT PIN DEFINITIONS (SSD2119)
// ========================================
constexpr int PIN_DC = 2; // RS / D-C
constexpr int PIN_CS = 3; // CS
constexpr int PIN_RST = 4; // RESET
constexpr int PIN_BL = 5; // Backlight
constexpr int PIN_WR = 22; // WR (active low)
constexpr int PIN_RD = 23; // RD (keep HIGH)
constexpr uint16_t TFT_W = 320;
constexpr uint16_t TFT_H = 240;
// Color constants
const uint16_t COLOR_BLACK = 0x0000;
const uint16_t COLOR_WHITE = 0xFFFF;
const uint16_t COLOR_RED = 0xF800;
const uint16_t COLOR_GREEN = 0x07E0;
const uint16_t COLOR_BLUE = 0x001F;
// ========================================
// 8-BIT TFT LOW-LEVEL FUNCTIONS
// ========================================
inline void wr_pulse() {
digitalWriteFast(PIN_WR, LOW);
delayMicroseconds(1);
digitalWriteFast(PIN_WR, HIGH);
delayMicroseconds(1);
}
inline void busWrite8(uint8_t v) {
for (int i = 0; i < 8; ++i) {
digitalWriteFast(DATA_PINS, (v >> i) & 1);
}
wr_pulse();
}
inline void writeIndex(uint16_t idx) {
digitalWriteFast(PIN_DC, LOW);
busWrite8(uint8_t(idx >> 8));
busWrite8(uint8_t(idx & 0xFF));
}
inline void writeData16(uint16_t val) {
digitalWriteFast(PIN_DC, HIGH);
busWrite8(uint8_t(val >> 8));
busWrite8(uint8_t(val & 0xFF));
}
inline void regWrite(uint16_t idx, uint16_t val) {
writeIndex(idx);
writeData16(val);
}
void tftReset() {
digitalWriteFast(PIN_RST, LOW);
delay(50);
digitalWriteFast(PIN_RST, HIGH);
delay(150);
}
```
---
### **CODE BLOCK 2: SSD2119 Initialization**
```cpp
void tftInit() {
digitalWriteFast(PIN_CS, LOW);
digitalWriteFast(PIN_RD, HIGH);
tftReset();
regWrite(0x0028, 0x0006);
regWrite(0x0000, 0x0001); // Start oscillator
delay(5);
regWrite(0x0010, 0x0000); // Sleep out
regWrite(0x0001, 0x72EF); // Driver output control
regWrite(0x0002, 0x0600); // LCD driving waveform
regWrite(0x0003, 0x6A38); // Power control
regWrite(0x000C, 0x0003);
regWrite(0x000D, 0x000A);
regWrite(0x000E, 0x2E00);
regWrite(0x001E, 0x00BE);
regWrite(0x000B, 0x5308);
regWrite(0x000F, 0x0000); // Gate scan start
regWrite(0x0025, 0x8000);
regWrite(0x0026, 0x7800);
regWrite(0x0011, 0x6870); // Entry mode
regWrite(0x0044, (239 << 8) | 0); // Vertical RAM address
regWrite(0x0045, 0); // Horizontal start
regWrite(0x0046, 319); // Horizontal end
regWrite(0x004E, 0); // Set GDDRAM X
regWrite(0x004F, 0); // Set GDDRAM Y
// Gamma settings
regWrite(0x0030, 0x0000);
regWrite(0x0031, 0x0104);
regWrite(0x0032, 0x0100);
regWrite(0x0033, 0x0305);
regWrite(0x0034, 0x0505);
regWrite(0x0035, 0x0305);
regWrite(0x0036, 0x0707);
regWrite(0x0037, 0x0300);
regWrite(0x003A, 0x1200);
regWrite(0x003B, 0x0800);
regWrite(0x0007, 0x0033); // Display on
delay(120);
}
inline void setXY(uint16_t x, uint16_t y) {
regWrite(0x004E, x);
regWrite(0x004F, y);
writeIndex(0x0022); // Prepare to write GRAM
}
void fillScreen_rowwise(uint16_t color) {
for (uint16_t y = 0; y < TFT_H; ++y) {
setXY(0, y);
uint8_t hi = color >> 8, lo = color & 0xFF;
digitalWriteFast(PIN_DC, HIGH);
for (uint16_t x = 0; x < TFT_W; ++x) {
busWrite8(hi);
busWrite8(lo);
}
}
}
```
---
### **CODE BLOCK 3: Hardware Initialization (in setup)**
```cpp
void initializeHardware() {
// Initialize 8-bit TFT pins
pinMode(PIN_DC, OUTPUT);
pinMode(PIN_CS, OUTPUT);
pinMode(PIN_RST, OUTPUT);
pinMode(PIN_BL, OUTPUT);
pinMode(PIN_WR, OUTPUT);
pinMode(PIN_RD, OUTPUT);
digitalWriteFast(PIN_BL, HIGH); // Backlight on
digitalWriteFast(PIN_WR, HIGH);
digitalWriteFast(PIN_RD, HIGH);
digitalWriteFast(PIN_CS, LOW); // CS stays LOW
for (int i = 0; i < 8; ++i) {
pinMode(DATA_PINS, OUTPUT);
digitalWriteFast(DATA_PINS, LOW);
}
// Initialize TFT
Serial.println("Initializing TFT...");
tft.begin(); // Calls tftInit()
// Color bar test - THIS WORKS FINE
Serial.println("Quick display test...");
tft.fillScreen(COLOR_BLACK);
delay(100);
tft.fillScreen(COLOR_RED);
delay(100);
tft.fillScreen(COLOR_GREEN);
delay(100);
tft.fillScreen(COLOR_BLUE);
delay(100);
tft.fillScreen(COLOR_BLACK);
Serial.println("Display test complete!");
tft.setRotation(0); // Landscape mode
}
```
---
### **CODE BLOCK 4: TFT Wrapper Class (Adafruit_GFX compatible)**
```cpp
class TFT_8bit : public Adafruit_GFX {
public:
TFT_8bit() : Adafruit_GFX(TFT_W, TFT_H) {}
void begin() {
tftInit();
}
void fillScreen(uint16_t color) {
fillScreen_rowwise(color);
}
void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
if((x >= TFT_W) || (y >= TFT_H)) return;
if((x+w-1) >= TFT_W) w = TFT_W-x;
if((y+h-1) >= TFT_H) h = TFT_H-y;
for(int16_t i=0; i<h; i++) {
setXY(x, y+i);
uint8_t hi = color >> 8, lo = color & 0xFF;
digitalWriteFast(PIN_DC, HIGH);
for(int16_t j=0; j<w; j++) {
busWrite8(hi);
busWrite8(lo);
}
}
}
void drawPixel(int16_t x, int16_t y, uint16_t color) {
if((x < 0) ||(x >= TFT_W) || (y < 0) || (y >= TFT_H)) return;
setXY(x, y);
writeData16(color);
}
void drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color) {
// Standard Bresenham circle algorithm
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);
}
}
};
TFT_8bit tft;
```
---
### **CODE BLOCK 5: The Problem - UI Initialization**
void initializeDisplay() {
Serial.println("Starting display initialization...");
lastDisplayedMode = (ControlMode)(-1);
tft.fillScreen(COLOR_BLACK);
Serial.println("Screen filled with black");
tft.setTextSize(1);
calculateEncoderPositions();
Serial.println("Encoder positions calculated");
drawStaticElements();
Serial.println("Static elements drawn");
if (controlMode == NOTE_MODE) {
Serial.println("Initializing NOTE_MODE");
pianoRollInitialized = false;
lastDrawnPlayhead = 255;
drawPianoRoll();
drawModeTitle();
drawMaxMinVisual();
displayNeedsInit = false;
return;
}
Serial.print("Initializing mode: ");
Serial.println(controlMode);
Serial.println("Drawing full Control Mode display...");
Serial.println(" - Drawing track display...");
updateTrackDisplay();
Serial.println(" - Drawing encoder knobs...");
for (int i = 0; i < NUM_ENCODERS; i++) {
drawEncoderKnob(i, true);
}
Serial.print(" - Drew ");
Serial.print(NUM_ENCODERS);
Serial.println(" encoder knobs");
Serial.println(" - Drawing mode title...");
drawModeTitle();
Serial.println(" - Drawing Max/Min visual...");
drawMaxMinVisual();
if (controlMode == MACRO_MODE) {
const char* title = "MACRO";
const char* subLabel = "";
int activeCount = 0;
for (int i = 0; i < NUM_ENCODERS; i++) {
if (macroEncodersActive) activeCount++;
}
if (activeCount > 0) {
char macroLabel[24];
snprintf(macroLabel, sizeof(macroLabel), " (%d CCs)", activeCount);
subLabel = macroLabel;
}
tft.setFont(NULL);
tft.setTextColor(COLOR_WHITE);
tft.setTextSize(1);
tft.setCursor(10, 220);
tft.print(title);
if (strlen(subLabel) > 0) {
tft.setCursor(10, 235);
tft.setTextColor(COLOR_LIGHTGREY);
tft.print(subLabel);
}
}
Serial.println("Full Control Mode display complete!");
displayNeedsInit = false;
}
void drawEncoderKnob(int encoderIndex, bool forceUpdate) {
if (!forceUpdate && !encoderNeedsUpdate[encoderIndex]) return;
int cx = encoderPositions[encoderIndex].x;
int cy = encoderPositions[encoderIndex].y;
drawEncoderCircle(encoderIndex);
float currentValue;
if (controlMode == DURATION_MODE) {
currentValue = map(currentSequenceDuration[encoderIndex], MIN_DURATION_STEPS, MAX_DURATION_STEPS, 0, 127);
} else if (controlMode == SEQUENCER_MODE) {
currentValue = currentSequence[encoderIndex];
} else if (controlMode == MACRO_MODE) {
if (macroEncodersActive[encoderIndex]) {
currentValue = allTracks[currentChannelIndex].macroSequences[encoderIndex][0];
} else {
currentValue = midiValues[encoderIndex];
}
} else {
currentValue = midiValues[encoderIndex];
}
float startAngle = 140.0;
float sweep = 270.0;
float valueRatio = currentValue / 127.0;
float angle = startAngle + valueRatio * sweep;
if (angle >= 360.0) angle -= 360.0;
float angleRad = angle * DEG_TO_RAD;
int x2 = cx + 15 * cos(angleRad);
int y2 = cy + 15 * sin(angleRad);
float perpAngleRad = angleRad + PI / 2;
int offsetX = round(cos(perpAngleRad));
int offsetY = round(sin(perpAngleRad));
uint16_t pointerColor = ((controlMode == SEQUENCER_MODE || controlMode == MACRO_MODE) && sequencerRunning && (encoderIndex == sequencerStep)) ? COLOR_GREEN : COLOR_WHITE;
tft.drawLine(cx, cy, x2, y2, pointerColor);
tft.drawLine(cx + offsetX, cy + offsetY, x2 + offsetX, y2 + offsetY, pointerColor);
encoderNeedsUpdate[encoderIndex] = false;
}
void drawEncoderCircle(int encoderIndex) {
int cx = encoderPositions[encoderIndex].x;
int cy = encoderPositions[encoderIndex].y;
int radius = encoderPositions[encoderIndex].radius;
uint16_t trackColor;
float currentValue;
if (controlMode == SEQUENCER_MODE) {
trackColor = (sequencerRunning && (encoderIndex == sequencerStep)) ? COLOR_GREEN : COLOR_WHITE;
currentValue = currentSequence[encoderIndex];
} else if (controlMode == DURATION_MODE) {
trackColor = (sequencerRunning && (encoderIndex == sequencerStep)) ? COLOR_GREEN : COLOR_WHITE;
currentValue = map(currentSequenceDuration[encoderIndex], MIN_DURATION_STEPS, MAX_DURATION_STEPS, 0, 127);
} else if (controlMode == MACRO_MODE) {
if (macroEncodersActive[encoderIndex]) {
trackColor = (currentChannel == 1) ? COLOR_GREEN : COLOR_BLUE;
currentValue = allTracks[currentChannelIndex].macroSequences[encoderIndex][0];
} else {
trackColor = COLOR_DARKGREY;
currentValue = midiValues[encoderIndex];
}
} else {
trackColor = (currentChannel == 1) ? COLOR_GREEN : COLOR_BLUE;
currentValue = midiValues[encoderIndex];
}
tft.fillCircle(cx, cy, radius + 5, COLOR_BLACK);
tft.drawCircle(cx, cy, radius, COLOR_DARKGREY);
tft.drawCircle(cx, cy, radius + 1, COLOR_DARKGREY);
if (currentValue > 0) {
float startAngle = 140.0;
float sweep = 270.0;
float progress = currentValue / 127.0;
float endAngle = startAngle + (progress * sweep);
if (endAngle >= 360.0) endAngle -= 360.0;
if (endAngle < startAngle) {
for (float angle = startAngle; angle < 360.0; angle += 2) {
float radians = angle * DEG_TO_RAD;
for (int thickness = 0; thickness < 2; thickness++) {
int x = cx + (radius + thickness - 1) * cos(radians);
int y = cy + (radius + thickness - 1) * sin(radians);
tft.drawPixel(x, y, trackColor);
}
}
for (float angle = 0; angle <= endAngle; angle += 2) {
float radians = angle * DEG_TO_RAD;
for (int thickness = 0; thickness < 2; thickness++) {
int x = cx + (radius + thickness - 1) * cos(radians);
int y = cy + (radius + thickness - 1) * sin(radians);
tft.drawPixel(x, y, trackColor);
}
}
} else {
for (float angle = startAngle; angle <= endAngle; angle += 2) {
float radians = angle * DEG_TO_RAD;
for (int thickness = 0; thickness < 2; thickness++) {
int x = cx + (radius + thickness - 1) * cos(radians);
int y = cy + (radius + thickness - 1) * sin(radians);
tft.drawPixel(x, y, trackColor);
}
}
}
}
}
---
Any help would be greatly appreciated!