昨日のarduino用FFTライブラリのスケッチを
もう少し見直ししてみた。
ライブラリのページに細かい使い方とか書いて
なかったので、FFTとTimerOneのライブラリソース
をザックリ読んでみた。
その内容を加味して、なんでサンプルレートを
上げた時にフリーズしちゃうのかを考えてみた。
どうやら、スケッチ中でcliやseiで割り込み許可を
直接弄ってること…特にFFTの計算と数十文字の
シリアル出力を行うまで一切の割込みを禁止したまま
っていうのは長すぎたみたい。まぁ普通に考えると
行儀悪すぎだな。きっとここが原因だろう。
と思って、タイマ1割込みの制御をTimerOneオブジェクト
のdetachInterruptとattachInterruptを使うように
修正してみた。↓こんな感じ。
#define LIN_OUT8 1 // use the lin output function
#define FFT_N 64 // set number of point fft
#include <FFT.h> // include the library
#include <TimerOne.h>
volatile int i; // index for data points
void timerIsr()
{
while(!(ADCSRA & 0x10)); // wait for adc to be ready
ADCSRA = 0xf5; // restart adc
byte m = ADCL; // fetch adc data
byte j = ADCH;
int k = (j << 8) | m; // form into an int
k -= 0x0200; // form into a signed int
k <<= 6; // form into a 16b signed int
fft_input[i] = k; // put real data into even bins
fft_input[i+1] = 0; // set odd bins to 0
i += 2;
}
void setup() {
Serial.begin(115200); // use the serial port
TIMSK0 = 0; // turn off timer0 for lower jitter
ADCSRA = 0xe5; // set the adc to free running mode
ADMUX = 0x40; // use adc0
DIDR0 = 0x01; // turn off the digital input for adc0
i = 0;
Timer1.initialize(30); // set a timer of length 100 microseconds (10000Hz)
Timer1.attachInterrupt( timerIsr ); // attach the service routine here
}
void loop() {
while(i < FFT_N*2) {
}
Timer1.detachInterrupt();
// cli(); // UDRE interrupt slows this way down on arduino1.0
fft_window(); // window the data for better frequency response
fft_reorder(); // reorder the data before doing the fft
fft_run(); // process the data in the fft
fft_mag_lin8(); // take the output of the fft
i = 0;
for (int l=0; l<FFT_N/2; l++){
Serial.print(fft_lin_out8[l]); // send out the data
Serial.print(",");
}
Serial.println();
Timer1.attachInterrupt( timerIsr );
// sei();
}
とりあえずサンプルレートを上げても普通に動く
様になった。よかった、よかった。
んで、音声信号を取り込むことを考えると、やっぱ
サンプリングレートを40000spsくらいまで上げられ
ないかという欲望がわいてくるのは必然。でも、
あまり割り込み頻度を上げすぎると、きっとどこかで
割り込み処理が完了する前に次の割込み要因が発生したり
して破綻するはず。破綻しないところを探ってみる。
まずは10000sps(ナイキスト周波数は5000Hz)、大丈夫。
20000sps(同10000Hz)、大丈夫。25000sps(同12500Hz)、
大丈夫。40000sps(同20000Hz)、駄目。
割り切れる綺麗な周波数では12500Hzあたりまでって
ことになるんだけど、もうちょっといけるはずだろう
と思って33333sps(同16667Hz)を試す…大丈夫。
どうやら、16667Hzと20000Hzの途中に境目があるっぽい。
(ちなみに、サンプリングレートを25000sps、33333sps、
40000spsとする時のTimer1.initialize(xxx)で指定する
引数は、それぞれ40、30、25となります…1000000÷指定値
なので)
まぁ、16000Hz以上となるとあまりこだわっても仕方ない
と思うのでこれでよしとすることに。
ただねぇ…mp3プレイヤーとかから直接音声を取り込ん
じゃおうとすると、16667Hzを越える周波数成分は
折り返しノイズとして拾っちゃうはずだから、もし
LPFで削り落としておかないと、13000hz付近~16667Hz
のところに折り返されたノイズが載っちゃうんだよな。
この程度と無視出来るなら、LPF無しでもそのままで
良いんだけどな。
補足:このスケッチでは、サンプル数を64点FFTとして、
あとFFT結果のスカラー化の後で対数を取らずに
8ビット値のまま扱ってみた。
(この方が、シリアルモニタで結果見るときに見やすい)
グライコのような処理をするなら、当然対数で扱った
方が都合が良いので、昨日みたいに対数指定にして
おいた方がよいかと。
とりあえず音声信号をリアルタイムでグリグリ処理する
用途でも結構使えそうなことはわかった。
あとはサンプルレートをどこまで上げられるかなんだ
けど、これについてはいくつか案を。
一つは、10ビットADCをやめて8ビットADCにすること。
ハンドリングする変数の数が減るので、少しだけ
計算時間を短く出来るかと。
もう一つは、タイマ割り込み処理内のスキーム見直し。
もうちょっと効率上げられるんじゃないかと。
そこまでやればギリギリ40000spsくらいまでは
上げられるんじゃないかな?ADCSRAを参照しなくても
良いんじゃね?とか、配列のインデックスはi++と
指定しておけば、その後ろのi+=2も要らなくて、
多分この辺は効率的にアセンブリ変換できるんじゃ
ないかと。
とりあえずFFTはこんなところまで見ておけば、
あとでサクサク使えそうかな。
http://zasshi.news.yahoo.co.jp/article?a=20121113-00000000-natiogeo-int
今日の天気と明日の天気が入れ替わってくれればなぁ…
http://zasshi.news.yahoo.co.jp/article?a=20121116-00000007-sasahi-ent
脱出ゲームの記事。へぇ、「大人は部活を求めている」
のかぁ。ニコニコ技術部とかじゃ(以下略)
simさんのツイッター経由でおち(転生準備中) さんの
ツイッターに。
http://www.nxp-lpc.com/lpc_micon/cortex-m0+/lpc800/
おぉ。NXPは8ピンのARM出すのか!しかも出力ピン
はスイッチマトリックスでコンフィギュアラブルなんだな。
弱点は…ないんじゃね?とか思ったんだけど、もし
発売されたら… 8ピンだろ?
こんな感じになるのか?