このところArduino環境、便利、便利と使わせていただいております。ちょっと気になったのは、その処理の実態たるloop()関数の「速さ」。いったい1秒間に何回くらい回っているの?「標準的な」Unoの場合117kHzという数字を見つけたのですが本当か? 同じ環境でもWio TerminalやM5Stackなどはきっと違う。測ってみます。大体のところ。
※「鳥なき里のマイコン屋」投稿順Indexはこちら
Arduinoのloop()関数について説明しておきます。ArduinoIDEの .ino 拡張子を持つソースコードの場合、最終的にはC++コンパイラでコンパイルされますが、その「プリプロセス」は独特です。通常のmain()関数は見えずユーザのプログラムの「起点」となるのは、以下の2つの関数なのです。
-
- 初期化のためのsetup()関数
- 繰り返し無限に実行されるloop()関数
よって、処理の中心となるのは通常の場合loop()関数です。loop()という名前からは、この関数の内部で回り続けるような錯覚を覚えるのですが、loop()の外側にある真の「ループ」から、ループを回る度に呼び出される関数です。
さて、Arduino世界の標準機として皆さん認識されている Arduino Unoの場合、特に負荷がなければ、loop()関数は 117kHz で呼び出されているという情報がありました(当方見解とは異なる。)その数字は最近以下のページで知りました。
Keep your void loop() clean (Blynk Help Center)
しかし、上記のページもまた引きで、オリジナルは以下でした。
Go Speed Racer…Arduino Speed Test (Sparkfun)
なお、オリジナルの情報の言い方だと、loop()使って117kHzで「トグル」できるです。
手元には、Arduino IDE環境で、Arduino互換機としてプログラムできるデバイスがUno以外に複数あります。こんな感じです。
デバイス | MCU | CORE | 動作周波数 |
---|---|---|---|
Arduino Uno | ATmega328P | AVR(8bit) | 16MHz |
Wio Terminal | ATSAMD51P19 | Arm Cortex-M4F | 120MHz |
M5Stack Gray | ESP32 | Xtensa | 240MHz |
Seeeduino Lotus | ATmega328MU | AVR(8bit) | 16MHz |
これら全てが皆同じ速さとは考えられないので、簡単に測ってみることにしました。方法は以下のようです。
loop()関数の中で、Arduinoのデジタル0番端子をトグルさせる
外部でD0端子に出てくる信号の周波数を測り、その2倍がloop()関数の呼び出し頻度となる(トグルなので)筈。「測定系」としては、D0とGNDの間に10kΩの抵抗を入れただけ、それを例によって Digilent社 Analog Discovery2のオシロ機能で測定です。
オリジナル Arduino Unoの場合
まずは、オリジナルのAruduino Unoの場合です。一応、使用したコードを記録しておくと、こんな感じ。
#define OPORT (0) uint8_t portSTAT; void setup() { pinMode(OPORT, OUTPUT); portSTAT = LOW; digitalWrite(OPORT, portSTAT); } void loop() { if (portSTAT == LOW) { portSTAT = HIGH; } else { portSTAT = LOW; } digitalWrite(OPORT, portSTAT); }
測定の様子は以下のようです。
このときの波形がこちら。右側の周波数測定機能からは124kHzという数字が得られました。数字的には先ほどの117kHzに近いです。コード的には117kHzの先例よりもちょい面倒なことをしているので、多少遅いのはその影響だと思えば良いのではないか、と。しかし、これはトグルなので「2分周」されてます。loop()関数自体の呼び出し頻度としては、約248kHzと理解すべきだと思います。
SeeedStudio社 Wio Terminalの場合
Wio Terminalの場合、Arduino Unoとのソフトウエア的な互換性が重視されています。装置の形態もMCUも異なるのですが、上のArudino Unoのコードをそのまま書きこんで(当然Arm用に再ビルドして)実行することができます。Arduinoのデジタル端子0番は正面右側のGroveポートに出力されているので、それを引き出せば即、測定可能です。
さて、測定した波形は以下のようです。Aruduino Unoのときと、時間軸は同じスケールにしてあります。かなり高速になっていることが分かります。トグルで約894kHzなので、loop()の呼び出し頻度は約1.8MHzほどとなりました。なお、Unoの場合、電圧は5V付近まで振れていますが、Wio Terminalは3.3VIOであることも見てとれます。
M5Stack Greyの場合
M5Stack Greyの場合、使用するソフトウエアを僅かに変更しないとなりませんでした。
#include <M5Stack.h> #define OPORT (3) uint8_t portSTAT; void setup() { M5.begin(); pinMode(OPORT, OUTPUT); portSTAT = LOW; digitalWrite(OPORT, portSTAT); } void loop() { if (portSTAT == LOW) { portSTAT = HIGH; } else { portSTAT = LOW; } digitalWrite(OPORT, portSTAT); }
M5Stack固有の初期化をするのと、端子番号の変更です。M5Stackの回路図を見れば、
Arduino Unoのデジタル0番端子(RX0でもある)がM5Stack上ではGPIO3
であることが分かります。このときプログラムはGPIO3の「3」で書かないとならないので、0としてあったところを3に書き換える必要があります。ハードウエア的には、M5Stackの側面の拡張端子にGPIO3が出ているのでそこから信号を取り出せばOKでした。
観察した波形は以下のとおりです。M5Stackは比較した4機種の中で最速の240MHzのクロック速度、Wio Terminalよりさらに速くなっています。トグル約1.9MHzなので、呼び出し頻度的には3.8MHzとほぼ倍速。信号レベルは3.3Vですが、波形かなり暴れているな(Wio Terminalと比べると。)
Seeeduino Lotusの場合
Seeeduino Lotusボードは、Groveポートだらけのボードですが、ほぼほぼArduino Uno互換です。マイコン的にはパッケージの違いのみ、ソフト的にもUnoそのもの。Unoと互換の拡張端子も使えるのですが、ここはGroveポートからデジタル0番端子を引き出してつかってみます。
デジタル0番=RXなのでUARTと書かれたGroveポートから信号もらいます
その様子がこちら。
まあ、これで「負荷の軽い」ときのloop()関数呼び出しの頻度は、大体わかった?と。大体。