ADが提供する演算ライブラリがいろいろあります.マジで便利そう.使えればですが.
さしあたってやりたいのが、32ksps→1kspsぐらいに間引きたい.単純に1/32に間引くとエイリアシングが出てしまうので、500HzのLPFを通しておいてから1/32に間引くというのが作法です.
LPFはFIR filterで構成します.FIR filterはhardwareでやるにせよ、softwareでやるにせよ、大きな計算量が必要です.結局のところ、間引き(decimation)というのはFIR filterが演算量の大部分を占めるってことになります.
ADのccesのインストフォルダを探すとfilter.hというのがあってその中にFIRやIIRのプロトが書かれています.
typedef struct{
fract32 *h; // coefficients
fract32 *d; // start of delay line
fract32 *p; // read/write pointer
int k; // number of coefficients
int l; // interpolation/decimation index
} _fir_fr32_state;
void fir_fr32 (
const fract32 _input[],
fract32 _output[],
int _length,
fir_state_fr32 *_filter_state);
あーそんな感じねと察します.でもsample programが無いとちょっとかったるい.delay lineの目的が不明です.検索したけど見当たりませんでした.
自力でやるのかくそう・・・・
ーーーー
それでやってみたのですが、意外と簡単にうごかせたかな.
1.FIRのタップ係数を決める
ライブラリとは別なのでこれを最初に説明するってのもどうかと思いますが、
こちらのサイトが便利です.好みのfilter特性を入力すると、タップ係数を出力してくれます.
自力で逆フーリエ変換してもタップ係数を算出できますけど、めんどくさいので自力計算はやめときました.
やたらと厳しいfilter特性を指定するとタップ係数が長くなります.タップ係数が長くなると計算時間がかかってDSPが処理できなくなります.BF706で500タップを実装したらhung-upしちゃいました.200タップなら動きました.
同サイトはタップ係数定義を吐き出すので、source codeにコピペしましょう.
キモは、
・32bit FIRならば、、、
・32bit intで係数出力させる(ライブラリ内ではfract32と型表記)
・係数のgain調整はしなかった
#define FILTER_TAP_NUM 85
static int filter_taps[FILTER_TAP_NUM] = {
-11666725,
-4238744,
-4883710,
-5507981,
-6093541,
-6612955, 以下略
2.FIR filterにタップ係数などを設定する
blackfinのライブラリを使います.定義はfilter.hに書かれてる.
_fir_fr32_state firparam; ←FIRのパラメータ構造体
構造体にFIRのパラメータを設定する.
fract32 delay[2*FILTER_TAP_NUM];
fir_init( firparam, filter_taps, delay, FILTER_TAP_NUM, 1 );
引数の意味はなんとなくわかるけど、、、
最凶に判らないのがdelayというもの.タップ数の2倍長のwork areaを与えてあげると正しく動く.→
こちらが参考末尾引数1は、間引き数、補間数なのでここでは1でいいかんじ.0じゃない.
3.FIR filterをうごかす
わたしは初めてDSPを使っているのですが、どーゆー風に実装するのかなぁ?とモヤモヤしていた事があります.
というのは、、、verilogでhardware FIRを設計する場合は毎サンプルに1つデータが入って来ます.それをガシャンと演算します.FIR pipelineから1つのデータが出力されます.これは判りやすい.設計経験もあります.
しかしDSPでは、ADCから入ってくるデータはFIFOに格納され、数~数10サンプルのブロックでデータが到来します.これをFIRにガガガッと演算させ、ブロックで出力される.ガガガッの最後には仕掛データが在庫のようにFIR内部に溜まっている.こんなの実装するのどうするのかな、かったるい、そんな風に思っていました.未体験ゾーンだし.
だがしかし、blackfinライブラリを使ったらすんなり動いたので中身がどうなっているのかは判らずじまいでスルーします.
それでFIR処理のdata flowはこんなです.
・hardwareは、BF706のI2Sに32bit ADCと32bit DACがついてる
・起動時にI2Sに「ADC FIFOが満タンになったらcallbackしてね」と設定しておく
・参考までにADC FIFO長は64サンプルにしてる(32bit 2ch 64sample=512BYTE)
・callbackが来たら以下の処理を行う
1)FIFOからdataを取り出し、fract32の64個配列に格納
2)同配列をFIRライブラリへ突っ込む
3)FIR出力が出てくる (fract32 64個配列)
4)出力配列をDACのFIFOにコピー
5)DAC FIFOをI2S DMAにおまかせする
fract32 input[64], output[64];
for(int i=0; i<64; i++){
input[i] = *pBufRx; ←ADC FIFO (1)
pBufRx += 2;
}
fir_fr32 ( input, output, 64, &firparam );(2)(3)
for(int i=0; i<64; i++){
*pBufTx = output[i]; ←DAC FIFO (4)
pBufTx += 2;
}
以上でさらっとおしまい.
#ライブラリにはdecimationもあるのでそれも試します.
すいませんが、project folder詰め合わせのupは諸般の事情でやめときます.ボケた写真でも載せておこう.右下がBF706です.
かしこ