2015年10月13日

ILI9340液晶の表示の高速化

前回の続きです。

前回の最後のほうで
現在は1ピクセルずつdrawPixelで書いている部分をsetAddrWindowとpushColorで書き直せば、もう少し高速化できると思います。
と書きましたが、大した手間でもないのでやってみました。

描画のループ部分で使う描画命令をdrawPixelからpushColorへ変更した結果、2倍ほど高速になりました。
あまり綺麗ではありませんが、動画を載せておきます。


修正したスケッチは以下に貼っておきます。

#include <arduino.h>
#include <SPI.h>
#include <FS.h>
#include <JPEGDecoder.h>
#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ILI9341.h> // Hardware-specific library

#define TFT_CS     15
#define TFT_RST    5
#define TFT_DC     4

Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);

void setup() {
 pinMode(TFT_RST, OUTPUT);
 digitalWrite(TFT_RST, LOW);
 digitalWrite(TFT_RST, HIGH);

 Serial.begin(115200);
 Serial.println("");
 delay(10);

 tft.begin();
 // read diagnostics (optional but can help debug problems)
 uint8_t x = tft.readcommand8(ILI9341_RDMODE);
 Serial.print("Display Power Mode: 0x"); Serial.println(x, HEX);
 x = tft.readcommand8(ILI9341_RDMADCTL);
 Serial.print("MADCTL Mode: 0x"); Serial.println(x, HEX);
 x = tft.readcommand8(ILI9341_RDPIXFMT);
 Serial.print("Pixel Format: 0x"); Serial.println(x, HEX);
 x = tft.readcommand8(ILI9341_RDIMGFMT);
 Serial.print("Image Format: 0x"); Serial.println(x, HEX);
 x = tft.readcommand8(ILI9341_RDSELFDIAG);
 Serial.print("Self Diagnostic: 0x"); Serial.println(x, HEX);

 tft.fillScreen(ILI9341_BLACK);

 SPIFFS.begin();
 jpegDraw("/test1.jpg");
}

void jpegDraw(char* filename) {
 char str[100];
 uint8 *pImg;
 int x,y,bx,by;
 
 // Decoding start
 JpegDec.decode(filename,0);

 // Image Information
 Serial.print("Width     :");
 Serial.println(JpegDec.width);
 Serial.print("Height    :");
 Serial.println(JpegDec.height);
 Serial.print("Components:");
 Serial.println(JpegDec.comps);
 Serial.print("MCU / row :");
 Serial.println(JpegDec.MCUSPerRow);
 Serial.print("MCU / col :");
 Serial.println(JpegDec.MCUSPerCol);
 Serial.print("Scan type :");
 Serial.println(JpegDec.scanType);
 Serial.print("MCU width :");
 Serial.println(JpegDec.MCUWidth);
 Serial.print("MCU height:");
 Serial.println(JpegDec.MCUHeight);
 Serial.println("");
 
 sprintf(str,"#SIZE,%d,%d",JpegDec.width,JpegDec.height);
 Serial.println(str);

 // Raw Image Data
 while(JpegDec.read()){
   pImg = JpegDec.pImage ;
   x = JpegDec.MCUx * JpegDec.MCUWidth;
   y = JpegDec.MCUy * JpegDec.MCUHeight;
   tft.setAddrWindow(x, y, x+JpegDec.MCUWidth-1, y+JpegDec.MCUHeight-1);
   for(by=0; by<JpegDec.MCUHeight; by++){
     for(bx=0; bx<JpegDec.MCUWidth; bx++){
       if(JpegDec.comps == 1){ // Grayscale          
         tft.pushColor(tft.color565(pImg[0], pImg[0], pImg[0]));
       }else{ // RGB
         tft.pushColor(tft.color565(pImg[0], pImg[1], pImg[2]));
       }        
       pImg += JpegDec.comps ;
     }
   }
 }
}
   
void loop() {
}

posted by boochow at 01:01| Comment(0) | ESP8266/ESP32 | このブログの読者になる | 更新情報をチェックする

2015年10月12日

激安QVGA液晶をESP8266で使う

esp826610.jpg


中国の通販サイトaliexpressで、QVGA(320x240)の2.2インチカラーLCDが送料込で5ドル少々だったので購入してみました。
インタフェースはSPIで、コントローラはILI9340Cということで、たぶん以下の記事で使われているのと同じものだと思います。

Cheap TFT 2.2 inch Display on Arduino (ILI9340C or ILI9341) - All

ちなみに購入したお店はこちらです。

New 2.2 inch 2.2 240x320 SPI TFT LCD Display Module ILI9341 PCB 5V/3.3V 51/AVR/STM32/ARM/PIC Free Shipping-in LCD Modules from Electronic Components & Supplies on Aliexpress.com | Alibaba Group

ピン配列は以下のようになっています。

1 : VCC
2 : GND
3 : CS
4 : RESET
5 : DC/RS
6 : SDI/MOSI
7 : SCK
8 : LED
9 : SDO/MISO

esp826610-01.jpg


SPIですので、基本的に接続方法はこれまで使っていたST7735のLCDと同じです(ピンの呼称が若干違いますが)。
ESP8266とは、以下のように接続しました。

LCD / ESP8266
CS / IO15
RST / IO5
DC / IO4
SDI / IO13
SCK / IO14
SDO / IO12

SDOはST7735では使っていませんでしたが、ILI9340ではLCDから情報を受け取るために使います。
他に、VCCとLEDを電源(3.3V)、GNDを電源のGNDに接続します。
LEDは抵抗が入っているかどうか怪しいので、5Vには直接接続しないほうが良いかもしれません。


ドライバはArduino IDE for ESP8266に最初から含まれています。
ただしILI9340ではなくILI9341用のものですが、問題なく動作しました。

Arduino/libraries/Adafruit_ILI9341 at esp8266 ・ esp8266/Arduino

また、スケッチ例として「graphicstest_esp8266」が含まれています。
ESP8266用のスケッチですので、結線情報など若干変更すれば動作しました。
ちなみに変更部分は以下の箇所です。

#include <Arduino.h>
#include <SPI.h>  //この行を追加
#include <Adafruit_GFX.h>  //この行を追加
#include <Adafruit_ILI9341.h>

// For the Adafruit shield, these are the default.
//結線を以下の通り変更
#define TFT_CS     15
#define TFT_RST    5
#define TFT_DC     4



私の環境では、上記の変更だけでは動作したりしなかったりという現象があったのですが、この記事のコメント欄を見ると、リセットはHighにしないと動作しない場合があるようです。

そこで、setupルーチンの冒頭に以下のコードも追加しています。

void setup() {
 pinMode(TFT_RST, OUTPUT);
 digitalWrite(TFT_RST, LOW);
 digitalWrite(TFT_RST, HIGH);



これでスケッチ例が動作するようになったので、前回のJPEG表示プログラムもILI9340用に書き換えて、320x240ピクセルの画像を表示させてみました。

動作結果がページ先頭の写真です。
表示させている画像は画像符号化の世界ではおなじみのレナさんです。

書き換えのポイントは以下の通りです。

・includeするファイルの変更
#include <Adafruit_ILI9341.h>

・TFT液晶のオブジェクト
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);

・液晶のリセットコード追加(前述)
・TFT初期化
  tft.begin();

・色変換の関数名
            tft.drawPixel(x, y, tft.color565(

・LCDが動作しているかどうかの診断(スケッチ例graphicstest_sep8266からコピー)

表示に1秒以上かかりますが、現在は1ピクセルずつdrawPixelで書いている部分をsetAddrWindowとpushColorで書き直せば、もう少し高速化できると思います。

最後にこのスケッチの全体を掲載しておきます。
JPEGファイルは、ファイル名test1.jpgで、あらかじめSPIFFSへ格納しておいてください。


#include <arduino.h>
#include <SPI.h>
#include <FS.h>
#include <JPEGDecoder.h>
#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ILI9341.h> // Hardware-specific library

#define TFT_CS     15
#define TFT_RST    5
#define TFT_DC     4

Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);

void setup() {
 pinMode(TFT_RST, OUTPUT);
 digitalWrite(TFT_RST, LOW);
 digitalWrite(TFT_RST, HIGH);

 Serial.begin(115200);
 Serial.println("");
 delay(10);

 tft.begin();
 // read diagnostics (optional but can help debug problems)
 uint8_t x = tft.readcommand8(ILI9341_RDMODE);
 Serial.print("Display Power Mode: 0x"); Serial.println(x, HEX);
 x = tft.readcommand8(ILI9341_RDMADCTL);
 Serial.print("MADCTL Mode: 0x"); Serial.println(x, HEX);
 x = tft.readcommand8(ILI9341_RDPIXFMT);
 Serial.print("Pixel Format: 0x"); Serial.println(x, HEX);
 x = tft.readcommand8(ILI9341_RDIMGFMT);
 Serial.print("Image Format: 0x"); Serial.println(x, HEX);
 x = tft.readcommand8(ILI9341_RDSELFDIAG);
 Serial.print("Self Diagnostic: 0x"); Serial.println(x, HEX);

 tft.fillScreen(ILI9341_BLACK);

 SPIFFS.begin();
 jpegDraw("/test1.jpg");
}

void jpegDraw(char* filename) {
 char str[100];
 uint8 *pImg;
 int x,y,bx,by;
 
 // Decoding start
 JpegDec.decode(filename,0);

 // Image Information
 Serial.print("Width     :");
 Serial.println(JpegDec.width);
 Serial.print("Height    :");
 Serial.println(JpegDec.height);
 Serial.print("Components:");
 Serial.println(JpegDec.comps);
 Serial.print("MCU / row :");
 Serial.println(JpegDec.MCUSPerRow);
 Serial.print("MCU / col :");
 Serial.println(JpegDec.MCUSPerCol);
 Serial.print("Scan type :");
 Serial.println(JpegDec.scanType);
 Serial.print("MCU width :");
 Serial.println(JpegDec.MCUWidth);
 Serial.print("MCU height:");
 Serial.println(JpegDec.MCUHeight);
 Serial.println("");
 
 sprintf(str,"#SIZE,%d,%d",JpegDec.width,JpegDec.height);
 Serial.println(str);

 // Raw Image Data
 while(JpegDec.read()){
   pImg = JpegDec.pImage ;

   for(by=0; by<JpegDec.MCUHeight; by++){
     for(bx=0; bx<JpegDec.MCUWidth; bx++){
       
       x = JpegDec.MCUx * JpegDec.MCUWidth + bx;
       y = JpegDec.MCUy * JpegDec.MCUHeight + by;
       
       if(x<JpegDec.width && y<JpegDec.height){
         if(JpegDec.comps == 1){ // Grayscale          
           tft.drawPixel(x, y, tft.color565(pImg[0], pImg[0], pImg[0]));
         }else{ // RGB
           tft.drawPixel(x, y, tft.color565(pImg[0], pImg[1], pImg[2]));
         }
       }
       
       pImg += JpegDec.comps ;
     }
   }
 }
}
   
void loop() {
}

posted by boochow at 17:39| Comment(0) | ESP8266/ESP32 | このブログの読者になる | 更新情報をチェックする

ESP8266でJPEG画像をTFT LCDに表示する

esp826609.jpg

前回はSPIFFSの中に格納したBMPファイルを表示させましたが、JPEG、GIF、PNGなどのファイルも表示させたくなりますね。
調べてみたところ、これらの画像形式の中でESP8266で比較的扱いやすいのはJPEGのようです。

JPEG、GIF、PNGはいずれも圧縮アルゴリズムを使っていますが、GIFとPNGは辞書形式の圧縮符号化方式(GIFはLZW、PNGはZlib)を使っています。
このタイプの圧縮アルゴリズムは、辞書をRAM上に展開すると、比較的大きなワーキングメモリが必要になります。
一方、JPEGは計算量はGIFやPNGよりも大きい代わりに、ワーキングメモリはそれほど大きくないようです。
picojpegというメモリ消費の少ないデコーダ実装があり、これをArduino上で利用した例も見つかりました。

Arduino用JPEGデコーダ | 遊舎工房

今回は、上記のデコーダを使って前回同様にSPIFFS上に置いたJPEGファイルをLCDに表示させてみました。


まず、デコーダライブラリを下記からダウンロードしてArduino IDEに追加(スケッチ→Include Library→Add .ZIP Library...)します。

MakotoKurauchi/JPEGDecoder

このライブラリはSDカードでの利用が想定されていますので、SPIFFSで使うために、以下の部分をちょこっと変更します。
変更するファイルは「マイドキュメント\Arduino\Libraries\JPEGDecoder-master\JPEGDecoder.cpp」です。

修正前: #include <SD.h>
修正後: #include <FS.h>

修正前: g_pInFile = SD.open(pFilename, FILE_READ);
修正後: g_pInFile = SPIFFS.open(pFilename, "r");


スケッチ本体は以下のようになります。
もともとのライブラリに、デコード結果をシリアルポートに表示する分かりやすいサンプルがありましたので、シリアルポート出力の代わりにLCDに点を打つだけで動作させることができました。

SPIFFSの中に"test1.jpg"というファイルを入れておくと、それをST7735ベースのLCDに表示します。
ESP8266のSPIFFSへのファイル転送のやり方は以前の記事を参照してください。


#include <arduino.h>
#include <SPI.h>
#include <FS.h>
#include <JPEGDecoder.h>
#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library

#define TFT_CS     15
#define TFT_RST    5
#define TFT_DC     4

Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS,  TFT_DC, TFT_RST);

void setup() {
 Serial.begin(115200);
 Serial.println("");
 delay(10);

 tft.initR(INITR_BLUETAB);
 tft.fillScreen(ST7735_BLACK);

 SPIFFS.begin();
 jpegDraw("/test1.jpg");
}

void jpegDraw(char* filename) {
 char str[100];
 uint8 *pImg;
 int x,y,bx,by;
 
 // Decoding start
 JpegDec.decode(filename,0);

 // Image Information
 Serial.print("Width     :");
 Serial.println(JpegDec.width);
 Serial.print("Height    :");
 Serial.println(JpegDec.height);
 Serial.print("Components:");
 Serial.println(JpegDec.comps);
 Serial.print("MCU / row :");
 Serial.println(JpegDec.MCUSPerRow);
 Serial.print("MCU / col :");
 Serial.println(JpegDec.MCUSPerCol);
 Serial.print("Scan type :");
 Serial.println(JpegDec.scanType);
 Serial.print("MCU width :");
 Serial.println(JpegDec.MCUWidth);
 Serial.print("MCU height:");
 Serial.println(JpegDec.MCUHeight);
 Serial.println("");
 
 sprintf(str,"#SIZE,%d,%d",JpegDec.width,JpegDec.height);
 Serial.println(str);

 // Raw Image Data
 while(JpegDec.read()){
   pImg = JpegDec.pImage ;

   for(by=0; by<JpegDec.MCUHeight; by++){
     for(bx=0; bx<JpegDec.MCUWidth; bx++){
       
       x = JpegDec.MCUx * JpegDec.MCUWidth + bx;
       y = JpegDec.MCUy * JpegDec.MCUHeight + by;
       
       if(x<JpegDec.width && y<JpegDec.height){
         if(JpegDec.comps == 1){ // Grayscale          
           tft.drawPixel(x, y, tft.Color565(pImg[0], pImg[0], pImg[0]));
         }else{ // RGB
           tft.drawPixel(x, y, tft.Color565(pImg[0], pImg[1], pImg[2]));
         }
       }
       
       pImg += JpegDec.comps ;
     }
   }
 }
}
   
void loop() {
}

posted by boochow at 11:32| Comment(0) | ESP8266/ESP32 | このブログの読者になる | 更新情報をチェックする

2015年10月10日

ESP8266のフラッシュメモリをファイルシステムとして使用する(2)

esp826607.jpg

前回の続きです。

SPIFFS(SPI Flash File System)をArduinoからファイルシステムとして使えるようになりましたので、BMPファイルを格納して、そのファイルを以前も使用したTFTカラーLCDに表示させてみます。

このLCDのドライバとしてはAdafruit ST7735 Libraryを使用していますが、スケッチ例として「spitftbitmap」という、SDカードからBMPファイルを読み出して表示するスケッチが付属しています。

このスケッチに含まれるBMP画像描画用の関数は、ちょっと改変するだけで、SPIFFSで利用することが可能です。

スケッチのメイン部分は以下のようになります。

#include <FS.h>
#include <SPI.h>
#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library

#define TFT_CS     15
#define TFT_RST    5
#define TFT_DC     4

Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS,  TFT_DC, TFT_RST);

void setup() {
 // put your setup code here, to run once:
 Serial.begin(115200);
 Serial.println("");
 delay(10);
 
 tft.initR(INITR_BLUETAB);
 tft.fillScreen(ST7735_BLACK);

 SPIFFS.begin();
 bmpDraw("/test1.bmp", 0,0);
}

void loop() {
 // put your main code here, to run repeatedly:

}



TFT液晶関連の結線やソースコードの修正は、以前の記事で書いたとおりですので説明は省略します。
  SPIFFS.begin();
 bmpDraw("/test1.bmp", 0,0);

でSPIFFS上の「test1.bmp」をLCDに表示させています。

bmpDraw関数は、上で書いたST7735 Libraryのサンプルスケッチに含まれる関数です。
サンプルスケッチ「spitftbitmap.ino」からloop{}よりも後の部分(「#define BUFFPIXEL 20」という行以降)をコピーして、さきほどのスケッチの後に貼り付けます。

そして、以下の2箇所を修正します。

修正前: if ((bmpFile = SD.open(filename)) == NULL) {
修正後: if (!(bmpFile = SPIFFS.open(filename,"r"))) {

修正前: bmpFile.seek(pos);
修正後: bmpFile.seek(pos,SeekSet);


以上で、SPIFFS上のビットマップファイルをLCDに表示できました。
posted by boochow at 16:18| Comment(0) | ESP8266/ESP32 | このブログの読者になる | 更新情報をチェックする

2015年10月04日

ESP8266のフラッシュメモリをファイルシステムとして使用する

ESP8266はSPI経由でフラッシュメモリが接続されています。
容量は、国内で使用可能なWROOM-02の場合は4MBです。

フラッシュメモリの空きスペースは、ストレージとして、プログラムで使用するデータを置いておくことができます。
通常のAVRベースのArduinoでは、外部ストレージとしてSDカードを使用するのが常道ですが、ESP8266の場合は外付けハードウェアを使わなくても、モジュール単体で3MB程度のデータを保持できます。

ESP8266用のArduino環境では、SPIFFS(SPI Flash File System)を利用して、フラッシュメモリ上でファイルシステムを使うことができます。

公式の解説は以下にあります。

Arduino/reference.md at esp8266 ・ esp8266/Arduino

今回はこの機能を使って、フラッシュ上に置いたBMPファイルをTFT LCDに表示させてみます。

そのためには、以下の準備が必要になります。

(1)Arduino IDE for ESP8266をstable版からstaging版へ移行
SPIFFSは現時点(2015/10/4)ではstable版に組み込まれておらず、利用するにはstaging版を使う必要があります。

(2)データアップロードツールのインストール
Windows上のファイルをESP8266のフラッシュメモリへ転送するためのArduino IDE用ツールが必要です。


それでは、まず(1)のstaging版への移行です。
これは、手順は以前にも紹介した以下の記事の通りです。

技適済み格安高性能Wi-FiモジュールESP8266をArduinoIDEを使ってIoT開発する為の環境準備を10分でやる方法 - Qiita

ただし、IDEの環境設定では「Staging version」のURLを入力します。
そして、「ツール」→「ボード」→「Boards Manager...」から「esp8266 by ESP8266 Community」を選択し、Installボタンを押してインストールします。

なお、Stable版とStaging版は共存せず、上記の操作でStable版はStaging版で上書きされるようです。

インストールが終わると、ボード選択情報がリセットされてしまいますので、下図のように再度ボードの情報を指定し直します。

esp826606-02a.png


上記では「Flash Size:」で「1M(128K SPIFFS)」を選択しています。
ESP8266は4MBのフラッシュを積んでいるので最大で「4M(3M SPIFFS)」まで利用できますが、このあと説明するアップロードツールでは、実際のファイルサイズとは関係なく、ここで指定したサイズを転送することになります。
3MBをシリアルポートで送信すると数分間はかかりますので、必要最低限のサイズを指定しておくほうが待ち時間が少なくて済みます。

次に(2)データアップロードツールのインストールです。
ツールのURLは、冒頭に挙げた公式の解説ページに記載されています。現時点では

http://arduino.esp8266.com/ESP8266FS-1.6.5-1105-g98d2458.zip

になっています。

まず、ドキュメントフォルダ内のArduinoフォルダに、新しく「tools」というフォルダを作って下さい。

esp826606-01.png


次に、上記のURLからダウンロードしたzipファイルを解凍し、解凍してできたフォルダをtoolsフォルダの中へコピーします。

これで、Arduino IDEを再起動すると、ツールメニューの中に「ESP8266 Sketch Data Upload」という項目が新しく追加されているはずです。

esp826606-02b.png


これで準備が整いましたので、まずは動作確認してみます。

以下のようなスケッチを用意します。
プログラムの中身は、SPIFFSのルートディレクトリにあるファイルのファイル名とファイルサイズを出力するものです。


#include <FS.h>

void setup() {
 // put your setup code here, to run once:
 Serial.begin(115200);
 Serial.println("");
 
 SPIFFS.begin();
 Dir dir = SPIFFS.openDir("/");
 while (dir.next()) {
   Serial.print(dir.fileName());
   File f = dir.openFile("r");
   Serial.println(String(" ") + f.size());
 }
}

void loop() {
 // put your main code here, to run repeatedly:

}



これを通常の手順に従ってESP8266へ書き込みます。

次に、SPIFFSに書き込むファイルを用意します。

まず、上記のスケッチのフォルダ(IDEのメニューの「スケッチ」→「スケッチのフォルダを表示」で表示されるフォルダ)に「data」というフォルダを作成します。

esp826606-03.png


そして、dataフォルダの中に、SPIFFSに入れたいファイルをコピーします。
ESP8266を書き込みモードにしてから、IDEの「ツール」→「ESP8266 Sketch Data Upload」を選択すると、dataフォルダの中身がESP8266のフラッシュへ転送されます。

このとき、シリアルモニタは閉じてください。シリアルモニタが動いているとアップロードツールは動作しません。
また、アップロードツールは、ESP8266のブートローダがStaging版IDEのものになっていないと動作しませんので、Staging版IDEでコンパイルしたスケッチを少なくとも1度書き込んだ後で使用してください。

正常に転送できれば、dataフォルダに入れたファイルのファイル名とファイルサイズがシリアルポートへ出力されるはずです。
posted by boochow at 11:44| Comment(4) | ESP8266/ESP32 | このブログの読者になる | 更新情報をチェックする
人気記事