« BeagleBone Blackを試す (1) | トップページ | 光当たる人工光合成 »

2014年1月 3日 (金)

BeagleBone Blackを試す (2)

BBB (BeagleBone Black、以下同様) のADC (Analog-to-Digital Converter、以下同様) を試してみた。標準的な使い方は他の種類のGPIOに比べると簡単だ。しかもわずかな外付け回路でもある程度動作確認できる。今回は100kΩの抵抗とICクリップが両端に付いたリード線を使ってみた。

/*-----------  -----------*/

試した環境

前回の記事で一番印象の良かったDebian weezyを使った。Ångströmではrootユーザーで使うのがディフォルトになっているので、この記事でsudoを使っている場合でもそれが不要になる。この事を除けば、他のLinuxでも同じカーネル (3.8.13) を使っていれば、基本的に同様である。

 

標準的な使い方

BBBに使われているSoC  Ti AM3359のADCの入力には、それ専用のピンが割り当てられている。これは他のピンが複数の機能で共有するようになっているのと対照的だ。そのためADCの標準的な使い方は有効化して値を読み出すだけでmV単位の入力電圧値が得られる。

debian@arm:~$ sudo -s
[sudo] password for debian:
root@arm:/home/debian# echo cape-bone-iio > /sys/devices/bone_capemgr.*/slots
root@arm:/home/debian# exit
exit
debian@arm:~$ cat /sys/devices/ocp.*/helper.*/AIN7
1696

AN7はGPIOヘッダーには接続されていない代わりに、VDD_3V3Bを半分に抵抗で分圧した信号に基板上で接続されている [Schematic A6 (PDF)]。

Ain7

従って全てが理想的なら1650であるべきだが、VDD_3V3BおよびADCの基準電圧VDD_ADCそれぞれの正確な設計値と諸々の公差を考慮すると、1563~1771が最悪ケースの許容範囲である。ただしこれはADCの入力に電流が流れないとした場合だ。実際には最大数μAの電流が流れるので、さらにその分数mVのズレが発生しているハズだ。

ここで気になる点が三つ見つかった。

  1. 操作対象のパスが一定でない
  2. ADCを有効化するのにrootユーザーの権限が必要
  3. 読み取れるのは0~1799の整数値なので12ビットの分解能が得られない

以下それぞれについて考察してみる。

 

操作対象のパスが一定でない

コマンドラインやシェルスクリプトなら、パスの可変部分にワイルドカード文字 (アスタリスク) を使っておけばシェルが実際に存在する名前に置き換え・展開してくれるのであまり不自由は無い。ただしADCの有効化の場合のようなリダイレクトのあて先について、bashは置き換えを行うが、shは行わないようだ。

その一方プログラミング言語から操作する時、多くの場合は正しいパスを特定する処理が必要になる。面倒だが、そんなに難しい処理ではないので、実用的なアプリには忘れずに造り込むべきだ。

 

ADCを有効化するのにrootユーザーの権限が必要

一般論として誰でもハードウエアを直接制御できるのはマズイ。だからと言って何でもできるroot権限が必要にすることも無かろう、と言う議論である。

これが問題かどうか使われ方次第だ。PICなどのマイクロコントローラーと同じような使い方に限れば、そもそも権限もヘッタクレも無い世界なので、Ångströmの様に常時パスワード不要のrootユーザーで使っていても一向に構わないだろう。しかしLinuxの動いているコンピューターなのでWebサーバーをインターネットに晒すことも可能である。こうなると全く別の話だ。

この辺の折り合いをつけるには、次の二つのアプローチが可能ではないかと思う。

一つ目はLinuxの起動時にroot権限で実行される一連の処理の中でADCの有効化を行ってしまうことである。これはADCのようにI/Oから入力する処理しか行わない場合に限り有効な方法だ。

二つ目はsysfs上のオブジェクトのアクセス許可を調整する方法だ。/dev/ttyS*の様に書き込み可能なグループを設定してやることである。こうすればI/Oに対して出力するプロセスは、そのグループに属するユーザーで動かしてやればよい。これはapache2のサービスプロセスをrootではなくwww-dataで動かすのと同様の発想である。

debian@arm:~$ ls -l /dev/ttyS*
crw-rw---T 1 root dialout 4, 64 Dec 31 09:21 /dev/ttyS0
crw-rw---T 1 root dialout 4, 65 Dec 31 09:21 /dev/ttyS1
crw-rw---T 1 root dialout 4, 66 Dec 31 09:21 /dev/ttyS2
crw-rw---T 1 root dialout 4, 67 Dec 31 09:21 /dev/ttyS3

どちらのアプローチも具体的に実現する方法がいくつかある。前者については/etc/rc.localに追記するのが良さそうに思う。しかし後者についてはsysfsのアクセス許可設定を恒久的に変更する適切な方法が良くわからなかったので、今回は前者の方法のみ試してみる。

 

得られるのが0~1799の整数値なので12ビットの分解能が得られない

あれこれ調べたら12ビット分解能の値もエクスポートされていることが分った。

debian@arm:~$ ls -l /sys/bus/iio/devices/iio\:device0/
total 0
drwxr-xr-x 2 root root    0 Dec 31 09:24 buffer
-r--r--r-- 1 root root 4096 Dec 31 11:02 dev
-rw-r--r-- 1 root root 4096 Dec 31 11:02 in_voltage0_raw
-rw-r--r-- 1 root root 4096 Dec 31 11:02 in_voltage1_raw
-rw-r--r-- 1 root root 4096 Dec 31 11:02 in_voltage2_raw
-rw-r--r-- 1 root root 4096 Dec 31 11:02 in_voltage3_raw
-rw-r--r-- 1 root root 4096 Dec 31 11:02 in_voltage4_raw
-rw-r--r-- 1 root root 4096 Dec 31 11:02 in_voltage5_raw
-rw-r--r-- 1 root root 4096 Dec 31 11:02 in_voltage6_raw
-rw-r--r-- 1 root root 4096 Dec 31 11:02 in_voltage7_raw
-r--r--r-- 1 root root 4096 Dec 31 11:02 name
drwxr-xr-x 2 root root    0 Dec 31 09:24 power
drwxr-xr-x 2 root root    0 Dec 31 09:24 scan_elements
lrwxrwxrwx 1 root root    0 Dec 31 09:23 subsystem -> ../../../../../bus/iio
drwxr-xr-x 2 root root    0 Dec 31 09:24 trigger
-rw-r--r-- 1 root root 4096 Dec 31 09:23 uevent

実はこのディレクトリーはリンクで、実体はいかにも名前が定まらなさそうな感じである。

debian@arm:~$ ls -l /sys/bus/iio/devices/iio\:device0
lrwxrwxrwx 1 root root 0 Dec 31 11:02 /sys/bus/iio/devices/iio:device0 -> ../../../devices/ocp.3/44e0d000.tscadc/tiadc/iio:device0

しかしこのリンク末尾の0 (ゼロ) も変わる可能性があるらしいので、前述の正しいパスを特定する処理が必要になる。

 

対策例

まずADCの有効化処理を/etc/rc.localに追加してみる。

for i in `find /sys/devices/bone_capemgr.*/ -maxdepth 1 -name slots`; do
    echo cape-bone-iio > $i
done

上記3行を、ファイル末尾に最初からあるexit 0の直前に追加する。rc.localはbashではなくshで動かすことになっているようなので、こうすればbashでリダイレクトの宛先パスにアスタリスクを含ませたのと同等のハズだ。

これは結構快適である。起動・ログインすれば直ぐにADCの値が読めるのは楽でいい。

debian@arm:~$ cat /sys/devices/ocp.*/helper.*/AIN7
1696
debian@arm:~$ cat /sys/bus/iio/devices/iio\:device*/in_voltage7_raw
3861

なお3861 × 1800 ÷ 4096 = 1696.728515625なので、in_voltage7_rawからAIN7への変換では端数が切り捨てられているらしい。

次にADCの値を読み込むパスを環境変数に保存するよう、/etc/profileの末尾に以下2行を追加してみた。

export AIN_DIR=`find /sys/devices/ocp.*/ -maxdepth 1 -name helper.*`
export RAW_ADC_DIR=`find /sys/bus/iio/devices/ -maxdepth 1 -name iio\:device*`

これで起動・ログイン直後に

debian@arm:~$ cat ${AIN_DIR}/AIN7
1696
debian@arm:~$ cat ${RAW_ADC_DIR}/in_voltage7_raw
3861

が可能になる。しかしShiftキーを押しながらたくさんタイプしなければならないのは楽ではないから、あまり実用的効果は無い。しかし邪魔にはならないし覚書としての効果はありそうなので、入れたままにしておいた。

 

GPIOヘッダーに引き出されている入力を使ってみる

AIN0 (P9_39) とGNDA_ADC (P9_34) の間に100kΩの抵抗を接続する。

Cimg0495

ADC入力からは最大数µAの電流が流れ出すようなので抵抗の両端に発生した電位差が読み出せるハズだ。

debian@arm:~$ cat /sys/devices/ocp.*/helper.*/AIN0
10
debian@arm:~$ cat /sys/bus/iio/devices/iio\:device*/in_voltage0_raw
25

mV単位の値は換算の際に端数切り捨てられているので、今後は12ビット分解能の値だけを読み出すことにする。100kΩは比較的高抵抗なのでノイズを拾っているかもしれない。少し連続的に読み出してみよう。

debian@arm:~$ for i in `seq 10`
> do cat /sys/bus/iio/devices/iio\:device*/in_voltage0_raw
> done
25
26
26
26
25
25
23
25
25
25
debian@arm:~$

12ビット分解能の値で3p-p、約1.3mVp-p程度のノイズがあるようだが、それを除くと平均25 = 約11mVの電位差が発生しているようだ。これに基づきADCから流れ出す電流地を計算すると

I = E/R = 11 ÷ 100  = 0.11µA

随分小さな数値だが、先ほど数µAと書いたのは最大値なので、まぁこんなものだろう。リード線で短絡すれば読み取り値はゼロになるはずである。

Cimg0493

debian@arm:~$ for i in `seq 10`
> do cat /sys/bus/iio/devices/iio\:device*/in_voltage0_raw
> done
25
1
1
0
1
2
1
0
1
0

最初に25が出ているのは、こちらのページ中ほどに赤い文字で書かれている内容に該当する現象かと思われる。

There is currently a bug in the ADC driver. You'll need to read the values twice in order to get the latest value.  [抄訳: 現状、ADCのドライバーにはバグがあって、最新の値を得るためには2回読み出さなくてはならない。]

このWebはIO Pythonに関するものだが「ドライバー」は通常カーネルに付属しているので、シェルと同じものを使っているハズだ。しかしカーネルバージョンを明記せずに「現状」って言われてもねぇ。

この「バグ」、比較的短い周期で連続して読み込み続けるような使い方なら1回ズレるだけなので影響が出ない場合も多いかもしれない。しかしオンデマンドで読み込むような場合は2回ずつ読まなくてはならない、って事だ。要注意だね。

バグと思われる部分を別にすると、ピーク値約1mVのノイズを拾っているような感じに見える。リード線がアンテナになっている疑いがあるので、ツイストしてみた。

Cimg0494

debian@arm:~$ for i in `seq 10`
> do cat /sys/bus/iio/devices/iio\:device*/in_voltage0_raw
> done
1
0
0
0
0
0
0
0
0
0
debian@arm:~$ for i in `seq 10`
> do cat /sys/bus/iio/devices/iio\:device*/in_voltage0_raw
> done
0
0
0
0
0
0
0
0
0
0

「バグ」の影響を明確にするため2回続けて同じ事を繰り返してみた。確かに1回目の最初だけ1になっているのはバグの説明と矛盾しない。またツイストするとノイズが無くなる。教科書に書いてある通りだ。

なるほど、1.5Vの乾電池の電圧を計るのに普通のリード線を使ったのではノイズを拾ってしまう。より対線かシールド線を使う必要がある、と言う事だね。

ADCから流れ出す電流の影響が無視できるようにするには、それによる読み取り値の誤差が0.5以下にする必要がある。今回の条件だと100kΩで25の誤差が出たので信号源インピーダンスを2kΩ以下にすれば良いことになるが、条件が変わった場合に対する余裕を見込むと1kΩ以下にしなければならないだろう。また信号源インピーダンスはADCの入力キャパシタンス、サンプルレート、およびサンプルスイッチの等価抵抗の値との関係で必要な精度を保つのに要求される値がある。残念ながらこちらの方の根拠となる諸元が分らないので推定だが、これも1kΩ以下を要求される可能性が高い。したがって、入力にOPアンプなどによるバッファ無しでは誤差の大きさを12ビット分解能に見合った程度に抑えるのが難しい場合が多いと思う。

 

ノイズ源の確認

ノイズ対策はこれでよいとして、その源が何か気になる。デジタルオシロのプローブにリード線をつなぎBBBのADCと条件が近くなるようにして測定してみた。ノイズ検出では定番のピーク検出モードを使っている。

Tek11

エンベロープの波形が電源周波数と同期しているようなので、何かインバーター方式の家電からのノイズかと思ったのだが、BBBのLinuxをpoweroffコマンドでシャットダウンすると

Tek12

ノイズのかなりの部分が消えて無くなる。強いノイズの大半はBBBの電源管理IC (TPS65217C) 周辺で発生していたのだろうか? スタンバイ状態でもTPS65217Cの一部は動いているが、それはRTCなどに電源供給しているリニアレギュレーターだったハズなので、上の図のノイズはBBB以外に由来している可能性が高い。

さらにBBBに電源を供給しているACアダプターをコンセントから抜くと

Tek13

トゲトゲが無くなったので、少なくともこれはACアダプターに由来するものらしい。上の図に現れているのは環境ノイズだ。最初の測定でベタッと出ていたノイズとピーク値で比較すると、環境ノイズは1/10くらいだろうか。

実は未だ最初の測定でベタッと出ていたノイズの出所には決着が付いていない。BBBの電源管理IC (TPS65217C) 周辺かもしれないし、ACアダプターかもしれないのだ。

決着をつけるべくBBBへコンピューターのUSBポートから給電してみたら、上の「環境ノイズ」の状態から全くノイズが増えない! 犯人はACアダプターだったのだ。ダメ押しで異なるACアダプターも使ってみたが、結局ノイズが出るのは最初に使っていたACアダプターだけであることが判明した。当然のことだが、ノイズの出るACアダプターを使わなければリード線をツイストしなくてもADCの読み取り値はゼロに張り付く。

BBBをpoweroffしてノイズが減ったのは負荷が軽くなってスイッチング周期が長くなったためだ。

問題のACアダプターはDL-AC20と言う型番のA型USBジャックに5Vを出力する製品である。ちゃんとPSEマークが付いている。今はもう売られていないようで、記憶を辿ると数年前に購入したものだ。ノイズが出るのは最初からだったのか、それとも経年変化なのか分らない。

 

ノイズの波形とADCで検出できる確率

ノイズの多いACアダプターを使った場合、BBBのADCはそれをどの位検出できるのか、あるいはどの位影響を受けるのか、考えてみた。

オシロのプローブにツイストしていないリード線をつないでノイズ波形を調べてみた。ADCに見えている波形に近づけるため、オシロの入力帯域幅を制限しているが、一番狭くして20MHzである。

Tek09

この通り見えているなら、ツイストしていないリード線で短絡したときのADC読み取り値が0~2程度に収まるハズが無い。この原因として考えられるのは、1) ADCのアナログ帯域幅が20MHzよりも狭い、そして2) ノイズの発生頻度に比べてサンプル数が少な過ぎる、の二つがある。1) はおそらくその通りなのだが、ADCの正確なアナログ帯域幅が分らないので定量的なことは分らない。定性的にはアナログ帯域幅が狭いほどADCが検出するピーク値が低くなる一方、波形が時間軸で広がるので検出される確率が上がるはずだ。2) については以下の通りである。

上の図でノイズのピーク値が20mVを超えている期間は約0.6µsである。ノイズの繰返し周期を調べると

Tek10_2

約40µs間隔で2回ずつ発生している。ランダムに1回採取したサンプルがノイズのプラス側の比較的高い値を取る確率を考えることにする。「プラス側」に該当するのはピーク値が20mVを超えている期間の半分、「比較的高い値を取る」のはさらにその半分だと定義しよう。この場合に遭遇する確率は2×0.6÷40÷2÷2 = 0.0075である。20回サンプリングしてもこの場合に1回も遭遇しない確率は [(1 - 0.0075)の20乗] ≑ 0.86 で、あまり見込みが無かった訳だ。

1,000回サンプルを採れば、プラス側の比較的高い値に1回も遭遇しない確率が0.1%を切るので充分だ。これはシェルスクリプトでも可能だが、結果の見せ方の選択肢が限られる。サンプル値を時系列プロットするのが良さそうだ。Pythonに使いやすいモジュールがあるので試してみようと思うが、これは次回のテーマに取っておくことにする。

考えようによっては、これはかなりいやらしいトラブルの元になったかもしれない。オンデマンドだけでADCを働かせていたとしよう。時々微妙に問題になる程度の誤差が出る。この時ACアダプターのノイズが原因だと気付くだろうか? ユーザーが勝手に標準装備とは違うノイズの多いACアダプターを使う場合もあり得る。自分がトラブルシューティングを担当させられたら、と思うとうなされそうな想定だ。

 

今回のまとめ

BBBのADCは比較的手軽に使うことができる。12ビット分解能が得られるのはうれしいが、それに見合ったアナログ信号の取り扱いが必要になる。多少ノイズの多いACアダプターを使ってしまっても、教科書通り対応すれば問題回避できる場合があるが、ブレッドボードやユニバーサル基板を使ったプロトタイプではノイズ対策がやりきれるかどうか疑問である。避けられるなら、ノイズを出すことが分っている部品は避けるのが基本だ。

|

« BeagleBone Blackを試す (1) | トップページ | 光当たる人工光合成 »

BeagleBone Black」カテゴリの記事

IT」カテゴリの記事

趣味」カテゴリの記事

コメント

コメントを書く



(ウェブ上には掲載しません)


コメントは記事投稿者が公開するまで表示されません。



トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/543745/58875134

この記事へのトラックバック一覧です: BeagleBone Blackを試す (2):

« BeagleBone Blackを試す (1) | トップページ | 光当たる人工光合成 »