70歳の挑戦... テトリス風ゲーム　タッチ機能は諦めて、キー入力でブロック操作することに

　前回の続きです。

https://qiita.com/Mobu_Kyoto/items/a9902a1aac1fac9a6f5f

せっかくSTM32F103C8T6(with 128k,Bluepill)から自作テトリス風ゲームが動かせるようなので、タッチ機能で操作できないかと悪戦苦闘しました。しかし、出来そうにありません。諦めてシリアル通信に変更、一応完成させました。ついでにArduinoIDEのToolsで２点ほど試しました（右下の写真参照）。

    １．CDC(Communication Device Class)を用いたシリアル通信
    ２．Optimize: "Fastest(-O3) with LTO"

![F103_Tenoros.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3189926/8e27debe-5999-7c4d-ab25-14451adec706.jpeg)

１により別途USBシリアルモジュールが不要になります。２は微妙ですが、2～3％程速くなってると思われます。スケッチにおけるシリアル通信操作は以下のようにしました。

```c:
      if (Serial.available())
      {
        char c=Serial.read();
        if(c== 'a'){ bLeft(bNum);
        }else if(c == 'd') {bRight(bNum);
        }else if(c == 's') {bRot(&bNum); // bNum と&bNum の違いに意味はありません
        }else if(c == 'z') {jamp=true;
        }else if(c == 'x') {scene=RESET;}
      }
```
こちらの方がブロック操作は楽でした。特に「Tera Term」であればリターンキーを押さなくても逐一、キー送信してくれるので楽です。
　ピンの接続です。

|TFT SPI|BluePill|
|----|----|
|LED|3.3|
|TFT_SCK|PA5|
|TFT_MOSI|PA7|
|TFT_DC(RS)|PA2|
|TFT_RESET|PA3|
|TFT_CS|PA4|
|GND|G|
|VCC|5V|

　TFT_eSPIライブラリでの「User_Setup.h」とAeduinoIDEのスケッチを以下に載せます。
```html:User_Setup.h
#define USER_SETUP_INFO "User_Setup"

// Define STM32 to invoke optimised processor support (only for STM32)
#define STM32

#define ILI9488_DRIVER     // WARNING: Do not connect ILI9488 display SDO to MISO if other devices share the SPI bus (TFT SDO does NOT tristate when CS is high)

// ######       EDIT THE PINs BELOW TO SUIT YOUR STM32 SPI TFT SETUP        ######

#define STM_PORTA_DATA_BUS
// The TFT can be connected to SPI port 1 or 2
#define TFT_SPI_PORT 1 // SPI port 1 maximum clock rate is 55MHz
#define TFT_MOSI PA7
#define TFT_MISO PA6
#define TFT_SCLK PA5

#define TFT_DC   PA2 // Data Command control pin to TFT DC (may be labelled RS = Register Select)
#define TFT_RST  PA3 // Reset pin to TFT RST (or RESET)
#define TFT_CS   PA4 // Chip select control pin to TFT CS

#define TOUCH_CS  PA1     // Chip select pin (T_CS) of touch screen

#define LOAD_GLCD   // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
#define LOAD_FONT2  // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
#define LOAD_FONT4  // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
#define LOAD_FONT6  // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm
#define LOAD_FONT7  // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-.
#define LOAD_FONT8  // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-.
//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT
#define LOAD_GFXFF  // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts

// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded
// this will save ~20kbytes of FLASH
//#define SMOOTH_FONT

// ##################################################################################
//
// Section 4. Other options
//
// ##################################################################################

#define SPI_FREQUENCY  18000000
#define SPI_READ_FREQUENCY  10000000

#define SPI_TOUCH_FREQUENCY  1000000
```
タッチ機能の部分は実際には役に立っていません。次にスケッチを示します。

<details><summary>F103_Tenoros.ino</summary>

```c:F103_Tenoros.ino
#include <TFT_eSPI.h>  // Hardware-specific library

#define Y_H 24   // フィールドの高さ（上3段と最下段の壁を含む）
#define X_W 12   // フィールドの幅（両側の壁を含む）
#define Vars 32  // ブロックの種類（回転で得られるもの全部を別種とする）
#define PI 18    // 縦横pixel数 フィールド縦18x24=432 dots, フィールド横18x12=216 dots
#define Y0 0     // フィールド表示位置
#define X0 52
#define Y TFT_YELLOW
#define W TFT_WHITE
#define R TFT_RED
#define D TFT_DARKGREY
#define K TFT_BLACK
#define B TFT_BLUE
#define G TFT_GREEN
#define M TFT_MAGENTA

TFT_eSPI tft = TFT_eSPI();  // Invoke custom library

int field[Y_H][X_W];                                     // ブロックなどのデータを設定
int aline[Y_H];                                          // 横一列の固定ブロック数
int bXL = 0, bYU = 0, bW = 0, bH = 0, bXR = 0, bYD = 0;  // 現ブロックのデータの代入先
int nextB5[5];
int bNum = 0;
int timer = 0;
enum { TITLE,
       PLAY,
       OVER,
       RST };  // 各シーンを定める定数
int scene;       // どのシーンの処理を行うか

int bSizeX[Vars] = { 4, 1, 4, 1,  // 各ブロックの横サイズ
                     3, 2, 3, 2,
                     3, 2, 3, 2,
                     3, 2, 3, 2,
                     3, 2, 3, 2,
                     2, 2, 2, 2,
                     3, 2, 3, 2,
                     3, 2, 3, 2 };
int bSizeY[Vars] = { 1, 4, 1, 4,  // 各ブロックの縦サイズ
                     2, 3, 2, 3,
                     2, 3, 2, 3,
                     2, 3, 2, 3,
                     2, 3, 2, 3,
                     2, 2, 2, 2,
                     2, 3, 2, 3,
                     2, 3, 2, 3 };

int blocks[Vars][4][4] =  // variety, y, x
  { { { 1, 1, 1, 1 },     // I
      { 0, 0, 0, 0 },
      { 0, 0, 0, 0 },
      { 0, 0, 0, 0 } },
    { { 1, 0, 0, 0 },
      { 1, 0, 0, 0 },
      { 1, 0, 0, 0 },
      { 1, 0, 0, 0 } },
    { { 1, 1, 1, 1 },
      { 0, 0, 0, 0 },
      { 0, 0, 0, 0 },
      { 0, 0, 0, 0 } },
    { { 1, 0, 0, 0 },
      { 1, 0, 0, 0 },
      { 1, 0, 0, 0 },
      { 1, 0, 0, 0 } },
    { { 1, 1, 1, 0 },  // 逆L
      { 0, 0, 1, 0 },
      { 0, 0, 0, 0 },
      { 0, 0, 0, 0 } },
    { { 0, 1, 0, 0 },
      { 0, 1, 0, 0 },
      { 1, 1, 0, 0 },
      { 0, 0, 0, 0 } },
    { { 1, 0, 0, 0 },
      { 1, 1, 1, 0 },
      { 0, 0, 0, 0 },
      { 0, 0, 0, 0 } },
    { { 1, 1, 0, 0 },
      { 1, 0, 0, 0 },
      { 1, 0, 0, 0 },
      { 0, 0, 0, 0 } },
    { { 0, 0, 1, 0 },  // L
      { 1, 1, 1, 0 },
      { 0, 0, 0, 0 },
      { 0, 0, 0, 0 } },
    { { 1, 0, 0, 0 },
      { 1, 0, 0, 0 },
      { 1, 1, 0, 0 },
      { 0, 0, 0, 0 } },
    { { 1, 1, 1, 0 },
      { 1, 0, 0, 0 },
      { 0, 0, 0, 0 },
      { 0, 0, 0, 0 } },
    { { 1, 1, 0, 0 },
      { 0, 1, 0, 0 },
      { 0, 1, 0, 0 },
      { 0, 0, 0, 0 } },
    { { 1, 1, 1, 0 },  // T
      { 0, 1, 0, 0 },
      { 0, 0, 0, 0 },
      { 0, 0, 0, 0 } },
    { { 0, 1, 0, 0 },
      { 1, 1, 0, 0 },
      { 0, 1, 0, 0 },
      { 0, 0, 0, 0 } },
    { { 0, 1, 0, 0 },
      { 1, 1, 1, 0 },
      { 0, 0, 0, 0 },
      { 0, 0, 0, 0 } },
    { { 1, 0, 0, 0 },
      { 1, 1, 0, 0 },
      { 1, 0, 0, 0 },
      { 0, 0, 0, 0 } },
    { { 0, 1, 0, 0 },  // 逆T
      { 1, 1, 1, 0 },
      { 0, 0, 0, 0 },
      { 0, 0, 0, 0 } },
    { { 1, 0, 0, 0 },
      { 1, 1, 0, 0 },
      { 1, 0, 0, 0 },
      { 0, 0, 0, 0 } },
    { { 1, 1, 1, 0 },
      { 0, 1, 0, 0 },
      { 0, 0, 0, 0 },
      { 0, 0, 0, 0 } },
    { { 0, 1, 0, 0 },
      { 1, 1, 0, 0 },
      { 0, 1, 0, 0 },
      { 0, 0, 0, 0 } },
    { { 1, 1, 0, 0 },  // 正方形
      { 1, 1, 0, 0 },
      { 0, 0, 0, 0 },
      { 0, 0, 0, 0 } },
    { { 1, 1, 0, 0 },
      { 1, 1, 0, 0 },
      { 0, 0, 0, 0 },
      { 0, 0, 0, 0 } },
    { { 1, 1, 0, 0 },
      { 1, 1, 0, 0 },
      { 0, 0, 0, 0 },
      { 0, 0, 0, 0 } },
    { { 1, 1, 0, 0 },
      { 1, 1, 0, 0 },
      { 0, 0, 0, 0 },
      { 0, 0, 0, 0 } },
    { { 0, 1, 1, 0 },  // 稲妻
      { 1, 1, 0, 0 },
      { 0, 0, 0, 0 },
      { 0, 0, 0, 0 } },
    { { 1, 0, 0, 0 },
      { 1, 1, 0, 0 },
      { 0, 1, 0, 0 },
      { 0, 0, 0, 0 } },
    { { 0, 1, 1, 0 },
      { 1, 1, 0, 0 },
      { 0, 0, 0, 0 },
      { 0, 0, 0, 0 } },
    { { 1, 0, 0, 0 },
      { 1, 1, 0, 0 },
      { 0, 1, 0, 0 },
      { 0, 0, 0, 0 } },
    { { 1, 1, 0, 0 },  // 逆稲妻
      { 0, 1, 1, 0 },
      { 0, 0, 0, 0 },
      { 0, 0, 0, 0 } },
    { { 0, 1, 0, 0 },
      { 1, 1, 0, 0 },
      { 1, 0, 0, 0 },
      { 0, 0, 0, 0 } },
    { { 1, 1, 0, 0 },
      { 0, 1, 1, 0 },
      { 0, 0, 0, 0 },
      { 0, 0, 0, 0 } },
    { { 0, 1, 0, 0 },
      { 1, 1, 0, 0 },
      { 1, 0, 0, 0 },
      { 0, 0, 0, 0 } } };

void initField() {  // フィールド情報配列の初期化
  for (int i = 0; i < Y_H - 1; i++) {
    for (int j = 0; j < X_W; j++) {
      if (j == 0 || j == X_W - 1)
        field[i][j] = 9;
      else field[i][j] = 0;  // 何も無い所は0（黒）
    }
    aline[i] = 0;  // 横一列の情報
  }
  for (int j = 0; j < X_W; j++) field[Y_H - 1][j] = 9;  // 壁は9（濃い灰色）
}

void drawWall(void) {                   // 壁を描画
  for (int i = 3; i <= Y_H - 1; i++) {  // 0,1,2は壁なし
    if (i == Y_H - 1)
      for (int j = 0; j < X_W; j++)
        tft.fillRect(X0 + PI * j, Y0 + PI * i, PI - 2, PI - 2, D);
    else {
      tft.fillRect(X0 + PI * 0, Y0 + PI * i, PI - 2, PI - 2, D);
      tft.fillRect(X0 + PI * (X_W - 1), Y0 + PI * i, PI - 2, PI - 2, D);
    }
  }
}

void drawField(void) {  // フィールド情報を描画
  for (int i = 0; i < Y_H - 1; i++) {
    for (int j = 1; j < X_W - 1; j++) {
      if (field[i][j] == 1) {  // 移動中のブロックは黄
        tft.fillRect(X0 + PI * j, Y0 + PI * i, PI - 2, PI - 2, Y);
      } else if (field[i][j] == 2) {  // 固定ブロックは白
        tft.fillRect(X0 + PI * j, Y0 + PI * i, PI - 2, PI - 2, W);
      } else {  // == 9
        tft.fillRect(X0 + PI * j, Y0 + PI * i, PI - 2, PI - 2, K);
      }
    }
  }
}

void bRight(int n) {  // 右移動
  if (bXR < X_W - 2) {
    for (int y = bH - 1; y >= 0; y--) {
      for (int x = bW - 1; x >= 0; x--)
        if (field[bYU + y][bXL + x + 1] > 1) return;  // 固定物に衝突
    }
    for (int y = bH - 1; y >= 0; y--) {
      for (int x = bW - 1; x >= 0; x--) {
        if (field[bYU + y][bXL + x] < 2 && field[bYU + y][bXL + x + 1] < 2)  // 自分自身のみ移動
          field[bYU + y][bXL + x + 1] = field[bYU + y][bXL + x];             // フィールドデータ移動
        if (field[bYU + y][bXL + x] >= 2 && field[bYU + y][bXL + x + 1] < 2)
          field[bYU + y][bXL + x + 1] = 0;
      }
      if (field[bYU + y][bXL] < 2) field[bYU + y][bXL] = 0;
    }
    bXL++;
    bXR++;
  }
}

void bLeft(int n) {  // 左移動
  if (bXL > 1) {
    for (int y = bH - 1; y >= 0; y--) {
      for (int x = 0; x < bW; x++)
        if (field[bYU + y][bXL + x - 1] > 1 && field[bYU + y][bXL + x] == 1) return;  // 衝突
    }
    for (int y = bH - 1; y >= 0; y--) {
      for (int x = 0; x < bW; x++) {
        if (field[bYU + y][bXL + x - 1] < 2 && field[bYU + y][bXL + x] < 2)  // 自分自身のみ移動
          field[bYU + y][bXL + x - 1] = field[bYU + y][bXL + x];
        if (field[bYU + y][bXL + x - 1] < 2 && field[bYU + y][bXL + x] >= 2)
          field[bYU + y][bXL + x - 1] = 0;
      }
      if (field[bYU + y][bXR] < 2) field[bYU + y][bXR] = 0;
    }
    bXL--;
    bXR--;
  }
}

int rotCheck(int n0, int bXL, int bYU) {
  int l, m, n = n0;
  int bW, bH, bXR, bYD;

  l = (int)(n / 4) * 4;
  m = ((n % 4) + 1) % 4;
  n = l + m;       // 回転後のブロック番号でチェック
  bW = bSizeX[n];  // 各情報更新
  bH = bSizeY[n];
  bXR = bXL + bW - 1;
  bYD = bYU + bH - 1;
  if (bXR >= X_W - 1) {  // 右壁にぶつかったとき
    bXL -= 1;
    bXR -= 1;
  }
  if (bXL < 1) {  // 左壁にぶつかったとき
    bXL += 1;
    bXR += 1;
  }

  bool flg = true;  // Collision Check
  for (int y = 0; y < bH; y++) {
    for (int x = 0; x < bW; x++) {
      if (blocks[n][y][x] == 1 && field[bYU + y][bXL + x] > 1) {
        flg = false;  // ブロック描画位置に何かある
      }
    }
  }
  if (flg == true) return 1;
  else return 0;
}

void bRot(int* num) {  // 回転(右のみ)
  int l, m, n = *num;
  if (rotCheck(n, bXL, bYU) == 0) return;  // 回転できない

  for (int y = 0; y < bH; y++) {  // 前ブロックを消す
    for (int x = 0; x < bW; x++) {
      field[bYU + y][bXL + x] -= blocks[n][y][x];
      tft.fillRect(X0 + PI * (bXL + x), Y0 + PI * (bYU + y), PI - 2, PI - 2, D);
    }
  }

  l = (int)(n / 4) * 4;
  m = ((n % 4) + 1) % 4;
  n = l + m;       // 回転後の新ブロック番号
  bW = bSizeX[n];  // 各情報更新
  bH = bSizeY[n];
  bXR = bXL + bW - 1;
  bYD = bYU + bH - 1;
  if (bXR >= X_W - 1) {  // 右壁にぶつかったとき
    bXL -= 1;
    bXR -= 1;
  }
  if (bXL < 1) {  // 左壁にぶつかったとき
    bXL += 1;
    bXR += 1;
  }

  for (int y = 0; y < bH; y++) {  // 回転後のブロック描画
    for (int x = 0; x < bW; x++) {
      if (field[bYU + y][bXL + x] <= 1) {
        if (blocks[n][y][x] == 1) {
          field[bYU + y][bXL + x] = 1;
          tft.fillRect(X0 + PI * (bXL + x), Y0 + PI * (bYU + y), PI - 2, PI - 2, Y);
        } else if (blocks[n][y][x] == 0) {
          field[bYU + y][bXL + x] = 0;
          tft.fillRect(X0 + PI * (bXL + x), Y0 + PI * (bYU + y), PI - 2, PI - 2, K);
        }
      }
    }
  }
  *num = n;
}

int rakka(int n) {
  bool descentflg = true;
  bW = bSizeX[n];
  bH = bSizeY[n];
  for (int x = 0; x < bW; x++) {
    for (int y = bH - 1; y > bH - 4; y--) {
      if (blocks[n][y][x] == 1 && field[bYU + y + 1][bXL + x] > 1) descentflg = false;  // ブロック落下不可
    }
  }
  int sum;
  if (descentflg == false) {
    for (int y = 0; y < bH; y++) {
      for (int x = 0; x < bW; x++) {
        if (blocks[n][y][x] == 1) field[bYU + y][bXL + x] = 2;  // ブロック固定
      }
    }

    int TG[Y_H];
    for (int i = 0; i < Y_H; i++) { TG[i] = -1; }
    sum = 0;
    for (int i = Y_H - 2; i >= 0; i--) {  // 横一列揃ったか調べる
      aline[i] = 0;
      for (int j = 1; j < X_W - 1; j++) {
        if (field[i][j] == 2) aline[i] = aline[i] + 1;
      }
      if (aline[i] == X_W - 2) {  // == 10 揃った
        for (int j = 1; j < X_W - 1; j++)
          tft.fillRect(X0 + PI * j, Y0 + PI * i, PI - 2, PI - 2, M);  // MAGENTAで強調
        TG[sum] = i;
        sum++;
        delay(50);
      }
    }

    if (aline[3] > 0) return aline[3];  //--- GAME OVER ----------------

    if (sum > 0) {  // 揃った行を消す
      for (int t = 0; t < sum; t++) {
        for (int i = TG[t] + t; i > 0; i--) {
          for (int j = 1; j < X_W - 1; j++)
            tft.fillRect(X0 + PI * j, Y0 + PI * i, PI - 2, PI - 2, R);  // 赤で強調
          delay(50);
          for (int j = 1; j < X_W - 1; j++) {
            field[i][j] = field[i - 1][j];  // 消去行に上段のデータを移動、上書き
            if (field[i][j] == 0) {
              tft.fillRect(X0 + PI * j, Y0 + PI * i, PI - 2, PI - 2, K);
            } else {  // field[i][j] == 2 固定ブロックは描画
              tft.fillRect(X0 + PI * j, Y0 + PI * i, PI - 2, PI - 2, W);
            }
          }
          aline[i] = aline[i - 1];  // 固定ブロック数の行データも移動
        }
        for (int j = 1; j < X_W - 1; j++) field[0][j] = 0;  // 最上段は0で埋める
      }
    }
    sum = 0;
    descentflg = true;

    return -1;  // 次の新ブロックの生成落下

  } else {  // 落下可能
    bYU++;
    bYD++;
    for (int i = Y_H - 2; i >= 0; i--) {  // 最上段の0,1,2行もブロックを描画、落下させる
      if (i == 0) {
        for (int j = 1; j < X_W - 1; j++) field[i][j] = 0;  // 最上段は0で埋める
      } else {
        for (int j = 1; j < X_W - 1; j++) {
          if (field[i][j] >= 2) { ; }  // 壁、固定物があるので移動できない
          else if (field[i - 1][j] == 2) {
            ;
          }  // 固定物は移動しない
          else {
            field[i][j] = field[i - 1][j];  // 一行下に移動
            field[i - 1][j] = 0;            // 移動後は空欄
          }
        }
      }
    }

    int tY = bYU;
    bool flg = true;
    while (flg) {
      tY += 1;
      for (int y = 0; y < bH; y++) {
        for (int x = 0; x < bW; x++) {  // 現状、どこまで落下できるか調べる
          if (field[tY + y][bXL + x] > 1 && blocks[n][y][x] == 1) {
            flg = false;
          }
        }
      }
    }
    tY -= 1;
    for (int y = 0; y < bH; y++) {  // 落下予想図
      for (int x = 0; x < bW; x++) {
        if (blocks[n][y][x] == 1) {
          tft.fillRect(X0 + PI * (bXL + x), Y0 + PI * (tY + y), PI - 2, PI - 2, R);
        }
      }
    }
    return 0;
  }
}

void nbDisp(int* b) {   // 次のブロック5個を表示
  int X1 = 0, Y1 = PI;  // 文字(番号)、ブロック位置
  int pi = PI - 5;      // 13
  int dw = pi * 4, dh = pi * 5;
  tft.fillRect(X1, Y1, dw, dh * 5, K);
  for (int n = 0; n < 5; n++) {
    int i = b[n];  // numbers of next 5 blocks
    for (int y = 0; y < bSizeY[i]; y++) {
      for (int x = 0; x < bSizeX[i]; x++) {
        if (blocks[i][y][x] == 1)
          tft.fillRect(X1 + pi * x, Y1 + pi + dh * n + pi * y, pi - 2, pi - 2, Y);
      }
    }
    tft.setTextColor(W);
    tft.drawString("Next " + String(n), X1, Y1 + dh * n, 2);
  }
  Serial.println("New Block");
}

void nbSet(int* num, int* b) {  // 新ブロックをセット
  bW = bSizeX[*num];
  bH = bSizeY[*num];
  bYU = 4 - bH;
  int j = random(0, X_W - 1 - bSizeX[*num]) + 1;
  bXL = j;
  bXR = bXL + bW - 1;
  bYD = bYU + bH - 1;
  for (int y = 0; y < bH; y++) {
    for (int x = 0; x < bW; x++) {
      if (blocks[*num][y][x] == 1) field[bYU + y][bXL + x] = 1;
      else field[bYU + y][bXL + x] = 0;
    }
  }
  for (int n = 0; n < 4; n++) b[n] = b[n + 1];  // キューの更新
  b[4] = random(0, 32);
}

void gStart() {
  initField();
  randomSeed(analogRead(A0));
  for (int i = 0; i < 5; i++) nextB5[i] = random(0, 24);  // 5個のブロック生成。初手は稲妻型以外。
  scene = PLAY;
  timer = -1;  // timer = -1 で新ブロック生成、落下が始まる
  Serial.println("New Game Start");
}

int dtimer = 5;
int machi = 200;
int countRot = 0;
int dButtonW = (320 - 50) / 3;  // =90
int bx0 = 0, bx1 = dButtonW + 25, bx2 = bx1 * 2;
int dBH = 479 - (Y0 + PI * 24);
int bx3 = X0 + PI * 12;
int rButtonW = X0, rBH = PI * 4;

void setup() {
  Serial.begin(115200);
  tft.init();          // DisplayとTouchは同じSPIを使用
                       // T_DOのみMISOと接続し、SDO(MISO)はMISOと接続しない。
                       // T_DIN、SDI(MOSI)はMOSIと接続する。
  tft.setRotation(2);  // J2Pinsが上部の縦使い。
  // 描画座標は、右にx(0-319)、下にy(0-479)。
  // タッチ座標は、上にt_x(0-319)、左にt_y(0-479)なので換算が必要。

  tft.fillScreen(TFT_BLACK);  // Clear the screen
  tft.setTextColor(K);
  tft.fillRect(bx0, Y0 + PI * 24, dButtonW, dBH, B);
  tft.drawString("Left", bx0 + PI, Y0 + PI * 24 + 10, 2);
  tft.fillRect(bx1, Y0 + PI * 24, dButtonW, dBH, R);
  tft.drawString("Rotation", bx1 + PI, Y0 + PI * 24 + 10, 2);
  tft.fillRect(bx2, Y0 + PI * 24, dButtonW, dBH, G);
  tft.drawString("RIGHT", bx2 + PI, Y0 + PI * 24 + 10, 2);
  tft.fillRect(bx3, Y0 + PI * 19, rButtonW, rBH, W);
  tft.drawString("Down", bx3 + 10, Y0 + PI * 20, 2);
  tft.fillRect(bx3, Y0 + PI * 14, rButtonW, rBH, M);
  tft.drawString("Reset", bx3 + 10, Y0 + PI * 15, 2);
  drawWall();
  scene = TITLE;
  timer = 0;
}

uint16_t t_x;  // To store the touch coordinates
uint16_t t_y;
bool pressed;
bool jamp = false;

void loop(void) {
  tft.fillRect(0, 0, 30, 14, TFT_BLACK);
  tft.drawString(String(timer, DEC), 0, 0, 2);
  switch (scene) {
    case TITLE:
      tft.fillRect(90, 185, 140, 32, W);
      tft.setTextColor(K);
      tft.drawString("TENOROS", 102, 192, 4);
      tft.setTextColor(R);
      tft.drawString("TENOROS", 100, 190, 4);
      if (timer > 10) {
        tft.fillRect(90, 185, 140, 32, K);
        gStart();
      }
      break;

    case PLAY:
      drawField();
      // Pressed will be set true as there is a valid touch on the screen
      pressed = false;
      t_x = -1;
      t_y = -1;

      if (Serial.available())
      {
        char c=Serial.read();
        if(c== 'a'){ bLeft(bNum);
        }else if(c == 'd') {bRight(bNum);
        }else if(c == 's') {bRot(&bNum);
        }else if(c == 'z') {jamp=true;
        }else if(c == 'x') {scene=RESET;}
      }

      if (tft.getTouch(&t_x, &t_y)) 
      {
        pressed = true;
        Serial.print(t_x);
        Serial.print(" : ");
        Serial.print(t_y);
        Serial.println("");
        delay(1);
        if (t_x < dBH * 0.65) {                                   // 0.66=320/480
          if (t_y < dButtonW * 1.5) bRight(bNum);                 //jamp = true; // 1.5= 480/320
          if (t_y > bx1 * 1.5 && t_y < (bx1 + dButtonW) * 1.4) {  // 感度に合わせ調整
            countRot++;
            if (countRot > 1) {
              bRot(&bNum);
              countRot = 0;
            }
          }
          if (t_y > 2 * bx1 * 1.4) bLeft(bNum);
        } else {
          if (t_x > (PI + dBH) * 0.6 && t_x < (PI + dBH + rBH) * 0.65) jamp = true;  // 感度に合わせ調整
          if (t_x > (PI + dBH + rBH + PI) * 0.65 && t_x < (PI + dBH + rBH + PI + rBH) * 0.66) scene = RESET;
        }
      }

      if (timer == 0) {
        bNum = nextB5[0];
        nbSet(&bNum, nextB5);
        nbDisp(nextB5);
      } else if (timer % dtimer == 0) {
        int t = rakka(bNum);      // ブロック落下処理
        if (t == -1) timer = -1;  // 次のtimer++でtimer==0がtrueになり、新ブロックの生成落下
        if (t > 0) {
          scene = OVER;  //if (aline[3] > 0)
          timer = 0;
        }
      }
      break;

    case OVER:
      tft.setTextColor(G);
      tft.drawString("GAME OVER", 88, 192, 4);
      tft.setTextColor(R);
      tft.drawString("GAME OVER", 86, 190, 4);
      Serial.println("GAME OVER");
      if (timer > 30) {
        tft.fillRect(80, 190, 160, 32, K);
        gStart();  //scene = PLAY;
      }
      break;

    case RST:
      tft.setTextColor(G);
      tft.drawString("GAME RESET", 81, 192, 4);
      tft.setTextColor(R);
      tft.drawString("GAME RESET", 79, 190, 4);
      if (timer > 20) {
        tft.fillRect(78, 190, 162, 32, K);
        gStart();  //scene = PLAY;
      }
      break;

    default:
      break;
  }

  timer++;
  if (jamp == true) {
    jamp = false;  // 待ち時間なし
    dtimer = 1;
  } else {
    dtimer = 5;
    delay(machi);  // jamp==false 一定時間待つ
  }
}
```
</details>

数百円のマイコンボードなのに結構遊べます。最後までご覧いただき有難うございました。