2016年11月27日

DSPによるFFT処理

今回はSTM32でのDSPによるFFTについて紹介します。

環境はこれまで同様、
・STM32F303K8
 +SW4STM32(System Workbench for STM32)
 +STM32CubeMX(F3_1.6.0)
です。

プロジェクトのDSPライブラリ設定等はこちらを参照してください。



ちょっとしたFFT処理であれば、精度よりも速さ重視なことが多いため、
今回はあえてFloatでなくQ15でのFFTを紹介します。
q_15をf32に置き換えればFloatと同じように使えます。


FFT処理は使用するライブラリの関数によって
配列の渡し方や複素数の扱いが異なるため、
間違えて配列オーバーなどしてしまいがちです。
長さに注意しながらコーディングが必要です。


ARMのFFTコード例は下記の通りです。
一部抜粋です。


#define LENGTH_SAMPLES     256 //Num of Real & Img
#define ifftFlag         0
#define doBitReverse    1
#define fftSize            LENGTH_SAMPLES/2

#define FFTLen arm_cfft_sR_q15_len128


q15_t FFT_Input[LENGTH_SAMPLES];
q15_t FFT_Output[LENGTH_SAMPLES/2];

enum{ WAIT,RUN};
//実部と虚部で256個でAD変換値は実部のため、サイズは128
enum{ ADC_BUFFER_LENGTH = LENGTH_SAMPLES/2 };
//DMA転送でHalfWordにつき16bit長で定義
static uint16_t g_ADCBuffer[ADC_BUFFER_LENGTH];

//AD変換後に呼び出される
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* AdcHandle)
{
 
    HAL_TIM_Base_Stop_IT(&htim2);
    HAL_ADC_Stop_DMA(&hadc1);

    for(int i=0;i<LENGTH_SAMPLES/2;i++){
        int tmp;
        tmp= g_ADCBuffer[i];
        //dsPICのFFTは構造体で実部と虚部を格納するが、
        //ARMでは配列に実部と虚部を交互に格納する

        FFT_Input[2*i]=tmp;   //Real part
        FFT_Input[2*i+1]=0.0;   //Img part
    }
    fft_status=RUN;
}

int main(void)
{
 //~~初期化処理~~//

  while (1)
  {

     if(fft_status==RUN){

             arm_cfft_q15(&FFTLen, FFT_Input, ifftFlag, doBitReverse);
             arm_cmplx_mag_q15(FFT_Input, FFT_Output, fftSize);
             arm_max_q15(FFT_Output, fftSize, &maxValue, &testIndex);
        
        //サンプル数は実部と虚部で256個
        //fftsizeは128個
        //FFT_Outputには0~127でFFT結果が格納されるが、
        //64~127は対称の値が入っているだけ
        //実際に使うのはFFT_Outputの中で0~63のデータ
        for(int j=0;j<fftSize/2;j++){
                 printf("%d, ",FFT_Output[j]);
             }
                fft_status=WAIT;
             HAL_ADC_Start_DMA(&hadc1, g_ADCBuffer, ADC_BUFFER_LENGTH);
             HAL_TIM_Base_Start_IT(&htim2);
     }
     else{
             //なぜか何かしらの処理を入れないとwhileに入らないのでnopを入れる
             __asm volatile("nop");
     }
 }
}


N555で35kHz前後で発振させた信号を読み込むと
下記のように35kHz前後の結果となりました。

fftres.png



DMA転送で設定をHalf Wordにしているにも関わらず、
g_ADCBufferバッファ変数長さを32bitにしてしまい、
AD変換結果のバッファを破壊するというバグに気付かず、
意図したFFT結果を得るのに時間が掛かってしまいました...
HalfWordではuint16_t g_ADCBufferが正解です。


FFT処理をしているとSTM32F303K8では
RAMサイズが16kB(実質12kB)のため、
STM32F303CCなどの32kBや40kBの大容量なRAMが欲しくなります。
一方でRAMサイズが大きくなるとピン数が48ピンになるため、
扱いやピン配列が面倒になります...
32ピンでRAMサイズが大きなものが出てほしいなあと思う限りです。

posted by Crescent at 00:00| Comment(0) | TrackBack(0) | ナレッジ | このブログの読者になる | 更新情報をチェックする

2016年11月20日

タイマ割り込みADCサンプリング

今回はSTM32でのタイマ割り込みでADCのサンプリングを行う際の設定について紹介します。


環境はこれまで同様、
・STM32F303K8
 +SW4STM32(System Workbench for STM32)
 +STM32CubeMX(F3_1.6.0)
です。
 

STM32CubeMXでADCは下記のように設定します。

ADsetting.png

今回はTimer2をトリガにしてADCサンプリングを行います。
ポイントはContinuousConvModeをDisableにする点。

最初、通常のADCサンプリング同様に
ContinuousConvModeをENABLEにしていました。
この状態ではタイマによらず、
ADC側のタイミングで連続して変換されてしまい、
意図したタイミングになりませんでした。



STM32CubeMXでTimer2は下記のように設定します。

timer2.png

タイマのPrescalerとPeriodは適当に設定して
後から下記のようにコード上で書き換えます。

  htim2.Instance = TIM2;
  htim2.Init.Prescaler = (SystemCoreClock / 1000000) - 1;  // 1us Tick
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 10-1; // 10us (100kHz)
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;

上記の設定でPrescaleをクロックに応じた式にすることで
Periodをusとしてそのまま直感的に定義することが可能です。
ただ、秒単位の場合は変数の範囲を超えるため、
必要に応じてClockDivisioやPrescalerの変更が必要です。






posted by Crescent at 00:00| Comment(0) | TrackBack(0) | ナレッジ | このブログの読者になる | 更新情報をチェックする

2016年11月14日

DSP処理前設定2

今回はSTM32でのDSP処理の前設定2について紹介します。

環境はこれまで同様、
・STM32F303K8
 +SW4STM32(System Workbench for STM32)
 +STM32CubeMX(F3_1.6.0)
です。

前回紹介したDSP処理の前設定だけではsqrtなどの指数関数がコード上にあると
下記のようなエラー発生し、コンパイルできないという不具合に遭遇しました。


undefined reference to `__errno'
collect2.exe: error: ld returned 1 exit status
make: *** [*XXXX.elf] Error 1


ハードFPUで指数関数等を処理するためにgccの引数を追記します。

Eclipseのプロジェクトから
「C/C++ Build」→「Settings」→「ToolSettings」→「MCU GCC Compiler」
「Command」欄を「gcc -fno-math-errno」へ変更します。


FFT_Setting2222.png



「OK」で設定を保存してください。



これで、指数関数がハードウェアFPUで使用可能になります。

    arm_sqrt_f32(a,&b);
    b=sqrtf(a);

  ※sqrt()はソフトFPUとなるため、使いません。



また、以前紹介したようにfloatでprintfするためには下記も追記してください。
Eclipseのプロジェクトから
「C/C++ Build」→「Settings」→「ToolSettings」
→「MCU GCC Linker」→「Miscellaneous」
「Linker flags」の欄を
「-specs=nosys.specs -specs=nano.specs -u _printf_float」へ変更します。


posted by Crescent at 00:00| Comment(0) | TrackBack(0) | ナレッジ | このブログの読者になる | 更新情報をチェックする

2016年11月09日

DSP処理の前設定

今回はSTM32でのDSP処理の前設定について紹介します。

環境はこれまで同様、
・STM32F303K8
 +SW4STM32(System Workbench for STM32)
 +STM32CubeMX(F3_1.6.0)
です。


STMのサイトからSTM32Cube_FW_F3_V1.6.0をダウンロードすると
いくつかサンプルコードが入っています。

その中にM4コア内臓のDSP処理を使用したFFTのサンプルがあります。
STM32Cube_FW_F3_V1.6.0\Drivers\CMSIS\DSP_Lib\Examples\arm_fft_bin_example\ARM


・FFT処理するためのサンプルデータ
 arm_fft_bin_data.c
・FFT処理のサンプルコード
 arm_fft_bin_example_f32.c


上記のコードをそのままCubeMXで生成したコード群に
そのまま組込んでもコンパイルが通らないため、
対処方法をご紹介します。

@
#include "stm32f3xx_hal.h"の次に
#define ARM_MATH_CM4
を追加。
F3のM4コアの使用を定義。
この定義を忘れるとコアの種類が分からない旨のコンパイルエラーが出る。
#error "Define according the used Cortex core ARM_MATH_CM7,
ARM_MATH_CM4, ARM_MATH_CM3, ARM_MATH_CM0PLUS or ARM_MATH_CM0"

A
#include "arm_math.h"
#include "arm_const_structs.h"
をインクルード。
コアの定義の後に上記のインクルードをする。


B
STM32Cube_FW_F3_V1.6.0\Drivers\CMSIS\Lib\ARM
内の
arm_cortexM4lf_math.lib
STM32Cube_FW_F3_V1.6.0\Drivers\CMSIS\Lib\GCC
内の
libarm_cortexM4lf_math.a
をプロジェクトフォルダ直下へ配置する。

 arm_cortexM4b_math.lib
 arm_cortexM4bf_math.lib
 arm_cortexM4l_math.lib
 arm_cortexM4lf_math.lib
から必要に応じて選択。
little endian, fpu, m4の場合で
arm_cortexM4lf_math.lib


C
プロジェクトのプロパティーで「C/C++Build」の設定内の
Linker、LibrariesでDSP処理のライブラリを追加する。

Librariesに下記を追加。
arm_cortexM4lf_math
m

Library search pathにワークスペース下のプロジェクトフォルダを選択。
自動的に下記になる。
"${workspace_loc:/${ProjName}}"

この設定を忘れると
undefined reference to `arm_cfft_f32'
cannot find -larm_cortexM4lf_math.lib
なエラーが出る

dsp.png


以上でコンパイルが通りました。

DSP処理のFFT関数の使い方などは後ほどご紹介します。
posted by Crescent at 00:00| Comment(0) | TrackBack(0) | ナレッジ | このブログの読者になる | 更新情報をチェックする

2016年11月04日

Beaglebone Node-Redインストール

今回はBeagleboneblackを使用して
Node-Red + Freeboardをインストールする方法をメモとして記します。


世の中は既にRaspberryPIの一強という感じですが、
BeagleboneblackはPCに接続して使用することを前提としているため、
使い方が大きくRaspberryPIと違います。

RaspberryPIはキーボードやマウス、ディスプレイが必須ですが、
BeagleboneblackはPCに接続し、ドライバをインストールすると標準で
USBネットワークとして認識し、
ブラウザやSSH経由でキーボードやマウス、ディスプレイなくとも簡単に
PCから開発やプログラミングができます。
この点が非常に気に入っています。



今回インストール方法をご紹介するNode-Redはブロックを線で繋げるだけで
簡単にプログラミングできるソフトウェアです。

簡単にIoTなものを作ることができます。



ただ、Beagleboneblack標準のDebianでは
最低限のNode-Redのみインストールされており、
BeagleboneblackのIO等は別途インストールが必要なようです。


Beagleboneblackの現時点でのOSイメージ
bone-debian-8.4-lxqt-4gb-armhf-2016-05-13-4gb
Node-RedインストールやBeagleboneblack用のノードインストール
の方法ではエラーが発生していまい上手くインストールできませんでした。

Node.js系はバージョンが少し変わるだけで色々エラーの嵐で
上手くいけば強力で非常に便利ですが、
上手くいかないとバージョン入れ替えなどの試行錯誤が必要で
初心者には敷居が高いイメージがあります。



下記の方法でエラーを回避できたため、
メモとしてご紹介します。


「サービス終了」
sudo service node-red stop

「アンインストール」
sudo npm uninstall -g --unsafe-perm node-red-node-beaglebone
sudo apt-get purge bb-node-red-installer

「インストール」
sudo apt-get install bb-node-red-installer


ついでに「Freeboard」のインストールはバージョン指定でインストールします。
現時点の0.0.7ではエラーが発生して上手くインストールできませんでした。

「Freeboard」
sudo npm install -g node-red-contrib-freeboard@0.0.6


これでセンサデータの読み取りやロギング、可視化が簡単に実現できます。

具体的な使用例はまた後ほどご紹介します。

posted by Crescent at 00:00| Comment(0) | TrackBack(0) | 電子工作 | このブログの読者になる | 更新情報をチェックする