INA219　電流電力計測ICの設定　with PIC16F18325

# シャント抵抗の両端をAD変換して、電流と電圧、電力値を計測表示してくれるIC　INA219まとめ
データシートは、２０１１年度版を参考にしました。２０１５年版のデータシートが最新の内容になっています。


![IMG_4996.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3693934/08486b44-4d72-685f-a2c4-88e3305bda40.jpeg)
チップ抵抗の値は、文字R100のRは小数点を表すので、0.1Ωになります。INA219は、この抵抗の両端の電圧差を計測して、電流を計算してくれるICです。

![IMG_4997.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3693934/bbb115fe-b31f-b016-55a6-1766df40064e.jpeg)

# PIC16F18325を使用。

I2C通信でINA219の測定値を取得し、液晶に表示します。
![IMG_4995.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3693934/600827cb-69bf-d04e-22ae-a206fd95e370.jpeg)

![IMG_5002.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3693934/fb95c55c-de39-d7e4-8028-589ed5352619.jpeg)

回路図
![INA219回路図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3693934/83dd21f3-f6b5-cf87-6156-5a6605000f9c.png)


データシートを読み込むわけですが、結構難解でした。

まず、データーシートのP.17，18ページでキャリブレーション値を計算しておきます。
![datasheetCal.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3693934/daa1d852-1f29-4272-509a-50f54e6d568c.png)

シャント抵抗値は0.1Ω
測定電圧の最大値は32V
電流値の最大は、3.2Aとして計算しました。
※最大電流値をもっと少なくして、たとえば１Aで計算しなおせば、精度が上がります。
![IMG_5004.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3693934/3e0adf12-3e7b-5d80-65ee-5954a1d98495.jpeg)


:::note
# PICマイコン側で、電流値、電力値を求めるときに必要な値。
上記で求めた値と、バス電圧のLSBを押さえておきます。

**電流値のLSB　Current_LSB:98uA
キャリブレーション値　Cal:4180(0x1054)
電力値のLSB　Power_LSB:1960uW(約2mWとして計算）
バス電圧のLSB 4mV(datasheetより）**

INA219がPICマイコンに返信してくる16ビットは、電流値、電圧値、電力値をスケーリングしてプロットした値なので、**ただの相対値**になっています。この相対値にLSB値を掛けると、欲しい値、電流値等になります。
:::

::: note
**INA219 busVoltage Resister(02h)の値にLSB 4ｍVを、
INA219 Power Resister(03h)の値に、LSB 2mWを、
INA219 Current Resister(04h)の値に、LSB 98uAをかけます。**

buVoltageレジスタの単位は、ｍV
Power レジスタの単位は、ｍW
Current レジスタの単位は、uA
なので、実際の物理量である電流量などは、マイコン側で、LSB値をかけて、計算しています。
:::

```C:計測値の計算
//*****************************************************
//INA219データ計算
//_add:INA219　I2Cスレーブアドレス
//_reg:INA219　レジスタアドレス
//呼び出したデータの種類によってLSB値を掛ける。
//*****************************************************
uint16_t INA219_RegRead(uint8_t _add, uint8_t _reg)
{
    uint16_t ltmp;
    uint32_t lval;
    
    ltmp=I2C1_b2Read(_add,_reg);
    
    switch(_reg)
    {   case 0x02://BusVoltage
            ltmp>>=3;//レジスタ下位3ビットはいらないのでシフト。
            ltmp*=4L;//LSB:4mV
            ltmp/=10;//表示調整
            unit[0]='V';
            break;
        case 0x03://Power
            lval = ltmp;
            lval=lval*20L;//PowerLSB=1.960mW(約２ｍW） 小数点第1位まで求める。
                          //LSB:2mWを、10倍（表示調整）しておく。
            if(lval>10000)
            {//1000mW以上の時の液晶表示単位
                lval/=1000;
                unit[0]='W';
                unit[1]=' ';
            }
            else
            {//1000mW以下の時の液晶表示単位
                unit[0]='m';
                unit[1]='W';  
            }
            ltmp=lval;
            break;
        case 0x04://current
            lval=ltmp*98L;//LSB:98uA
            ltmp=lval/1000L;
           
            unit[0]='m';
            unit[1]='A';
            
            break;
    }
    return ltmp;   
}


 
```
:::note
# INA219の初期化
INA219のレジスタは00hから05hまでの6個だけです。以下の2つのレジスタの値を設定します。

**Configration Register(00h)を0x3E67で初期化。**
BusVoltageRangeを32Vに設定。PGA(ShuntVoltage only)を'÷８'0b11に設定。
SADCとBADCは、0b1100（16回平均)に設定。
modeは、0b111で、Shunt and Bus,Continuousに設定。

**Calibration Register(05h)に上記で計算した値4180(0x1054)をセット**


:::


```main.c

// PIC16F18325 Configuration Bit Settings

// 'C' source line config statements

// CONFIG1
#pragma config FEXTOSC = OFF    // FEXTOSC External Oscillator mode Selection bits (Oscillator not enabled)
#pragma config RSTOSC = HFINT1  // Power-up default value for COSC bits (HFINTOSC (1MHz))
#pragma config CLKOUTEN = OFF   // Clock Out Enable bit (CLKOUT function is disabled; I/O or oscillator function on OSC2)
#pragma config CSWEN = ON      // Clock Switch Enable bit (The NOSC and NDIV bits cannot be changed by user software)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is disabled)

// CONFIG2
#pragma config MCLRE = ON     // Master Clear Enable bit (MCLR/VPP pin function is digital input; MCLR internally disabled; Weak pull-up under control of port pin's WPU control bit.)
#pragma config PWRTE = OFF       // Power-up Timer Enable bit (PWRT enabled)
#pragma config WDTE = OFF       // Watchdog Timer Enable bits (WDT disabled; SWDTEN is ignored)
#pragma config LPBOREN = OFF    // Low-power BOR enable bit (ULPBOR disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable bits (Brown-out Reset disabled)
#pragma config BORV = LOW       // Brown-out Reset Voltage selection bit (Brown-out voltage (Vbor) set to 2.45V)
#pragma config PPS1WAY = ON     // PPSLOCK bit One-Way Set Enable bit (The PPSLOCK bit can be cleared and set only once; PPS registers remain locked after one clear/set cycle)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable bit (Stack Overflow or Underflow will cause a Reset)
#pragma config DEBUG = OFF      // Debugger enable bit (Background debugger disabled)

// CONFIG3
#pragma config WRT = OFF        // User NVM self-write protection bits (Write protection off)
#pragma config LVP = ON        // Low Voltage Programming Enable bit (High Voltage on MCLR/VPP must be used for programming.)

// CONFIG4
#pragma config CP = OFF         // User NVM Program Memory Code Protection bit (User NVM code protection disabled)
#pragma config CPD = OFF        // Data NVM Memory Code Protection bit (Data NVM code protection disabled)

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.



#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include "myLibrary18325.h"

#include "I2C_MSSP1.h"
#include "SSD1306.h"
#include "INA219.h"

#define LED2 RA2

#define nonPrint

uint8_t val;
uint8_t flag;
void Port_init(void);

void delay10ms(uint16_t num);
void SSD1306_Print(uint16_t ival);
void SSD1306_PrintS(uint16_t sval,uint16_t cal);
void SSD1306_Print2(uint16_t ival);
void SSD1306_Print3(uint16_t ival);
uint8_t ioc_flag;
uint8_t ioc_val;

void __interrupt()isr()
{  
    if(PIR0bits.TMR0IF)
    {     
        tm0.cnt[0]++;
       
               
        if(tm0.cnt[0]==3)
        {
            tm0.up=T0_UP;
            
            tm0.cnt[0]=0;
        }else
        {
            TMR0L=0x61;//100ms
            PIR0bits.TMR0IF=0;
        } 
    }      
}


int main(int argc, char** argv)
{
  
    uint8_t ret,tmp[3];
    uint16_t val16[6];
    
    uint32_t longval;
    int32_t longval1;
    //Oscillator
    OSCCON1bits.NOSC=0b110;
    OSCCON1bits.NDIV=0b000;
    OSCFRQbits.HFFRQ=0b101; //100:8MHz 101:12Mhz
    __delay_ms(500);
    //initial--------------------------------------
    Port_init();
    I2C1_Reg_Init();
    Timer0_INIT();
    SSD1306_initialize();
            
    //Configration Resistor------------------------
    //32VRange, /8, 12bitsADC
    tmp[0]=0x00;
    tmp[1]=0x3E;//(24.9.30)
    tmp[2]=0x67;
    tmp[0]=I2C1_bnWrite(INA219add,tmp,3);
    
    //Caribration Resistor-------------------------
    tmp[0]=0x05;
    
#define MaxCurrent3_2Amax2
    
#ifdef MaxCurrent1Amax
    tmp[1]=0x33;
    tmp[2]=0x9C;
#endif    
    
#ifdef MaxCurrent2Amax
    tmp[1]=0x1A;
    tmp[2]=0x3A;
#endif
#ifdef MaxCurrent3Amax
    tmp[1]=0x11;
    tmp[2]=0x79;
#endif
#ifdef MaxCurrent3_2Amax
    tmp[1]=0x10;
    tmp[2]=0x54;
#endif
#ifdef MaxCurrent3_2Amax2
    tmp[1]=0x10;
    tmp[2]=0xCC;
#endif    
    
    tmp[0]=I2C1_bnWrite(INA219add,tmp,3);
    
    Interrupt_START();
    while(1)
    {
      
       
       if(tm0.up==T0_UP)
       {
            INTCONbits.GIE=0;
            INTCONbits.PEIE=0;
            tm0.up=T0_STOP;
            
            //電流表示
            val16[4]=INA219_RegRead(INA219add,0x04);
            SSD1306_Print(val16[4]);
            
            val16[1]=INA219_RegRead(INA219add,0x01);
            val16[5]=INA219_RegRead(INA219add,0x05);
            SSD1306_PrintS(val16[1],val16[5]);
            
            //電圧表示
            val16[2]=INA219_RegRead(INA219add,0x02);
            SSD1306_Print2(val16[2]);
            
            
            //電力表示
            val16[3]=INA219_RegRead(INA219add,0x03);
            SSD1306_Print3(val16[3]);
            
            PIE1bits.TMR1IE=1;
            INTCONbits.GIE=1;
       }
          

    }
    return (EXIT_SUCCESS);
}

void Port_init(void)
{
    //Port init--------- 
    //PORTA initialize
    TRISA = 0x08;   //RA3:MCLR
    ODCONA = 0x00;  //When anODCONA bit is set, the corresponding port output becomes an open-drain driver capable of sinking current only.
    ANSELA = 0x00;
    WPUA =0x00;
    PORTA=0x00;

    //PORTC initialize
    TRISC = 0x03;//RC0;SCL RC1:SDA
    ODCONC =0x00;
    ANSELC=0x00;
    WPUC=0x00;
    PORTC=0x00;
    LATC =0x00;
    
//PPS init-----------------------    
    PPSLOCK = 0x55;
    PPSLOCK = 0xAA;
    PPSLOCKbits.PPSLOCKED = 0;
  
    //I2C RC0:SCL1 RC1:SDA1
    SSP1CLKPPS=0x10;
    SSP1DATPPS=0x11;
    RC0PPS=0x18;
    RC1PPS=0x19;
    TRISC0=1;
    TRISC1=1;
    
    PPSLOCK = 0x55;
    PPSLOCK = 0xAA;
    PPSLOCKbits.PPSLOCKED = 1;
//PPS re-Locked---------------------
}

void delay10ms(uint16_t num)
{
    do{
        __delay_ms(10);
        num--;
    }while(num>0);
}


uint8_t hpt=12;

//電流値表示
void SSD1306_Print(uint16_t ival)
{
    uint8_t page;
    char txt[30];
    
    page=1;
    sprintf(txt,"I:\n");
    SSD1306_Print6(page,hpt+0,txt);
    
    page=0;
    sprintf(txt,"%4d\n",ival);
    SSD1306_Print12(page,hpt+6*2+1,txt);
    sprintf(txt,"  \n");
    SSD1306_Print6(page,hpt+6*2+1+12*4+1,txt);
    sprintf(txt,"mA\n");
    SSD1306_Print6(page+1,hpt+6*2+1+12*4+1,txt);   
}

//シャント電圧表示
void SSD1306_PrintS(uint16_t sval, uint16_t cal)
{
    uint8_t page;
    char txt[30];
    
    page=2;
    sprintf(txt,"%6u\n",sval);
    SSD1306_Print6(page,hpt+0,txt);
    sprintf(txt,"%6u\n",cal);
    SSD1306_Print6(page,hpt+6*6+10,txt);
    
}


//電圧表示
void SSD1306_Print2(uint16_t ival)
{
    uint8_t page;
    char txt[30];
    
    page=4;
    sprintf(txt,"V:\n");
    SSD1306_Print6(page,hpt+0,txt);
    
    page=3;
    sprintf(txt,"%2ld.%02ld\n",ival/100L, ival%100L);
    SSD1306_Print12(page,hpt+6*2+1,txt);
    
    sprintf(txt,"V\n");
    SSD1306_Print6(page+1,hpt+6*2+1+12*5+1,txt);
}

//電力表示
void SSD1306_Print3(uint16_t ival)
{
    uint8_t page;
    char txt[30];
     
    
    page=7;
    sprintf(txt,"Pw:\n");
    SSD1306_Print6(page,hpt+0,txt);
    
    page=6;
    sprintf(txt,"%4ld.%ld\n",ival/10L,ival%10L);
    //sprintf(txt,"%6ld\n",ival);
    SSD1306_Print12(page,hpt+6*3+1,txt);
    
    sprintf(txt,"%c%c\n",unit[0],unit[1]);
    SSD1306_Print6(page+1,hpt+6*3+1+12*6
            +1,txt);
}
```

```C:INA219.h
#ifndef INA219_H
#define	INA219_H

#ifdef	__cplusplus
extern "C" {
#endif
    
#include <xc.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "I2C_MSSP1.h"

    
//#define MaxCurrent1Amax
//#define  MaxCurrent3_2Amax
#define MaxCurrent3_2Amax2
   
uint8_t dec[5];
uint8_t unit[3];//??????
uint8_t u_digit;//???

uint8_t INA219add=0x80;

uint16_t INA219_RegRead(uint8_t _add, uint8_t _reg);


#ifdef	__cplusplus
}
#endif

#endif	/* INA219_H */
```

```C:INA219.c

#include "INA219.h"

//*****************************************************
//INA219データ計算
//_add:INA219　I2Cスレーブアドレス
//_reg:INA219　レジスタアドレス
//呼び出したデータの種類によってLSB値を掛ける。
//*****************************************************
uint16_t INA219_RegRead(uint8_t _add, uint8_t _reg)
{
    uint16_t ltmp;
    uint32_t lval;
    
    ltmp=I2C1_b2Read(_add,_reg);
    
    switch(_reg)
    {   case 0x02://BusPower
            ltmp>>=3;//レジスタ3ビットはいらないのでシフト。
            ltmp*=4L;//LSB:4mV
            ltmp/=10;//表示調整
            unit[0]='V';
            break;
        case 0x03://Power
            lval = ltmp;

#ifdef MaxCurrent1Amax
            lval=lval*6L;//PowerLSB=1.960mW 小数点第1位まで求める。
                          //2mW×10倍しておく。
#endif

#ifdef MaxCurrent3_2Amax2
            lval=lval*20L;//PowerLSB=1.960mW 小数点第1位まで求める。
                          //2mW×10倍しておく。
#endif
            if(lval>10000)
            {//1000mW以上の時の液晶表示単位
                lval/=1000;
                unit[0]='W';
                unit[1]=' ';
            }
            else
            {//1000mW以下の時の液晶表示単位
                unit[0]='m';
                unit[1]='W';  
            }
            ltmp=lval;
            break;
        case 0x04://current
#ifdef MaxCurrent1Amax            
            lval=ltmp*30L;//LSB:98uA(MaxExpectedCurrent:1A)
#endif            
#ifdef MaxCurrent3_2Amax2
            lval=ltmp*98L;//LSB:98uA(MaxExpectedCurrent:3.2A)
#endif              
            ltmp=lval/1000L;
            //液晶表示単位をセット。
            unit[0]='m';
            unit[1]='A';
            
            break;
    }
    return ltmp;   
}


```