電力測定をどう手軽に実現するか

普通ならばPSoC 4のADCを使いたいところだし、実際精度とかサンプリング速度ではADCを使うほうが有利であるが、問題は測定回路とのアイソレーションをどうするかで、これが結構悩みの種である。電圧はまだしも、電流のほうの測定がかなり面倒である。そこで今回は測定回路をアイソレーションし、しかも結果をI2C経由で返してくれるモジュールを使ってこのあたりを逃げることにした。そんな訳で、I2Cの使い方サンプルと考えていただきたい。

さて、測定モジュールはI2C Slaveということになるので、PSoC 4の方はI2C Masterとして構成する(Photo35)。

Photo35:デフォルトだとこれがSlaveになっている。Slave同士の通信は出来ないので、Masterとして設定する必要がある

ピンはP0[4]とP0[5]をそれぞれ割り当てた。配線の注意点としては、I2CではSCL/SDAの信号線はプルアップする必要があるが、PSoC 4では標準でそういう機能がないので、外部にプルアップ抵抗を忘れないことだ(10KΩ×2)。これを忘れると信号が変わってしまい、通信できなくなる。また測定モジュールに繋ぐUSBの方は、適当なUSB延長ケーブルを2つにぶった切り、信号線はそのまま繋ぎなおし、VBus(+5V)とGNDのみ、図1の様に引っ張って繋いでいる。

さてプログラムの方であるが、初期設定そのものは

    I2C_Start();                 // I2C Master初期化

を呼び出すものだ。次のCalibrateI2C()は後述する。実際の通信は、というとInquireI2C()という関数の中にまとめてある。これはモジュールから電圧/電流/電力の3つを読み出す際に毎回同じ処理を繰り返すので、まとめたほうが楽という配慮からだ。

手順は簡単で、

    I2C_I2CMasterWriteBuf(I2C_SLAVE_BASEADDR, (uint8 *)&I2CWriteBuf[0],
                      01u, I2C_I2C_MODE_COMPLETE_XFER);

を使い、モジュールのアドレス(I2C_SLAVE_BASEADDR)と、読み出したいレジスタ番号(I2CWriteBuf[0])をまず送出する。これをSlaveが受け取ったことを

    // Slaveの受信待ち
    while(0u == (I2C_I2CMasterStatus() & I2C_I2C_MSTAT_WR_CMPLT));

で確認、一度

    // I2C Masterのステータスクリア
    (void)I2C_I2CMasterClearStatus();

でステータスをクリアしたあとで、今度は結果を

    // I2C Slaveより結果受信
    I2C_I2CMasterReadBuf(I2C_SLAVE_BASEADDR, (uint8 *)&I2CReadBuf[0],
                     02u, I2C_I2C_MODE_COMPLETE_XFER);

として受け取る。ただこれはまだリクエストを出しただけで、制御が帰ってきた≠受信できた、なので

    // 受信完了まで待機
    while(0u == (I2C_I2CMasterStatus() & I2C_I2C_MSTAT_RD_CMPLT));

として受信完了を待つ。最後に

// I2C Masterのステータスクリア
(void)I2C_I2CMasterClearStatus();

で処理完了だが、今回の場合モジュールからの返答は16bit固定であり、一方I2Cライブラリは8bit単位での受信となるので

return (uint32)(I2CReadBuf[0]*256+I2CReadBuf[1]);

として16bit値で返すようにしている。

ところで先ほど出てきたCalibrateI2C()という関数だが、これは測定モジュール特有の処理で、一度レジスタ5番にキャリブレーション値(0x0500)をセットしないと、電流/電力値が0のままになるという癖があるため、これを設定するためのロジックである。

4つのLEDとボタンを連動

今回はLEDを4つ駆動する。これはCapSenseでボタンを4つ用意するのに対応している。LEDそのものは、これも秋月電子通商で大量売りしているOSDR3133Aを使っている。このLEDは2.0Vで20mAなので、3.3V出力のピンに繋ぐには65Ωほどの分圧抵抗を入れればよい計算になるが、経験的に100Ω程度でも問題ないということで、今回は100Ωを使ってつないでいる。

さてプログラムの方であるが、Photo30にあるようにLED1~LED4は「単なるDigital I/Oピン」とPSoC 4は認識しており、なので生成したソースを見ると例えばLED1に対しては

  • void LED1_Write(uint8 value)
  • void LED1_SetDriveMode(uint8 mode)
  • uint8 LED1_Read(void)
  • uint8 LED1_ReadDataReg(void)
  • uint8 LED1_ClearInterrupt(void)

などの関数が用意されるが、今回使うのはLEDx_Write()(x:1~4)のみである。Digital出力なので、1を出力すれば信号がHigh、つまり3.3Vが出るので点灯、0を出力すれば信号がLow=0Vなので消灯となる仕組みだ。というわけで、プログラム中でも

    LED2_Write(1u);

といった具合に記述している。