工学男子の日常

モノづくりが好きな男子の日記です。

Arduino IDEからSTM32のDSPを使ってみた話

近頃はヘテロジニアスコンピューティング、ハードウェアアクセラレータとか、プログラマブルうんたらとか、各種のエンコーダデコーダFPGAGPGPU等々まさにアクセラレータ花盛りですね。ムーアの法則の限界が見えてきた昨今、消費電力をケチりつつ性能を上げるには汎用プロセッサより専用回路のほうが都合がいいということらしいです。

 

 

ところでマイコンに慣れてきたらDSPという言葉はよく耳にすると思います。デジタルシグナルプロセッサ、まぁ早い話が音声とかのデータをソフトで処理すると実時間に間に合わないてんで、浮動小数点演算とか積和演算やらをハードウェアで実装しちまえというわけですね。

 

通常ならCPUとレジスタでコネコネして浮動小数点演算するところ、横に載ってるブラックボックスにデータを投げ込むと、よくわからんけど答えがお出しされるという代物です。

 

 

先述のとおりの近頃のアクセラレータ流行りにのって、FPGAやらを物色していたんですがHDLを勉強するのが面倒まずは基本に立ち返って身近なマイコンにのっているDSPをやってみようと思ったわけです。

 

STM32duino

話は変わりますが、STM32duino皆さん使ってますか?自分は以前までCubeIDE、HAL、C言語でしこしこ書いていたわけですが、ここのところのSTM32duinoは凄いです。

 

記憶が正しければ最初はSTM32F1xxとSTM32F4xxにしか対応してなかったと思うんですが、STMicroelectronicsが正式にサポートするようになってから爆速で対応ボードが増え続けて2.6.0現在540種類というお化けライブラリになっております。

 

しかもこれ偉いのが、どっかのマイコンみたいにソフトウェアPWMでお茶を濁すようなことをせず、各種ペリフェラルがハードで実装されています。純正Arduinoでは制約が多いServoライブラリがタイマ1個で12本までPWMが出せるようになっていたり、公式IDEでいじれるところは基本いじれるようになっているなど、親切かつ気合の入った作りになっています。

さらにLoRaやBLEまで統合し始めて、お前はどこに向かっているんだと言う感じです。

 

その流れでARMのDSPライブラリであるCMSIS-DSPが追加されているのに気づいたので、Arduino IDEで試してみることにしました。

CMSIS-DSP

曰く

CMSIS-DSPライブラリは、ArmがさまざまなCortex-Mプロセッサーコア用に最適化したリッチなDSP機能のコレクションです。CMSIS – Arm®

だそうです。

 

DSPの元来の用途である

  • 各種フィルタ
  • FFT

のほか

  • 高速化算術関数
  • 行列演算
  • 三相モータ制御に使う計算関数
  • 姿勢制御に便利なQuaternion
  • PID

そのほか組込やさんには馴染みが薄そうな

などの関数も含まれています。

 

検証

STM32のなかでCortex-M4とCortex-M7のチップにはDSPが載っています。

家にはCortex-M3とM0+とXtensa、RISC-Vマイコンとかしかなかったので部室に転がってたNucleo-32 STM32G431KBを使いました。

#include "CMSIS_DSP.h"

特に追加の設定などなくインクルードするだけで使えます。

#include "CMSIS_DSP.h"

void setup() {
  Serial.begin(115200);
}

void loop() {
  Serial.println("N,sum(nomal),time(nomal)[us],sum(dsp),time(dsp)[us],");
  for(int n = 10;n <= 10000000;n*=10){
    Serial.print(n);Serial.print(',');  
    float x,dx,sum;
    unsigned long start_time,end_time;
   
    x = 0,dx = PI/n,sum = 0;
    start_time = micros();
    for(int i = 0;i < n;i++){
      sum += sin(x) * dx;
      x += dx;
    }
    end_time = micros();
    Serial.print(sum,6);Serial.print(',');
    Serial.print(end_time - start_time);Serial.print(',');
   
    x = 0,dx = PI/n,sum = 0;
    start_time = micros();
    for(int i = 0;i < n;i++){
      sum += arm_sin_f32(x) * dx;
      x += dx;
    }
    end_time = micros();
    Serial.print(sum,6);Serial.print(',');
    Serial.print(end_time - start_time);Serial.print(',');
   
    Serial.println();
  }
  delay(3000);
}

上記コードで分割数ごとにsinを0~πまで積分した結果を求め、時間を計測しました。

(最適化オプションによる違いはほとんど有りませんでした)

結果

N sum(nomal) time(nomal)[us] sum(dsp) time(dsp)[us]
10 1.983523 13 1.983499 5
100 1.999835 89 1.99981 40
1000 1.999974 836 1.99995 324
10000 1.999972 8176 1.999948 3087
100000 1.997334 81354 1.997307 30628
1000000 2.017668 812448 2.017648 306029
10000000 2.000000 8125558 2.000000 3060007

DSP有りの場合6割ほど高速化されているのがわかると思います。

さらにN=10程度の少ない回数でも同じ割合で早くなっています。

副次的効果としてDSP有り関数で書いた場合、math.hで書いた場合に比べ2KBほどバイナリサイズが小さくなっていました。算術関数を多用するプログラムでFLASHがカツカツのときには使えるかもしれません。

 

以上になります。簡単なのでぜひ皆さんもSTM32duinoでDSP試してみてください。

本当はマイクをADCに繋いでDSPでフィルタ処理、ADCに出力とかやってみたかったんですがマイクが見つからなかったです。見つけたらやってみようと思います。

ご覧いただきありがとうございました。