Arduino IDEでRaspberry Pi Pico Wを使う
Arduino Pro Miniに代わるデバイスを探す
以前の記事で、ADS1015を使った電力計の試作を行いました。
ty-note.hatenablog.com
動作確認レベルだったので手元のArduino Pro Mini(の互換ボード)を使いましたが、最終目標である家全体の消費電力モニタに応用するには次の課題があります。
Arduino IDEでRaspberry Pi Pico Wを使う
Raspberry Pi Picoは本来microPythonでプログラミングを行うようですが、せっかく機能試作をarduinoで行ったのでこのままarduino環境で開発ができると楽です。(新しい開発環境に移行する手間を惜しんだともいえます)
詳細な説明は諸先輩方が詳しく解説していますので、以下手順のみ簡単に示します。
1. Arduino IDEのPreferences内にあるAdditional boards manager URLs: に以下を追記する
https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
2. Arduino IDEのBoardmanagerからRP2040で検索し、出てくるRaspberry Pi Pico/RP2040 by Earle F. Philhower, III (本稿執筆時点でRev. 3.9.2)をインストールする

3. Arduino IDEでBoardsからRaspberry Pi Pico Wを選択する

4. デバイス本体のBOOTSELボタンを押しながらUSBケーブルをPCに接続する。ボード選択で通常COMポートとなるところにUF2の選択肢が出るのでそれをセレクトする。

5. 適当なコードをビルドして転送する。そうすることで、以降は通常のCOMポートから書き込みができるようになります。コードが間違っている可能性も否定できないため、最初は内蔵LEDを点滅させるような簡単なプログラムを拾ってきて試すといいでしょう。
なお、WebからダウンロードしたUF2ファイルを直接書き込む方法をガイドしているページもあり最初はそうしていましたが、トラブル復帰時にいろいろ試行錯誤していたところ、初期状態のRaspberry Pi Pico Wに対しこの方法で書き込みができることに気づきました。
Raspberry Pi Pico WのI2C用ピン選択のナゾ
さて、とりあえずArduino環境でプログラムの書き込みができることが分かったため、早速移植作業を始めようと思いましたが、I2Cのピンが分かりません。
以下はラズパイPicoのデータシートにあるピン配置表ですが、I2C0とI2C1に割り当て可能なピンがたくさんあり、これではどれがwire()ライブラリを使用したときに選択されるのか分からないのです。

けっこう探し回ったところ、
C:\Users\UserName\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\3.9.2\variants\rpipicow
にあるpins_arduino.hで宣言されているのを発見しました。

Wire0()なら4と5、Wire1()なら26と27のようです。
・・・と思ったのですが、どうにも通信できません。不思議に思ってオシロで各ピンを当たったところ、6,7番ピンからI2Cの信号が出ていることを発見しました。
どういうことか分かりませんが、いろいろ試行錯誤した結果、以下が分かりました。
- Wire()はSDA: PIN_6, SCL: PIN_7が割り当てられている(ラズパイPicoのデフォルト)
- Wire1()はSDA: PIN_31, SCL: PIN_32が割り当てられている
- I2Cで使用するピンを指示できるSetSDA()およびSetSCL()については
- Wireライブラリ(Wire.cpp)で許可されているピンのペア(以下)が指定されている場合、機能しない(指定は無視される。笑)
- 許可されている以外のピンのペア(たとえば0と1をWire1に割り当てなど)を指定するとコンパイルは通るがアップロードするとRaspberry Pi Pico Wは動作を停止する。

私にはこの謎を解き明かす技量はありませんし、ピンは選べないものの2chのI2Cが使えるのであれば実害はないため、デフォルトのピンペアで先へ進むことにします。
ADS1015をArduinoで使う - ③差動入力チャネル数の謎を解く
差動入力時の入力チャネル数の謎
以前の記事で、ADS1015はシングルエンドであれば4ch、差動入力であれば2chの入力が可能と書きました。
ty-note.hatenablog.com
実際、ADS1015のデータシートには
4つのシングルエンド入力または2つの差動入力(ADS1015)
との記載があります。
ところが、データシートにはまた
The ADS1015 contains an input multiplexer (MUX), as shown in Figure 10. Either four single-ended or two differential signals can be measured. Additionally, AIN0 and AIN1 may be measured differentially to AIN3.
とも書いてあり、後半の一文「加えて、AIN0 と AIN1 は、 AIN3 と差動で測定できます」の意味がよく分かりません。
Configuration registerで指定する入力マルチプレクサの設定を見ると、差動入力の入力ピン設定(片側がGNDではない)は

と
と
と
と
の4つとなっています。普通に考えれば、 と
で1ch、
と
で1chの計2chが無難に感じますが、ではこの真ん中二つは一体・・?
あえて図に上記4通りの組み合わせをすべて記入すると以下になります。(ここで[CT]とはカレントトランス、つまり電圧発生源としてください)

これでは、たとえば と
の電位差を測定しようとしたとき、0~1と1~3間に接続されている別のCTと並列接続になるため正しい値は得られません。
ですが、一か所あきらめて以下とすると、少なくとも電位差を測定するときに他のCTが悪影響を及ぼすようには見えません。

それぞれ電気的に独立した回路として測定を行う場合は2chなものの、をCommonとして使ってよいのなら3chの差動測定が可能なのではないでしょうか?
実験してみる
上記回路の通り、をCommonとして3つのCTを接続します。
Configuration registerのMUXフィールドを
- 001
- 010
- 011
で変化させ、それぞれのチャネルの電力を計測します。
具体的なデータ収集方法と計算方法は、以下の記事のままです。

結果とまとめ
結論から書きますと、正しく測定できました。計測の都合上3つのCTは同一の回路にクランプしているため、それぞれ個別につけ外しすることで値の変化を確認しましたが、お互いに測定値に影響を及ぼすことなく個々の測定が行えている様子です。

ADS1015はアドレスピンの接続先を変えることでI2Cアドレスを4種類に変更することができるので、この結果より1デバイスあたり3ch×4=12chの差動測定が一つのI2Cバスでできることになります。「いやそんなにいらないでしょ」と思う方もいらっしゃるかと思いますが、CTを分電盤の系統それぞれに付けて部屋や機器ごとの電力をモニタするHEMS製作を考えているため、チャネル数は稼げたほうが良いのです。
データシートの日本語抄訳はありがたいですが、きちんと使いこなしたかったら真面目に原文を読む必要がありますね。
CTを使って電力を測る - ②試作と精度の確認
試作
前回の記事
ty-note.hatenablog.com
で大まかに仕様を決めたので、実際に回路を作って実験をしてみます。
ハードウェアは、以前の記事
ty-note.hatenablog.com
と同じ以下とします。
回路構成は以下とします。とりあえずの機能実証と精度確認が目的なので、表示機能は設けず、プログラム書き込みに使うUSB-シリアル変換基板を介してシリアルモニタでデータを受け取ります。Arduino IDEはよく出来ていて便利ですね。

Arduino IDEのサンプルコードは以下の通りです。
setup()でADS1015の初期設定と読み取りレジスタの指定を行った後、loop()で16.66msの間に取れるだけデータを読み取り、二乗平均の平方根を取って値を換算しシリアル通信で出力します。
コメントアウトしているFor文を実行すると、計算に使った全サンプルデータをCSVで吐き出します。
#include <Wire.h> #define ADS1015_ADRS 0x48 // Device I2C ID int data_L, data_H; // For receiving data from ADS1015 int i; // Number of samples long data, buffer; int measurement[200]; unsigned long t1,t2; float Vrms, Watt; // For output void setup() { Serial.begin(9600); // Start serial communication, 9600bps Wire.begin(); // Start I2C communication Wire.setClock(400000); // Set I2C data rate to 400kHz Wire.beginTransmission(ADS1015_ADRS); Wire.write(0x01); //Set target register, Config register Wire.write(B00000110); //Upper byte Wire.write(B11100011); //Lower byte Wire.endTransmission(); Wire.beginTransmission(ADS1015_ADRS); Wire.write(0x00); // Set target register, Data register Wire.endTransmission(); Serial.println("Init done"); } void loop() { delay(500); i = 0; data = 0; Vrms = 0; buffer = 0; t1 = micros(); do { Wire.requestFrom(ADS1015_ADRS,2); while(Wire.available()){ data_H = Wire.read(); data_L = Wire.read(); } data_H = data_H << 8; measurement[i] = (data_H + data_L) >> 4; buffer = measurement[i]; data = data + (buffer * buffer); i = i+1; t2 = micros(); } while ((t2-t1)<16666); Vrms = data; Vrms = sqrt(Vrms / i) * 0.5; Watt = Vrms * 3; Serial.print("i="); Serial.print(i); Serial.print(",data="); Serial.print(data); Serial.print(",Vrms="); Serial.print(Vrms); Serial.print(",Watt="); Serial.print(Watt); Serial.print(","); // for (int j=0; j<i; j++){ // Serial.print(measurement[j]); // Serial.print(","); // } Serial.println("EndOfData"); }
実行すればシリアルモニタに以下が表示されます。

サンプル数は約140個取れており、ADS1015のサンプルレートを大幅に超えている(無駄に同じ値を読んでいる)ため問題ないと思います。
取得した波形
内部バッファmeasurement[i]に溜めたデータをシリアルモニタで出力し、Excelで波形にした結果が以下です。
スイッチングACアダプタを接続したとき。力率の悪そうな波形になっています。

gootのセラミックヒータ式はんだごてを追加で接続した場合。

一応きっちり1周期分取れているようです。所詮3300サンプル/秒なので、特にスイッチング電源などのピーク電流を取りこぼしている可能性もあり、数回はサンプルを取って平均するような動作がいいでしょう。
精度の確認
都合のいい測定器は持ち合わせていないので、比較対象としてサンワサプライのワットチェッカーを使います。
www.sanwa.co.jp
かなり前に購入したものですが、これは皮相電力・有効電力を分けて表示できる(=力率の概念がある)優れものです。
いくつか手元の家電でチェックした結果が以下です。
当たり前ですが力率が高い機器ほど測定値とワットチェッカーの値が近く、力率の悪いスイッチング電源ではかなりの差(50W⇔80W)が出ています。
想定外だったのがエアコンです。インバータ式の2.2kW機で、Panasonicの最廉価グレードです。Panasonicはカタログで力率を書いていない(ダイキンは書いてある)ので仕様はわかりませんが、ワットチェッカーでは力率0.81となっています。ある程度運転が安定した状態で測定しているので、立ち上げ時などフルパワー近くで運転しているときはもう少しましなのかもしれませんが・・。エアコンによる冷暖房費、および同じ仕組みを使っているエコキュートの電気代は季節によっては大きなウエイトを占めるので、これが2割近い誤差を生むのはちょっと困りものです。
| 測定対象 | 自作メータ(W & VA) | ワットチェッカー(W) | ワットチェッカー(VA) | 力率 |
| セラミックヒータ・弱 | 615 | 643 | 647 | 0.99 |
| セラミックヒータ・強 | 1067 | 1094 | 1085 | 0.99 |
| ドライヤー・弱 | 600 | 580 | 828 | 0.69 |
| ドライヤー・強 | 1110 | 1132 | 1136 | 0.99 |
| スイッチングACアダプタ | 80 | 50 | 91 | 0.55 |
| エアコン(暖房) | 710 | 578 | 711 | 0.81 |
以下は、それぞれの電流波形です。
セラミックヒータ 弱

セラミックヒータ 強

ドライヤー 弱
半波整流して50%出力にしているんですね・・

ドライヤー 強

ACアダプタ

インバータエアコン 暖房運転 立ち上げ時
思いのほか力率が悪いのは安物だからでしょうか。

ADS1015をArduinoで使う - ②負の電圧を計測する
ADS1015で負の電圧を計測する方法
前回の記事
ty-note.hatenablog.com
で説明した通り、今回ADS1015を使う目的はCTの出力を計測するためです。
CTは”トランス”の名の通り交流電流を計測し交流の出力となるため、A/Dコンバータで読み取るには負電圧の入力を処理する必要があります。
巷にある制作例だと、電源電圧(主に5V)を抵抗で分圧して2.5Vを作りだし、CTの電圧をオフセットさせる例が多いようです。これは方法としては単純でいいのですが、以下の問題があります。
- 分圧する電圧の精度が計測値に直接影響する(基準電圧ICを使ったり、ソフトでキャリブレーションするなど手はあると思いますが)
- オフセットしてしまっているので、せっかく内蔵されているPGAのゲインを上げられない。変位量が微小なのでこれは辛い
- 12bit ADCなのに、実質11bitしか使えない*1
日本語版データシートの概要欄に以下記載があるため、まるでそのまま交流信号を入れていいように記載している解説を何例か見たのですが、シングルエンドモードでは無理です。
PGAは入力電圧範囲が± 256mV~±6.144Vで、振幅の小さな信号から大きな信号まで高精度に測定できます。
ダメなことは、データシートの 9.1.2 Single-ended Inputs にもはっきり記載があります。
The single-ended signal ranges from 0V up to positive supply or +FS, whichever slower. Negative voltages cannot be applied to these devices because the ADS101x can only accept positive voltages with respect to ground.
シングルエンド入力の信号レンジは0Vから電源電圧または+FSのどちらか低いほうまでです。ADS101xはGNDに対して正の電圧しか許容しないため、負の電圧を入力することはできません。
詳しくはデータシートのFigure.10を見るとわかります。各入力のすぐ後ろにダイオードでVDDとGNDにクランプされているので、負電圧を入れてもほぼGND電位になってしまいます。

最大絶対定格もAnalog input voltage (MIN.) が GND-0.3V と表記されています。
ではなぜ最大で±6.144Vの入力範囲だとデータシートに記載されているのか?それは差動入力モードを使うからです。
シングルエンドモードと差動入力モード
シングルエンドは通常のマイコン内蔵ADCでお使いの方も多いと思いますが、GNDという基準に対する電位差を計測するものです。入力する線が1本で済みますが(もう片側はGNDで共通のため)、外部からのノイズに弱く、今回のケースでは負の電圧も測れません。(シングルエンド入力で負電圧測れるADCってあるのでしょうか?)
シングルエンド入力に設定されると、ADC前段にあるマルチプレクサの回路が閉じ、 はGNDに接続されます。
差動入力モードはその名の通り2つの入力間の差を読み取る方法です。外部からノイズが入っても、(信号P+ノイズ)ー(信号N+ノイズ)で計算されノイズが打ち消されることから、ノイズに強い計測方法だと言われます*2。このデバイスの場合、差動入力時のAD変換値は、
で計算されます。
難点としては2つの入力を使って差分を取るため、実質的に入力チャネルが半減してしまうことですね。
余談
なぜ差動入力だと負の電圧が計測できるのか、私もうまく電気的に説明が出来ない(理解していない)のですが、マイナスの電位というのはあくまでGND基準であるというところがキモなのだと思います。差動入力に接続されている電圧源はGNDに対して浮いているので、GNDに対する電位差は不定(何らかの内部回路や特性によってバランスが取れるところ)というのが真実なのではないでしょうか?
試しに以下回路の波形をオシロスコープで観測してみた結果は以下です。

Ch.1、Ch.2ともに、CTからの入力信号(60Hz)に同期した動きはしているものの、電圧もデタラメで波形も不思議な形をしています。
しかし、Ch.1と2の差分を取ると、CT単体の入力をオシロで観測したときの波形そのままが現れました。安物のスイッチング電源を接続しているため力率の悪い波形をしていますが・・・。

差動入力信号について解説している様々な資料を当たってみましたが、まだここについてしっくりくる解説に出会えていません。
CTを使って電力を測る - ①仕様の検討
真の実効値(TrueRMS)の計算
前回の記事でCTからの交流信号をADS1015で変換できることは確認したため、いよいよCTの入力を電圧(電流)値として取り込む方法を考えます。
今回計測対象とするのは交流信号のため、実効値・最大値・平均値といった概念が必要になります*1。
予備試験として手近な機器の電流波形をCTを使って観測したところ、特に省電力の機器ほど波形のゆがみが大きく、中でも安物のスイッチング電源はもはやサイン波とも言えない代物が多いことが分かりました。これらの測定で、波形が正弦波であると仮定する平均値方式ではまともな計測結果が期待できません。
よって、真の実効値(TrueRMS)と呼ばれる方式にチャレンジしようと思います。
テクトロニクスの資料によれば
電流の実効値は、電流の二乗平均平方根で求められます。
だそうです。RMS: Root Means Square たるゆえんですね。
https://download.tek.com/document/55Z_29828_0_3.pdf
以下の数式で表される通り、真の実効値は瞬時値の二乗の総和をサンプル数で割り(=平均値)、平方根を取ればいいわけです。
試しにを0~360°観測したと仮定してRMSをExcelで計算してみたところ、0.707が得られました。完全なsin波であれば振幅の最大値は実効値の
倍なので、振幅±1の時の実効値は
となることから、正しい値になっていそうです。

サンプリング速度
商用電源の周波数は地域によって50または60Hzですので、1周期は以下です。
ADS1016の最大サンプリング速度は3300サンプル/secなので、1周期あたりのサンプル数は以下です。
なので、
- 50Hz地域なら20.0ms以内に66回以上サンプリング
- 60Hz地域なら16.6ms以内に55回以上サンプリング
できれば、少なくともADS1015の最大サンプリング能力を使い切ることができるはずです。これが電流波形のサンプリングとして十分かはわかりませんが。。
このことから、最初はタイマ割込みを使って303μS毎に読み込みをかける処理を考えたのですが、I2C通信にもタイマ割込みを使っている?らしく、割り込みをさせると正常に動作できなくなる問題が起きました。I2C通信の間は割り込みをかけない処理にしたり、関係しそうな変数をvolatileで宣言したりと少し試行錯誤してみましたが、単純に解決できそうになかったため、最終的には一定時間の間できるだけサンプルを取る方式に変えています。
CTの選定と特性
今回使う予定のCTは、台湾DER EE社製の分割型CT SCT-10です。80Armsまで計測可能で、秋月電子でも扱いがあります。
https://ja.deree.com.tw/sct-10-split-core-ct.html
電流センサー(CTセンサー・変流器) 80A Φ10mm 分割型 SCT-10: 計測器・センサ・ロガー 秋月電子通商-電子部品・ネット通販
数年前にこの制作をリサーチしたときはU_RD社製CTL-10-CLSが候補だったのですが、このDER EE製SCT-10は見た目からスペックまでそっくりです。実物を比較していないのですがOEMなのでしょうか・・・?
超小型クランプ式交流電流センサ(φ10/80Arms)[仕様] | 株式会社ユー・アール・ディー
CTの出力電圧は、以下の式で求められます。
ここで
SCT-10は巻き数3000で、今回は負荷抵抗に100Ωを選定しました。結合特性は比較的特性の良好な小電流部分でしか使わないため1で計算します。
フルスケール20Armsを目標にすると、最大の出力電圧は約±0.95V程度となりそうです。したがって、ADS1015のPGAゲインは±1.024V(4倍)を選べばいいでしょう。
(急に登場した
電圧(電流)の分解能
ADS1015の出力値は、0x7FF0(32752)~ 0x8000(-32752)になります。・・が、ADコンバータ分解能は12bitのため16bitのうち末尾4桁は常時0なので、実際には0x7FF(2047)~0x800(-2048)の4096ステップ()になります。
| 入力 | 測定値(16bit) | 測定値(12bit) |
| +1.024V | 0x7FF0 (32752) | 0x7FF (2047) |
| 0V | 0x0000 (0) | 0x000 (0) |
| -1.024V | 0x8000 (-32752) | 0x800 (-2048) |
PGAゲインを4倍にしてフルスケール±1.024Vとすれば、最小分解能1bitあたり0.5mVになります。先ほどのCTの出力電圧の式を用いると、
となり、十分な分解能が取れそうです(あくまで分解能で、精度はまた別の話)。100V電源であれば×100で1.5W単位ですね。
まとめ
現在検討している電力計は以下で実現できそうです。
- ADS1015のPGAを4倍に設定し、CTセンサSCT-10を負荷抵抗100Ωで差動入力させる
- 1サイクルあたり55または66回以上サンプリングする
- サンプリングした値の二乗平均平方根を求める
- 求めた値に0.5mVを乗じて、実効電圧 Vrms (mV)を求める
より
を使って実効電流を求める
- 電圧100Vをかけて皮相電力(VA)にする。力率は考慮しない(できない)ので、これを有効電力(W)として扱う
次はこれを実装してみます。
*1:このあたりは計測器メーカなどが分かりやすい解説をしている記事が多数あるため本稿では割愛します
ADS1015をArduinoで使う - ①接続と設定
I2C接続、12bit 4ch、PGA付きと便利なTI製ADS1015を使用した制作例をよく見かけます。
ここでは自分の制作に使用するにあたり調べたことをメモします。
TI ADS1015について
CT(カレントトランス)を使った電力計の制作を考えていたのですが、CTの出力電圧はモノにもよりますが計測値0.1A(≒10W)で出力電圧33mVなどと小さく、また交流出力のため負電圧への対応が必要であり信号の扱いがやや面倒です。ADS1015は、そんな要件に適したデバイスです。
ADS1015の主な特徴は以下の通りです。
- 12bit分解能のA/Dコンバータが4ch (差動入力で2ch)
- I2Cでマイコンとデジタル接続が可能
- 設定ピンの接続先を変えることでI2CデバイスIDを4種類に変更できる→一つのI2Cインターフェースに対して最大4つ接続可能
- PGA (プログラマブル・ゲイン・アンプ)が内蔵されていて、微小信号を外付け部品なしに増幅して取り込める
- 最大サンプリングレートは3300サンプル/sec
PGAがなくてもオペアンプで増幅回路を組めば済む話ですが、私の経験上素人が特に微小な信号を扱う回路を組むとノイズであったり誤差であったりと余計な壁に当たる可能性が高く、ワンチップで完結しているADS1015は魅力的です。
兄弟製品としてADS1115という16bit分解能の製品もありますが、こちらは最大サンプリングが860サンプル/secとなり、アプリケーションを選ぶ感じです。
国内では秋月電子がモジュール化した製品(adafruit製の互換製品?)を扱っていますね。
ADS1015使用 PGA機能搭載12bitADコンバーター: 半導体 秋月電子通商-電子部品・ネット通販
ハードウェア構成
今回は以下で試しました。いずれも秋月電子で手に入ります。
AE-ATMEGA328-MINIはArduino Pro Mini互換基板で、I2Cは基板シルクでA4, A5のピンです。ジャンパJ2, J3が接続された状態(プルアップされた状態)にします。
ADS1015基板側でもジャンパをショートさせればプルアップ抵抗を有効にできるようですが、マスター側でやるのがスジだと思います。
測定する電圧源は単三乾電池(エネループ)とし、A0、A1に接続します。将来的に交流で出力されるCTの信号を受け取りたいので差動入力を選択しています。

I2CでADS1015を設定し、結果を読み出す
Arduinoの場合、便利なライブラリを使うことで非常に簡単にデータを得られます。しかしデバイス専用ライブラリは便利な反面、細かな設定であったり機能の詳細を隠蔽してしまうため、ちょっと凝ったことをしようと思ったとたん融通が利かなくなるうえ、覚えた知識の応用も難しくなります。
今回はI2Cこそライブラリの力を借りますが、ADS1015との通信は直接プログラムすることにします。
サンプリングモードの設定
ADS1015は、動作設定をConfiguration registerに書き込むことで設定します。プログラムの手順としては以下です。
- 対象のデバイスIDに対してI2C通信を開始する
- 0x01を送信して、書き込み先をConfiguration registerに設定する
- Configuration registerの内容を、上位8bit、下位8bitの順番に送信する
- I2C通信を終了する
Configuration register (16bit)各bitの意味は以下の通りです。

幅広い設定が可能なので詳細はデータシートを見てください。以下は概要です。
| Field | 内容 |
| OS | ReadかWriteかで意味が異なってきます。シングルショットの時は1を書くとサンプリング開始です。連続変換ならどちらでもOK ("No effect"です) |
| MUX | 入力チャネルの選択、および動作モード(シングルエンド又は差動入力)の切り替えを行います。デフォルト000でA0とA1ピンでの差動入力で動作します |
| PGA | プログラマブル・ゲイン・アンプのゲインを設定します。デフォルトは010で±2.048Vです |
| MODE | 連続変換かシングルショットかを選択します。0で連続変換です |
| DR | サンプリング周期を設定します。デフォルトは100で1600サンプル/秒です |
| COMP_MODE | 内部コンパレータの設定を行います。割愛。 |
| COMP_POL | 内部コンパレータの設定を行います。割愛。 |
| COMP_LAT | 内部コンパレータの設定を行います。割愛。 |
| COMP_QUE | 内部コンパレータの設定を行います。割愛。 |
今回の例では以下としました。

データの読み出し
Configuration registerに適切な設定を行えば、ADS1015は指定した周期でサンプリングを行い、データを逐次Conversion registerに書き込みます。これの読み出し手順は以下です。
- 対象のデバイスIDに対してI2C通信を開始する
- 0x00を送信して、I2C通信で読み取るレジスタのアドレスをConfiguration registerに設定する
- 送信を終了する
- 上位8bit、下位8bitの順にデータを読み取る
- 上位8bitのデータを8bit分ビットシフトさせて、下位8bit分のデータと足し合わせることで結果を得る
Conversion registerは以下の通りで、12bitのデータが上位詰めで格納されています。

12bitのデータを一度に読み出せないため、8bitずつ読み取ったデータを以下手順で処理します。

一度読み出し先のアドレス0x00を設定しさえすれば、チャネル設定を変更しない限りステップ4~5を繰り返すことで最新のデータを読み出すことができます。
ところで、ADS1015は4chのADコンバータではありますが、内部マルチプレクサで回路を切り替えているだけでADCコア自体は1個しか搭載されていません。Configレジスタでチャネルを指定して、変換したデータを読み取るというプロセスなので、3300サンプル/秒は1chあたりの最大サンプリングです。チャネル変更→読み出しには複数回のI2C通信を行う必要があるので、4chすべてを読み出す場合の最大サンプル数はかなり少なくなるでしょう。このあたりは安いオシロスコープと似たような作りですね。
サンプルプログラム
上記設定の動作確認を以下プログラムで行いました。A0~A1間の電位差を計測し、シリアル通信で電圧出力を行います。I2C通信および内部での変換処理を1サイクル実行する時間も併せて計測し出力します。
#include <Wire.h> #define ADS1015_ADRS 0x48 // Device I2C ID int data, data_L, data_H; // For receiving data from ADS1015 unsigned long t, t1; // For time measurement float voltage; // For output void setup() { Serial.begin(9600); // Start serial communication, 9600bps Wire.begin(); // Start I2C communication Wire.setClock(400000); // Set I2C data rate to 400kHz Wire.beginTransmission(ADS1015_ADRS); Wire.write(0x01); //Set target register, Config register Wire.write(B00000010); //Upper byte Wire.write(B11100011); //Lower byte Wire.endTransmission(); Serial.println("Init done"); Wire.beginTransmission(ADS1015_ADRS); Wire.write(0x00); // Set target register, Data register Wire.endTransmission(); } void loop() { t1 = micros(); Wire.requestFrom(ADS1015_ADRS,2); while(Wire.available()){ data_H = Wire.read(); data_L = Wire.read(); } data_H = data_H << 8; data = data_H + data_L; voltage = data * (4.094/32767); t = micros() - t1; // Get acquisition time in micro sec Serial.print("V="); Serial.print(voltage); Serial.print(" Acquisition time(us)="); Serial.println(t); delay(500); }
結果は以下の通りです。

1回の読み出し・データ処理で120μ秒程度かかっています。
実は、最初に試したときは1回の処理で600μSec程度かかっていました。ADS1015の最大サンプリングは3300サンプル/秒のため、1読み出しあたり約300μSecを超えてしまうと最大サンプリングでデータを読み取れなくなってしまいます。I2Cの通信速度を最高速の400kHzに変更(デフォルトは100kHz)したり、読み取るレジスタの指定を毎回行わない(一度指定すれば何度でも繰り返し読めることに後で気づきました)などの改善で120μ秒まで短縮しています。
最後の電圧変換するFloatの演算をある程度サンプリングした後に行うなど処理の工夫でまだ改善できるはずですが、とりあえずこれでADS1015の設定とデータ読み取りはできたので先に進むこととします。