ESP8266 Direct Access Port で Lチカ
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)とほぼ一致しています。
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
初めまして。
こちらの記事をとても参考にさせていただきました。
おかげ様で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
[…] こちらも→ ESP8266 Direct Access Port で Lチカ […]
ESP8266 ( ESP-WROOM-02 ) SPI 通信の高速化に挑戦 | mgo-tec電子工作
10月 6, 2016 at 2:25 pm
mgo-tecさん、ブログを見に来ていただきありがとうございます。
高速化で解り安いのは 定番の「Lチカ」かと思って試しました。
ダイレクト レジスタ アクセス さらに アセンブラ とか マシン語 に進むと速くなるかと思っています。
ただし、私には 難しいレベルになってきています。
macsbug
10月 6, 2016 at 9:40 pm
なるほど・・・。
アセンブラやマシン語は私にはとても無理です。
Arduino IDE スケッチで精一杯です。
いつかトライしたいとは思っています。
mgo-tec
10月 7, 2016 at 11:32 am