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

ESP8266 ( ESP-WROOM-02 )

3.無限ループのウォッチ・ドッグ・タイマ ( Watchdog Timer ) 問題

さて、メインloop() の繰り返しが遅いならば、動作速度を上げる為に、whileループでやってしまうとどうなるのでしょうか?
試しに以下のスケッチをコンパイル実行させてみてください。

【ソースコード】 (※無保証 ※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() {
  while(1){
    for(int i=0; i<8; i++){
      SPI.write(B10010010);
    }
//    yield(); //WDTを動作させるために必要
  }
}

すると、シリアルモニタではこんなエラーになります。

そうそう、よく見るエラー

Soft WDT reset

ですね。
これは、つまり、ウォッチ・ドッグ・タイマ( Watchdog Timer 略して WDT ) が実行されないことによるエラーだそうです。
強制リセットが繰り返されてしまいます。

お恥ずかしながら、今までマイコンの記事を書いてきましたが、この事はつい最近知ったばかりです。
当方は独学で記事を書いてますので、何卒ご容赦ください。
m(_ _)m
今までこういうエラーが出たり、フリーズしていたりしたら、これが原因の可能性があります。

マイコンというものはウォッチドッグタイマを装備していて、ループ内のある一定区間を休止させて、裏側で監視機能を動作させるタイマがあるとのことです。
ESP8266 ( ESP-WROOM-02 )ではWi-Fiの電波発信、受信などの動作を裏で動作させているのだろうと考えられます。
ですから、先で述べたメインのloop() 繰り返し中の休止区間ではこのウォッチドッグタイマ(WDT) が動作しているわけです。
これを無視してwhileループを組んでしまうと Soft WDT reset エラーが出てしまうわけです。

これを回避しつつ、loop()よりも速度アップさせるには、20行目のコメントアウトを解除してください。
つまり、whileループの中に

yield();

という関数を置いて下さい。
これは、ループ内で他の裏のタスクを実行させる関数です。
delay(1) でも同じような動作をするらしいです。

それでコンパイル実行させると、見事にエラーが出なくなりましたね。

ついでにロジアナ LAP-C で速度を見てみると、こうなりました。

休止区間が1.32us と大幅に短くなりました。
メインloop() よりも格段に高速化できたことになります。
delay(1) の場合は1ms も休止させてしまいますが、yield() は格段に速いです。恐るべし。

ただ、これでも2.5ms経過すると95.9us休止する区間が出現します。
これもウォッチドッグタイマの類ではないでしょうか。

ということで、この件は意外とネットでも情報がありました。
ループ内の休止区間もこれでほとんど解明できたのではないかと思います。

教訓: loop() 以外で無限ループを組む場合にはyield()を置くべし!!

私がこれに気が付いて、記事を書いている最中、同じことを述べている Ikeuchi Toru さんという方の以下の記事を見つけました。

Server-Sent Event(SSE)を利用して、スマホのブラウザにセンサデータを表示
(この記事は消去されたようです)

これは、私が以前書いた記事のスケッチのエラーを解消されています。
素晴らしいですね。 感謝いたします。
ありがとうございました。m(_ _)m

実は、この yield() は、Wi-Fi でHTTP GET してテキストデータを受信する時のwhileループではあまり効果を発揮しないような気がします。
他の原因として、ESP8266 ( ESP-WROOM-02 ) の過大電流による電圧低下も考えられますが、瞬時降下した場合はリセットするので、その原因は考えられないような気がします。

例えば以下の記事、

Twitter 検索結果のツイートを有機EL ( OLED )に表示させてみた

ではツイートを定期取得していますが、これを動作させながら他のパソコンでネットサーフィンしていると、しばらく経つとHTTP受信中にフリーズしてしまうことが多々ありました。
ループを抜けられるようにタイムアウト設定も組んでみたのですが、なかなかwhileループやforループを抜けてくれませんでした。
そこで、この前知ったyield() を試してみたのですが、それでも抜けてくれませんでした。
予想できるのが、他のIPアドレスでネットサーフィンしているとESP8266に送られるパケットが遅れてウォッチドッグタイマが上手く機能しないのではないかと思いました。

それで、今、実験中なのですが、yield()と合わせて、delayMicroseconds(); を所々追加して試しております。
delay(1)では遅すぎるので、ウォッチドッグタイマを確実に動作させつつ、動作速度を保つためにいろいろと試行錯誤中です。
この結果は後日報告したいと思います。

WDT エラーについては、配列の文字数が大きすぎたり、String文字列が多すぎたりした場合も発動することが分かりました。
詳しくは以下のページを参照してください。
Arduino / ESP8266 の使用できるRAM 領域を再考

その他に何かご意見がありましたら、是非情報を頂けると有り難いです。

4.SPIライブラリとSDライブラリとGPIO レジスタ Direct Access を共用する使い方

今回の実験で使うのは
●ESPr Developer ( ESP-WROOM-02 ( ESP8266 ))

ESP-WROOM-02開発ボード
スイッチサイエンス(Switch Science)

ESPr Developer(ピンソケット実装済)
スイッチサイエンス(Switch Science)

●SparkFun マイクロSDカードスロット・ピッチ変換基板

SparkFun マイクロSDカードスロット・ピッチ変換基板

micro SDHC カード class10

ロジックアナライザー ZEROPLUS  LAP-C

ZEROPLUS ロジックアナライザ LAP-C(16032)
ZEROPLUS
¥22,504(2024/03/28 20:55時点)

ロジアナ測定だけですので、他のSPIデバイスはここでは使いません。

接続はこんな感じです。


Arduino core for ESP8266 の SPIライブラリを使う場合は、

SCLK  GPIO #14
MOSI  GPIO #13
MISO  GPIO #12

のように、自動的にGPIOピンが割り当てられます。
SDカードライブラリでは、SDカードのCS ( Chip Select )ピンは

SD CS  GPIO #15

に割り当てられます。

ですから、そのピン以外をレジスタ Direct Access に使うことになりますが、16番ピンはDirect Access を割り当てることができません。
16番ピンはDeep Sleep 解除などの割込み入力に使われる端子です。
よって、Direct Access で自由に使える端子は

GPIO #0
GPIO #2
GPIO #4
GPIO #5

となります。
これをSDカード以外のCSピン、DC ( Data Command ) ピン、リセットピンに割り当てます。

コメント

  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をコピーしました