Arduino TFT LCD Çok Amaçlı Aktivite Merkezi Yapımı

👤 Yazar: ozcan 📅 Tarih: 15.07.2026 13:26 👁️ Görüntüleme: 18

Arduino TFT LCD Çok Amaçlı Aktivite Merkezi Yapımı

Bu projemizde, Arduino Uno ve dokunmatik TFT LCD ekran modülünü bir üst seviyeye taşıyarak bir "Aktivite Merkezi" tasarlıyoruz. Durum makinesi (State Machine) mimarisiyle yazılan bu gelişmiş proje; dinamik bir ana menü, renk paletli ve fırça boyutlu bir resim çizim atölyesi, Arduino'ya karşı oynayabileceğiniz yapay zeka algoritmalı bir XOX (Tic-Tac-Toe) oyunu ve ekran sapmalarını sıfırlayan EEPROM kayıtlı 5 noktalı hassas kalibrasyon menüsünü tek bir kod içinde birleştiriyor.

resim çizme ve xox oyunu + kalibrasyon

Görsel: TFT LCD Ekran Üzerinde Ana Menü ve Alt Uygulama Arayüzleri

Projenin Gelişmiş Modülleri ve Çalışma Mantığı

Proje, ekran verimliliğini ve kullanıcı deneyimini artırmak için nesne yönelimli mantıkla tasarlanmış 4 ana modülden oluşur:

  • 1. Durum Makinesi Tabanlı Ana Menü (State Machine): Kod içerisindeki enum EkranDurumu yapısı sayesinde Arduino o an hangi ekranda olduğunu bilir. Menü butonlarına dokunulduğunda klik efekti (görsel geri bildirim) tetiklenir ve ilgili mod sorunsuz bir şekilde başlatılır.
  • 2. Gelişmiş Resim Atölyesi (Paint Mode): Üst kısımda 5 farklı renk paleti barındırır. Ek olarak en sağ alanda bir silgi modülü yer alır. Alt menüden ekran tamamen temizlenebilir (Clear), fırça boyutu döngüsel olarak S, M ve L (Küçük, Orta, Büyük) boyutlarına ayarlanabilir ve ana menüye güvenli dönüş sağlanır.
  • 3. Akıllı XOX Oyunu (Tic-Tac-Toe): Oyuncu 'X' sembolüyle hamle yaparken, sıra Arduino'ya geçtiğinde kod bir yapay zeka mantığı işletir. Arduino önce kendi kazanma hamlesini kontrol eder, ardından oyuncunun kazanma hamlesini engeller (bloklar), merkez hücreyi kapmaya çalışır ve en son boş hücrelere rastgele yönelir. Skorlar üst barda anlık güncellenir.
  • 4. 5-Noktalı Hassas Kalibrasyon ve EEPROM: Rezistif ekranların en büyük sorunu olan dokunmatik sapmaları engellemek için "En Küçük Kareler Yöntemi (Least Squares)" ile çalışan 5 noktalı bir kalibrasyon algoritması içerir. Başarılı test çiziminden sonra katsayılar Arduino'nun kalıcı hafızasına (EEPROM) yazılır, böylece cihaz kapatılıp açılsa bile ayarlar kaybolmaz.

Donanımsal İpucu: Paylaşımlı Pin Yönetimi

Kritik Bilgi: TFT LCD ekranların kontrol pinleri ile dokunmatik panelin okuma pinleri (A1, A2, 6, 7 nolu pinler) donanımsal olarak ortaktır. Dokunmatik panelden veri okunduktan sonra ekranın düzgün çizim yapabilmesi için pinlerin tekrar çıkış (OUTPUT) yapılması ve HIGH seviyesine çekilmesi gerekir. Kod yapımız bu döngüyü otomatik olarak yöneterek ekranın kilitlenmesini engeller.

Arduino Proje Kodları

#include "TFTLCD.h"
#include "TouchScreen.h"
#include <EEPROM.h>

// Dokunmatik Ekran Donanımsal Pinleri
#define YP A1
#define XM A2
#define YM 7
#define XP 6

TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);

// TFT LCD Kontrol Pinleri
#define LCD_CS A3
#define LCD_CD A2
#define LCD_WR A1
#define LCD_RD A0
#define LCD_RESET A4

// 16-bit Gelişmiş Renk Paleti (RGB565 formatı)
#define BLACK     0x0000
#define BLUE      0x001F
#define RED       0xF800
#define GREEN     0x07E0
#define WHITE     0xFFFF
#define YELLOW    0xFFE0
#define CYAN      0x07FF
#define MAGENTA   0xF81F
#define GRAY      0x7BEF
#define DARK_GRAY 0x31A6

TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);

// Kalibrasyon Katsayıları
float tsA = 0.3117, tsB = 0.0, tsC = -46.75;
float tsD = 0.0, tsE = -0.39, tsF = 366.8;

struct CalibData {
  byte magic; // Kalibrasyon geçerlilik imzası (0xCD)
  float A; float B; float C;
  float D; float E; float F;
};

// Durum Makinesi (State Machine) Tanımları
enum EkranDurumu { MENU, PAINT, GAME, CALIB };
EkranDurumu mevcutDurum = MENU;

// Resim Atölyesi Değişkenleri
uint16_t cizimRengi = GREEN;
int fircaBoyutu = 3;

// XOX Oyunu Değişkenleri
char xoxTahta[3][3];
char siraKimde = 'X'; // Oyuncu = 'X', Arduino = 'O'
int oyuncuSkor = 0;
int arduinoSkor = 0;
bool oyunBitti = false;
String oyunSonucMesaji = "";

#define MINPRESSURE 10
#define MAXPRESSURE 1000

void setup(void) {
  Serial.begin(9600);

  tft.reset();
  tft.initDisplay();
  tft.setRotation(0); // Dikey Ekran Yönü (240x320)

  // EEPROM'dan kalibrasyon değerlerini yükle
  eepromYukle();

  // Ana Menüyü Çiz
  anaMenuyuCiz();
}

void loop() {
  TSPoint p = ts.getPoint();

  // Paylaşımlı pinlerin ekran kontrolü için çıkış yapılması
  pinMode(XM, OUTPUT);
  pinMode(YP, OUTPUT);
  digitalWrite(XM, HIGH);
  digitalWrite(YP, HIGH);

  if (p.z > MINPRESSURE && p.z < MAXPRESSURE) {
    // Kalibre edilmiş hassas koordinatlar
    int x = tsA * p.x + tsB * p.y + tsC;
    int y = tsD * p.x + tsE * p.y + tsF;

    x = constrain(x, 0, tft.width() - 1);
    y = constrain(y, 0, tft.height() - 1);

    // Mevcut duruma göre dokunma işlemlerini yürüt
    switch (mevcutDurum) {
      case MENU:
        anaMenuDokunmaYonet(x, y);
        break;
      case PAINT:
        resimAtolyesiDokunmaYonet(x, y);
        break;
      case GAME:
        xoxOyunuDokunmaYonet(x, y);
        break;
      case CALIB:
        // Kalibrasyon kendi iç döngüsünü yönetir
        break;
    }
  }
}

void anaMenuyuCiz() {
  mevcutDurum = MENU;
  tft.fillScreen(BLACK);

  // Başlık Şeridi
  tft.fillRect(0, 2, tft.width(), 45, BLUE);
  tft.drawString(3, 16, "TFT AKTIVITE MERKEZI", WHITE, 2);

  // Buton 1: Resim Atölyesi
  tft.fillRect(15, 75, 210, 55, DARK_GRAY);
  tft.drawRect(15, 75, 210, 55, WHITE);
  tft.drawString(45, 93, "RESIM ATOLYESI", GREEN, 2);

  // Buton 2: XOX Oyunu
  tft.fillRect(15, 150, 210, 55, DARK_GRAY);
  tft.drawRect(15, 150, 210, 55, WHITE);
  tft.drawString(65, 168, "XOX OYUNU", CYAN, 2);

  // Buton 3: Kalibrasyon
  tft.fillRect(15, 225, 210, 55, DARK_GRAY);
  tft.drawRect(15, 225, 210, 55, WHITE);
  tft.drawString(45, 243, "EKRAN AYARLARI", YELLOW, 2);

  // Alt Bilgi
  tft.drawString(30, 300, "Secmek icin ekrana dokunun.", GRAY, 1);
}

void anaMenuDokunmaYonet(int x, int y) {
  // Resim Atölyesi Seçimi
  if (x > 15 && x < 225 && y > 75 && y < 130) {
    butonGeriBildirimEfekti(15, 75, 210, 55);
    resimAtolyesiniBaslat();
  }
  // XOX Oyunu Seçimi
  else if (x > 15 && x < 225 && y > 150 && y < 205) {
    butonGeriBildirimEfekti(15, 150, 210, 55);
    xoxOyununuBaslat();
  }
  // Kalibrasyon Seçimi
  else if (x > 15 && x < 225 && y > 225 && y < 280) {
    butonGeriBildirimEfekti(15, 225, 210, 55);
    mevcutDurum = CALIB;
    kalibrasyonYonetimi();
  }
}

void resimAtolyesiniBaslat() {
  mevcutDurum = PAINT;
  tft.fillScreen(BLACK);

  // Çizim Alanı Sınırları (Üst ve alt menüler hariç)
  tft.drawRect(0, 50, 240, 220, GRAY);

  // Üst Menü: Renk Paletleri
  // Renk kutuları (X: 5'ten başlayarak 35px genişlik, 35px yükseklik, 10px boşluk)
  uint16_t renkler[] = {RED, GREEN, BLUE, YELLOW, WHITE};
  for (int i = 0; i < 5; i++) {
    tft.fillRect(5 + (i * 45), 5, 35, 35, renkler[i]);
    tft.drawRect(5 + (i * 45), 5, 35, 35, GRAY);
  }
  // Silgi Butonu (En sağda)
  tft.fillRect(230, 5, 10, 35, BLACK);
  tft.drawRect(230, 5, 10, 35, WHITE);
  
  // Alt Menü Kontrolleri
  // "TEMIZLE" Butonu
  tft.fillRect(5, 280, 80, 35, RED);
  tft.drawString(12, 290, "TEMIZLE", WHITE, 1);

  // Fırça Boyutu Butonu
  tft.fillRect(95, 280, 60, 35, DARK_GRAY);
  tft.drawString(102, 290, "FIRCA: M", WHITE, 1);

  // "GERI" Butonu
  tft.fillRect(165, 280, 70, 35, BLUE);
  tft.drawString(185, 290, "GERI", WHITE, 1);
  
  cizimRengi = GREEN;
  fircaBoyutu = 3;
}

void resimAtolyesiDokunmaYonet(int x, int y) {
  // Üst Palet Seçimleri (Y: 5 - 40)
  if (y > 5 && y < 40) {
    for (int i = 0; i < 5; i++) {
      int btnX = 5 + (i * 45);
      if (x > btnX && x < (btnX + 35)) {
        uint16_t renkler[] = {RED, GREEN, BLUE, YELLOW, WHITE};
        cizimRengi = renkler[i];
        return;
      }
    }
    // Silgi seçimi (En sağdaki küçük alan)
    if (x > 225) {
      cizimRengi = BLACK;
    }
  }

  // Alt Buton Alanları (Y: 280 - 315)
  else if (y > 280 && y < 315) {
    // TEMIZLE Butonu
    if (x > 5 && x < 85) {
      tft.fillRect(1, 51, 238, 218, BLACK);
      delay(150);
    }
    // FIRCA BOYUTU Butonu
    else if (x > 95 && x < 155) {
      if (fircaBoyutu == 1) {
        fircaBoyutu = 3;
        tft.fillRect(95, 280, 60, 35, DARK_GRAY);
        tft.drawString(102, 290, "FIRCA: M", WHITE, 1);
      } else if (fircaBoyutu == 3) {
        fircaBoyutu = 6;
        tft.fillRect(95, 280, 60, 35, DARK_GRAY);
        tft.drawString(102, 290, "FIRCA: L", WHITE, 1);
      } else {
        fircaBoyutu = 1;
        tft.fillRect(95, 280, 60, 35, DARK_GRAY);
        tft.drawString(102, 290, "FIRCA: S", WHITE, 1);
      }
      delay(200);
    }
    // GERI Butonu
    else if (x > 165 && x < 235) {
      anaMenuyuCiz();
      delay(200);
    }
  }

  // Çizim Alanı İçinde Kalınma Durumu (Y: 52 - 268)
  else if (y > (50 + fircaBoyutu) && y < (270 - fircaBoyutu) && x > fircaBoyutu && x < (240 - fircaBoyutu)) {
    tft.fillCircle(x, y, fircaBoyutu, cizimRengi);
  }
}

void xoxOyununuBaslat() {
  mevcutDurum = GAME;
  tft.fillScreen(BLACK);

  // Üst Bar (Skorlar ve Geri Butonu)
  tft.fillRect(0, 2, tft.width(), 40, BLUE);
  tft.drawString(10, 15, "SKOR: OYUNCU ", WHITE, 1);
  tft.drawString(100, 15, String(oyuncuSkor).c_str(), GREEN, 1);
  tft.drawString(120, 15, " - ", WHITE, 1);
  tft.drawString(140, 15, String(arduinoSkor).c_str(), RED, 1);
  tft.drawString(155, 15, " ARDUINO", WHITE, 1);

  // "GERI" Menü Butonu (Sağ Üstte)
  tft.fillRect(200, 7, 35, 30, RED);
  tft.drawString(208, 18, "<", WHITE, 1);

  // Oyun Tahtasını Sıfırla
  for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
      xoxTahta[i][j] = ' ';
    }
  }
  oyunBitti = false;
  siraKimde = 'X';
  oyunSonucMesaji = "";

  xoxGridCiz();
}

void xoxGridCiz() {
  tft.fillRect(30, 70, 180, 180, BLACK);
  tft.drawRect(30, 70, 180, 180, WHITE);

  // Dikey Çizgiler
  tft.drawLine(90, 70, 90, 250, WHITE);
  tft.drawLine(150, 70, 150, 250, WHITE);

  // Yatay Çizgiler
  tft.drawLine(30, 130, 210, 130, WHITE);
  tft.drawLine(30, 190, 210, 190, WHITE);

  // Alt Durum Bilgisi Alanı
  tft.fillRect(10, 260, 220, 55, BLACK);
  if (!oyunBitti) {
    tft.drawString(45, 275, "SIRA SIZDE (X)", CYAN, 2);
  }
}

void xoxOyunuDokunmaYonet(int x, int y) {
  if (x > 200 && x < 235 && y > 7 && y < 37) {
    anaMenuyuCiz();
    delay(200);
    return;
  }

  if (oyunBitti) {
    xoxOyununuBaslat();
    delay(250);
    return;
  }

  if (siraKimde == 'X') {
    if (x >= 30 && x <= 210 && y >= 70 && y <= 250) {
      int sutun = (x - 30) / 60;
      int satir = (y - 70) / 60;

      if (xoxTahta[satir][sutun] == ' ') {
        xoxTahta[satir][sutun] = 'X';
        sembolCiz(satir, sutun, 'X');
        
        if (xoxDurumKontrol()) {
          oyunSonlandir();
        } else {
          siraKimde = 'O';
          tft.fillRect(10, 260, 220, 55, BLACK);
          tft.drawString(30, 275, "ARDUINO DUSUNUYOR...", RED, 1);
          delay(800); 
          arduinoHamlesiYap();
        }
      }
    }
  }
}

void arduinoHamlesiYap() {
  if (oyunBitti) return;

  int secilenSatir = -1;
  int secilenSutun = -1;

  for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
      if (xoxTahta[i][j] == ' ') {
        xoxTahta[i][j] = 'O';
        if (kontrolKazanma('O')) {
          secilenSatir = i;
          secilenSutun = j;
          xoxTahta[i][j] = ' '; 
          break;
        }
        xoxTahta[i][j] = ' '; 
      }
    }
    if (secilenSatir != -1) break;
  }

  if (secilenSatir == -1) {
    for (int i = 0; i < 3; i++) {
      for (int j = 0; j < 3; j++) {
        if (xoxTahta[i][j] == ' ') {
          xoxTahta[i][j] = 'X';
          if (kontrolKazanma('X')) {
            secilenSatir = i;
            secilenSutun = j;
            xoxTahta[i][j] = ' '; 
            break;
          }
          xoxTahta[i][j] = ' '; 
        }
      }
      if (secilenSatir != -1) break;
    }
  }

  if (secilenSatir == -1) {
    if (xoxTahta[1][1] == ' ') {
      secilenSatir = 1;
      secilenSutun = 1;
    }
  }

  if (secilenSatir == -1) {
    for (int i = 0; i < 3; i++) {
      for (int j = 0; j < 3; j++) {
        if (xoxTahta[i][j] == ' ') {
          secilenSatir = i;
          secilenSutun = j;
          break;
        }
      }
      if (secilenSatir != -1) break;
    }
  }

  if (secilenSatir != -1 && secilenSutun != -1) {
    xoxTahta[secilenSatir][secilenSutun] = 'O';
    sembolCiz(secilenSatir, secilenSutun, 'O');
    
    if (xoxDurumKontrol()) {
      oyunSonlandir();
    } else {
      siraKimde = 'X';
      tft.fillRect(10, 260, 220, 55, BLACK);
      tft.drawString(45, 275, "SIRA SIZDE (X)", CYAN, 2);
    }
  }
}

void sembolCiz(int satir, int sutun, char tur) {
  int merkezX = 30 + (sutun * 60) + 30;
  int merkezY = 70 + (satir * 60) + 30;

  if (tur == 'X') {
    tft.drawLine(merkezX - 15, merkezY - 15, merkezX + 15, merkezY + 15, CYAN);
    tft.drawLine(merkezX + 15, merkezY - 15, merkezX - 15, merkezY + 15, CYAN);
  } else {
    tft.drawCircle(merkezX, merkezY, 15, RED);
  }
}

bool kontrolKazanma(char oyuncu) {
  for (int i = 0; i < 3; i++) {
    if (xoxTahta[i][0] == oyuncu && xoxTahta[i][1] == oyuncu && xoxTahta[i][2] == oyuncu) return true;
  }
  for (int j = 0; j < 3; j++) {
    if (xoxTahta[0][j] == oyuncu && xoxTahta[1][j] == oyuncu && xoxTahta[2][j] == oyuncu) return true;
  }
  if (xoxTahta[0][0] == oyuncu && xoxTahta[1][1] == oyuncu && xoxTahta[2][2] == oyuncu) return true;
  if (xoxTahta[0][2] == oyuncu && xoxTahta[1][1] == oyuncu && xoxTahta[2][0] == oyuncu) return true;
  return false;
}

bool xoxDurumKontrol() {
  if (kontrolKazanma('X')) {
    oyunSonucMesaji = "KAZANDINIZ! :)";
    oyuncuSkor++;
    oyunBitti = true;
    return true;
  }
  if (kontrolKazanma('O')) {
    oyunSonucMesaji = "ARDUINO KAZANDI! :(";
    arduinoSkor++;
    oyunBitti = true;
    return true;
  }

  bool tahtaDolu = true;
  for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
      if (xoxTahta[i][j] == ' ') tahtaDolu = false;
    }
  }

  if (tahtaDolu) {
    oyunSonucMesaji = "BERABERE BITTID!";
    oyunBitti = true;
    return true;
  }
  return false;
}

void oyunSonlandir() {
  tft.fillRect(10, 260, 220, 55, BLACK);
  tft.drawString(15, 270, oyunSonucMesaji.c_str(), YELLOW, 2);
  tft.drawString(30, 295, "Yeni Oyun icin Dokun", WHITE, 1);
  siraKimde = 'X';
}

void kalibrasyonYonetimi() {
  tft.fillScreen(BLACK);
  tft.fillRect(0, 2, tft.width(), 38, BLUE);
  tft.drawString(35, 14, "EKRAN AYARLARI", WHITE, 2);

  tft.drawString(15, 60, "Ekraninizi kalibre", WHITE, 2);
  tft.drawString(15, 85, "etmek ister misiniz?", WHITE, 2);

  tft.fillRect(15, 140, 210, 45, GREEN);
  tft.drawRect(15, 140, 210, 45, WHITE);
  tft.drawString(55, 154, "KALIBRE ET", BLACK, 2);

  tft.fillRect(15, 205, 210, 45, RED);
  tft.drawRect(15, 205, 210, 45, WHITE);
  tft.drawString(75, 219, "VAZGEC", WHITE, 2);

  while (true) {
    TSPoint p = ts.getPoint();
    pinMode(XM, OUTPUT);
    pinMode(YP, OUTPUT);
    digitalWrite(XM, HIGH);
    digitalWrite(YP, HIGH);

    if (p.z > MINPRESSURE && p.z < MAXPRESSURE) {
      int tx = tsA * p.x + tsB * p.y + tsC;
      int ty = tsD * p.x + tsE * p.y + tsF;
      tx = constrain(tx, 0, tft.width() - 1);
      ty = constrain(ty, 0, tft.height() - 1);

      if (tx > 15 && tx < 225 && ty > 140 && ty < 185) {
        dokunmaBirakilanaKadarBekle();
        kalibrasyonYap5Nokta();
        break;
      }
      if (tx > 15 && tx < 225 && ty > 205 && ty < 250) {
        dokunmaBirakilanaKadarBekle();
        anaMenuyuCiz();
        break;
      }
    }
    delay(50);
  }
}

void kalibrasyonYap5Nokta() {
  tft.fillScreen(BLACK);
  tft.drawString(30, 100, "5-NOKTALI HASSAS", CYAN, 2);
  tft.drawString(30, 130, "KALIBRASYON", CYAN, 2);
  tft.drawString(30, 160, "BASLIYOR...", WHITE, 2);
  delay(1500);

  long tX[5] = {30,  210, 30,  210, 120}; 
  long tY[5] = {40,  40,  280, 280, 160}; 
  long rX[5] = {0, 0, 0, 0, 0};
  long rY[5] = {0, 0, 0, 0, 0};

  const char* adimIsimleri[5] = {
    "1/5: SOL-UST HEDEF",
    "2/5: SAG-UST HEDEF",
    "3/5: SOL-ALT HEDEF",
    "4/5: SAG-ALT HEDEF",
    "5/5: ORTA HEDEF"
  };

  for (int i = 0; i < 5; i++) {
    hedefCiz(tX[i], tY[i], adimIsimleri[i]);
    
    long rawXSum = 0;
    long rawYSum = 0;
    int basariliOkuma = 0;
    
    while (true) {
      TSPoint p = ts.getPoint();
      pinMode(XM, OUTPUT);
      pinMode(YP, OUTPUT);
      digitalWrite(XM, HIGH);
      digitalWrite(YP, HIGH);
      if (p.z > MINPRESSURE && p.z < MAXPRESSURE) {
        delay(100); 
        break;
      }
    }

    for (int numSamples = 0; numSamples < 16; numSamples++) {
      TSPoint p = ts.getPoint();
      pinMode(XM, OUTPUT);
      pinMode(YP, OUTPUT);
      digitalWrite(XM, HIGH);
      digitalWrite(YP, HIGH);
      if (p.z > MINPRESSURE && p.z < MAXPRESSURE) {
        rawXSum += p.x;
        rawYSum += p.y;
        basariliOkuma++;
      }
      delay(15);
    }

    rX[i] = rawXSum / basariliOkuma;
    rY[i] = rawYSum / basariliOkuma;

    dokunmaBirakilanaKadarBekle();
  }

  float nA, nB, nC, nD, nE, nF;
  if (calculateLeastSquares(rX, rY, tX, tY, nA, nB, nC, nD, nE, nF)) {
    dogrulamaVeTestEkrani(nA, nB, nC, nD, nE, nF);
  } else {
    tft.fillScreen(BLACK);
    tft.drawString(20, 120, "HATA: KALIBRASYON", RED, 2);
    tft.drawString(20, 150, "BASARISIZ OLDU!", RED, 2);
    delay(2000);
    kalibrasyonYap5Nokta();
  }
}

bool calculateLeastSquares(long rx[], long ry[], long tx[], long ty[], float &outA, float &outB, float &outC, float &outD, float &outE, float &outF) {
  double Sxx = 0, Sxy = 0, Sx = 0, Syy = 0, Sy = 0;
  double Vx0 = 0, Vx1 = 0, Vx2 = 0;
  double Vy0 = 0, Vy1 = 0, Vy2 = 0;

  for (int i = 0; i < 5; i++) {
    double x = rx[i];
    double y = ry[i];
    double X = tx[i];
    double Y = ty[i];

    Sxx += x * x;
    Sxy += x * y;
    Sx  += x;
    Syy += y * y;
    Sy  += y;

    Vx0 += x * X;
    Vx1 += y * X;
    Vx2 += X;

    Vy0 += x * Y;
    Vy1 += y * Y;
    Vy2 += Y;
  }

  double a = Sxx; double b = Sxy; double c = Sx;
  double e = Syy; double f = Sy;  double k = 5.0;

  double det = a * (e * k - f * f) - b * (b * k - c * f) + c * (b * f - c * e);

  if (abs(det) < 0.0001) {
    return false; 
  }

  double m00 = e * k - f * f;
  double m01 = c * f - b * k;
  double m02 = b * f - c * e;
  double m11 = a * k - c * c;
  double m12 = b * c - a * f;
  double m22 = a * e - b * b;

  outA = (m00 * Vx0 + m01 * Vx1 + m02 * Vx2) / det;
  outB = (m01 * Vx0 + m11 * Vx1 + m12 * Vx2) / det;
  outC = (m02 * Vx0 + m12 * Vx1 + m22 * Vx2) / det;

  outD = (m00 * Vy0 + m01 * Vy1 + m02 * Vy2) / det;
  outE = (m01 * Vy0 + m11 * Vy1 + m12 * Vy2) / det;
  outF = (m02 * Vy0 + m12 * Vy1 + m22 * Vy2) / det;

  return true;
}

void hedefCiz(int x, int y, const char* baslik) {
  tft.fillScreen(BLACK);
  tft.drawString(10, 10, baslik, CYAN, 2);
  
  tft.drawLine(x - 20, y, x + 20, y, RED);
  tft.drawLine(x, y - 20, x, y + 20, RED);
  tft.drawCircle(x, y, 6, WHITE);
  tft.drawCircle(x, y, 12, BLUE);
  tft.drawCircle(x, y, 18, WHITE);
  
  tft.drawString(15, 140, "Kalem ucuyla tam", WHITE, 2);
  tft.drawString(15, 170, "merkeze dokunup", WHITE, 2);
  tft.drawString(15, 200, "bekleyin.", WHITE, 2);
}

void dogrulamaVeTestEkrani(float nA, float nB, float nC, float nD, float nE, float nF) {
  tft.fillScreen(BLACK);
  tft.drawRect(5, 5, 230, 220, GRAY);
  tft.drawString(10, 15, "TEST ALANI: Ekrana", CYAN, 1);
  tft.drawString(10, 30, "dokunarak cizgi cizin.", CYAN, 1);

  tft.fillRect(10, 240, 105, 50, GREEN);
  tft.drawRect(10, 240, 105, 50, WHITE);
  tft.drawString(25, 258, "KAYDET", BLACK, 2);

  tft.fillRect(125, 240, 105, 50, RED);
  tft.drawRect(125, 240, 105, 50, WHITE);
  tft.drawString(135, 258, "YENIDEN", WHITE, 2);

  bool kararVerildi = false;

  while (!kararVerildi) {
    TSPoint p = ts.getPoint();
    pinMode(XM, OUTPUT);
    pinMode(YP, OUTPUT);
    digitalWrite(XM, HIGH);
    digitalWrite(YP, HIGH);

    if (p.z > MINPRESSURE && p.z < MAXPRESSURE) {
      int testX = nA * p.x + nB * p.y + nC;
      int testY = nD * p.x + nE * p.y + nF;

      testX = constrain(testX, 0, tft.width() - 1);
      testY = constrain(testY, 0, tft.height() - 1);

      if (testX > 8 && testX < 228 && testY > 8 && testY < 220) {
        tft.fillCircle(testX, testY, 2, GREEN);
      }

      if (testX > 10 && testX < 115 && testY > 240 && testY < 290) {
        dokunmaBirakilanaKadarBekle();
        
        tsA = nA; tsB = nB; tsC = nC;
        tsD = nD; tsE = nE; tsF = nF;

        CalibData data;
        data.magic = 0xCD;
        data.A = tsA; data.B = tsB; data.C = tsC;
        data.D = tsD; data.E = tsE; data.F = tsF;
        EEPROM.put(0, data);

        tft.fillScreen(BLACK);
        tft.drawString(30, 120, "KALIBRASYON", GREEN, 2);
        tft.drawString(30, 150, "KAYDEDILDI!", GREEN, 2);
        delay(1500);
        kararVerildi = true;
        anaMenuyuCiz();
      }

      if (testX > 125 && testX < 230 && testY > 240 && testY < 290) {
        dokunmaBirakilanaKadarBekle();
        kalibrasyonYap5Nokta(); 
        kararVerildi = true;
      }
    }
  }
}

void eepromYukle() {
  CalibData data;
  EEPROM.get(0, data);

  if (data.magic == 0xCD) {
    tsA = data.A; tsB = data.B; tsC = data.C;
    tsD = data.D; tsE = data.E; tsF = data.F;
    Serial.println("Kayitli kalibrasyon verileri yuklendi.");
  } else {
    tsA = 0.3117; tsB = 0.0; tsC = -46.75;
    tsD = 0.0; tsE = -0.39; tsF = 366.8;
    Serial.println("Gecerli kalibrasyon yok! Varsayilan yuklendi.");
  }
}

void dokunmaBirakilanaKadarBekle() {
  delay(100);
  bool basili = true;
  while (basili) {
    TSPoint p = ts.getPoint();
    pinMode(XM, OUTPUT);
    pinMode(YP, OUTPUT);
    digitalWrite(XM, HIGH);
    digitalWrite(YP, HIGH);
    if (p.z < MINPRESSURE || p.z > MAXPRESSURE) {
      basili = false;
    }
    delay(30);
  }
  delay(100);
}

void butonGeriBildirimEfekti(int bx, int by, int bw, int bh) {
  tft.drawRect(bx, by, bw, bh, GREEN);
  delay(120);
  tft.drawRect(bx, by, bw, bh, WHITE);
}

Sonuç ve Geliştirme Önerileri

Bu proje, gömülü sistemlerde GUI (Grafik Kullanıcı Arayüzü) geliştirmenin ve çoklu görev yönetiminin mantığını anlamak adına eksiksiz bir kütüphanedir. Eğer projeyi daha da büyütmek isterseniz, EEPROM yapısını genişleterek XOX oyunundaki skorları kalıcı hafızaya kaydedebilir ya da Aktivite Merkezi'ne yeni bir durum (state) ekleyerek basit bir dijital saat veya alarm modülü entegre edebilirsiniz.

💬 Yorumlar

Henüz yorum yapılmamış. İlk yorumu siz yapın!

Bir Yorum Bırakın

Güvenlik Kontrolü:

Yenidir.com
↑ Sayfa Başı
📞 İletişim ✉ E-Posta Gönder 🟥 YouTube
© 2026 Yenidir.com
Yenidir CMS V0.1