実例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つを怠ると、上記のソースをビルドできませんでした.



inserted by FC2 system