DSPライブラリの使い方1(デジタルフィルタ)

dsPIC33C

概要

dsPICシリーズは「dsP」と頭文字がつくように、DSP(*1)が内蔵されています。
今回はこのDSPを用いFIRフィルタ(*2)を構成します。

なお本記事は絶版書になりますがCQ出版社から出版されていた以下の著書を参考にしております。

(fig.1) dsPIC基板で始めるディジタル信号処理

[絶版2017.1.24] dsPIC基板で始めるディジタル信号処理 (cqpub.co.jp)

(*1)DSPとは

デジタル・シグナル・プロセッサの略で信号をデジタル的に処理する回路のこと

FIRフィルタは以下のような構造となっております。入力を1サンプル毎に遅延させたものに
それぞれの係数(h)を乗算し総和を取ります。

このhの段数をタップといい(fig.2)では4段となります。このタップ数と係数によってローパスだけでなくハイパスやバンドパスなど様々な特性が実現できます。

(fig.2) FIRフィルタ
(*2)FIRフィルタとは

Finite Impulse Responseの略で移動平均を拡張したようなフィルタのことで、有限個のデータをある一定期間のみ使用します。

このフィルタ係数の求め方は様々な方法が有り、Microchip純正のツールも有りますが、今回はDIGITALFILTER.COM様のDSPLinksというソフトを使用します。

①リンク先からツールをダウンロード&インストール
②DSPLinksの起動

(fig.3)DSP Links起動画面

③トランジスタのマークをクリックしPart Select画面で 「RMZLPF」を選択し「OK」をクリック

(fig.4)Parts Select画面

④配置されたブロックを右クリック > 「Attribute」をクリックするとParts Attributeウィンドウが開きます。更に「Change Parameters」をクリックします。

(fig.5)Parts Attribute画面

⑤FIR type LPFウィンドウが開きますので、下記のローパスフィルタ特性を入力します。
Tapの項目は未入力とし、「Tap Count Estimation」をクリックすると自動で値が入力されます。

項目記号単位備考
サンプリング周波数Fs16000HzAD変換時などのサンプリング周波数
カットオフ周波数1Fc12000Hz減衰開始周波数
カットオフ周波数2Fc23000Hz減衰終了周波数
アッテネーションatt-40db減衰量
リップルファクターrp0.05平坦域でのリップル量
(fig.6)フィルタ係数

次に「Quantize Coefficients」をクリックします。

(fig.7)FIR type LPF画面

⑥Quantize Coefficients ウィンドウが開きますので、Quantize by に16bit / Format to save coeffsを Dec に設定し「OK」をクリック。

(fig.8)Quantize Coefficients画面

⑦FIR type LPFウィンドウに戻るので「OK」をクリック。
⑧Parts Attributeウィンドウに戻るので「OK」をクリック。
⑨配置したブロックを右クリックし「Open a Monitor」をクリック

(fig.9)モニターを開く

⑩フィルタ特性画面が開きますのでこの状態で

(fig.10)フィルタ特性画面

⑪ 「Tools」 > 「Save Coefficients」をクリックすると、保存ダイアログが開きますので適当な場所に保存します。

(fig.11)係数の保存

⑫保存したテキストファイルを開くと、以下のようにQuantized by 16[bit]以下に係数が出力されますのでこれを、カンマ区切りにしてフィルター係数とします。

(fig.12)出力されたフィルタ係数

ハードウェア構成と制御ブロック

今回のプログラムはハードウェアは不要でシミュレータ上で動作させます。

レジスタ

レジスタは未使用です。

ソースコード

DSP命令を利用する時は(fig.13)のようにプロジェクトのLibrariesに「libdsp-elf.a」をインポートします。

(fig.13)ライブラリのインポート

また「dsp.h」をインクルードします。

#include <dsp.h>

DSPによるフィルタの実行ですが、初期設定として
①フィルタ係数の初期化(FIRStructInit(…)関数)
②遅延バッファのクリア(FIRDelayIniy(…)関数)
を行います。

今回のサンプルは最初にすべてのダミーデータを準備してループで順にFIR(…)関数を実行していますが実際の使い方としては、サンプリングごとにFIR(…)関数を実行します。

コンフィグレーション設定について

コンフィグレーション設定についてはコンフィグレーション設定に記載しております。

コピーして下記のソースコードの「 //ここにコンフィグレーション設定を挿入する// 」の位置に挿入してください。

クロック設定について

クロック設定用関数 vds_Main_Init_Clock_Register(); のソースコードはクロック設定のページに記載しております。

コピーして下記のソースコードの「 //ここにクロック設定ソースをコピペする// 」の位置に挿入してください。

/*----------------------------------------------------------------------------*/
/* <Chapter>        CHAPTER_4_1_1_DSP_FIR */
/* <Function>       DSPを利用したFIRフィルタ */
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
/* コンフィグレーション設定*/
/*----------------------------------------------------------------------------*/
//ここにコンフィグレーション設定を挿入する//
/*----------------------------------------------------------------------------*/
/* インクルードファイル*/
/*----------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
#include <libpic30.h>
#include <dsp.h>
/*----------------------------------------------------------------------------*/
/* 定数定義*/
/*----------------------------------------------------------------------------*/
#define FIR_TAP    37                //FIRタップ数
#define FIR_SFREQ    16000           //FIRサンプリング周波数
#define FIR_DUMMY_A_FREQ    200.0
#define FIR_DUMMY_B_FREQ    4000.0
#define FIR_DUMMY_A_GAIN    2047.0
#define FIR_DUMMY_B_GAIN    2047.0

#define FIR_SPL_NUM         512
/*----------------------------------------------------------------------------*/
/* 変数定義*/
/*----------------------------------------------------------------------------*/
const fractional firCoeff_16k2k3k37T[] __attribute__ ((space(auto_psv))) = 
{
33,-239, -56, 150, 296, 163,-234,-551,-369, 331, 
977,793,-418,-1800,-1841, 478,4665,8736,10422,8736,
4665, 478,-1841,-1800,-418, 793, 977, 331,-369,-551,
-234,163, 296, 150, -56,-239,33
};

//入力ダミーデータ
fractional fir_FIRDummyData[FIR_SPL_NUM];
//ディレイバッファ
fractional __attribute__ ((space(ymemory), aligned (256))) firDelay[127];
//フィルタ構造
FIRStruct firFilter;
//フィルタ出力
fractional fir_Output[FIR_SPL_NUM];
/*----------------------------------------------------------------------------*/
/* クロック設定 */
/*----------------------------------------------------------------------------*/
//ここにクロック設定ソースをコピペする// 
/*----------------------------------------------------------------------------*/
/**
 * @fn          main(int argc, char** argv) 
 * @brief       CHAPTER_4_1_1_DSP_FIR
 * @param[in]   argc argument count
 * @param[in]   argv argument vector
 * @retval      EXIT_SUCCESS 成功
 * @retval      EXIT_FAILURE 失敗
 * @details     DSPによるローパスフィルタ
 * @note        
 */
/*----------------------------------------------------------------------------*/
int main(int argc, char** argv) 
{
    /*------------------------------------------------------------------------*/
    /* ローカル変数の定義*/
    /*------------------------------------------------------------------------*/
        unsigned int u2l_Loop;
        unsigned int u2lCnt;
        float f4_Time;
        float f4_DummyA;
        float f4_DummyB;
    
    /*------------------------------------------------------------------------*/
    /* クロック初期化*/
    /*------------------------------------------------------------------------*/
        vds_Main_Init_Clock_Register();     /* クロック初期化 */
    /*------------------------------------------------------------------------*/
    /* フィルタ係数初期化*/
    /*------------------------------------------------------------------------*/
        FIRStructInit( 
            &firFilter,                                 // フィルター構造体
            FIR_TAP,                                    // フィルター係数タップ数
            (fractional *)firCoeff_16k2k3k37T,          // フィルタ係数ポインタ
            __builtin_psvpage( firCoeff_16k2k3k37T ),   // フィルタ係数PSVページ
            firDelay );                                 // ディレイバッファ
    
    /*------------------------------------------------------------------------*/
    /* ディレイバッファクリア*/
    /*------------------------------------------------------------------------*/
        FIRDelayInit( &firFilter ); // フィルター構造体
    /*------------------------------------------------------------------------*/
    /* ダミーデータ生成*/
    /*------------------------------------------------------------------------*/
        for (u2l_Loop = 0;u2l_Loop < FIR_SPL_NUM ;u2l_Loop ++)
        {
            f4_Time = (float)u2l_Loop/(FIR_SFREQ);
            f4_DummyA = sin(f4_Time * FIR_DUMMY_A_FREQ * 2 * PI) 
                * FIR_DUMMY_A_GAIN * u2l_Loop /511;
            f4_DummyB = sin(f4_Time * FIR_DUMMY_B_FREQ * 2 * PI) 
                * FIR_DUMMY_B_GAIN * u2l_Loop /511;

            fir_FIRDummyData[u2l_Loop] = (signed int )f4_DummyA + f4_DummyB;
        }
    /*------------------------------------------------------------------------*/
    /* フィルタ演算*/
    /*------------------------------------------------------------------------*/
        for (u2lCnt = 0;u2lCnt < FIR_SPL_NUM;u2lCnt ++)
        {
            FIR( 1,                                 // 入力信号個数
                &fir_Output[u2lCnt],                // 出力サンプル変数ポインタ
                &fir_FIRDummyData[u2lCnt],      // 入力サンプル変数ポインタ
                &firFilter );                       // フィルター構造体
          
        }
    /*------------------------------------------------------------------------*/
    /* メインループ*/
    /*------------------------------------------------------------------------*/
        while(1)
        {
            
        }    
}

結果

グローバル変数の「fir_Output」と「fir_FIRDummyData」をCSV出力し、結果をExcelで解析を行いました。

(fig.14)は入力とフィルタ出力の時系列データを示したもので、元データに含まれる4kHzの成分が除去されている事が確認できます。

(fig.14) 入力とフィルタ出力の時系列データ

(fig.15)は入力とフィルタ出力をFFT解析したもので、元データには200Hzと4kHzが混ざっていますがフィルタ出力には200Hzの成分しか残っていない事が確認できます。

(fig.15) 入力とフィルタ出力の周波数データ

上記FIR命令を実行するサイクル数は以下の通り

実行サイクル実行時間(μsec)条件
1862.0637タップ、FCY = 90MHz

コメント

タイトルとURLをコピーしました