SSブログ

Raspberry PiとADC [Raspberry Pi]

こないだ買ったRaspberry Pi model BにA/Dコンバータを接続した。

なんのためか、というと仕事で使いたいから。これまではMacから直接アクセスするためのA/DコンバータはNational Instrumentsの安いUSB接続の箱をつないで使っていた。ところがまたNIはOS Xのサポートをやめるつもりでいる(NIのサポートにメールしたら日本語で返事が返ってきた)らしい。OS Xで使える汎用のA/Dコンバータの安いのはもうない(もちろんWindows用はどれを選べばいいのか迷うくらいいろいろあるのに)。しょうがないので以前遊んでいたEZ-USBを使ってI2C接続のADチップをつなごうと思った。ようするにまた例によってNIのDAQのパチモンを、趣味もかねて自作しようというわけである。

ところがちょっと待てよ、もっといいのがあるじゃないか。Raspberry PiにはI2CもSPIもあってしかもちゃんとしたLinuxが走るので、EZ-USBよりインテリジェントなA/Dコンバータモジュールができるじゃないか。これを使わない手はない、と思い立った....

僕のRaspberry PiはWolfram-Engineをインストールしてあってゴミがたまってるのか8GBのCDカードではaptのupgradeができなくなってきた。しょうがないので近所のヤマダ電機に行って16GBを買ってきてまたNOOBSからやりなおしになった(もとの8GBをコピーするのとどっちが簡単だったろうか)。まえもそうだったんだけど、イメージのほうをSDカードに流し込んでもRaspberry Piがブートしない。よくわからない。

やってみると新しいNOOBSにははじめからWolfram-Engineが入った状態になっている。正式版になってなにか変わったのかな、と思ったけどとくに目新しいことはなかった。Mathematica10はいろいろなオプションパラメータ(たとえばPlotのポイント数やPlot3Dのポリゴン数なんか)が普通のPC向けになっていて、そのままだとRaspberry Piではかなり時間がかかる。昔のMathematica2.2あたりのパラメータに簡単に置き換える方法はないかな。

いやいや、今回はMathematicaがやりたいのではない。A/Dコンバータをつなぎたいんだった。さいしょI2C接続でワンチップのADCを使おうと思っていたけど、ここにすごく親切な記述のサイトがあった。このサイトはI2Cではなく、SPI接続のADCであるMCP3204/3208のRaspberry Piでの使い方を解説してくれている。MCP3204/8は12ビット幅で100kspsぐらいまで使えてしかもひとつ300円ぐらいなので使いやすい。精度はそれなりのようだけど、NIの安いDAQもとんとんなのでこんなもんだろ、ということで試してみようと思った。

Raspberry Piの基板からフラットケーブルを引っ張るのは気が進まなかったので、ちょっと値は張るけどサンハヤトのUB-RPI01を買った。ちょうどRaspberry PiのP1コネクタで屋根のようになる。これだとケーブルをひっかけて壊したりせずにすむだろう、と思った。

部品を買って(ADCにはMCP3208を使いたかったが今回は納期の関係でMCP3204になった)、ハンダ付けして、Raspberry Piに乗せてみた。
1029raspiAD.jpg


おお、いいじゃん(写真ではRaspberry Piの基板と同じ幅のユニバーサルの上にADCのチップだけが乗っている。基板の真ん中はM3のスペーサが通るのかと思ったらダメだった。インチなの?)。先の親切なサイトの記述の通りにspidevというkernelモジュールを組み込もうとした。ところが何度やってもlsmodコマンドのリストの中にspidevが現れない。spi_bcm2708というモジュールはあって(本家のドキュメントにはこれに関する記述しかない)、/dev/にはデバイスファイルのspidev0.0とspidev0.1ができている。サイトにあったサンプルを動かしても0しか読めない。3.3Vに直接つないでも同じだった。どうやらspidevがちゃんとインストールされてないのか、と思っていろいろやってみた。

会社の工場に持っていって(また今週も来ている)オシロでSPI0_SCLK(クロック信号のピン)を見てもグランドに落ちたままでうんともすんとも言わない。やっぱりspidevがないのがいけないのかなあ、いろいろググってみてもあまり情報が見つからない。

いろいろなことを試して(udevのファイルをいじったり、違うディストリビューションのこんな話があったので比べてみたり)るうちに、なぜかSPI0_SCLKが立つようになった。lsmodにはspidevはリストされないまま。よくわからない。こういうのって困るなあ。何がダメで何がそれを解決したのか、そもそも僕はLinuxのデバイスドライバがどうなってるのか全く知らないので全然わからない。いまさら勉強しようとも思わないので、動いたのはよしとして、次のA/Dマシンを作るときに見直すことにする。

まずハードの確認のため、親切なサイトのサンプルコードをそのまま実行して、チャンネル1に会社の工場にころがっていた安定化電源をつないだ。電源の電圧を変えながらマルチメータで電圧を測って、その値に対するA/D出力を読んだ。A/Dは12ビット(0〜4095)の積分非直線性誤差が±2LSBのチップ(MCP3204-B)。VDDにUSBの5V電源を、基準電圧にRaspberry Piから出ている3.3Vを与えてある。クロックは1MHzで実質サンプリングレートは約41ksps(カタログの値と比較できるように書くと83ksps)になる。

原点を通る直線で近似してそれからの残差をプロットしたのが下の図。
1029raspiAD.png
誤差は全体としては±1mVだいたい±1LSBとちょっとということ。マルチメータの読みが0.1mVの桁は安定しないのでその誤差も含まれている。すばらしい。光学用途に使うには普通はこれで十分じゃん。すごいな、最近のチップは。

ハード的にはとりあえず十分だとして、あとはソフトの問題。やりたいのはたいていフォトディテクタからの信号をA/DしてOS Xに読み込みたいというものである。Raspberry Piに常駐のA/Dコンバータdaemonを作って、そいつがUDPのサーバになっていて、UDPの決められたポートにコマンドを書き込むと、ADされた値が返ってくる、というようにしようと思う。これならサーバ側のプログラミングはバカチョンでできる。ただし、いくらUDPが軽いと言ってもリアルタイム性を保証できるわけではないので、Raspberry Pi内で閉じたなんらかのトリガは用意しないといけない。でもそれもGPIOを使えばいいだけだろう。

最初はGigE Visionカメラと同じようにブロードキャストでA/Dの乗ったRaspberry Piを探すようにしないといけないかと思ったけど、avahiも動くし、ホスト名で個体識別できるので、デバイス探索は必要ない。こういうところはやはりフルバージョンのLinuxならでは。

あとは適当にポート番号を決めて、Raspberry Pi側で待ち受けて
typedef struct _startCommand {
    UInt8   commandKey;     //  コマンド識別
    UInt8   chipSelect;     //  チップセレクト
    UInt8   channels;       //  アナログチャンネル指定
    UInt8   pileUpTimes;    //  積算回数
    UInt32  waitmsec;       //  データごとの間隔
    UInt32  count;          //  データ取得回数(0は無限回)
} startCommand;

typedef struct adData {
    UInt8   ackKey;         //  ACK識別
    UInt8   chipSelect;     //  データソースのチップ
    UInt8   channels;       //  有効なチャンネル
    UInt8   pileUpTimes;    //  積算の回数
    UInt32  baseClock;      //  ADCのクロック周波数
    UInt32  timeStampmsec;  //  タイムスタンプ
    UInt32  restCount;      //  残回数
    UInt32  data[8];        //  各チャンネルのA/Dデータ(3204ではdata[4]以上は0)
} adData;

typedef struct _stopCommand {
    UInt8   commandKey;     //  コマンド識別
    UInt8   chipSelect;     //  チップセレクト
    UInt16  reserved;
} stopCommand;
みたいなコマンドと応答のやりとりをUDPですればいいだけでできる(ちなみにstartCommandを送ることで定期的にデータを取ってUDPで送るのを開始する。ワンショットはcountを1に、取り続けるときはcountを0にする。取るたびにRaspberry PiからadDataが返ってくる。pileUpTimes回連続してデータを取って積算してその結果をひとつのデータにする。data[]をpileUpTimesで割ればその平均になる。timeStampmsecはstartCommandが来たときを起点とする経過秒数で、restCountは回数を指定されたときの残り回数。無限回のときは無視。abortCommandは無限回のときや途中でやめるときに送る。その応答として最後のデータを受け取る)。非常に簡単。EZ-USBと違って特殊関数なんかの数値計算も簡単だし、いずれはもっと難しいことをRaspberry Pi側でやらせたい。Mathlinkを経由してWolframKernelを使うという手もあるし。

今回のための金額はRaspberry Pi本体含めて7千円ぐらい。見た目は悪いので箱に入れないといけない(百円ショップでサイズの合うプラケースを探すか)のと、USB接続に比べてレイテンシが大きい(応答が遅い)ので工夫が必要だけど、複雑なプログラミングがずっと簡単になるし、NIのUSBDAQを買うよりはすいぶん安くすむ。汎用の光学計測用途では精度に問題はないし(反射防止膜の透過率や、PBSの消光比を測るとかはダメ)。よし、これからはこれでいこう。

今回それほど大きな問題なくいったのでうれしい。
nice!(0)  コメント(0)  トラックバック(0) 

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。