Windows на Arduino

Внизу представлен код программы для Arduino Uno и сенсорного TFT LCD экрана. Данная программа содержит несколько подпрограмм, придающие LCD-дисплею, подключенную к Arduino UNO, внешний вид операционной системы Windows 9x. В ней имеется рабочий стол с ярлыком на калькулятор. Так же имеется панель задач и меню «Пуск». При нажатии на меню «Пуск» открывается меню с возможностью запуска нужного приложения из доступных. На данный момент имеются только три приложения:

Меню «Пуск» и внешний вид рабочего стола

  • графический редактор;
  • Калькулятор;
  • Игра пинпонг.

Запущенная игра

Конец игры

Калькулятор

Графический редактор

Завершение работы

Выключение компьютера

Разумеется эти приложения максимально упрощены.

#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_TFTLCD.h> // Hardware-specific library
#include <TouchScreen.h>
#if defined(__SAM3X8E__)
    #undef __FlashStringHelper::F(string_literal)
    #define F(string_literal) string_literal
#endif
//Определение граничных значений сенсора
#define TS_MINX 150
#define TS_MINY 120
#define TS_MAXX 920
#define TS_MAXY 940
//Определение выводов дисплея
#define YP A3  // must be an analog pin, use "An" notation!
#define XM A2  // must be an analog pin, use "An" notation!
#define YM 9   // can be a digital pin
#define XP 8   // can be a digital pin
TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);
#define LCD_CS A3
#define LCD_CD A2
#define LCD_WR A1
#define LCD_RD A0
// optional
#define LCD_RESET A4

// Определение цветов:
#define  BLACK   0x0000
#define BLUE    0x001F
#define RED     0xF800
#define GREEN   0x07E0
#define CYAN    0x07FF
#define MAGENTA 0xF81F
#define YELLOW  0xFFE0
#define WHITE   0xFFFF
#define GRAY   0b1000010000010000
#define LIGHTGRAY   0b1110011100011100
//Объявления объекта дисплея
Adafruit_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);

//int oldcolor, currentcolor;
int xm;
int ym;
bool nazh;
bool nazh1;

void(* resetFunc) (void) = 0;//объявление функцию reset с адресом 0

void form(int x1,int y1,int x2,int y2, String caption){//Windows-форма
  //tft.fillRect(x1, y1, x2, 30, LIGHTGRAY);
  tft.fillRect(x1+1, y1+1, x2-1, 29, BLUE);//Заголовок окна
  tft.drawRect(x1, y1, x2, 30, LIGHTGRAY);
  tft.fillRect(x1+x2-30, y1+1, 29, 28, RED);//Кнопка закрытия окна
  tft.setCursor(x1+x2-24, y1-3);
  tft.setTextColor(WHITE);
  tft.setTextSize(4);
  tft.print('x');
  tft.setCursor(x1+14, y1+6);//Вывод заголовка окна
  tft.setTextColor(WHITE);
  tft.setTextSize(2);
  tft.print(caption);
  tft.fillRect(x1+1, y1+31, x2-1, y2, LIGHTGRAY);//Само окно
}
void button(int x,int y,int w,int h, String caption, bool cl){//Кнопка
  tft.fillRect(x+1, y+1, w-2, h-2, LIGHTGRAY);
  if (cl){//RКогда она нажата
    tft.drawFastHLine(x, y,w,GRAY);
    tft.drawFastHLine(x, y+h,w,WHITE);
    tft.drawFastVLine(x, y,h,GRAY);
    tft.drawFastVLine(x+w, y,h,WHITE);
    tft.setCursor(x+4, y+3);
    tft.setTextColor(YELLOW);
    tft.setTextSize(2);
    tft.print(caption);
  
    }
  else {//Когда она отпущена
    tft.drawFastHLine(x, y,w,WHITE);
    tft.drawFastHLine(x, y+h,w,GRAY);
    tft.drawFastVLine(x, y,h,WHITE);
    tft.drawFastVLine(x+w, y,h,GRAY);
    tft.setCursor(x+4, y+3);
    tft.setTextColor(BLACK);
    tft.setTextSize(2);
    tft.print(caption);

    }
}
void edit(int x,int y,int w, String caption){//Однострочный редактор
  tft.fillRect(x+1, y+1, w-2, 25, YELLOW);
  tft.drawFastHLine(x, y,w,GRAY);
  tft.drawFastHLine(x, y+25,w,WHITE);
  tft.drawFastVLine(x, y,25,GRAY);
  tft.drawFastVLine(x+w, y,25,WHITE);
  tft.setCursor(x+4, y+3);
  tft.setTextColor(BLACK);
  tft.setTextSize(2);
  tft.print(caption);
}
#define MINPRESSURE 10
#define MAXPRESSURE 1000

bool ButtonNazh(int x,int y,int w,int h, String caption){//Для отслежживания события нажатия на кнопку
  if ((xm>x)&&(xm<x+w)&&(ym>y)&&(ym<y+h)&&nazh) {
    button(x,y,w,h,  caption,  1);
    delay(300);
    button(x,y,w,h,  caption,  0);
    return(1);
  } else return(0);
}
bool CE=1;
void events(){//Слежение за событиями вообще
  pinMode(13, OUTPUT);
  digitalWrite(13, HIGH);
  TSPoint p = ts.getPoint();//Считывание из сенсора экрана
  digitalWrite(13, LOW);
  pinMode(XM, OUTPUT);
  pinMode(YP, OUTPUT); 
  nazh1=nazh;
  nazh=p.z > MINPRESSURE && p.z < MAXPRESSURE;
  p.x = map(p.x, TS_MINX, TS_MAXX, tft.width(), 0);
  p.y = map(p.y, TS_MINY, TS_MAXY, tft.height(), 0);
  xm=p.x;//x-координата нажатия
  ym=p.y;//y-координата нажатия
  
}
void ShowMessage(String msg){//вывод небольших сообщений в окне.
  form(30, 100, 180, 100, "");
  tft.setCursor(40, 150);
  tft.setTextColor(BLACK);
  tft.setTextSize(2);
  tft.print(msg);
  button(70, 180, 90, 30, "   Ok", false);
  CE=false;
  while (not CE){
    events();
    CE=ButtonNazh(70, 180, 90, 30, "   Ok");
    CE=CE||((nazh)&&(xm>180)&&(xm<210)&&(ym>100)&&(ym<130));
  }
}
void Pinpong(){//Игра пинпонг
  int px=100;
  int py=280;
  int shx=100;
  int shy=100;
  int vx=2;
  int vy=2;
  int r=10;
  form(1,0,240,319,"Pinpong.EXE");
  tft.fillRect(3, 35, 235, 290, WHITE);
  tft.fillRect(px, py, 50, 10, RED);
  
  CE=false;
  while (not CE){
    events();
    CE=nazh && xm>211 && ym< 28;
    if ((nazh)&&(xm<100)){
      tft.fillRect(px, py, 50, 10, WHITE);
      px=px-5;
      if (px<0) px=0;
      tft.fillRect(px, py, 50, 10, RED);
    }
    if ((nazh)&&(xm>120)){
      tft.fillRect(px, py, 50, 10, WHITE);
      px=px+5;
      if (px>190) px=190;
      tft.fillRect(px, py, 50, 10, RED);
    }

    tft.fillRect(shx, shy, r, r, WHITE);
    shx=shx+vx;
    shy=shy+vy;
    tft.fillRect(shx, shy, r, r, BLUE);
    if ((shx>235)||(shx<5)) vx=-vx;
    if (shy<35) vy=-vy;
    if ((shy>py-r)&&(shy<py+r)&&(shx>px)&&(shx<px+50)) vy=-vy;
    delay(20);
    if (shy>py+r) {
      ShowMessage ("Game Over");
      Pinpong();
    }
  }
  resetFunc();
}
void desktop(){//Рабочий стол
  tft.fillScreen(CYAN);
  button(0,290,240,30,"",0);
  button(2,292,65,25,"Start",0);
  button(80,100,45,65,"",0);
  tft.fillRect(90, 110, 30, 5, YELLOW);
  edit (50,175,100,"CALC.EXE")  ;
}
void setup(void) {
  tft.reset();//Сброс дисплея
  uint16_t identifier = tft.readID();
  tft.begin(identifier);//Инициализация дисплея
  desktop();//Прорисовка рабочего стола
}

long ch=0;
long ch1=0;
int dey=0;
long calc(){
  switch (dey){
    case 0: ch=ch1;  
      break;
    case 1: ch=ch+ch1;  
      break;
    case 2: ch=ch-ch1;  
      break;
    case 3: ch=ch*ch1;  
      break;
    case 4: ch=ch/ch1;  
      break;
  } 
  ch1=0;
  edit(15,50,200,String(ch));

}

void CalcEXE(){//Калькулятор
  form(1,0,240,280,"Calc.EXE");
  for (int i=1; i<10; i++) {
    int x=(i-1)%3*60+7;
    int y=(i-1)/3*30+180;
    button(x,y,55,25,String(i),0);
  }
  button(185,180,50,25,"+",0);
  button(185,210,50,25,"-",0);
  button(185,240,50,25,"*",0);
  button(185,270,50,25,"/",0);
  button(7,270,55,25,"0",0);
  button(67,270,115,25,"=",0);
  button(7,150,115,25,"C",0);
  edit(15,50,200,"");
  CE=false;
  while (not CE){
    events();
    CE=nazh && xm>211 && ym< 28;
    for (int i=1; i<10; i++) {
      int x=(i-1)%3*60+7;
      int y=(i-1)/3*30+180;
      if (ButtonNazh(x,y,55,25,String(i))) {
        ch1=ch1*10+i;
        edit(15,50,200,String(ch1));
      }
    }
    if (ButtonNazh(185,180,50,25,"+")){
      calc();
      dey=1;
    };
    if (ButtonNazh(185,210,50,25,"-")){
      calc();
      dey=2;
    };
    if (ButtonNazh(185,240,50,25,"*")){
      calc();
      dey=3;
    };
    if (ButtonNazh(185,270,50,25,"/")){
      calc();
      dey=4;
    };
    if (ButtonNazh(7,270,55,25,"0")){
      ch1=ch1*10;
      edit(15,50,200,String(ch1));
    };
    if (ButtonNazh(67,270,115,25,"=")){
      calc();
      };
    if (ButtonNazh(7,150,115,25,"C")){
      ch1=0;
      ch=0;
      dey=0;
      edit(15,50,200,"");
      
    };
  
  }  
  ch1=0;
  ch=0;
  dey=0;
  resetFunc();
}
void MSPaintEXE(){//Графический редактор
  int colors[6]={RED, YELLOW, GREEN, CYAN, BLUE, MAGENTA};
  form(1,0,240,319,"Paint.EXE");
  tft.fillRect(5, 35, 230, 240, WHITE);
  for (int i=0; i<7; i++){
      tft.fillRect(i*40, 280, 40, 40, colors[i]);
  }
  int currentcolor = RED;
  CE=false;
  while (not CE){
    events();
    CE=nazh && xm>211 && ym< 28;
    for (int i=0; i<7; i++)
      if (nazh && xm>i*40 && ym>280 && xm< i*40+40){
        currentcolor=colors[i];
        tft.drawRect(i*40, 280, 40, 40, WHITE);
      }
    if (nazh && xm>5 && ym>35 && xm<5+230 && ym<35+240)
      tft.fillRect(xm-2,ym-2,4,4,currentcolor);
  }
  resetFunc();
}
void shutdoun(){//Завершение работы
  desktop();
  form(20,40,200,150,"Shutdown");
  button(40,80,150,30,"Shutdown",0);
  button(40,115,150,30,"Restart",0);
  button(40,160,150,30,"Cancel",0);
  CE=false;
  while (not CE){
    events();
    CE=nazh && xm>200 && xm<220 && ym>40 && ym < 60;
    if (ButtonNazh(40,80,150,30,"Shutdown")){//Выключение
      tft.fillScreen(BLACK);
      tft.setCursor(20, 100);
      tft.setTextColor(RED);
      tft.setTextSize(2);
      tft.print("It's now safe to ");
      tft.setCursor(20, 120);
      tft.print("turn off");
      tft.setCursor(20, 140);
      tft.print("your computer");
      while(1) {};
    }
    CE=ButtonNazh(40,115,150,30,"Restart");//Перезагрузка
    CE=CE||ButtonNazh(40,160,150,30,"Cancel");//Отмена завершения работы
  
  }
  resetFunc();//Перезагрузка

}
void start(){//Меню пуск
  CE=false;
  button(20,262,125,25,"shutdown",0);//Элементы меню
  button(20,238,125,25,"Paint.exe",0);
  button(20,214,125,25,"Calc.exe",0);
  button(20,190,125,25,"Pinpong",0);
  tft.fillRect(1, 190, 19, 100, BLUE);
  
  while (not CE){
    events();
    CE=nazh;
    if (ButtonNazh(20,262,125,25,"shutdown")) shutdoun();//Отслеживание событий с меню пуск.
    if (ButtonNazh(20,238,125,25,"Paint.exe")) MSPaintEXE();
    if (ButtonNazh(20,214,125,25,"Calc.exe")) CalcEXE();
    if (ButtonNazh(20,190,125,25,"Pinpong")) Pinpong();
  }
  desktop();
}
void loop()
{
  events();
  if (nazh && xm>80 && ym> 100 && xm<125 && ym<165) CalcEXE();//Нажали ли на ярлык на рабочем столе
  if (ButtonNazh(2,292,65,25,"Start")) start();//Нажатие на пуск
  if (nazh&&(xm<65)&&(ym>270)) {//Нажатие на пуск доп. из-за кривого сенсора
    while (nazh){events();}
    start();
  }
}