macsbug

Just another WordPress.com site

ESP8266 Direct Access Port で Lチカ

with 9 comments

ESP8266 の GPIO を直接操作して「Lチカ」をしてみました。    2016.02.18

「 Lチカ」は 電子工作の基本とでもいいましょうか 通常は digitalWrite 命令ですね。
Direct Access Port は digitalWrite を使用せず I/O をダイレクトに操作する方法です。
ちょっと上級者の気分になれるかも知れません。

digitalWrite 命令は 最高1MHzまでの信号を出力したり解析をする事ができます。
Direct Access Port 命令は 最高6MHzまで可能で 高速な処理をする場合に約に立つ
と思います。例えば32bit信号で25KHzの繰り返しの場合 Arduino UNO では性能
ぎりぎりですが ESP8266ならば かなり余裕が出てきます。

他にアナログデジタルコンバーター ADC 24bit になるとかなり高速な操作が必要で
ライブラリの中で高速処理を行っているようです。ライブラリーはある程度上級
者レベルですが スケッチレベルで高速処理ができる事になる訳です。


スケッチ:Arduino IDE にある、01.Basics の Blink ができます。

#define PIN_OUT *(volatile uint32_t *)0x60000300
#define PIN_DIR *(volatile uint32_t *)0x6000030C
#define PIN_13  *(volatile uint32_t *)0x6000035C

void setup() {
  PIN_OUT = (1 << 13);
  PIN_DIR = (1 << 13);
}

void loop() {
  PIN_13 = 0;
  delay(1000);
  PIN_13 = 1;
  delay(1000);
}

事例は 13番ピンの GPIO13 を出力に指定し “1” 又は “0” を出力するものです。
以下のGPIO Registers表 に基づいて ピンに応じた アドレス を使用すると
他の GPIO も入出力がコントロールできる訳です。
GPIO Resistor 表から参照して 他のポートも試してみてください。


追記:2016.02.19
最高速「Lチカ」:上記スケッチの delay を取り除き 不完全な無限ループ
_  ですが最高速の出力が得られます。
_  前回計測では 6.25MHz なのですが 実際には 5.764MHz でした。

void loop() {
  for(;;){
    PIN_13 = 0;
    PIN_13 = 1;
  }
}

宿題:無限ループはどのようにスケッチするか?
_  上記のスケッチは3秒に1度 0.25sec間 信号がでない場面があります。
_  while(1); でも同様です。goto文を使用するとしばらくして停止します。


詳細:
GPIOを直接操作するには GPIO のアドレス(Registor)の把握とそのアドレス
_ に #define で名前を定義します。
例:GPIO13(PIN_13)のアドレスは 「0x6000035C」です。
_ 名前の定義は、#define で行います。
_ #define PIN_13  *(volatile uint32_t *)0x6000035C   となります。
_ 使用する時は、
_ PIN_13 = 0;      // “0” を 13 bit ( 13番ピン) に出力する。
_ PIN_13 = 1 ;     // “1” を 13 bit ( 13番ピン) に出力する。


GPIO Registers(アドレス表):

GPIO ADDRESS GPIO NAME PIN
————- ———————– ———————————— —————————-
0x60000300 GPIO_OUT  PIN_OUT
0x60000304 GPIO_OUT_W1TS  PIN_OUT_SET:HIGH
0x60000308 GPIO_OUT_W1TC  PIN_OUT_CLEAR
0x6000030C GPIO_ENABLE  PIN_DIR:入出力
0x60000310 GPIO_ENABLE_W1TS  PIN_DIR_OUTPUT
0x60000314 GPIO_ENABLE_W1TC  PIN_DIR_INPUT
0x60000318 GPIO_IN  PIN_IN
0x6000031C GPIO_STATUS
0x60000320 GPIO_STATUS_W1TS
0x60000324 GPIO_STATUS_W1TC
 GPIO 0 0x60000328 GPIO_PIN0  GPIO0_U,PIN_0
0x6000032C GPIO_PIN1  U0TXD_U
 GPIO 2 0x60000330 GPIO_PIN2  GPIO2_U,PIN_2
0x60000334 GPIO_PIN3  U0RXD_U
 GPIO 4 0x60000338 GPIO_PIN4  GPIO4_U
 GPIO 5 0x6000033C GPIO_PIN5  GPIO5_U
0x60000340 GPIO_PIN6  SD_CLK_U
0x60000344 GPIO_PIN7  SD_DATA0_U
0x60000348 GPIO_PIN8  SD_DATA1_U
0x6000034C GPIO_PIN9  SD_DATA2_U
0x60000350 GPIO_PIN10  SD_DATA3_U
0x60000354 GPIO_PIN11  SD_CMD_U
 GPIO12 0x60000358 GPIO_PIN12  MTDI_U
 GPIO13 0x6000035C GPIO_PIN13  MTCK_U
 GPIO14 0x60000360 GPIO_PIN14  MTMS_U
 GPIO15 0x60000364 GPIO_PIN15  MTDO_U
0x60000368 GPIO_SIGMA_DELTA
0x6000036C GPIO_RTC_CALIB_SYNC
0x60000370 GPIO_RTC_CALIB_VALUE

 


リンカースクリプトに追加する方法と使用方法。
注:不可視ファイルの中の操作は危険ですので上級者向けかも知れません。

「Arduino15」ファイル内の「eagle.app.v6.ld」に追加をします。
例:PROVIDE(PIN_OUT = 0x60000300);
スケッチでの宣言:extern uint32_t PIN_OUT;
スケッチでの使用方法:PIN_OUT = 0x01;
PROVIDE(PIN_OUT = 0x60000300);
PROVIDE(PIN_OUT_SET = 0x60000304);
PROVIDE(PIN_OUT_CLEAR = 0x60000308);
PROVIDE(PIN_DIR = 0x6000030C);
PROVIDE(PIN_DIR_OUTPUT = 0x60000310);
PROVIDE(PIN_DIR_INPUT = 0x60000314);
PROVIDE(PIN_IN = 0x60000318);
PROVIDE(PIN_0 = 0x60000328);
PROVIDE(PIN_2 = 0x60000330);


初心のメモ:スケッチあれこれ。
PIN_DIR = (1<<0);                 // GPIO 0 の bit を “1” にする。HIGH
_               //      出力方向:”1”は出力。”0”は入力。
PIN_DIR = (1<<2);                // GPIO 2 の bit を “1” にする。 100 <- 001
PIN_DIR = 5;                         // GPIO 2と0の bit を”1” にする。 101
PIN_DIR = (1<<2 | 1<<0);    // GPIO 2と0の bit を”1” にする。 101
PIN_DIR = 0x05;                   // GPIO 2と0の bit を”1” にする。 101
PIN_DIR = 0b101;                 // GPIO 2と0の bit を”1” にする。 101
PIN_DIR = (1<<13);              // GPIO 13の bit を”1” にする。HIGH
PIN_13 = 0;                           // GPIO 13の bit を”0” にする。LOW
PIN_13 = 1;                           // GPIO 13の bit を”1” にする。HIGH
PIN_OUT (1<<13);                // GPIO 13の bit を”1” にする。HIGH

 


課題:
Direct Access Port を使いこなすには delayMicroseconds(us) に変わる
これ以下の delay が必要です。
命令のマシンサイクルが不明の為に マシンサイクルから算出したdelay
を使用する事ができない状態です。
Arduino UNOやDUEの場合は マシンサイクルが解っていいますので
ある命令は xx nsec delay として使うことが出来ました。


参考:
JACK’s LAB:ESP8266 Memory Map:詳細なメモリーマップ。これは必見です。
ESP8266 Community Forum:gpio registers
blog.naver.com:[ESP8266] GPIO Direct Access [1]
blog.naver.com:[ESP8266] GPIO Direct Access [2]
blog.naver.com:[ESP8266] GPIO Direct Access [3]
blog.naver.com:[ESP8266] GPIO 출력방식에 따른 비교 분석:6.665MHz(150ns)
ESP8266 Pins list function, register, strapping


感想:
JACK’s LABのESP8266 Memory Mapは大変参考になりました。アドレスを
理解すると様々なことが出来るかと思います。
[ESP8266] GPIO 출력방식에 따른 비교 분석には、6.665MHz(150ns) と
書かれており 私の測定値(6.25MHz,160nsec)とほぼ一致しています。


 

Written by macsbug

2月 18, 2016 @ 11:26 am

カテゴリー: ESP8266

コメント / トラックバック9件

Subscribe to comments with RSS.

  1. Direct Access Portの使用に関してさらに発展されていますね。

    1つ、お時間ある時に教えてください。

    PIN_OUT = (1 ** 13); というのがありますが、この「**」はどういう演算でしょうか?
    FORTRANなどでは 2 ** 3 (=8)のようにして、階乗の計算に使われていますが、1を基数としているためそうではないと思います。 ビット演算関連でしょうか?

    > 命令のマシンサイクルが不明の為に マシンサイクルから算出したdelayを使用する事ができない状態です。

    http://playground.arduino.cc/Main/AVR
    にありますno operationをアセンブラで使って動作させて、Lチカさせながらオシロスコープで測定する、というのは可能でしょうか?

    __asm__(“nop\n\t”);

    アセンブラ実行はまだこちらでは実行したことがないため、無責任なコメントかもしれません。

    7of9

    2月 19, 2016 at 3:43 am

    • >PIN_OUT = (1 ** 13);
      解りづらくて申し訳ありません。
      >> なのですが記載すると文字化けするので、 >> を ** にして書きました。
      PIN_OUT = (1<<13); です。
      その旨を、ブログに書きましたが見づらかったと思います。
      その後、この件は修正して旨く表示ができるようになりました。
      原因は、私のWordPressの詳細な使用方法を熟知していない為でした。(特殊記号の書き方)

      delayのご指摘と、大ヒントをありがとうございました。
      なるほどそこに書かれていたのですね。
      大正解です!!
      他のページも見つかり、最小単位が NOP命令で
      __asm__(“nop\n\t”); 又は __asm__ __volatile__ ("nop");
      で、出来る事が解りました。
      1/80000000 second : 12.5 nsec です。
      理論値からオシロとスペクトラムで確かめながら、値はぴったりとした1MHzの値が取れました。
      ありがとうございました。

      macsbug

      2月 19, 2016 at 3:03 pm

      • PIN_OUT = (1 ** 13);
        についての記載ありがとうございます。
        PIN_OUT = (1 < 1/80000000 second : 12.5 nsec です。
        > 理論値からオシロとスペクトラムで確かめながら、値はぴったりとした1MHzの値が取れました。

        マシンサイクル12.5nsecという情報を実際に測定できているのがすごいです。

        このブログの魅力としては精緻な測定を行いその情報を惜しげもなく公開されていることです。
        僕を含めて後に続く人にとってかなり有用な情報でいっぱいです。

        7of9

        2月 20, 2016 at 2:47 am

  2. 初めまして。
    こちらの記事をとても参考にさせていただきました。
    おかげ様でOLEDの文字列スクロールの速度アップが実現できました。
    この情報を公開して下さって感謝しております。
    ありがとうございました。

    当方のブログ( mgo-tec電子工作 ) にリンクを貼らせていただきました。
    https://www.mgo-tec.com/blog-entry-esp8266-wroom-spi-speed-up.html
    もし、不都合がありましたらご連絡ください。

    mgo-tec

    10月 6, 2016 at 2:08 pm

    • mgo-tecさん、初めまして。ブログを見に来ていただきありがとうございます。
      リンクをありがとうございます。お互いに盛り上げましょう。

      Adafruit のライブラリーは速度が遅く困っていました。
      その中で、頑張って作られた事に驚いています。
      Adafruit OLED SSD1351 SPI の高速化は、動画を見てその速度に驚嘆しています。
      この動画は何度見ても気持ち良く嬉しくなります。

      macsbug

      10月 6, 2016 at 9:28 pm

      • macsbugさんの記事のおかげです。
        ありがとうございました。

        そうなんですよね、Adafruitのライブラリはいろいろと困っているので、結構独自に改変しています。

        元々、Adafruitライブラりでは動作しなかったところをshiitakeoさんとう方がdigitalWrite関数を使ってSPI信号を生成する方法に書き換えているものを使わせていただきました。
        今回はそれをダイレクトアクセスに変えたら、2~3倍速くなりました。
        もっと詰めれば更に早くなりそうですね。

        今後とも参考にさせていただきますので、よろしくお願いいたします。

        mgo-tec

        10月 7, 2016 at 11:29 am

  3. […] こちらも→ ESP8266 Direct Access Port で Lチカ […]

    • mgo-tecさん、ブログを見に来ていただきありがとうございます。
      高速化で解り安いのは 定番の「Lチカ」かと思って試しました。
      ダイレクト レジスタ アクセス さらに アセンブラ とか マシン語 に進むと速くなるかと思っています。
      ただし、私には 難しいレベルになってきています。

      macsbug

      10月 6, 2016 at 9:40 pm

      • なるほど・・・。
        アセンブラやマシン語は私にはとても無理です。
        Arduino IDE スケッチで精一杯です。

        いつかトライしたいとは思っています。

        mgo-tec

        10月 7, 2016 at 11:32 am


コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中

%d人のブロガーが「いいね」をつけました。