You are currently viewing Arduino + KUMAN 2.8 TFT 画像描画

Arduino + KUMAN 2.8 TFT 画像描画

Arduino Mega2560 と KUMAN 2.8inch TFT での開発

Arduino UNO での開発事例はよくみかけるが、Megaでの情報が少ないためポイントを示すとともに、ハードへの理解を深められるようにする。
またArduino での TFT コントロールは処理が重いため、標準的な方法と高速化する方法を対比してスピードの比較をすることで、実用的な実装方法をある程度定義付ける。

利用する TFT について

今回利用する TFT は KUMAN の2.8inch TFT を利用する。
Amazon で購入できる。価格は約1,200円! 考えられないくらい安いです。

Kuman Arduino用ディスプレイ タッチスクリーン UNO R3 2.8 inch TFT モニター SDカードコンセントつき 320*240ピクセル LCD 16bit Arduino Nano Mega2560適用ディスプレイ K60

スペック

  • 解像度 : 320 * 240 pix
  • 画面サイズ : 2.8 inch
  • タッチパネル : 抵抗膜タッチ
  • 外部記憶装置 : SDCARD スロット付き

抵抗膜の感度は過不足ない感じ。この値段で SDCARD スロットまでついているのだから大したものだ。付属品としては写真にもあるスタイラスとライブラリとサンプルコードを収録したCD-ROM。

Arduino Mega2560 の概要と Arduino UNO との比較

Arduino Mega2560 の互換ボードは、色々でていますが 1,200円〜 とこれも大変安いです。ここまで単価が下がれば UNO ではなく Mega2560 のほうがお得感がありますね。

Kuman Arduinoに対策 MEGA2560 R3 互換ボード for Arduino Rev3 Mega 2560 Rev3 USBケーブル付属 k16

細かい比較については、他のWebの情報にまかせて、ここではこのKUMANのTFTを利用するにあたって理解しておかなければならないポイントについて説明する。

ざっくり言うと、基本的には UNOを長くした形状になっていて、GPIO が大幅に増えている。
伸びた部分以外は、UNOのピン配列とほぼ互換になっているため UNO用のシールドが利用できることが多い

なんとなく歯切れの悪い言い方で申し訳ないが、実はちょっとした違いがある。この違いのために、UNO用シールドがそのまま使えないことがある。

ATmega2560 のピン配列と ARDUINO Mega2560のピンアサインの詳細

ATmega328 のピン配列と ARDUINO UNOのピンアサインの詳細

ARDUINOの GPIO の多くの Pin は複数の機能が割り当てられていて、切り替えて使えるのだが、よりによってSPI 通信で使う、SS, SCK, MOSI, MISO が Digital pin 50〜53に割り当てられている。UNO は Digital pin 10〜13 に割り当てられているのでこれがネックになる。

SDCARD を使えるようにする

今回の話では、SDCARD の読み書きで SPI 通信を利用するので、上記ピンアサインを解決しなければならない。このアサインはソフト的に変更できないので、物理的にやるしかない。
方法は簡単で、シールドの pin 4本を折り曲げて、そのピンの出力を Digital pin 50〜53 にいれればOK。

対応表

機能 UNO Mega
SS 10 53
MOSI 11 51
MISO 12 50
SCK 13 52

接続イメージ

ライブラリのインストールと概要

ライブラリの格納先は標準的には下記になる。
C:\Users\\Documents\Arduino\libraries

ここに Adafruit_GFX、Adafruit_TFTLCD、TouchScreen のライブラリをコピーする。

インクルードするライブラリは以下の通り。

#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_TFTLCD.h> // Hardware-specific library
#include <TouchScreen.h>     // Touch Screen library
#include <SD.h>
#include <SPI.h>

Mega2560 向けにソースを修正

SD-CARDを利用できるように対応するソースを次のように書き換える。

#define PIN_SD_CS 53 // Mega2560用に変更 Unoは10

int touchPin = 52; // Mega2560->52 Uno->13

digitalRead, digitalWrite の置き換え

マイコンを使うからには、GPIOの情報を最速でGetしないといけない。 (と思う)
一般的には、digitalRead(ピンNo) で状態を取得するが、そのルーチンは様々なチェック処理などがはいっていて、はっきりいって遅い。CPUを 44サイクルも必要とする。

実はポートをダイレクトに読みに行く方法がある。処理はおそらく 3サイクルしか使わない。Read部分は14倍高速になる計算ですね。とはいっても他の処理がはいってくるので、今回の場合は全体として3.5倍程度のスピードアップ。

例 : Mega2560 のピンアサインにある PC7 (Digitalpin 30番)の値取得

digitalRreadであれば、引数に30 と入れれば取得できる。簡単。

val = digitalRead(30);

置き換えは少しだけ頭を切り替えて、A~H までのそれぞれのポートが8ビットで構成されているということを理解してください。この例の Digital 30番ピンはPC7というところにアサインされているのがわかります。つまり PCの7ビット目 (0ビットから始まる)であることを示しています。

val = PINC & _BV(7)

上記のように記述します。 Aポートであれば、 PINA となります。
PINC は B10000000 と定義されていて、
_BV($) が取得してきた $ビット目の対応ポートの値 (0,1)との論理積を求めているだけです。
これにより、確認したいボートの値がLOW であれば 0 になり、 HIGH であれば 0以外になります。

drawPixel の置き換え

データの転送を早くする方法があるのでライブラリを書き換えても良いが、それよりも下記ライブラリに置き換えてしまうほうが速いので、その方法をすすめる。

ライブラリを差し替える

PDQ_GFX

高速化ライブラリ GitHub – PDQ_GFX_Libs

文字列の書き込み

tftライブラリを利用する。

tft.setCursor(x,y); //x,y は左上からのピクセル数
tft.fillRect(Left,Top,XSize,YSize); //消さないと文字が重なって描画される
tft.setTextColor(0xFFFF); //RGB565で指定 詳しくは後述
tft.setTextSize(x); // 文字サイズはx倍
tft.print("A"); //文字列

BMP画像の読み込み

SDCardのサンプルをMega用にピンアサインを変更すればOK。

画像の読み込み例。背景画像を読み込んだところ (上下の画像)

画面いっぱいの画像を読み込むと10秒くらいかかります。。。遅い!

RGB565

RGBを2バイト(16ビット)で指定する。
Red 5バイト(32階調)、Green 6バイト(64階調)、Blue 5バイト(32階調)
マイコンでは画像データを少なくするために良く利用されるフォーマット。
PhotoShopなどでもRGB565で出力できます。

高速化の方法

Arduino はお手軽であるが他のSBCに比べて処理スピードが遅いので、色々な面で高速化するのが望ましい。

タッチ対応

タッチ処理については、TouchScreen.h をインクルードして利用する。
プログラム例を下記に示す。 ここでもdigitalWriteを使わずに、ダイレクトにポートを操作する。ことで高速化する。

  //タッチ処理
  //digitalWrite(touchPin,HIGH);
  PINB |= _BV(1);

  TSPoint p = ts.getPoint();

  //digitalWrite(touchPin,LOW);
  PINB |= ~_BV(1);

  //タッチしてみて方向があうように
  // if sharing pins, you'll need to fix the directions of the touchscreen pins
  //pinMode(XP, OUTPUT);
  pinMode(XM, OUTPUT);
  pinMode(YP, OUTPUT);
  //pinMode(YM, OUTPUT);  

  // アナログ値が0~1023なのでそれを解像度でスケーリング
  p.x = map(p.x, TS_MINX, TS_MAXX, tft.width(), 0);
  p.y = (tft.height()-map(p.y, TS_MINY, TS_MAXY, tft.height(), 0));

  //押圧が範囲内であれば押圧とみなす
  if (p.z > MINPRESSURE && p.z < MAXPRESSURE) {
    Serial.print("X = "); Serial.print(p.x);
    Serial.print("\tY = "); Serial.print(p.y);
    Serial.print("\tPressure = "); Serial.println(p.z);
    tft.fillCircle(240-p.x,320-p.y, 3, 0xFFFF);  
  }

コメントを残す

CAPTCHA