シャント抵抗の両端をAD変換して、電流と電圧、電力値を計測表示してくれるIC INA219まとめ
データシートは、2011年度版を参考にしました。2015年版のデータシートが最新の内容になっています。
チップ抵抗の値は、文字R100のRは小数点を表すので、0.1Ωになります。INA219は、この抵抗の両端の電圧差を計測して、電流を計算してくれるICです。
PIC16F18325を使用。
I2C通信でINA219の測定値を取得し、液晶に表示します。
データシートを読み込むわけですが、結構難解でした。
まず、データーシートのP.17,18ページでキャリブレーション値を計算しておきます。
シャント抵抗値は0.1Ω
測定電圧の最大値は32V
電流値の最大は、3.2Aとして計算しました。
※最大電流値をもっと少なくして、たとえば1Aで計算しなおせば、精度が上がります。
INA219 busVoltage Resister(02h)の値にLSB 4mVを、
INA219 Power Resister(03h)の値に、LSB 2mWを、
INA219 Current Resister(04h)の値に、LSB 98uAをかけます。
buVoltageレジスタの単位は、mV
Power レジスタの単位は、mW
Current レジスタの単位は、uA
なので、実際の物理量である電流量などは、マイコン側で、LSB値をかけて、計算しています。
計測値の計算
//*****************************************************
//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(約2mW) 小数点第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;
}
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);
}
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 */
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;
}