わー、なんだか久しぶり。
以前、磁石の動きを検出するのにアナログ出力のホール素子を使ってみたのですが、かなり強力な磁石を使っても10cm程度がやっとで、あとはノイズに埋もれてしまいました。まぁアナログ技術のある人ならもうちょっとマシな結果になるかもしれませんが。
ふと思い立って、FRDM-KL46Zについている電子コンパスMAG3110を使ってみました。測定してみると何もしなくても地球磁場が600-1200ぐらいの値を返してくるのですが、すぐ近くにネオジム磁石をおいてみても200程度しか値が変わりません。ただ、地球磁場は当然ながら安定しているので、ネオジム磁石の動きは十分信号として検出できそうな気配。
ということで、ずっと部品沼で眠っていたAQM1248(秋月の超小型グラフィック液晶)、最速の誉れも高き大浦先生のFFTライブラリなどを寄せ集めて、実験してみました。
■まず予備実験■
FRDM-KL46Z内蔵のMAG3110
(ライブラリ:MAG3110…ワーニング出まくるので、あとで直してpush requestしたい) を内蔵の液晶
(ライブラリ:SLCD) で表示してみました。MAG3110でボード内部のI2C端子名を指定するんですが、参照した資料が間違っていて苦労しました。
MAG3110 mag(PTE25, PTE24);
これで使えます、ご参考まで。
さて、表示してみると前述の通り、値は向きによって600-1200程度の幅で変化します。そこにネオジム磁石を近づけると200ぐらい増減します。当然離すと急激に弱くなりますが、値としてはわりとはっきりしているので、たとえば自転車のホイール回転のように周波数レンジの決まっているものであれば何とか処理できそう。
ということで信号処理はFFTを使い、表示に超小型グラフィック表示液晶を使うことにしました。
■まずAQM1248■
電子工作を再開したばかりのころ、秋月の「超小型液晶」を基板にナナメにハンダ付けしてしまったのは良い思い出…今も元気に活躍しておられます、ナナメのままで。
ということで、まずフラックスを塗って置いてしばらく放置し一カ所だけハンダ付けをして向き角度などを確認してから装着。FRDM-KL46Zとの配線は以下の通り:
AQM1248 FRDM-KL46Z
Vcc 3.3v
CS D10
RESET D9
RS D8
SCLK D13
SDI D11
mbed用のほどよいライブラリaqm1248a_lcdをリンクしサンプルをそのままコピペして動作を確認。なお、同ライブラリはプラットフォーム(mbedの機種)を識別してGPIOなどを切り替える作りになっているのですが、概ね「LPC1768とKL05、それ以外」という感じなのでLPC1768とKL05, KL46以外の方はソースの中身と配線をご確認の上、お使いになった方が良いです。
一昨日aitendoのカラー液晶に悪戦苦闘したのですが、こっちは一発動作。やっぱ私にはaitendoはハードルが高すぎます。相場?の半値ぐらいで買えたんですが、家でビニール袋から出してみたら基板に液晶が接着済(笑)。工程ミスか指示ミスか知らないけど、液晶が端子を半分覆い隠す状態になっていて普通なら納品できない品だから安かったんでしょうね。まさにジャンク。
まぁそれはともかく、これで表示はばっちり。接続ピン番号埋め込まれていてちょっと不安だったものの動作は安定しているのでグラフ描画もかなり簡素化できそうな気配。ライブラリ開発者の方に改めてお礼申し上げます。
■高速FFTライブラリ■
今回はこれを使います。
コンパクトで高速で美しい…。mbedで使うには、
パッケージをダウンロード、解凍
fft4g.cをブラウザ上のmbedプロジェクトにドラッグ&ドロップ
ファイル名をfft4g.cppに変更
あとはサンプルを参考に書くだけ
余談ですが、サンプルではfft->ifftで誤差を確認する、という処理が書かれていて、「FFT+IFFTでフィルタ作りたい」と思っている私には拍子抜けするほど簡単w
数学どころか算数レベルの私には基数って何よ?の世界なのですが、ともかくこれも一発動作。今まで使ったFFTライブラリの中で一番簡単だったかもしれません。
■ということでプログラムの構造■
IntervalでMAG3110を読みバッファに値を書き込んでいく
メインループでバッファをチェックし、一杯になったらFFTかけて液晶にグラフ表示
これだけです。
で処理の結果ですが…まず、電子機器で一杯の室内で放置した状態でのスペクトル
ごらんのように、ほぼ全域に不規則な成分が出てます。実は圧倒的に直流成分が多いのですがスケールアウトしてしまうのでカットしてます
(そりゃそうだ) 。そして次が、FRDM上約10cmのところでネオジム磁石をおおよそ2秒で3往復ほどの周期で振ってみたところのスペクトル
明確にでかい山が出来てます。必死に高速フリフリをすると右へ移動するし、ゆっくりにすると左へ移動するので、なんとなくできてるかなーと。さて、次は、「こういうぐあいにでっかい山ができていることを検出する処理」というのを書かないといけないのですが、パターン認識とか苦手なんす。どなたかアイディアください。
以下ソースです。
ほとんどサンプルからのコピペですが、
mbedリポジトリに公開 しましたのでご笑覧くださいませ。下にも貼っておきますが、何かの参考にされるのであればリポジトリの方をご覧下さい
(基本的に以下のソースは更新し忘れることが多いと思うので) 。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "mbed.h"
#include "SLCD.h"
#include "MAG3110.h"
#include "fft4g.h"
#include "aqm1248a_lcd.h"
#define NMAX 256
#define NMAXSQRT 32
#define MAX(x,y) ((x) > (y) ? (x) : (y))
MAG3110 mag(PTE25, PTE24);
SLCD slcd;
aqm1248a_lcd lcd;
Ticker reader;
bool modeFilling = true;
int nFilled = 0;
double magBuffer[NMAX+1];
void readerFunction() {
if (modeFilling) {
float x;
mag.getX(&x);
if (nFilled < NMAX) {
magBuffer[nFilled++] = x;
if (nFilled >= NMAX) {
modeFilling = false;
}
}
}
}
void putdata(int n, double *a)
{
int j;
double pi2 = 3.14159265*2 / n;
for (j = 0; j <n; j++) {
a[j] = sin(j*pi2*10)*10 + sin(j*pi2*15)*5 + sin(j*pi2*20)*10;
}
}
int main()
{
char buf[80];
int n, ip[NMAXSQRT + 2];
double w[NMAX * 5 / 4];
ip[0] = 0;
n = NMAX;
int cnt = 0;
lcd.setmode(NORMAL);
lcd.set_contrast(25);
mag.enable();
wait(0.1);
reader.attach(&readerFunction, 0.0333333);
while (1) {
while(modeFilling == true) wait(0.1);
rdft(n, 1, magBuffer, ip, w);
int n2 = n/2;
int height = lcd.height();
int width = lcd.width();
double max = 0;
for (int i = 0; i < n2; i++) {
int i2 = i*2;
magBuffer[i] = magBuffer[i2]*magBuffer[i2] + magBuffer[i2+1]*magBuffer[i2+1];
if (i > 0 && magBuffer[i] > max) max = magBuffer[i];
}
lcd.cls();
lcd.locate(0,0);
lcd.printf("%lf", max);
max = height / max;
for (int i = 1; i < n2; i++) {
lcd.line(i, height-1, i, height-1-max*magBuffer[i], 1);
}
sprintf(buf, "%4d", cnt++);
slcd.printf(buf);
if (cnt > 9999) cnt = 0;
nFilled = 0;
modeFilling = true;
}
}