INA226をやっとハンダ付け

随分前にストロベーリーリナックスで購入したINA2226に端子をハンダ付けしました。
手持ちで豊富にあるLPC1114版mbedでプログラムを書いてみた。
仕様は以下の通り

  1. USB接続機器のデータを計測できること
  2. LPC1114版mbedを利用
  3. LCDは秋月の8×2 I2C LCD
  4. SDカードにデータを記録
  5. UARTにて制御・データ収集できること

これらの機能を含んだソースを大まかに書いてコンパイルしたところ、32K以内にプログラムが収まった。
LPC1114で製作決定である。

INA226ドライバ

INA226は電圧が高くなったり低くなったり電流が多くなったり少なくなったりしたときに、アラート信号を発することができる。今回は未実装であるがmbed用に書いたドライバが動くようになった。
mbed版はすべて出来上がってから公開ということで、TinyI2CMaster対応版を書いてみた。

mbed版は浮動小数点演算をやってるが、8ビットなので使ってない。これは妥協の電子工作さんのINA226の試用(USB電流計を作る)のプログラムを参考にさせてもらいました。

//========================================================================
// File Name    : INA226.h
//
// Title        : INA226 ドライバ・ヘッダファイル
// Revision     : 0.1
// Notes        :
// Target MCU   : AVR ATtiny series
// Tool Chain   : AVR toolchain Ver3.4.1.1195
//
// Revision History:
// When         Who         Description of change
// -----------  ----------- -----------------------
// 2014/09/28   ばんと      製作開始
//------------------------------------------------------------------------
// This code is distributed under Apache License 2.0 License
//      which can be found at http://www.apache.org/licenses/
//========================================================================

#ifndef __INA226_H_
#define __INA226_H_

/* define --------------------------------------------------------------*/
/* INA226 シャント抵抗値定義(mΩ)                     */
#define INA226_SHUNT_R              2

/*                                                      */
/* CalibrationRegister設定値定義(スケーリング用)        */
/* Current = ShuntVoltage * CalibrationRegister         */
/*                                                      */
#define INA226_CALIBRATION ( 2.5 * 2048 / INA226_SHUNT_R )

/* スレーブアドレス定義                                 */
/* A1,A0端子の電圧によって16種類のアドレスを設定可能    */
#define INA226_I2C_ADDR             0x40    /* A1=GND ,A0=GND   */
//#define INA226_I2C_ADDR           0x41    /* A1=GND ,A0=VS+   */
//#define INA226_I2C_ADDR           0x42    /* A1=GND ,A0=SDA   */
//#define INA226_I2C_ADDR           0x43    /* A1=GND ,A0=SCL   */
//#define INA226_I2C_ADDR           0x44    /* A1=VS+ ,A0=GND   */
//#define INA226_I2C_ADDR           0x45    /* A1=VS+ ,A0=VS+   */
//#define INA226_I2C_ADDR           0x46    /* A1=VS+ ,A0=SDA   */
//#define INA226_I2C_ADDR           0x47    /* A1=VS+ ,A0=SCL   */
//#define INA226_I2C_ADDR           0x48    /* A1=SDA ,A0=GND   */
//#define INA226_I2C_ADDR           0x49    /* A1=SDA ,A0=VS+   */
//#define INA226_I2C_ADDR           0x4a    /* A1=SDA ,A0=SDA   */
//#define INA226_I2C_ADDR           0x4b    /* A1=SDA ,A0=SCL   */
//#define INA226_I2C_ADDR           0x4c    /* A1=SCL ,A0=GND   */
//#define INA226_I2C_ADDR           0x4d    /* A1=SCL ,A0=VS+   */
//#define INA226_I2C_ADDR           0x4e    /* A1=SCL ,A0=SDA   */
//#define INA226_I2C_ADDR           0x4f    /* A1=SCL ,A0=SCL   */

// INA226 Registers
#define INA226_REG_CONGIGURATION                0x00    // Configuration Register (R/W)
#define INA226_REG_SHUNT_VOLTAGE                0x01    // Shunt Voltage (R)
#define INA226_REG_BUS_VOLTAGE                  0x02    // Bus Voltage (R)
#define INA226_REG_POWER                        0x03    // Power (R)
#define INA226_REG_CURRENT                      0x04    // Current (R)
#define INA226_REG_CALIBRATION                  0x05    // Calibration (R/W)
#define INA226_REG_MASK_ENABLE                  0x06    // Mask/Enable (R/W)
#define INA226_REG_ALERT_LIMIT                  0x07    // Alert Limit (R/W)
#define INA226_REG_DIE_ID                       0xFF    // Die ID (R)

// Operating Mode (Mode Settings [2:0])
#define INA226_CONF_MODE_POWER_DOWN             (0<<0)  // Power-Down
#define INA226_CONF_MODE_TRIG_SHUNT_VOLTAGE     (1<<0)  // Shunt Voltage, Triggered
#define INA226_CONF_MODE_TRIG_BUS_VOLTAGE       (2<<0)  // Bus Voltage, Triggered
#define INA226_CONF_MODE_TRIG_SHUNT_AND_BUS     (3<<0)  // Shunt and Bus, Triggered
#define INA226_CONF_MODE_POWER_DOWN2            (4<<0)  // Power-Down
#define INA226_CONF_MODE_CONT_SHUNT_VOLTAGE     (5<<0)  // Shunt Voltage, Continuous
#define INA226_CONF_MODE_CONT_BUS_VOLTAGE       (6<<0)  // Bus Voltage, Continuous
#define INA226_CONF_MODE_CONT_SHUNT_AND_BUS     (7<<0)  // Shunt and Bus, Continuous (default)

// Shunt Voltage Conversion Time (VSH CT Bit Settings [5:3])
#define INA226_CONF_VSH_140uS                   (0<<3)  // 140us
#define INA226_CONF_VSH_204uS                   (1<<3)  // 204us
#define INA226_CONF_VSH_332uS                   (2<<3)  // 332us
#define INA226_CONF_VSH_588uS                   (3<<3)  // 588us
#define INA226_CONF_VSH_1100uS                  (4<<3)  // 1.1ms (default)
#define INA226_CONF_VSH_2116uS                  (5<<3)  // 2.116ms
#define INA226_CONF_VSH_4156uS                  (6<<3)  // 4.156ms
#define INA226_CONF_VSH_8244uS                  (7<<3)  // 8.244ms

// Bus Voltage Conversion Time (VBUS CT Bit Settings [8:6])
#define INA226_CONF_VBUS_140uS                  (0<<6)  // 140us
#define INA226_CONF_VBUS_204uS                  (1<<6)  // 204us
#define INA226_CONF_VBUS_332uS                  (2<<6)  // 332us
#define INA226_CONF_VBUS_588uS                  (3<<6)  // 588us
#define INA226_CONF_VBUS_1100uS                 (4<<6)  // 1.1ms (default)
#define INA226_CONF_VBUS_2116uS                 (5<<6)  // 2.116ms
#define INA226_CONF_VBUS_4156uS                 (6<<6)  // 4.156ms
#define INA226_CONF_VBUS_8244uS                 (7<<6)  // 8.244ms

// Averaging Mode (AVG Bit Settings[11:9])
#define INA226_CONF_AVG_1                       (0<<9)  // 1 (default)
#define INA226_CONF_AVG_4                       (1<<9)  // 4
#define INA226_CONF_AVG_16                      (2<<9)  // 16
#define INA226_CONF_AVG_64                      (3<<9)  // 64
#define INA226_CONF_AVG_128                     (4<<9)  // 128
#define INA226_CONF_AVG_256                     (5<<9)  // 256
#define INA226_CONF_AVG_512                     (6<<9)  // 512
#define INA226_CONF_AVG_1024                    (7<<9)  // 1024

// Reset Bit (RST bit [15])
#define INA226_CONF_RESET_ACTIVE                (1<<15)
#define INA226_CONF_RESET_INACTIVE              (0<<15)

/* typedef -------------------------------------------------------------*/
/* macro ---------------------------------------------------------------*/
/* variables -----------------------------------------------------------*/
/* function prototypes -------------------------------------------------*/
uint8_t TinyI2C_readRegister16( uint8_t slave_7bit_addr, uint8_t mem_addr, uint16_t *data );
uint8_t TinyI2C_writeRegister16( uint8_t slave_7bit_addr, uint8_t mem_addr, uint16_t data );

uint8_t INA226_reset(void);
uint8_t INA226_configure(uint16_t config);
uint8_t INA226_calibrate(void);
uint8_t INA226_readShuntVoltage(uint32_t *volt);
uint8_t INA226_readBusVoltage(uint32_t *volt);
uint8_t INA226_readPower(uint32_t *power);
uint8_t INA226_readCurrent(uint32_t *current);

uint8_t INA226_getDieID(uint16_t *id);

#endif  /*  #ifndef */
//========================================================================
// File Name    : INA226.c
//
// Title        : INA226 ドライバ・ソースファイル
// Revision     : 0.1
// Notes        :
// Target MCU   : AVR ATtiny series
// Tool Chain   : AVR toolchain Ver3.4.1.1195
//
// Revision History:
// When         Who         Description of change
// -----------  ----------- -----------------------
// 2014/09/28   ばんと      製作開始
//------------------------------------------------------------------------
// This code is distributed under Apache License 2.0 License
//      which can be found at http://www.apache.org/licenses/
//========================================================================

/* Includes ------------------------------------------------------------*/
#include <avr/io.h>
#include "delay.h"
#include "TinyI2CMaster.h"
#include "INA226.h"

/* local define --------------------------------------------------------*/
/* local typedef -------------------------------------------------------*/
/* local macro ---------------------------------------------------------*/
/* local variables -----------------------------------------------------*/
/* local function prototypes -------------------------------------------*/

/* [ここからソース] ==================================================== */

//========================================================================
//  16ビットレジスタ読み込み
//------------------------------------------------------------------------
// 引数: uint8_t slave_7bit_addr : ターゲットの7ビットアドレス
//       uint8_t mem_addr        : レジスタのメモリアドレス
//       uint16_t* data          : 読み込むデータ
// 戻値: 0=正常終了 それ以外I2C通信エラー
//========================================================================
uint8_t TinyI2C_readRegister16( uint8_t slave_7bit_addr, uint8_t mem_addr, uint16_t *data )
{
    uint8_t status;
    uint8_t buff[2];

    // アドレス送信
    status = TinyI2C_write_data(slave_7bit_addr, &mem_addr, 1, NO_SEND_STOP);
    if(status != TINYI2C_NO_ERROR)
    {
        return status;
    }

    status = TinyI2C_read_data(slave_7bit_addr, buff, 2, SEND_STOP);
    if(status != TINYI2C_NO_ERROR)
    {
        return status;
    }

    *data = (uint16_t)(buff[0]);
    *data = (*data << 8) | (uint16_t)(buff[1]);

    return status;
}

//========================================================================
//  16ビットレジスタ書き出し
//------------------------------------------------------------------------
// 引数: uint8_t slave_7bit_addr : ターゲットの7ビットアドレス
//       uint8_t mem_addr        : レジスタのメモリアドレス
//       uint16_t data           : 設定データ
// 戻値: 0=正常終了 それ以外I2C通信エラー
//========================================================================
uint8_t TinyI2C_writeRegister16( uint8_t slave_7bit_addr, uint8_t mem_addr, uint16_t data )
{
    uint8_t buff[3];

    buff[0] = mem_addr;
    buff[1] = (uint8_t)((data >> 8) & 0x00ff);
    buff[2] = (uint8_t)(data & 0x00ff);

    return TinyI2C_write_data( slave_7bit_addr, buff, 3, SEND_STOP );
}

//========================================================================
//  リセット
//------------------------------------------------------------------------
// 引数: なし
// 戻値: 0=正常終了 それ以外I2C通信エラー
//========================================================================
uint8_t INA226_reset(void)
{
    return TinyI2C_writeRegister16( INA226_I2C_ADDR, INA226_REG_CONGIGURATION, INA226_CONF_RESET_ACTIVE );
}

//========================================================================
//  コンフィグレジスタの設定
//------------------------------------------------------------------------
// 引数: uint16_t config         : コンフィグデータ
// 戻値: 0=正常終了 それ以外I2C通信エラー
//========================================================================
uint8_t INA226_configure(uint16_t config)
{
    return TinyI2C_writeRegister16( INA226_I2C_ADDR, INA226_REG_CONGIGURATION, config );
}

//========================================================================
//  キャリブレーション
//------------------------------------------------------------------------
// 引数: なし
// 戻値: 0=正常終了 それ以外I2C通信エラー
//========================================================================
uint8_t INA226_calibrate(void)
{
    return TinyI2C_writeRegister16( INA226_I2C_ADDR, INA226_REG_CALIBRATION, INA226_CALIBRATION );
}


//========================================================================
//  シャント抵抗の電圧取得
//------------------------------------------------------------------------
// 引数: uint32_t* volt       : 取得する電圧(uV)
// 戻値: 0=正常終了 それ以外I2C通信エラー
//------------------------------------------------------------------------
// 【補註】
//  プログラムサイズ増加回避の為、浮動小数点演算を使ってない
//========================================================================
uint8_t INA226_readShuntVoltage(uint32_t *volt)
{
    uint16_t val;
    uint8_t status;

    status = TinyI2C_readRegister16( INA226_I2C_ADDR, INA226_REG_SHUNT_VOLTAGE, &val );
    if(status != TINYI2C_NO_ERROR)
    {
        return status;
    }
    *volt = (uint32_t)val * 250UL / 100UL;  /* 1LSB=2.5uV           */

    return status;
}


//========================================================================
//  バスの電圧取得
//------------------------------------------------------------------------
// 引数: uint32_t* volt       : 取得する電圧(単位:mV)
// 戻値: 0=正常終了 それ以外I2C通信エラー
//------------------------------------------------------------------------
// 【補註】
//  プログラムサイズ増加回避の為、浮動小数点演算を使ってない
//========================================================================
uint8_t INA226_readBusVoltage(uint32_t *volt)
{
    uint16_t val;
    uint8_t status;

    status = TinyI2C_readRegister16( INA226_I2C_ADDR, INA226_REG_BUS_VOLTAGE, &val );
    if(status != TINYI2C_NO_ERROR)
    {
        return status;
    }
    *volt = (uint32_t)val * 125UL / 100UL;  /* 1LSB=1.25mV          */

    return status;
}


//========================================================================
//  電力取得
//------------------------------------------------------------------------
// 引数: uint32_t* power      : 取得する電力(mW)
// 戻値: 0=正常終了 それ以外I2C通信エラー
//------------------------------------------------------------------------
// 【補註】
//  プログラムサイズ増加回避の為、浮動小数点演算を使ってない
//========================================================================
uint8_t INA226_readPower(uint32_t *power)
{
    uint16_t val;
    uint8_t status;

    status = TinyI2C_readRegister16( INA226_I2C_ADDR, INA226_REG_POWER, &val );
    if(status != TINYI2C_NO_ERROR)
    {
        return status;
    }

    if ( val & 0x8000 ) 
	{ 
		*power = 0UL;   /* マイナス時は強制的に0*/
	}
	else
	{
		*power =  (uint32_t)val * 25UL; /* 1step=25mW           */
	}
	
    return status;
}


//========================================================================
//  電流取得
//------------------------------------------------------------------------
// 引数: uint32_t* current    : 取得する電流(mA)
// 戻値: 0=正常終了 それ以外I2C通信エラー
//------------------------------------------------------------------------
// 【補註】
//  プログラムサイズ増加回避の為、浮動小数点演算を使ってない
//========================================================================
uint8_t INA226_readCurrent(uint32_t *current)
{
	uint16_t val;
    uint8_t status;

    status = TinyI2C_readRegister16( INA226_I2C_ADDR, INA226_REG_POWER, &val );
    if(status != TINYI2C_NO_ERROR)
    {
        return status;
    }

    if ( val & 0x8000 )
	{
		*current = 0UL;    /* マイナス時は強制的に0*/
	}
	else
	{
		*current = (uint32_t)val;
	}

    return status;
}

//========================================================================
//  DIE ID取得
//------------------------------------------------------------------------
// 引数: uint32_t* id    : 取得するID
// 戻値: 0=正常終了 それ以外I2C通信エラー
//========================================================================
uint8_t INA226_getDieID(uint16_t *id)
{
    return TinyI2C_readRegister16( INA226_I2C_ADDR, INA226_REG_DIE_ID, id );
}

/* =====================================================[ここまでソース] */

メインをここまで書いてコンパイルしてみた。RTCと違いドライバは書き込んでないので、非常にコンパクトだ。もしかしたらATtiny2313でもLCD表示だけなら製作できるかもしれません。

/*
 * INA226テストプログラム
 *
 * Created: 2014/09/29 20:07:50
 *  Author: bant
 */ 

#include <avr/io.h>
#include "TinyI2CMaster.h"
#include "ST7032i.h"
#include "INA226.h"
#include "xitoa.h"
#include "delay.h"


int main(void)
{
	char buf[10];
	
    TinyI2C_Master_init();

    ST7032i_Init();
    ST7032i_setContrast(30);

	INA226_reset();
	INA226_configure(	INA226_CONF_RESET_INACTIVE 			|
						INA226_CONF_MODE_CONT_SHUNT_AND_BUS	|
						INA226_CONF_VSH_1100uS				|
						INA226_CONF_VBUS_1100uS				|
						INA226_CONF_AVG_64 );
	INA226_calibrate();

    while(1)
    {

    }
}

INA226はレジスタ参照・書き込みで制御やデータの受け渡しするよくあるタイプなんだが、8ビットレジスタではなく16ビットレジスタなんだよね。
整合性を高めるためTinyI2CMasterライブラリィを改良する必要があるかも…