ラズピコでArduino IDEの実行速度を見てみるのと改善方法

Arduinoはコンパイル時にcppに落とされてからコンパイルされるので、基本はコンパイラ言語ですが、学習用にかなり冗長な作りになっているようなので、実行速度が問題になるケースも多そうです

というわけで簡単にベンチマーク

元はLEDブリンクのソースに計測用のコードを追加しています

<mainのinoファイル>

ただし時間測定時には、loop1()のwhile処理(loop0()からのメッセージ受信)はコメントアウト

#include "common.h"

// constants won't change. Used here to set a pin number:
const int ledPin = LED_BUILTIN;  // the number of the LED pin

// Variables will change:
int ledState = LOW;  // ledState used to set the LED

// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
unsigned long previousMillis = 0;  // will store last time LED was updated

// constants won't change:
const long interval = 500;  // interval at which to blink (milliseconds)

uint32_t message_s = 0;
uint32_t message = 999;

void setup() {
  // set the digital pin as output:
  pinMode(ledPin, OUTPUT);
}

void loop() {
  // send message to core1
  rp2040.fifo.push(message_s);
  message_s += 1;

  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    // save the last time you blinked the LED
    previousMillis = currentMillis;

    // if the LED is off turn it on and vice-versa:
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }
    // set the LED with the ledState of the variable:
    digitalWrite(ledPin, ledState);
  }
}

void setup1(){
  pinMode(0, OUTPUT);  
}

void loop1(){
  while (rp2040.fifo.available()>0){
    message = rp2040.fifo.pop();
    message *= 10;
  }
  for (int i = 0; i < 8; i++){
    for (int j = 0; j < 8; j++){ if (disp_data[i][j] > 7){
          dest_data[i][j] = disp_data[i][j];
        }
        else{
          dest_data[i][j] = 0;
        }
    }
  }

  clock_func();
}

<common.hファイル:byte型の二次元配列を2個確保しています>

データの中身に取り立てて意味はありません


// display data
byte disp_data[8][8] = {
   {0,1,1,2,3,5,8,13},
   {13,8,5,3,2,1,1,0},
   {0,1,1,2,3,5,8,13},
   {13,8,5,3,2,1,1,0},
   {0,1,1,2,3,5,8,13},
   {13,8,5,3,2,1,1,0},
   {0,1,1,2,3,5,8,13},
   {13,8,5,3,2,1,1,0}
};

byte dest_data[8][8] = {
};

<clock.inoファイル>

gpioへの書き込みが極端に遅いので、c++の関数を呼び出せるということなので切り替えた(digitalWrite() -> gpio_put())、パルス幅確保のためにハイレベルを5回書き込んでます



void clock_func(){
  //digitalWrite(0, HIGH);
  gpio_put(0, 1);
  gpio_put(0, 1);
  gpio_put(0, 1);
  gpio_put(0, 1);
  gpio_put(0, 1);
  //digitalWrite(0, LOW);
  gpio_put(0, 0);
}

実行速度の測定結果

測定はgpio0のポイントをオシロのプローブであたって時間測定

・digitalWrite()使用時

・gpio_put()使用時

考察

ループの外でGPIO書き込みを行っているのに、全体の処理時間を3割程度長くしてしまうArduinoの標準書き込み関数は時間がクリティカルな場所では使えない、配列の処理に関してはcに比較したら遅いだろうけれども許容範囲か、ただしコードの記述には工夫が必要(配列の要素を一度変数に入れてから処理とかはやめた方が良い

やりたいことはLEDアレイ(HUB75で64*32)の点灯をコア1で行うようにしたいから、16階調処理だと一行のループ処理をおよそ69μs以内(50Hz以下になると人の目に見えるから、1000000/(60*15*16) =69.444μs )ぐらいで実行必要だからターゲットはそこだけど、なんとかなりそうな感触

 

admin

ラズピコのマルチコア使ったアプリケーションを考える

ラズパイzeroでもコアが一個しかないのに、picoには二個あります、その分クロック周波数は1GHz vs 133MHzですが、

で、マルチコアを使ったアプリとしてHUB75インターフェースのLEDパネルを駆動させるのが取っ付きやすそうに思う

なぜなら、LEDパネルは擬似的な点灯(だから静止画や動画撮影ではまともに見えない)で時間を保証(数十μsオーダー)して点滅させないといけないので、通信や表示データを用意する余裕はないだろうから、表示制御にはコア一個を割り振り、もう一個のコアでは通信と表示データを用意させる処理を実行させる

無論メモリは両方のコアで共通だから、完全に負荷分散ができる訳でもないけどシングルコアよりはかなり余裕があるはず、上はメモリやペリフェラル含めた全体図、下はコア部分だけに着目したブロック図でAHB-Liteがコアの分離とアクセス制御をやっているように見える

データの受け渡しは、表示データの完全性の保証は必要ないからFIFOは制御用のデータ受け渡しに使い、表示データは共通のRAMエリアで2面持たせて、切り替え使用するようにすればいいだろう

 

admin

ラズピコ wでのマルチコアの使い方(Arduino IDE)

ラズピコはマルチコアになっていて、MicroPythonでもマルチコアを使えるけれど、実行速度考えたらArdunino言語を使った方が高速だろうから、

https://ameblo.jp/pta55/entry-12798012318.html

で見つけたやり方を参考に、Lチカアプリをマルチコア使って他のコアにメッセージを送ってみた

ここでは固定値message_sをfifoで送信していますが、受信側で使われるglobal変数はloop1のfifo受信処理で書き換えられています(最初のブレークポイントではグローバル変数の初期値999ですが、その後のブレークでは1になります(fifoバッファーのデータ有無チェックしてるのに初期値になるのは変!

https://arduino-pico.readthedocs.io/en/latest/multicore.html

には、

int rp2040.fifo.available()

Returns the number of values available to read in this core’s FIFO.

とありますが

// constants won't change. Used here to set a pin number:
const int ledPin = LED_BUILTIN;  // the number of the LED pin

// Variables will change:
int ledState = LOW;  // ledState used to set the LED

// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
unsigned long previousMillis = 0;  // will store last time LED was updated

// constants won't change:
const long interval = 500;  // interval at which to blink (milliseconds)

uint32_t message = 999;

void setup() {
  // set the digital pin as output:
  pinMode(ledPin, OUTPUT);
}

void setup1(){
}

void loop() {
  // here is where you'd put code that needs to be running all the time.

  // check to see if it's time to blink the LED; that is, if the difference
  // between the current time and last time you blinked the LED is bigger than
  // the interval at which you want to blink the LED.
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    // save the last time you blinked the LED
    previousMillis = currentMillis;

    // if the LED is off turn it on and vice-versa:
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }

    // set the LED with the ledState of the variable:
    digitalWrite(ledPin, ledState);

    // send message via FIFO
    uint32_t message_s = 0;
    rp2040.fifo.push(message_s);
  }
}

void loop1(){
  if (rp2040.fifo.available()>0){
    message = rp2040.fifo.pop();
    message += 1;
  }
}

マルチコアの使い方は、

① コアごとの動作はsetup/setup1、loop/loop1で指定する

② コアプロセス間の通信はfifo(rp2040.fifo)を使って行う、データ幅は4バイトで長さは8らしい(ソフト的なfifoなら任意に設定できるはずなのでこれはハードレジスタ)、受信するデータはglobal変数で定義しないといけない、送信側はlocal変数でもglobal変数でも大丈夫

デバッガで動かしてみると、マルチコア動作が確認できるのと、sync.hなるファイルが自動で生成されています、これはステップイン機能(停止したポイントの関数の中も追える)ですね

 

画面を見ればわかるようにコア二個分のデバッグ情報を見ることができます

 

admin