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

記事公開日:2016年10月12日
最終修正日:2017年10月7日

スポンサーリンク

こんばんは。

前回の記事では、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 の場合はデータシートのタイミングチャートにこんな感じで記載されています。

SPI_SPEED_UP2_01

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 にしておきます。
SPI_SPEED_UP2_02

#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 を使って測定していきます。
ロジアナがあると無いのとでは雲泥の差でエラー原因特定やデバッグに有利です。
私はこれを買ってホントに正解だったと思っています。
Amazon.co.jp

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

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

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

SPI_SPEED_UP2_03

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

【 Mode 0 】
SPI_mode_0

【 Mode 1】
SPI_mode_1

【 Mode 2】
SPI_mode_2

【 Mode 3】
SPI_mode_3

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

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

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

スポンサーリンク


mgo-tec電子工作 関連コンテンツ ( 広告含む )

投稿者:

mgo-tec

Arduino , ESP32 ( ESP-WROOM-32 ) , ESP8266 ( ESP-WROOM-02 )等を使って、主にスマホと連携した電子工作やプログラミング記事を書いてます。ライブラリも作ったりしてます。趣味、独学でやってますので、動作保証はしません。
電子回路やプログラミングの専門家ではありません。
畑違いの仕事をしてます。
でも、少しだけ電気の知識が必要な仕事なので、電気工事士や工事担任者等の資格は持ってます。

「ESP8266 ( ESP-WROOM-02 ) SPI 通信高速化、その2 ( 複数デバイス、Mode、Watchdog Timer 問題 )」への4件のフィードバック

  1. 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

    1. macsbugさん

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

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

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

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

    1. nishioka.sstさん

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

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

コメントを残す

メールアドレスが公開されることはありません。

*画像の文字を入力してください。(スパム防止の為)