« BeagleBone Black Rev. C の供給状況 | トップページ | ワットチェッカー的なものを自作する (その3) »

2014年8月 8日 (金)

ワットチェッカー的なものを自作する (その2)

以前の記事ではPCのオーディオ機能を使ってさっくり片づけてみたが、意外とイイ感じで動いてしまった。しかしPCを持ち出すのはやっぱり大袈裟である。

当初の構想に戻りマイクロコントローラーを使うにしても、内蔵ADCではなくオーディオ用のものを別途外付けして使うのもアリかもしれない。そう思ってオーディオ用ADCを調査していたら、同じ「データ変換」グループに「エネルギー測定IC」のカテゴリーがあるのを見つけた。どうやらこれはスマートメーター向け信号処理用デバイスの事らしい。電流・電圧信号をAD変換して有効電力やrms値の計算までハードウエアで行うICだ。

数百円で購入できる。必要な機能はほぼ全て入っているし精度などの性能もかなり良さそうである。

内蔵ADCを使いマイクロコントローラーだけでどこまでできるか試すのも興味があるが、これはまたの機会にとっておいて、今回は「エネルギー測定IC」を使ってみることにした。

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

どれを使おうか

複数の会社が「エネルギー測定IC」を出しているが、私が見た印象ではAnalog Devicesの製品が品揃えと入手し易さで優れているようである。正直に白状すると品揃えが豊富過ぎて選ぶのに少し苦労した。

多相用と単相用に大別され、中には電流6相+電圧1相で合計7台のADCを内蔵したデバイス (ADE7816) もある。しかし今回の目的には単相用で良く、その中で測定できる項目の多いADE7753を使うことにした。

Ade7753_bdg

Source: http://www.analog.com/jp/analog-to-digital-converters/energy-measurement/ade7753/products/product.html (ADE7753.pdf, p.1, Figure 1)

ADE7753を単純な電力計として使う場合はCFピンに出力される周波数変換された有効電力値だけを使うようだが、SPIインターフェイスを使えば以下の測定項目のデータを読み取ることができる。

  • 波形 (電流と電圧の瞬時値、および有効電力の移動平均値)
  • 有効電力 (積算値とサイクル平均値)
  • 皮相電力 (積算値とサイクル平均値)
  • リアクティブ電力 (サイクル平均値)
  • 電流rms値 (移動平均)
  • 電圧rms値 (移動平均)
  • ピーク電流
  • ピーク電圧
  • チップ温度
  • 商用電源周期

データシートによればSPIは校正用に使うとある。しかしオフセット補正値などはSPI経由で設定するから、リセット直後のディフォルト設定で使うのでなければマイクロコントローラーをつなげておいて電源投入の都度初期化を行うようにする必要があるはずだ。

今回のワットチェッカー的用途ではCF信号は使わずにSPIからのデータだけを使う。

電力関係データは取得タイミングがさほど厳しくないのでBBB (Beaglebone Black) や Rpi (Raspberry Pi) などのLinuxからspidevドライバー経由でアクセスしても大丈夫そうである。しかしrms値関係は商用電源のゼロクロスに同期して取得しないと正しい値を安定して取得できない。また波形関係のデータは一番遅くても約285μs周期 (3.5ksps) で取得しなければならないからLinuxのユーザーモードで動いているプログラムで取得するのは多分無理だ。

今回はBBBを使い、タイミングの厳しい部分はPRUを利用することにした。

ちなみにADE7753をPGAゲイン1で使った場合、1:1000のダイナミックレンジで有効電力の誤差0.1% (代表値) となっている。最大1,000Wまで測れるようにしておいて1Wを測った場合の誤差が1mW。これはかなり期待できそうだ。ただし入力信号にはじめから存在するノイズなどの影響は考慮していないはずなので、実装の問題でノイズを拾うようだと十分な性能を発揮できない恐れがある。

 

回路

データシートにある性能テスト用回路を、前回記事で紹介した電流・電圧インターフェイスおよびBBBに接続できるよう修正したものだ。

Ade7753_schema

Original Source: http://www.analog.com/jp/analog-to-digital-converters/energy-measurement/ade7753/products/product.html (ADE7753.pdf, p.15, Figure 30)

青緑色が修正した部分である。SPI以外にIRQとZX (ゼロクロス) 信号を使っている。ADE7753は5VロジックなのでBBBの入力につなぐ信号は2N7000を使ったレベル変換回路を入れた。この方式のレベル変換回路はMOS FETのVGS(th)が1.7V以上ないとBBB側のHレベルが3.3Vを超えてしまう。今回使ったものはどちらも約2.2V程度で問題なかったが、2N7000の仕様では最小値0.8Vなので個別に調べておかないと危ない。もしVGS(th)が2Vを下回っている様ならゲートを5Vではなく3.3Vに接続すれば良いはずである。

ちなみにBBBの出力をADE7753につなぐ部分は直結している。

3v35vdirect

この場合BBBのHレベル出力の最低値が、ADE7753が必要とするHレベル入力の最低値よりも0.45V高いから全く問題ない。このように3.3Vロジックの出力を5Vロジックの入力と直結しても問題ない場合が多いが、一応確認しておかないと危ない。

ADE7753はリードピッチ0.65mmのSSOPなのでピッチ変換基盤を使った。そんなに動作が速い回路ではないのであまり問題ないはずである。

 

BBBのSPI利用環境

いきなりADE7753をつないで試してうまく動かない場合、もしかするとBBBの環境に問題があるかもしれない。そうなってから慌てないよう予めBBBのSPIが問題なく使えていることを確かめておく。spidevドライバーは多くのLinuxディストリビューションでサポートされていると思うが、以下の手順がうまくいかない場合はサポートされていない可能性がある。

以前の記事「BeagleBone BlackのPRUを試す」と同じDebian 7.5 + カーネル3.8.13-bone28である。最近出てきたカーネル3.14.4系ではDevice Tree Overlayの扱いが変わった、と言うか、Cape Managerが無くなてしまったようなので、今後その方向に合わせる必要があるのかもしれないしかし3.8.13系カーネルも引き続きメンテナンスされている様なので、急いで移行する事も無さそうである。

先ずspi0デバイスをを使えるようにするためDebianイメージに含まれているDevice Tree OverlayをCape Managerに読ませる。

debian@arm:~$ ls /lib/firmware/*SPI*
/lib/firmware/BB-SPIDEV0-00A0.dtbo  /lib/firmware/BB-SPIDEV1-00A0.dtbo  /lib/firmware/BB-SPIDEV1A1-00A0.dtbo

debian@arm:~$ sudo bash -c 'echo BB-SPIDEV0 > /sys/devices/bone_capemgr.9/slots'
[sudo] password for debian: 

debian@arm:~$ ls /dev/spi*
/dev/spidev1.0  /dev/spidev1.1

debian@arm:~$

/dev/spidev1.0と/dev/spidev1.1は、データやクロックは共通でCS信号だけが分かれていたはずだ。ただしspi0_cs1信号はピンヘッダーに引き出されていないので使えるのは/dev/spidev1.0だけである。 次にテストプログラムをビルドして実行する。私の使った環境では /boot/uboot/tools/exp_bds/spidev/ にテストプログラムとビルド用スクリプトが入っていた。

debian@arm:~$ mkdir spidev_test
debian@arm:~$ cd spidev_test/
debian@arm:~/spidev_test$ cp /boot/uboot/tools/exp_bds/spidev/* ./
debian@arm:~/spidev_test$ ./build.sh 

spidev_test.c should output

spi mode: 0
bits per word: 8
max speed: 500000 Hz (500 KHz)

FF FF FF FF FF FF
40 00 00 00 00 95
FF FF FF FF FF FF
FF FF FF FF FF FF
FF FF FF FF FF FF
DE AD BE EF BA AD
F0 0D 

Beagle
sudo ./spitest -D /dev/spidev3.0
debian@arm:~/spidev_test$ sudo ./spitest -D /dev/spidev1.0
spi mode: 0
bits per word: 8
max speed: 500000 Hz (500 KHz)

FF FF FF FF FF FF 
FF FF FF FF FF FF 
FF FF FF FF FF FF 
FF FF FF FF FF FF 
FF FF FF FF FF FF 
FF FF FF FF FF FF 
FF FF 
debian@arm:~/spidev_test$ 

ビルド用スクリプトのメッセージと結果が違うが、これは入出力を接続してループバックしていないからである。プルアップされた入力からHレベルを読み込んでいるだけなので、これは異常ではない。またビルドツールがインストールされていないと自動的にダウンロード・インストールするようになっていた。 P9-18 (spi0_d1) とP9-21 (spi0_d0) を接続して実行すれば正しい結果が表示されるはずである。

debian@arm:~/spidev_test$ sudo ./spitest -D /dev/spidev1.0
spi mode: 0
bits per word: 8
max speed: 500000 Hz (500 KHz)

FF FF FF FF FF FF 
40 00 00 00 00 95 
FF FF FF FF FF FF 
FF FF FF FF FF FF 
FF FF FF FF FF FF 
DE AD BE EF BA AD 
F0 0D 
debian@arm:~/spidev_test$

spidev_test.cをインターネットからダウンロードしてきた場合、ビルドの際に以下のようなエラーが出るかもしれない。

spidev_test.c:60:13: error: 'SPI_TX_QUAD' undeclared (first use in this function)

これはspidev_test.cが新し過ぎてドライバーがサポートしていない機能を使おうとしているのが原因だ。少し古いバージョンのspidev_test.cを使えば解決するはずである。

 

ADE7753テスト用プログラム

spidev_test.cにハードコードされているテストデータを修正・ビルドして実行すればADE7753のテストに使えないことは無いが面倒である。spidev_test.cを下敷きにして、任意のテストデータをコマンドラインの引数で指定できるようなプログラムを作ってみた。

debian@arm:~/spidev_test$ cat ade7753test.c 
/*
 * ADE7753 testing utility
 *
 * Copyright (c) 2014  mitaka1954
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License.
 *
 */

#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>

#define ARRAY_SIZE 100

static void pabort(const char *s)
{
        perror(s);
        abort();
}

static const char *device = "/dev/spidev1.0";
static uint8_t mode = SPI_CPHA;
static uint8_t bits = 8;
static uint32_t speed = 200000;
static uint16_t delay = 0;
static uint8_t tx[ARRAY_SIZE] = {0, };
static uint8_t rx[ARRAY_SIZE] = {0, };
static int array_len;

static void transfer(int fd)
{
        int ret;
        struct spi_ioc_transfer tr = {
                .tx_buf = (unsigned long)tx,
                .rx_buf = (unsigned long)rx,
                .len = array_len,
                .delay_usecs = delay,
                .speed_hz = speed,
                .bits_per_word = bits,
        };

        ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
        if (ret < 1)
                pabort("can't send spi message");

        printf("Sent:       ");
        for (ret = 0; ret < array_len; ret++) {
                if (!(ret % 6) && ret!=0)
                        puts("");
                printf("0x%.2x ", tx[ret]);
        }
        puts("");
        printf("Received:   ");
        for (ret = 0; ret < array_len; ret++) {
                if (!(ret % 6) && ret!=0)
                        puts("");
                printf("0x%.2x ", rx[ret]);
        }
        puts("");
}

static void parse_opts(int argc, char *argv[])
{
        array_len = 0;
        while ((array_len+1) < argc)
                tx[array_len++] = strtol(argv[array_len+1], NULL, 16);
}

int main(int argc, char *argv[])
{
        int fd;

        parse_opts(argc, argv);

        fd = open(device, O_RDWR);
        if (fd < 0)
                pabort("can't open device");

        /* spi mode */
        if (ioctl(fd, SPI_IOC_WR_MODE, &mode) == -1)
                pabort("can't set spi wr mode");
        if (ioctl(fd, SPI_IOC_RD_MODE, &mode) == -1)
                pabort("can't get spi rd mode");

        /* bits per word */
        if (ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits) == -1)
                pabort("can't set wr bits per word");
        if (ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits) == -1)
                pabort("can't get rd bits per word");

        /* max speed hz */
        if (ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed) == -1)
                pabort("can't set wr max speed hz");
        if (ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed) == -1)
                pabort("can't get rd max speed hz");

        printf("spi mode: %d\n", mode);
        printf("bits per word: %d\n", bits);
        printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);
        transfer(fd);
        close(fd);

        return 0;
}

debian@arm:~/spidev_test$ make ade7753test
cc     ade7753test.c   -o ade7753test
debian@arm:~/spidev_test$ sudo ./ade7753test 9 0 0
spi mode: 1
bits per word: 8
max speed: 200000 Hz (200 KHz)
Sent:       0x09 0x00 0x00 
Received:   0x09 0x00 0x00 
debian@arm:~/spidev_test$

ループバックしたままだったのでコマンドラインで指定した送信データがそのまま受信されているが、これで正常である。なお引数で指定されたものはすべて16進データのつもりで解釈してしまう。これは自分で使うテストプログラムなので手抜きした。16進数に該当しない文字だとゼロに変換される。これはプログラムの出力で確認できる。またspidev_test.cの場合はオプションで指定する必要のあるDevice、SPI modeおよびSpeedはADE7753に適した値をハードコードしておいた。

AD7753を接続して正常に動いていれば、読み出しコマンドに対して返された応答を受信できるはずである。

debian@arm:~/spidev_test$ sudo ./ade7753test 9 0 0
spi mode: 1
bits per word: 8
max speed: 200000 Hz (200 KHz)
Sent:       0x09 0x00 0x00 
Received:   0xff 0x00 0x0c 
debian@arm:~/spidev_test$ sudo ./ade7753test 89 70 c
spi mode: 1
bits per word: 8
max speed: 200000 Hz (200 KHz)
Sent:       0x89 0x70 0x0c 
Received:   0xff 0xff 0xff 
debian@arm:~/spidev_test$ sudo ./ade7753test 9 0 0
spi mode: 1
bits per word: 8
max speed: 200000 Hz (200 KHz)
Sent:       0x09 0x00 0x00 
Received:   0xff 0x70 0x0c 
debian@arm:~/spidev_test$

ディフォルト状態のMODEレジスターを読み取り、値を書き込んだ後再び読み取ってみた。ADE7753から返されるデータに見合ったバイト数のダミーデータを送ってやる必要がある。上では値をゼロにしているが読まれないので何でもよい。またADE7753がデータを返さないバイトは0xFFが受信される。

先ず最初にCHKSUMレジスターを使ってSPIインターフェイスに異常がないか確かめておくと良いだろう。

debian@arm:~/spidev_test$ sudo ./ade7753test 9 0 0
spi mode: 1
bits per word: 8
max speed: 200000 Hz (200 KHz)
Sent:       0x09 0x00 0x00 
Received:   0xff 0x70 0x0c 
debian@arm:~/spidev_test$ sudo ./ade7753test 3e 0
spi mode: 1
bits per word: 8
max speed: 200000 Hz (200 KHz)
Sent:       0x3e 0x00 
Received:   0xff 0x05 
debian@arm:~/spidev_test$ sudo ./ade7753test 3e 0
spi mode: 1
bits per word: 8
max speed: 200000 Hz (200 KHz)
Sent:       0x3e 0x00 
Received:   0xff 0x02 
debian@arm:~/spidev_test$

アドレス3EがCHKSUMレジスターである。最初の読出しで0x06が返ってきているが、これはその直前にADE7753が送り出したデータ0x70 0x0c = 01110000b 00001100bに含まれていた「1」ビットの数である。2回目のCHKSUMレジスター読出しではその直前に返した0x06 = 00000110bに含まれていた「1」ビットの数0x02が正しく返ってきている。これでSPIインターフェイスは一応異常なく動いていることが分かる。

 

アナログ部分を含めた動作確認

今回紹介した回路でディフォルト設定のADE7753を使った場合、IRMSおよびVRMSレジスターから読みだされる値は以下の式で求められる。

  • IRMS = 0x19cdf x 電流値[A] = 105695 x 電流値[A]
  • VRMS = 0x2564 x 電圧値[V] = 9572 x 電圧値[V]  (50Hz)
  • VRMS = 0x247e x 電圧値[V] = 9342 x 電圧値[V]  (60Hz)

これらの係数の計算については別の機会に触れたいと思う。

消費電力と電流が明らかな負荷、例えば100V 90Wの白熱電球を接続して値を読んでみよう。

debian@arm:~/spidev_test$ sudo ./ade7753test 16 0 0 0
spi mode: 1
bits per word: 8
max speed: 200000 Hz (200 KHz)
Sent:       0x16 0x00 0x00 0x00 
Received:   0xff 0x01 0x71 0xab 
debian@arm:~/spidev_test$ sudo ./ade7753test 17 0 0 0
spi mode: 1
bits per word: 8
max speed: 200000 Hz (200 KHz)
Sent:       0x17 0x00 0x00 0x00 
Received:   0xff 0x0e 0xcd 0x67 
debian@arm:~/spidev_test$

電流0.9A、電圧100Vなので、先の係数を用いると電流の読み取り値は 105695 x 0.9 = 95125.5、そして電圧は50Hz地域なので 9572 x 100 =  957200になるはずである。実際に読み取った値は電流0x0171ab = 94635、電圧0x0ecd67 = 970087だ。それぞれあらかじめ計算しておいた値の99.5%と101.3%である。

使っているパーツの誤差が最大5%、rms値のリップルが数%ある可能性が考えられるので10%程度の差異は想定範囲内だ。99.5%と101.3%は少し気持ち悪いくらいピッタリである。

これでADE7753の動作確認完了。正常に動作している様である。今回はここまで。

 

今回のまとめ

今回のような専用用途ICがある場合、使わないのはもったいない。全く同じことをマイクロコントローラーだけで実現するのはかなり難しいだろう。仮にできるとしても、消費電力やコストで不利になる可能性が高い。

ただしここまでの性能は必要ない。安いマイクロコントローラーで実現できる範囲の機能で構わない、と言う場合もあるだろう。これはまた別の話だ。シンプルな構成でどこまでできるか、挑戦してみるのも面白いと思う。

今回使ったADE7753はそんなに高価なデバイスではないが結構機能が多い。と言ってもできることは電流・電圧・電力などを測ることだけだが、色々と調整できる項目が多いのだ。ほとんどはディフォルトから変更する必要は無いが、「ある機能を使うにはこの設定をする必要がある」的な項目が落とし穴だ。データシートを通して読む必要がある。しかし決して読み易くない。分かり切った基礎理論が多くて、利用する上で必須の情報が埋もれてしまっているのだ。それに一貫性に欠ける印象や、必要な情報が欠落している印象を受ける。読み方が悪いのかもしれないが・・・。

次回はそんなデータシートと格闘した結果を報告する。レジスターの読み取り値の換算係数の求め方など、このデバイスを使う上でキモとなる情報を紹介したいと思っている。

|

« BeagleBone Black Rev. C の供給状況 | トップページ | ワットチェッカー的なものを自作する (その3) »

BeagleBone Black」カテゴリの記事

パソコン・インターネット」カテゴリの記事

趣味」カテゴリの記事

コメント

コメントを書く



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


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



トラックバック

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

この記事へのトラックバック一覧です: ワットチェッカー的なものを自作する (その2):

« BeagleBone Black Rev. C の供給状況 | トップページ | ワットチェッカー的なものを自作する (その3) »