Arduinoでタッチスクリーンを使う・タッチ

Arduino

タッチスクリーン

概要

Elegoo TFTタッチスクリーンには、抵抗膜によるタッチ位置検出機能があります。画面上のどこか一点を押すと、x軸・y軸それぞれの方向について、位置を電圧として読み取ることができるようになっています。

具体的な技術資料がないので、公式サンプルから使用法を読み取ることにします。

簡単なサンプル

公式サンプルを大幅に簡略化してプログラムの流れが見やすいサンプルを作成しました。

#include "Elegoo_GFX.h"
#include "Elegoo_TFTLCD.h"
#include "TouchScreen.h"

// ピンの定義
// 表示機能用
#define LCD_CS A3
#define LCD_CD A2
#define LCD_WR A1
#define LCD_RD A0
#define LCD_RST A4

// タッチ機能用
#define YP A3
#define XM A2
#define YM 9
#define XP 8

// タッチ検出および座標変換用
#define MINPRESSURE 10
#define MAXPRESSURE 1000
#define TS_MINX 120
#define TS_MAXX 900
#define TS_MINY 70
#define TS_MAXY 920

// 色の定義
#define	BLACK   0x0000
#define	BLUE    0x001F
#define	RED     0xF800
#define	GREEN   0x07E0
#define CYAN    0x07FF
#define MAGENTA 0xF81F
#define YELLOW  0xFFE0
#define WHITE   0xFFFF

// インスタンスの生成
Elegoo_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RST);
TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);

void setup() {
  // 初期化
  tft.reset();
  uint16_t identifier = tft.readID();
  if(identifier==0x0101){     
    identifier=0x9341;
  }else if(identifier==0x1111){     
    identifier=0x9328;
  }
  tft.begin(identifier);
  tft.fillScreen(WHITE);
  tft.setRotation(3);
}

void loop() {
  // タッチ位置の読み取り
  digitalWrite(13, HIGH);
  TSPoint p = ts.getPoint();
  digitalWrite(13, LOW);
  pinMode(XM, OUTPUT);
  pinMode(YP, OUTPUT);

  if(p.z>MINPRESSURE && p.z<MAXPRESSURE){
    int x = map(p.y, TS_MINY, TS_MAXY, 0, tft.width()-1);
    int y = map(p.x, TS_MINX, TS_MAXX, 0, tft.height()-1);
    tft.fillCircle(x,y,5,RED);
  }
}

プログラムの解説

ライブラリのインクルード
#include "Elegoo_GFX.h"
#include "Elegoo_TFTLCD.h"
#include "TouchScreen.h"

タッチ機能ライブラリを使用する場合、”TouchScreen.h”をインクルードする必要があります。

定数の設定
// タッチ機能用
#define YP A3
#define XM A2
#define YM 9
#define XP 8

この部分はタッチ機能に使用する入出力端子を定数として定義しています。公式サンプルの丸写しです。座標読み取りにはアナログ値を使用するため、YPとXMは必ずアナログ端子を使用します(シールドとして使用する場合はそもそも選択の余地がないですが…)。一部の端子はLCDの表示制御用と兼用です。

// タッチ検出および座標変換用
#define MINPRESSURE 10
#define MAXPRESSURE 1000
#define TS_MINX 120
#define TS_MAXX 900
#define TS_MINY 70
#define TS_MAXY 920

ここも公式サンプルのままです。
MINPRESSUREとMAXPRESSUREは『ペンによってタッチされた』ことの判定に使用されます。
TS_MINXとTS_MAXXはX座標の読み取り値の最小値と最大値、TS_MINYとTS_MAXYはY座標の読み取り値の最小値と最大値を表しています。読み取り値を座標値に変換するために使用されます。

インスタンスの生成
// インスタンスの生成
Elegoo_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RST);
TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);

タッチの読み取りのためには、TouchScreenクラスのインスタンスを使用します。先に定義した入出力端子を引数としてインスタンスを初期化しています。

画面の初期化
void setup() {
  // 初期化
  tft.reset();
  uint16_t identifier = tft.readID();
  if(identifier==0x0101){     
    identifier=0x9341;
  }else if(identifier==0x1111){     
    identifier=0x9328;
  }
  tft.begin(identifier);
  tft.fillScreen(WHITE);
  tft.setRotation(3);
}

setup()関数の中身は以前のものとほぼ同じですが、loop()関数内でタッチ位置取得をループするためfillScreen(WHITE)とsetRotation(3)をsetup()関数の最後に移動しました。

タッチ位置の取得
  // タッチ位置の読み取り
  digitalWrite(13, HIGH);
  TSPoint p = ts.getPoint();
  digitalWrite(13, LOW);
  pinMode(XM, OUTPUT);
  pinMode(YP, OUTPUT);

Javaなどの高級言語だと『画面にタッチ』のような事項は『イベント』として処理され、自動的に特定の関数を呼び出すようになっているのですが、Arduinoではそのようなことはありません。プログラム側で定期的にポートの値を読み取り、『いまタッチされているか』の判定やタッチ位置の取得を行う必要があります。

digitalWrite()関数で D13端子(スクリーンのSD_SCK)を HIGH にして TouchScreen.getPoint()関数を実行すると、タッチ関係の各ポート値が読み取られます。

その後の3行は読み取り後の後始末です。まず digitalWrite()関数で先ほど HIGH にした SD_SCK を LOW にします。また、getPoint()関数内の処理で XM と YP (それぞれ Arduino側の D9端子と D8端子)をアナログ入力モードに変更しているのですが、変えっぱなしで戻してくれません。これらの端子は表示データの送信と兼用なので、pinMode()関数をつかって出力に直しています。

読み取った座標値に応じた処理
  if(p.z>MINPRESSURE && p.z<MAXPRESSURE){
    int x = map(p.y, TS_MINY, TS_MAXY, 0, tft.width()-1);
    int y = map(p.x, TS_MINX, TS_MAXX, 0, tft.height()-1);
    tft.fillCircle(x,y,5,RED);
  }

読み取ったタッチ位置は getPoint()関数の返却値である TouchPointクラスのメンバ(x,y,z)に格納されています。このうちzの値はタッチの圧力に応じた値のようで、この値が MINPRESSURE と MAXPRESSURE の間にあるときにタッチされたと判定します。

タッチされたと判定した場合は読み取り値(p.xおよびp.y)を画面上の座標値に変換します。読み取り値は setRotation()関数での指定に依らず座標軸が固定されています。USBコネクタが下に来るように画面を置いたときの左下隅が x読み取り値・ y読み取り値ともに最小、右が x軸正向き、上が y軸正向きです(下図参照)。

タッチ位置読み取り値の座標系
画面上のピクセルの座標系(setRotation=3のとき)

よって、setRotation(3)を指定しているこのサンプルプログラムでは、x座標と y座標を入れ替えています。またタッチ位置の読み取り値は値の範囲も座標値の最小値・最大値と異なります。これらのことをまとめると、

タッチ位置y値(値域:TS_MINY ~ TS_MAXY) → 画面上のx座標(値域:0 ~ width()-1)
タッチ位置x値(値域:TS_MINX ~ TS_MAXX) → 画面上のy座標(値域:0 ~ height()-1)

という座標変換が必要になります。この計算を行っているのが map()関数です。

座標の変換ができたら、画面にタッチした位置を中心として fillCircle()関数で小さな円を描画しています。ペイントソフトのようにタッチペンで画面上に絵を描くことができます。

実行結果

まとめ

公式サンプルを簡略化しただけとはいえ、ライブラリを使用すれば非常に簡単にタッチスクリーンを用いたプログラムが作れることが判りました。

次回はこのタッチスクリーンを用いた簡単なゲーム作りに挑戦してみます。

コメント

タイトルとURLをコピーしました