実例34 浮動小数点演算、関数演算の実行速度
----巨大データのコンパイル方法----
たかが8bitマイコンに、浮動小数点演算をさせられる.これはすごいことだと思います.
ここでは、離散フーリエ変換をさせてみてどのくらいの時間がかかるのかを、測定してみます.
また、8bitマイコンにしては大量のfloatデータを定義しますので、コンパイラの設定変更が必要でした. → 末尾を参照
で、いきなり結論ですけど、こんな写真が演算結果です.
計算に1分0秒41かかったよという結果を得ました.
何の演算をさせたかというと、256個の離散フーリエ演算です.8bit CPUの割にけっこう早いじゃんと思うのは私だけでしょうか?
ハードウエアの準備
結果をLCD表示器に表示します.
STM8S-DISCOVERYへLCD表示器を取り付ける方法はこちらのページを参照してください.
また、LCDドライバルーチンの解説もこのページを参照してください.
やっぱ表示装置がついてると便利だわ.
ソースコードの解説
サンプルプログラムをこちらに置きました.
以下はmain.cです.
●#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "math.h"
Cの標準関数を利用するのでインクルードします.
●#include "stm8s.h"
ライブラリを使うためにインクルードします.
●#include "usrlib-lcd.h"
LCDドライバルーチンです.私の自作関数です.
●#define N 256
256個の離散フーリエ変換をするので256にしましたが、よく考えたら255にするべきでした.
私はこの間違いを良くやります.
(FFTではなくてDFTですのでお間違えのなきよう)
●typedef struct complex {
float re;
float im;
} COMPLEX;
複素数演算をするので、COMPLEXという構造体を作ります.
●COMPLEX CompMul(COMPLEX a, COMPLEX b) {
COMPLEX x;
x.re=a.re * b.re - a.im * b.im;
x.im=a.re * b.im + a.im * b.re;
return x; }
複素数のかけ算ルーチンです.
●volatile float in[N];
256ヶのフーリエ変換入力データの格納変数です.
volatileとしたのは、コンパイラの最適化で消去されなくするためです.
float in[256]は、1つで4BYTE占有するので、totalで1024バイトもメモリを食います.
これはSTM8Sにとって看過できないメモリ消費量です.
auto変数で宣言するとスタックオーバーフローで宣言できません.
なので、gloval変数で宣言してあります.
また、float in[512]で宣言するとgloval変数ですらメモリ不足で不可能です.
●int day=0,hour=0,min=0,sec=0,sec10=0,sec100=0;
10msの精度で時間を測定する変数です.
●CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO, CLK_SOURCE_HSE, DISABLE, CLK_CURRENTCLOCKSTATE_DISABLE);
クロックは、外部水晶発振子の16MHzを利用します.(HSE)
HSE設定ではデフォルトでfCPU=16MHzになりますので、プリスケーラの設定は省略しました.
●LCD_Init();
LCDドライバの初期化をします.
●TIM2_DeInit();
TIM2_TimeBaseInit( TIM2_PRESCALER_128, 1249 ); // 16MHz/128/1250=100Hz
TIM2_ITConfig(TIM2_IT_UPDATE, ENABLE);
時間測定するための10mSec割り込みタイマです.
●sprintf(str,"operation start");
LCD_Disp_Str(str,LCD_DISP_UPPERLINE);
LCDの1行目に、計算開始メッセージを表示します.
●TIM2_Cmd(ENABLE);
enableInterrupts();
タイマを動かします.時間測定を開始します.
●for(k=0;k<=N;k++) in[k]=rand();
ここからが離散フーリエ変換プログラムです.
ここではin[k]にランダムな数値を入れています.
●for(k=0;k<=N;k++)
{
volatile float re,im,out;
re=0;im=0;
for(n=0;n<=N;n++)
{
float wn;
wn=(float)n*(float)k/((float)N+1);
a.re= cos(2*3.14*wn);
a.im=(-1)*sin(2*2.14*wn);
b.re=in[n];
b.im=0;
c=CompMul(a,b);
re=re+c.re;
im=im+c.im;
}
out=sqrt(re*re+im*im);
}
これが離散フーリエ変換プログラムの本体です.
複素数の周波数領域データを256ヶ作成することと、それを使ってゲイン特性(out)を計算しています.
●TIM2_Cmd(DISABLE);
離散フーリエ変換演算が終わったら、タイマを停止します.
●sprintf(str,"operation compl");
LCD_Disp_Str(str,LCD_DISP_UPPERLINE);
sprintf(str,"%02ud%02uh%02um%02u.%1u%1us",day,hour,min,sec,sec10,sec100);
LCD_Disp_Str(str,LCD_DISP_LOWERLINE);
LCDの1行目に演算終了のメッセージを表示し、
2行目には演算の所要時間を表示します.
●void TIM2_interrupt(void) interrupt 13 {
GPIO_WriteReverse(GPIOD, GPIO_PIN_0);
sec100++;
if(sec100>=10) {
sec100=0;
sec10++;
if(sec10>=10) {
sec10=0;
sec++;
if(sec>=60)
{
sec=0;
min++;
if(min>=60)
{
min=0;
hour++;
if(hour>=24)
{
hour=0;
day++;
}
}
}
} }
TIM2_ClearFlag(TIM2_FLAG_UPDATE);
}
これで、10mSec精度で時間をカウントしています.
コンパイルの注意点
私はいまReisonance Cコンパイラを使っているわけですが、project settingでコンパイラの設定を変えないと、大量のメモリ消費を処理できませんでした.
注意点は、
1) Gloval variables のところを、in DATAにすること
2) Program model のところを、STM8 Large model にすること
この2つを怠ると、上記のソースをビルドできませんでした.