Raspberry Pi でAD変換を行おうと思ったら、色々なチップがあると思いますが、今回は定番のADS1015を用います。販売元はAdafruitです。お値段は1,500円ほどと、少々かかりますが、I2C対応で使いやすいのが利点です。データシートは「Ultra-Small, Low-Power, 12-Bit ADC with Internal Reference (Rev. C)」です。
ADS1015の仕様
主なスペックは以下の通りです。
- 通信方式:I2C
- サンプリング周波数:128,250,490,920,1600,2400,3300 Hz に対応
- 4ch のシングルエンド または 2ch の差動信号
- 耐電圧 2.0 - 5.5V
シングルエンドで最大4chの入力があります(シングルエンド入力と差動入力の違いについては「アナログ入出力・A/D変換・D/A変換の基礎知識と用語集|コンテック|コンテック」を参照)。デフォルトは1600 Hzとなっています。入力インピーダンスが数Mオームはあるので、Raspbery Piで使う電圧程度なら過電流の心配はなさそうです。
入手方法ですが、例によってスイッチサイエンスさんが仕入れてくれています(「ADS1015搭載 12BitADC 4CH 可変ゲインアンプ付き - スイッチサイエンス」)。
また、Amazonにもあります。
手軽なAD変換としてそのほかよく使われるものとしては、MCP3002やMCP3008などがあるでしょうか(「Raspberry Pi でSPI通信のAD変換 MCP3008 を使う」)。そちらはSPI対応となっています。
準備、配線
通信にはI2Cを利用しますから、まず Raspberry Pi でI2Cを使えるようにします。手順は「Raspberry Pi で I2C を使用する準備 : 或る阿呆の記」にまとめてある通りです。耐電圧は 2.0 - 5.5 V なので、3.3Vでも5.0Vでも使えるのですけれど、今回は 3.3V を利用します。そのほか、A0 - A3が、ADS1015のアナログ入力で、今回テストでA0を使います。なおI2Cアドレスは 0x48 ですので、要確認。
で、テスト用の配線ですが、とにかく電圧値を読みだすだけであれば、Raspberry Piの3.3Vを取り出して、そのまま A0 - A3 に入れたらよいと思うのですけれど、せっかくのAD変換で値が変化しないのもつまらないので、今回適当な抵抗と照度を測るCdSセルを3.3V(RPi) - A0の間に挟んでみました。
例によってあまり人に見せる気を感じられない適当な写真ですが。
値を読み取る
まず、普通に一発分の値を読み取ります。有難いことに、AdafruitさんがPython用のライブラリを作成してくれているので、それを利用します。ソースコードをダウンロードするために、適当なディレクトリ下で以下を実行します(「ADS1015(A/Dコンバータ)を使ってみた。 - ついてる工房」を参考させていただきました)。
$ git clone https://github.com/adafruit/Adafruit-Raspberry-Pi-Python-Code.git
ダウンロードしたディレクトリ下の、Adafruit_ADS1x15 ディレクトリ以下に、サンプルコードがありますので、そこで作業します。今回使うサンプルコードは ads1x15_ex_singleended.py で、まぁそのままでも使えるのですけれど、初期設定でADS1115用になっているようなので、35行目あたりの adc = ADS1x15(ic=ADS1115) となっている部分を、ADS1015 と書き換えてあげるとよいと思います。
$ cd Adafruit-Raspberry-Pi-Python-Code/Adafruit_ADS1x15/
# 別にvi でも nanoでもよいがとにかくテキスト編集
$ emacs ads1x15_ex_singleended.py
35行目あたりを以下に書き換え
adc = ADS1x15(ic=ADS1015)
$ python ads1x15_ex_singleended.py
これで、電圧の値が V 単位で表示されたはずです。まぁコードを見ればわかると思いますが、設定としては、入力chとしてA0を使用、サンプリング周波数 250 Hz、4.096V レンジです。
値を連続的に取得する
以上の一発分値を取得するのは、Power-down single-shot mode とされ、デフォルトです。低電力がウリのモードです。これに対し、常にAD変換をし続ける Continuous conversion mode もあります。その分電力は食います。本節ではこれを使ってみます。
前節と同じくads1x15_ex_singleended.pyを用います。 volts = adc.readADCSingleEnded(0, gain, sps) / 1000 の下に、以下のコードを挿入します。
volts = adc.startContinuousConversion(0,gain,sps)
start_time=time.time()
i=0
while i < 1000:
volts = adc.getLastConversionResults()
print(i,start_time - time.time(),volts)
i += 1
adc.stopContinuousConversion()
この状態で実行すると、 以下のような結果が出力されます。
(0, -0.0065038204193115234, 2968.0)
(1, -0.007253885269165039, 2968.0)
(2, -0.007879018783569336, 2968.0)
(3, -0.008533000946044922, 2968.0)
(4, -0.009218931198120117, 2976.0)
(5, -0.009907007217407227, 2976.0)
...
左からループ回数、時間、電圧値(mV) です。コードを軽く解説すると、adc.startContinuousConversion で continuous mode に入ります。このとき、voltsには一番最初の値が格納されます。この状態になると、 adc.getLastConversionResults() でそのときのレジスタの値が返されるようになるので、それを1000回繰り返した後、ループを抜けて adc.stopContinuousConversion により元の single-shot mode に戻ります。時間を表示しているのはついでです。だいたい 0.7ms 毎に値取りにいけてるようなので、250sps(周期4ms)くらいは十分追っかけられそうです。雑ですが。
とはいうものの、実際にRaspberry Piで直接AD変換のレジスタの値を叩くような用途では、基本的にsingle-shot mode で十分な気がします。普通のAD変換ボードみたいに、どこかメモリに値をためて、それを取ってくるようにできたらいいんですけど…そういうのって何をどうすれば実装できるんでしょう…。
コメント