鳥なき里のマイコン屋(110) Arduino, Wio, M5Stack, loop()の速さ

Joseph Halfmoon

このところArduino環境、便利、便利と使わせていただいております。ちょっと気になったのは、その処理の実態たるloop()関数の「速さ」。いったい1秒間に何回くらい回っているの?「標準的な」Unoの場合117kHzという数字を見つけたのですが本当か? 同じ環境でもWio TerminalやM5Stackなどはきっと違う。測ってみます。大体のところ。

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);
}

測定の様子は以下のようです。

ArduinoUno_MeasureLoopHzこのときの波形がこちら。右側の周波数測定機能からは124kHzという数字が得られました。数字的には先ほどの117kHzに近いです。コード的には117kHzの先例よりもちょい面倒なことをしているので、多少遅いのはその影響だと思えば良いのではないか、と。しかし、これはトグルなので「2分周」されてます。loop()関数自体の呼び出し頻度としては、約248kHzと理解すべきだと思います。UnoLoopHz

SeeedStudio社 Wio Terminalの場合

Wio Terminalの場合、Arduino Unoとのソフトウエア的な互換性が重視されています。装置の形態もMCUも異なるのですが、上のArudino Unoのコードをそのまま書きこんで(当然Arm用に再ビルドして)実行することができます。Arduinoのデジタル端子0番は正面右側のGroveポートに出力されているので、それを引き出せば即、測定可能です。

WioTerminal_MeasureLoopHzさて、測定した波形は以下のようです。Aruduino Unoのときと、時間軸は同じスケールにしてあります。かなり高速になっていることが分かります。トグルで約894kHzなので、loop()の呼び出し頻度は約1.8MHzほどとなりました。なお、Unoの場合、電圧は5V付近まで振れていますが、Wio Terminalは3.3VIOであることも見てとれます。

WioLoopHz

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_MeasureLoopHz観察した波形は以下のとおりです。M5Stackは比較した4機種の中で最速の240MHzのクロック速度、Wio Terminalよりさらに速くなっています。トグル約1.9MHzなので、呼び出し頻度的には3.8MHzとほぼ倍速。信号レベルは3.3Vですが、波形かなり暴れているな(Wio Terminalと比べると。)

M5StackLoopHz

Seeeduino Lotusの場合

Seeeduino Lotusボードは、Groveポートだらけのボードですが、ほぼほぼArduino Uno互換です。マイコン的にはパッケージの違いのみ、ソフト的にもUnoそのもの。Unoと互換の拡張端子も使えるのですが、ここはGroveポートからデジタル0番端子を引き出してつかってみます。

デジタル0番=RXなのでUARTと書かれたGroveポートから信号もらいます

その様子がこちら。

SeeeduinoLotus_MeasureLoopHz波形がこちら。Unoとの違いは誤差の範囲かと。

LotusLoopHzまあ、これで「負荷の軽い」ときのloop()関数呼び出しの頻度は、大体わかった?と。大体。

鳥なき里のマイコン屋(109) NUC120で28BYJ-48、2相励磁 へ戻る

鳥なき里のマイコン屋(111) NUC120で28BYJ-48、1相2相1-2相?  へ進む