Hi everyone, i’ve been experimenting with my Arduino Mega 2560 and a 2.8” touchscreen + SD card ILI9341 shield that i bought from Robotistan.
I noticed that this shield was super different from the ones on the internet, i spent some time figuring it out.
I discovered that the XP XM YP YM touch pins overlap with the LCD control pins somehow? Here’s what i used:
#define YP A3
#define XM A2
#define YM 9
#define XP 8
TouchScreen ts(XP, YP, XM, YM, 300);
MCUFRIEND_kbv tft(A3, A2, A1, A0, A4);
Since i’m new to the touchscreen + TFT tech i asked AI to give me a simple PDA-like interface as if it was from Windows CE XP.
The code includes a menu, files, settings and an about screens.
Here’s the code i’m using right now:
#include <MCUFRIEND_kbv.h>
#include <TouchScreen.h>
#include <EEPROM.h>
#define TS_MINX 920
#define TS_MINY 120
#define TS_MAXX 150
#define TS_MAXY 940
#define YP A3
#define XM A2
#define YM 9
#define XP 8
#define MINPRESSURE 10
#define MAXPRESSURE 1000
TouchScreen ts(XP, YP, XM, YM, 300);
MCUFRIEND_kbv tft(A3, A2, A1, A0, A4);
// Colors
#define BLACK 0x0000
#define WHITE 0xFFFF
#define RED 0xF800
#define GREEN 0x07E0
#define BLUE 0x001F
#define CYAN 0x07FF
#define MAGENTA 0xF81F
#define GREY 0xC618
#define ORANGE 0xFD20
// Screen States
enum Screen { MENU, FILES, SETTINGS, ABOUT };
Screen currentScreen = MENU;
// Menu buttons
struct Button { int x, y, w, h; uint16_t color; const char* label; };
Button menuButtons[] = {
{30,60,180,50,ORANGE,"Files"},
{30,130,180,50,CYAN,"Settings"},
{30,200,180,50,MAGENTA,"About"},
{30,270,180,50,GREEN,"Exit"}
};
const int numButtons = sizeof(menuButtons)/sizeof(menuButtons[0]);
// Exit bar
Button exitBar = {200,5,40,20,RED,"X"};
// Files app
uint16_t currentColor = RED;
uint16_t colorPalette[5] = {RED, GREEN, BLUE, CYAN, MAGENTA};
Button colorButtons[5], eraserButton, clearButton, saveButton, loadButton;
// Layout
#define CANVAS_TOP 30
#define CANVAS_BOTTOM 250
#define TOOLBAR_TOP 250
#define TOOLBAR_BOTTOM 320
#define CANVAS_WIDTH 240
#define SCROLLBAR_WIDTH 10
int canvasOffsetY = 0;
#define EEPROM_WIDTH 16
#define EEPROM_HEIGHT 10
int fontSize = 2;
// Prototypes
void drawMenu(), drawFiles(), drawSettings(), drawAbout(), exitOS();
void handleMenuTouch(int,int), handleFilesTouch(int,int), handleSettingsTouch(int,int);
void drawButton(Button), saveCanvasEEPROM(), loadCanvasEEPROM(), drawScrollbar();
void setup(){
Serial.begin(9600);
uint16_t ID = tft.readID();
if(ID==0xD3D3) ID=0x9341;
tft.begin(ID);
tft.setRotation(0);
tft.fillScreen(GREY);
tft.fillRect(0,0,CANVAS_WIDTH,30,BLUE);
tft.setTextColor(WHITE); tft.setTextSize(fontSize);
tft.setCursor(10,8); tft.print("PDA OS");
drawMenu();
for(int i=0;i<5;i++) colorButtons[i] = {10 + i*45, TOOLBAR_TOP+5, 40, 30, colorPalette[i], ""};
eraserButton = {10 + 5*45, TOOLBAR_TOP+5, 40, 30, WHITE, "E"};
clearButton = {60 + 5*45, TOOLBAR_TOP+5, 50, 30, GREY, "C"};
saveButton = {120, TOOLBAR_TOP+5, 60, 30, ORANGE, "Save"};
loadButton = {190, TOOLBAR_TOP+5, 60, 30, CYAN, "Load"};
}
void loop(){
TSPoint p = ts.getPoint();
pinMode(XM, OUTPUT); pinMode(YP, OUTPUT);
if(p.z > MINPRESSURE && p.z < MAXPRESSURE){
int x = map(p.x, TS_MINX, TS_MAXX, 0, tft.width());
int y = map(p.y, TS_MINY, TS_MAXY, 0, tft.height()) + 10;
if(y > tft.height()) y = tft.height();
if(x>=exitBar.x && x<=exitBar.x+exitBar.w && y>=exitBar.y && y<=exitBar.y+exitBar.h){
exitOS(); return;
}
switch(currentScreen){
case MENU: handleMenuTouch(x,y); break;
case FILES: handleFilesTouch(x,y); break;
case SETTINGS: handleSettingsTouch(x,y); break;
case ABOUT: currentScreen=MENU; drawMenu(); break;
}
}
}
void drawButton(Button b){
tft.fillRoundRect(b.x,b.y,b.w,b.h,8,b.color);
tft.drawRoundRect(b.x,b.y,b.w,b.h,8,WHITE);
if(strlen(b.label)>0){
tft.setTextColor(BLACK); tft.setTextSize(2);
tft.setCursor(b.x+5,b.y+7); tft.print(b.label);
}
}
void drawScrollbar(){
int sbHeight = map(CANVAS_BOTTOM-CANVAS_TOP,0,(CANVAS_BOTTOM-CANVAS_TOP)*2,0,CANVAS_BOTTOM-CANVAS_TOP);
int sbY = map(canvasOffsetY,0,(CANVAS_BOTTOM-CANVAS_TOP),0,CANVAS_BOTTOM-CANVAS_TOP-sbHeight);
tft.fillRect(CANVAS_WIDTH-SCROLLBAR_WIDTH, CANVAS_TOP+sbY, SCROLLBAR_WIDTH, sbHeight, GREY);
}
void drawMenu(){
tft.fillScreen(GREY);
tft.fillRect(0,0,CANVAS_WIDTH,30,BLUE);
tft.setTextColor(WHITE); tft.setTextSize(fontSize);
tft.setCursor(10,8); tft.print("Ahmet OS");
for(int i=0;i<numButtons;i++) drawButton(menuButtons[i]);
drawButton(exitBar);
}
void drawFiles(){
tft.fillRect(0,CANVAS_TOP,CANVAS_WIDTH,CANVAS_BOTTOM-CANVAS_TOP,WHITE);
for(int i=0;i<5;i++) drawButton(colorButtons[i]);
drawButton(eraserButton); drawButton(clearButton);
drawButton(saveButton); drawButton(loadButton);
drawButton(exitBar);
drawScrollbar();
}
void drawSettings(){
tft.fillScreen(GREY);
tft.setTextColor(WHITE); tft.setTextSize(2);
tft.setCursor(10,50); tft.print("Settings:");
tft.setCursor(10,80); tft.print("Font size:");
tft.fillRect(120,80,40,30,BLUE); tft.setTextColor(WHITE);
tft.setCursor(125,85); tft.print(fontSize);
tft.setCursor(10,120); tft.print("Touch to change font size");
drawButton(exitBar);
}
void drawAbout(){
tft.fillScreen(GREY); tft.setTextColor(WHITE); tft.setTextSize(2);
tft.setCursor(10,50); tft.print("Ahmet's Mini OS");
tft.setCursor(10,80); tft.print("Touch anywhere to go back");
drawButton(exitBar);
}
void exitOS(){ asm volatile (" jmp 0"); }
void handleMenuTouch(int x,int y){
for(int i=0;i<numButtons;i++){
Button b = menuButtons[i];
if(x>=b.x && x<=b.x+b.w && y>=b.y && y<=b.y+b.h){
switch(i){
case 0: currentScreen=FILES; drawFiles(); break;
case 1: currentScreen=SETTINGS; drawSettings(); break;
case 2: currentScreen=ABOUT; drawAbout(); break;
case 3: exitOS(); break;
}
}
}
}
void handleFilesTouch(int x,int y){
static int lastX=-1,lastY=-1;
if(y>=TOOLBAR_TOP){
for(int i=0;i<5;i++){ Button b=colorButtons[i];
if(x>=b.x && x<=b.x+b.w && y>=b.y && y<=b.y+b.h){ currentColor=colorPalette[i]; return; }
}
if(x>=eraserButton.x && x<=eraserButton.x+eraserButton.w && y>=eraserButton.y && y<=eraserButton.y+eraserButton.h){
currentColor=WHITE; return;
}
if(x>=clearButton.x && x<=clearButton.x+clearButton.w && y>=clearButton.y && y<=clearButton.y+clearButton.h){
tft.fillRect(0,CANVAS_TOP,CANVAS_WIDTH,CANVAS_BOTTOM-CANVAS_TOP,WHITE); drawFiles(); return;
}
if(x>=saveButton.x && x<=saveButton.x+saveButton.w && y>=saveButton.y && y<=saveButton.y+saveButton.h){
saveCanvasEEPROM(); tft.setCursor(10, TOOLBAR_TOP+40); tft.setTextColor(BLACK); tft.setTextSize(2); tft.print("Saved!"); return;
}
if(x>=loadButton.x && x<=loadButton.x+loadButton.w && y>=loadButton.y && y<=loadButton.y+loadButton.h){
loadCanvasEEPROM(); tft.setCursor(10, TOOLBAR_TOP+40); tft.setTextColor(BLACK); tft.setTextSize(2); tft.print("Loaded!"); return;
}
}
if(y>=CANVAS_TOP && y<CANVAS_BOTTOM){
if(lastX!=-1 && lastY!=-1) tft.drawLine(lastX,lastY,x,y,currentColor);
else tft.fillCircle(x,y,2,currentColor);
lastX=x; lastY=y;
} else { lastX=-1; lastY=-1; }
}
void handleSettingsTouch(int x,int y){ fontSize++; if(fontSize>5) fontSize=1; drawSettings(); }
void saveCanvasEEPROM(){
for(int i=0;i<EEPROM_WIDTH;i++){
for(int j=0;j<EEPROM_HEIGHT;j++){
int sx=i*(CANVAS_WIDTH/EEPROM_WIDTH), sy=j*((CANVAS_BOTTOM-CANVAS_TOP)/EEPROM_HEIGHT)+CANVAS_TOP;
uint16_t c=tft.readPixel(sx,sy);
EEPROM.put((i*EEPROM_HEIGHT+j)*2,c);
}
}
}
void loadCanvasEEPROM(){
for(int i=0;i<EEPROM_WIDTH;i++){
for(int j=0;j<EEPROM_HEIGHT;j++){
uint16_t c; EEPROM.get((i*EEPROM_HEIGHT+j)*2,c);
int sx=i*(CANVAS_WIDTH/EEPROM_WIDTH), sy=j*((CANVAS_BOTTOM-CANVAS_TOP)/EEPROM_HEIGHT)+CANVAS_TOP;
tft.fillRect(sx,sy,CANVAS_WIDTH/EEPROM_WIDTH,(CANVAS_BOTTOM-CANVAS_TOP)/EEPROM_HEIGHT,c);
}
}
drawFiles();
}
What’s working well:
- Navigation between menus work very well.
- Paint works half-like (the colors and exit bars etc. are cramped and overlapped on the bottom of the screen)
- Basic drawing works
Issues: - Tab alignment is bad
- About tab's text visibility isn't that great
- EEPROM drawing storage, it has a 16x10 grid. Is there any better way to save and load drawings much faster and better?
- Pin setup, it looks like i am using the correct pins for the touchscreen, i'm not going to include the screen since that's handled by MCUFRIEND, but i also want to try something else than MCUFRIEND.
I’m new to this type of project and haven’t been able to find tutorials that fit my shield and my goals. Any advice or examples on improving UI alignment, text visibility and drawing storage would be appreciated a lot!
Thanks in advance,
THE_COM3_PORT
Note: my timezone is GMT+3 so i might not see your responses. Excuse me for that: