ESP8266 ( ESP-WROOM-02 ) SPI 通信高速化、その2 ( 複数デバイス、Mode、Watchdog Timer 問題 )

ESP8266 ( ESP-WROOM-02 )

こんばんは。

前回の記事では、Arduino core for ESP8266ライブラリのdigitalWrite() は速度が遅く、レジスタにダイレクトアクセスした方が3倍速くなり、さらにSPIライブラリ中のSCLK信号生成にdigitalWrite()関数を使っている場合では、GPIO レジスタ Direct Access にすれば高速化できるというお話でした。

Adafruit の OLED ( 有機EL ) SSD1351 ライブラリは、そのままではESP8266 ( ESP-WROOM-02 ) では使えなくて、shiitakeo さんによる digitalWrite() 仕様に改変すれば動くようになりました。 (こちらの記事参照
そして、そのdigitalWrite() をGPIO レジスタDirect Access に変えれば高速化できます。

実は、ツイッターで@arms22 さんから Arduino core for ESP8266 のSPIライブラリでSPI.setFrequency を使えば速度が上げられるということを教えていただきました。
Adafruitライブラリ中のSPIライブラリは使えないと思っていたのですが、よくよく調べてみると使えるということが分かったんです。

それで、ESPRESSIF社の以下のサイト
ESPRESSIF Support Documents
にある esp8266-technical_reference_en.pdf というファイルをよく読んでみると、レジスタDirect Access でSPI設定もできるじゃぁないですか・・・!

そこで、Arduino core for ESP8266 標準の SPI ライブラリを更に解読してみました。
すると、ちゃんとライブラリ中でSPIのレジスタDirect Access を使っているじゃぁないですか!!!

また、前回の記事で、メインloop() よりもForループやwhileループの方が速いということ述べましたが、実は、メインloop() ではウォッチドッグタイマが動作していて、それで休止時間が生じているということが分かりました。
つまり、ESP8266 ではWi-Fi機能などやその他の機能の監視をループ中に行う余地が無ければ、正常に動作しないようです。
WiFi.begin を動作させて、loop()内でwhile(1)ループを作ってしまうと、シリアルモニタにWDTエラーになって、延々とリセットを繰り返します。
WDT とはつまり、ウォッチ・ドッグ・タイマ というわけですね。
いや~、お恥ずかしながら、これは知りませんでした・・・。

要するに、私がESP8266 やSPIについていろいろと無知だっただけなんですけど、今回は新たな発見があり、とても勉強になりました。
@arms22さんに感謝です。

前回説明したレジスタ Direct Access は CS や DC 信号に使えますので、それと合わせてSPIライブラリを高速化して使えば、SPI については最大速度で使用することができます。
それについてはいろいろと注意点がありました。

では、具体的にスケッチやプログラミングでどのように使っていったら良いのかを説明していきたいと思います。

スポンサーリンク

1.SPIデバイスの最大周波数の確認

まず、使うSPIデバイスの最大周波数を確認します。
データシートに大体記載されています。
例えば、OLED SSD1351 の場合はデータシートのタイミングチャートにこんな感じで記載されています。

Clock Cycle Time 最少 = 50 ns

とあります。
つまり、SCLK パルス1波長50ns ということは周波数でいうと20MHz になります。
これを調べた上で、ESP8266 ( ESP-WROOM-02 ) のSPI 周波数を決めます。

因みに、Amazonで販売している、このTFT液晶ディスプレイ


ドライバにILI9340 を使用しているようですが、データシートに66ns とありました。
つまり、約15MHz となります。

2.SPI ライブラリの使い方

では、まず、以下の簡単なスケッチをArduino IDE に入力してみます。
ESP8266 ボード設定は下図の様にCPU Frequency = 160MHz にしておきます。

【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)

#include <SPI.h>

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

  SPI.begin();
  SPI.setFrequency(20000000);
  SPI.setDataMode(SPI_MODE2); //※これは実は Mode 3
  
  Serial.println("wait-------------------");
  delay(5000);
}

void loop() {
  for(int i=0; i<8; i++){
    SPI.write(B10010010);
  }
}

【解説】
シリアルモニタに” wait—————-” と表示されたら5秒以内にロジアナを起動させて測定するプログラムです。

9行目の
SPI.setFrequency(20000000);
というところで、SPI信号を20MHz で出力させます。
前回の記事で、GPIO レジスタ Direct Access では最高6MHz ですので、それのさらに3倍以上の速度です。
先にも述べたように、OLED SSD1351 側の受け入れクロック周波数は20MHz までですので、これに合わせます。

10行目で、SPI のモードを設定します。
しかし、いささか問題がありました。後で述べます。

GPIO MOSIピンに8bit データを出力させるには、18行目のように
SPI.write(B10010010);
という感じでOKです。
MOSIピンを指定する必要はありません。
SPIライブラリを使うと自動的にESP8266 のGPIO #13 に割り当てられます。

では、これをコンパイル書き込み実行させます。

ロジックアナライザ ZEROPLUS LAP-C を使って測定していきます。
ロジアナがあると無いのとでは雲泥の差でエラー原因特定やデバッグに有利です。
私はこれを買ってホントに正解だったと思っています。

ZEROPLUS ロジックアナライザ LAP-C(16032)
ZEROPLUS
¥22,503(2024/04/20 10:45時点)

では、まず、ザッとロジック波形はこんな感じになりました。

前回の記事で述べましたが、メインloop()の繰り返しより、forループの繰り返しの方が速いですね。
forループの初回の後は長めの休止区間、2回目以降は短めの休止区間があります。
これについては解明しましたので、後で述べます。

パルスをズームして見てみるとこうなります。

そうなんです。
スケッチ上でSPI_MODE2 と指定しているのに、パルスがMODE 3 になってます。
SPIインターフェースのMODEの違いは以下の様なはずです。

【 Mode 0 】

【 Mode 1】

【 Mode 2】

【 Mode 3】

SPI.write(B10010010); で送っているMOSI信号のビットを見てみても、明らかにMode 3 ですよね。
これは私の考え方が間違っているのか、Arduino core for ESP8266 ライブラリが間違えているのか・・・?

いずれにしても、OLED SSD1351 のデータシートでもMode 3 で記述してあり、それでしか動かないので、ライブラリが誤っていると思われます。

ということで、皆さん、気を付けてください。
ESP8266 のSPI ライブラリでは、
SPI.setDataMode(SPI_MODE2); は Mode 3 です!!

コメント

  1. macsbug より:

    mgo-tecさん、こんにちわ。
    ロジックアナライザーやマニュアルの確認等々、詳細な追求をご苦労様でした。
    このような具体的な波形を含んだ丁寧な記事は大変助かっています。
    私も ESP8266 + 2.2″ 240×320 SPI TFT ILI9341 の高速抽画での WDT Error で悩まされています。
    結論:現在(2016.10.13現在)、Adafruit_ILI9341_ライブラリーのサンプルプログラム_graphicstestにてWDT Errorは発生していません。
    理由:サンプルプログラムに yield(); が 16カ所追加されています。
    ライブラリー:以下を使用。
    https://github.com/adafruit/Adafruit_ILI9341
    https://github.com/adafruit/Adafruit-GFX-Library
    2016年4月頃のgraphicstest サンプルには、yield(); が無く WDTが発生していました。
    その時の私の結論は ESP.wdtDisable(); を3カ所記載して動き済ましています。

    TFTの価格:以下は紹介されているアマゾン(1599円)より約半額で2個買えます。
    ebaでの最安値は、733円($7.07)です。業者は sensesmart。
    240×320 3.3V 2.4″ SPI TFT LCD Touch Panel Serial Port Module with PBC ILI9341
    http://www.ebay.com/itm/2-4-240×320-SPI-TFT-LCD-Touch-Panel-Serial-Port-Module-with-PBC-ILI9341-3-3V/172226401724?_trksid=p2047675.c100005.m1851&_trkparms=aid%3D222007%26algo%3DSIC.MBE%26ao%3D1%26asc%3D39242%26meid%3D73e174f5697a42ce9fbbd4c16b9ff194%26pid%3D100005%26rk%3D4%26rkt%3D6%26sd%3D162005196054

    販売数が1番の業者は modulefans で 736円です。
    http://www.ebay.com/itm/240×320-3-3V-2-4-SPI-TFT-LCD-Touch-Panel-Serial-Port-Module-with-PBC-ILI9341-/171983887298?hash=item280b09dbc2:g:4LkAAOSwI-BWMzzZ
    736円

    2.4″ 240×320 TFT の少し前の記事ですが参考になれば、、。
    Try ESP8266 Adafruit_ILI9341 again
    https://macsbug.wordpress.com/2016/04/20/try-esp8266-adafruit_ili9341-again-2/

    Using the TFT LCD display in the ESP8266
    https://macsbug.wordpress.com/2016/04/16/using-the-tft-display-in-the-esp8266/

    How to use the UTFT Library the TFT LCD in ESP8266
    https://macsbug.wordpress.com/2016/04/18/how-to-use-the-utft-library-the-tft-lcd-in-esp8266/

    How to touch operation of the TFT LCD in ESP8266
    https://macsbug.wordpress.com/2016/04/25/how-to-touch-operation-of-the-tft-lcd-in-esp8266-2/

    Bodmer氏はArduinoでTFTのグラフィックス描画性能にトライされ表にあるようにスピードアップされたとの事。
    ただし、これはArduinoでESP8266では動作しませんが参考になります。(答えでなくて申し訳ない)
    Arduino TFT display and font library
    http://www.instructables.com/id/Arduino-TFT-display-and-font-library/?ALLSTEPS
    Step 10: TFT_ILI9341 library now on Github

    • mgo-tec mgo-tec より:

      macsbugさん

      とても有益な情報ありがとうございます。
      そして、記事をお読みいただき感謝いたします。
      yield()はかなり有効ですよね・・・。

      私も今、高速応答TFTまたはOLEDを探しております。
      なかなか安くてSPIの高速通信(例えば、SPI 40MHz以上)対応のものが無いんですよね。
      このリンクを参考に探してみます。

      いろいろとありがとうございました。

  2. nishioka.sst より:

    nishioka.sstです。以前コメントさせて頂いたことがあります。
    いろいろな製作記事があってとても楽しみにしております。
    記事中のSoft WDT reset、yield(); ・・について参考になりました。
    私も仕事に関係するのですが、最近似たような?ことがありました。
    http://nskikaku.sakura.ne.jp/NS2016/ns2016.html
    (2016,10/5)の記事は関連していると思います。
    原因はまだ良くわからないのですがyield();で上手く動いていますね。
    でもdelay(1);ではダメだったです。

    • mgo-tec mgo-tec より:

      nishioka.sstさん

      ご無沙汰しております。
      いつも当ブログに訪れていただいているようで、感謝いたします。

      記事拝見させていただきました。なるほど・・・!!
      かなり高度なことをやっておられますね。
      しかも、yield()は私が気付く前に既に試されていたんですね。
      スバらしいです!!
      当方の記事と全く同じ症状ですね。
      これは安定動作にはとても有効な方法だと思います。
      これからこういう情報がドンドン出てきて、ますますESP8266が安定して来そうですね。
      また何かありましたらドンドン情報くださいませ。
      m(_ _)m

  3. nagi より:

    mgo-tecさん、こんにちは。
    私は今ESP32devkitcとSSD1351を使用してmgo-tecさんのようにスピードアップを試みています。
    Adafruit_SPITFT.*内のSPI_CS_HIGH()等をdigitalWrite()からDirect Accessに変更することでそれが実現できると踏んでいましたが、何も表示されず上手くいきません。
    私の力不足故のことではありますが、アドバイスの程よろしくお願いします。

    • mgo-tec mgo-tec より:

      nagiさん

      記事をご覧いただきありがとうございます。
      3件同じ内容で投稿されていたので、直近の1件を挙げてお答えします。

      まず、この記事は古く、現在の環境ではうまく動かないことがあることをご了承ください。

      そして、残念ながらこの記事のGPIO Direct Accessは、ESP8266専用です。
      ESP32 は全く異なるレジスタ番号になるので、ESP32では動きません。

      ただ単に表示を高速化したいのであれば、最もお勧めなのが、ライブラリのLovyanGFXを使うことです。
      これはTwitterでもお世話になっているLovyanさんが製作されたものです。
      これは現在、ESP32で最も高速でLCDを描画できるライブラリです。
      インストール方法は以下の記事
      LovyanGFXライブラリのインストール
      で紹介してますし、使い方はネットでも徐々に出始めています。

      最近日本語フォントに対応したらしいので、Twitterでお世話になっているたなかまさゆきさんの以下の記事
      LovyanGFX入門 その3 日本語フォント描画系
      に書かれています。
      そのブログには、LovyanGFXの使い方が多数紹介されていますので参考にしてみてください。

      私もESP32やM5StackのLCD描画高速化にいろいろ挑戦しましたが、Lovyanさんの仕事量と知識量には到底かないませんでした。
      とにかく超高速なのでおススメですよ。

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