一、引言

为满足精准的电源管理需求,国科安芯推出的AS32X601开发板特别搭载了INA226双向电流/功率监测芯片。该芯片不仅支持高达36V的总线电压,更能通过其集成的Δ-Σ ADC对分流电阻器上的压降进行高精度采样,从而实现毫伏级电压与微安级电流的精确测量。这一特性使其非常适用于电池充放电监控、服务器/工控设备能耗分析以及太阳能系统发电效率评估等高级应用场景。

二、INA226简介

INA226 是一款分流/功率监视器,具有 I2CSMBUS 兼容接口。 该器件监视分流压降和总线电源电压。可编程校准值、转换时间和取平均值功能与内部乘法器相结合,可实现电流值和功率值的直接读取。

2.1引脚功能

IN+

电流输入

IN-

电流输出

GND

电源地

VS

电源正

SCL

时钟线

SDA

数据线

A0

I2C地址设置引脚

A1

I2C地址设置引脚

Alert

报警输出引脚

VBUS

总线电压输入引脚

INA226有两个地址引脚,A0A1。下图描述了16个可能地址中的每一个的引脚逻辑。INA226模块A0 A1引脚被下拉至 GND ,故 IIC 地址则为 Write1000 0000 0x80 Read1000 0001 0x81

2.2寄存器介绍

2.2.1配置寄存器 0x00

配置寄存器设置控制设备的工作模式。该寄存器控制分流和总线电压测量以及所用的平均模式的转换时间设置。

Bit

字段

类型

默认值

描述

15

RST

R/W

0

设置成1   复位

14-12

NC

R/W

100

采样平均次数

   000 1

   001 4

   010 16

   011 64

   …

11-9

AVG

R/W

000

总线采样时间

   000 140µs

   001 204µs

   010 332 µs

   011 588 µs

   100 1.1ms

   101 2.116ms

   110 4.156ms

   111 8.244ms

8-6

VBUSCT

R/W

100

总线采样时间

   000 140µs

   001 204µs

   010 332 µs

   011 588 µs

   100 1.1ms

   101 2.116ms

   110 4.156ms

   111 8.244ms

5-3

VSHCT

R/W

100

分流采样时间

同上

2-0

MODE

R/W

111

操作模式

   000 关闭

   001 分流电压 触发

   010 总线电压 触发

   011 分流及总线电压 触发

   100 关闭

   101 分流电压 连续测量

   110 总线电压 连续测量

   111 分流及总线电压 连续测量

2.2.2分流电压寄存器 0x01

用于存储当前的分流电压读数Vshunt。负数采用二进制补码格式表示。生成一个负数的补码方法是:对其绝对值的二进制数取反,然后加1。如果最高有效位(MSB)为1,则表示这是一个负数。最小分辨率(Vshunt _LSB)为 2.5uV

注:分流电压最大81.92mV,而该模块的采样电阻是0.1R,故最大电流为 81.92mV / 0.1R = 819.2mA

分流电压计算:Vshunt = 寄存器值 * Vshunt _LSB(2.5uV)

2.2.3.总线电压寄存器 0x02

用于存储最近一次的总线电压读数 Vbus。如果启用了平均功能,则该寄存器显示的是平均后的数值。满量程范围为 40.96V(对应 7FFF);最小分辨率(Vbus_LSB)为 1.25 mV

总线电压计算:Vbus = 寄存器值 * Vbus_LSB(1.25 mV )

2.2.4.功率寄存器 0x03

用于存储功率读数 Power ,如果启用了平均功能,该寄存器将显示平均值。功率寄存器的最低有效位(LSB)在内部被设定为等于 Current_LSB 所设定值的 25 倍。功率寄存器通过将电流寄存器的十进制值与总线电压寄存器的十进制值相乘来记录以瓦特(Watts)为单位的功率。

功率计算: Power = 寄存器值 * Power_ LSB

由手册可知 Power_LSB(功率最小分辨率) = Current_LSB(电流最小分辨率) * 25

Current_LSB 手册给出了计算公式,如下

Current_LSB = 最大电流 / 2^15 = 819.2mA / 2^15 = 0.025mA

2.2.5.电流寄存器 0x04

用于存储电流读数 Current,如果启用了平均功能,该寄存器将显示平均值。电流寄存器的值是通过将分流电压寄存器中的十进制值与校准寄存器中的十进制值相乘来计算的

电流值 = 寄存器值 * Current_LSB(0.05mA) (Current_LSB 的计算在上文功率寄存器小节已经给出)

2.2.6.基准寄存器 0x05

该寄存器为器件提供用于产生测量差分电压的分流电阻值。它还设置电流寄存器的分辨率。编程该寄存器可设置 Current_LSB  Power_LSB。该寄存器也适用于整体系统校准。

该寄存器主要设置系统基准的,将 基准值(CAL) 写入寄存器即可

Current_LSB= 0.05mA (上文功率寄存器小节已经给出) ,根据原理图可知 Rshunt = 0.1R

CAL = 0.00512 / (0.00005A * 0.1R) = 1024 = 0x0400

三、I2C时序说明

3.1 写时序

 FlagStatus I2C_MEEPROMWriteByte(I2C_TypeDef* I2Cx, uint8_t addr, uint16_t reg, uint16_t data, uint32_t timeout)  
{  
    /*等待总线释放*/  
    while (!I2C_CheckStatus(I2Cx, I2C_BUS_IDLE))  
    {  
        I2C_StartClear(I2Cx);  
        I2C_GenerateStop(I2Cx);  
        I2C_ClearITPendingBit(I2Cx);  
        if ((timeout--) == 0)  
        {  
            return RESET;  
        }  
        delay_ms(1);  
    }  
    I2C_GenerateStart(I2Cx);  
    /*等待启动信号完成*/  
    while (!I2C_CheckStatus(I2Cx, MASTER_START_READY))  
    {  
        if ((timeout--) == 0)  
        {  
            return RESET;  
        }  
        delay_ms(1);  
    }  
    I2C_Send7bitAddress(I2Cx, addr, I2C_WRITE);  
    I2C_StartClear(I2Cx);  
    I2C_ClearITPendingBit(I2Cx);  
    /*等待从机接收完成地址并发送ack*/  
    while (!I2C_CheckStatus(I2Cx, MSEND_WADDR_ACK))  
    {  
        if ((timeout--) == RESET)  
        {  
            I2C_GenerateStop(I2Cx);  
            I2C_ClearITPendingBit(I2Cx);  
            return RESET;  
        }  
        delay_ms(1);  
    }  
  
    I2C_SendData(I2Cx, (uint8_t)(reg >> 0));  
    I2C_ClearITPendingBit(I2Cx);  
    /*等待从机接收完成数据并发送ack*/  
    while (!I2C_CheckStatus(I2Cx, MSEND_DATA_ACK))  
    {  
        if ((timeout--) == 0)  
        {  
            I2C_GenerateStop(I2Cx);  
            I2C_ClearITPendingBit(I2Cx);  
            return RESET;  
        }  
        delay_ms(1);  
    }  
    I2C_SendData(I2Cx, (data&0xff00)>>8);  
    I2C_ClearITPendingBit(I2Cx);  
    /*等待从机接收完成数据并发送ack*/  
    while (!I2C_CheckStatus(I2Cx, MSEND_DATA_ACK))  
    {  
        if ((timeout--) == 0)  
        {  
            I2C_GenerateStop(I2Cx);  
            I2C_ClearITPendingBit(I2Cx);  
            return RESET;  
        }  
        delay_ms(1);  
    }  
          
        I2C_SendData(I2Cx, data&0x00ff);  
    I2C_ClearITPendingBit(I2Cx);  
    /*等待从机接收完成数据并发送ack*/  
    while (!I2C_CheckStatus(I2Cx, MSEND_DATA_ACK))  
    {  
        if ((timeout--) == 0)  
        {  
            I2C_GenerateStop(I2Cx);  
            I2C_ClearITPendingBit(I2Cx);  
            return RESET;  
        }  
        delay_ms(1);  
    }  
  
    I2C_GenerateStop(I2Cx);  
    I2C_ClearITPendingBit(I2Cx);  
    return 1;  
}  

    3.2 读时序

    FlagStatus I2C_MEEPROMRead(I2C_TypeDef* I2Cx, uint8_t addr, uint16_t reg, uint8_t* pData, uint32_t Size, uint32_t timeout)  
    {  
      
        uint32_t num = 0x00;  
        /*等待总线释放*/  
        while (!I2C_CheckStatus(I2Cx, I2C_BUS_IDLE))  
        {  
            I2C_StartClear(I2Cx);  
            I2C_GenerateStop(I2Cx);  
            I2C_ClearITPendingBit(I2Cx);  
            if ((timeout--) == 0)  
            {  
                return RESET;  
            }  
            delay_ms(1);  
        }  
            I2C_AcknowledgeConfig(I2Cx, I2C_IICAA_ACK);  
        I2C_GenerateStart(I2Cx);  
        /*等待启动信号完成*/  
        while (!I2C_CheckStatus(I2Cx, MASTER_START_READY))  
        {  
            if ((timeout--) == 0)  
            {  
                        I2C_StartClear(I2Cx);  
                        I2C_GenerateStop(I2Cx);  
                        I2C_ClearITPendingBit(I2Cx);  
                return RESET;  
            }  
            delay_ms(1);  
        }  
        I2C_Send7bitAddress(I2Cx, addr, I2C_WRITE);  
        I2C_StartClear(I2Cx);  
        I2C_ClearITPendingBit(I2Cx);  
        /*等待从机接收完成地址并发送ack*/  
        while (!I2C_CheckStatus(I2Cx, MSEND_WADDR_ACK))  
        {  
            if ((timeout--) == 0)  
            {  
                I2C_GenerateStop(I2Cx);  
                I2C_ClearITPendingBit(I2Cx);  
                return RESET;  
            }  
            delay_ms(1);  
        }  
    //  I2C_SendData(I2Cx, (uint8_t)(reg >> 8));  
    //  I2C_ClearITPendingBit(I2Cx);  
    //  /*等待从机接收完成数据并发送ack*/  
    //  while (!I2C_CheckStatus(I2Cx, MSEND_DATA_ACK))  
    //  {  
    //      if ((timeout--) == 0)  
    //      {  
    //          I2C_GenerateStop(I2Cx);  
    //          I2C_ClearITPendingBit(I2Cx);  
    //          return 0;  
    //      }  
    //      delay_ms(1);  
    //  }  
        I2C_SendData(I2Cx, (uint8_t)(reg >> 0));  
        I2C_ClearITPendingBit(I2Cx);  
        /*等待从机接收完成数据并发送ack*/  
        while (!I2C_CheckStatus(I2Cx, MSEND_DATA_ACK))  
        {  
            if ((timeout--) == 0)  
            {  
                I2C_GenerateStop(I2Cx);  
                I2C_ClearITPendingBit(I2Cx);  
                return RESET;  
            }  
            delay_ms(1);  
        }  
        I2C_GenerateStart(I2Cx);  
        I2C_ClearITPendingBit(I2Cx);  
        /*等待从机接收完成数据并发送ack*/  
        while (!I2C_CheckStatus(I2Cx, MASTER_START_REPEAT))  
        {  
            if ((timeout--) == 0)  
            {  
                I2C_GenerateStop(I2Cx);  
                I2C_ClearITPendingBit(I2Cx);  
                return RESET;  
            }  
            delay_ms(1);  
        }  
      
        I2C_Send7bitAddress(I2Cx, addr, I2C_READ);  
        I2C_ClearITPendingBit(I2Cx);  
        /*等待从机接收完成地址并发送ack*/  
        while (!I2C_CheckStatus(I2Cx, MSEND_RADDR_ACK))  
        {  
            if ((timeout--) == 0)  
            {  
                            I2C_StartClear(I2Cx);  
                I2C_GenerateStop(I2Cx);  
                I2C_ClearITPendingBit(I2Cx);  
                return RESET;  
            }  
            delay_ms(1);  
        }  
      
              
        for (num = 0; num < Size; num++)  
        {  
            if (num == (Size - 1))  
            {  
                /* IIC sends NACK */  
                I2C_AcknowledgeConfig(I2Cx, I2C_IICAA_NACK);  
            }  
            else  
            {  
                I2C_AcknowledgeConfig(I2Cx, I2C_IICAA_ACK);  
            }  
      
            I2C_StartClear(I2Cx);  
            I2C_ClearITPendingBit(I2Cx);  
      
            /* Wait for the slave to send the completed data, and the host will send an ack */  
            while (!(I2C_CheckStatus(I2Cx, MREAD_DATA_ACK) || I2C_CheckStatus(I2Cx, MREAD_DATA_NACK)))  
            {  
                if ((Timeout--) == 0)  
                {  
                                    I2C_StartClear(I2Cx);  
                    I2C_GenerateStop(I2Cx);  
                    I2C_ClearITPendingBit(I2Cx);  
                    return RESET;  
                }  
                          delay_ms(1);  
            }  
            *pData++ = I2C_ReceiveData(I2Cx);  
        }  
            I2C_StartClear(I2Cx);  
        I2C_GenerateStop(I2Cx);  
        I2C_ClearITPendingBit(I2Cx);  
        return SET;  
    }  

      四、程序

      指令

      #define I2C               I2C0
      
      // INA226 I2C地址(根据A0和A1引脚配置决定)
      
      #define INA226_ADDRESS         0x40
      
      // INA226寄存器定义
      
      #define INA226_REG_CONFIG      0x00
      
      #define INA226_REG_SHUNT_VOLT  0x01
      
      #define INA226_REG_BUS_VOLT    0x02
      
      #define INA226_REG_POWER       0x03
      
      #define INA226_REG_CURRENT     0x04
      
      #define INA226_REG_CALIB       0x05
      
      #define INA226_REG_MASK_ENABLE 0x06
      
      #define INA226_REG_ALERT_LIMIT 0x07
      
      #define INA226_REG_MANUFACTURER 0xFE
      
      #define INA226_REG_DIE_ID      0xFF
      
      #define Byte_NUMBER             2

      main.c

      void main()  
      {  
          Systemclock_Init();  
            
          uint8_t rdata[2] = {0};  
          delay_init(SMU_ClocksStruct.AXIBus0_Frequency/1000000);  
       #ifdef DEBUG  
          IIC0_RESET();  
          delay_ms(100);  
          IIC0_SET();  
      #endif  
            
          /* Initialize print usart */  
          User_Print_Init(115200);  
        
          Printf( "INNA226  test begin!  \n");  
        
          User_I2C_Init();  
            
          /*  Configuration Register */  
          I2C_MEEPROMWriteByte(I2C, INA226_ADDRESS, INA226_REG_CONFIG, 0x4527, 1000);  
          delay_ms(1);  
            
          /*  Calibration Register */  
          I2C_MEEPROMWriteByte(I2C, INA226_ADDRESS, INA226_REG_CALIB, 0x0A00, 1000);  
          delay_ms(1);  
            
       /* Printf INNA226 ID */  
          I2C_MEEPROMRead(I2C, INA226_ADDRESS, INA226_REG_MANUFACTURER, rdata, Byte_NUMBER, 2000);  
          Printf("INNA226 ID:%x%x \r\n",rdata[0], rdata[1]);   
            
          for(int i = 0; i<10; i++)  
          {  
                
         /* Printf INA226_REG_SHUNT_VOLT */  
            uint16_t shuntVolt=0;  
            I2C_MEEPROMRead(I2C, INA226_ADDRESS, INA226_REG_SHUNT_VOLT, rdata, Byte_NUMBER, 2000);  
            shuntVolt = rdata[0]<<8&0xff00 | rdata[1];  
            Printf("SHUNT VOLT: %f mV\r\n", shuntVolt*2.5*0.001);  
            delay_ms(10);  
              
            uint16_t busVolt=0;  
            I2C_MEEPROMRead(I2C, INA226_ADDRESS, INA226_REG_BUS_VOLT, rdata, Byte_NUMBER, 2000);  
            busVolt = rdata[0]<<8&0xff00 | rdata[1];  
            Printf("busVolt: %f mV\r\n", shuntVolt*1.25);  
            delay_ms(10);  
              
           /* Printf INA226_REG_POWER */  
            uint16_t Power=0;  
            I2C_MEEPROMRead(I2C, INA226_ADDRESS, INA226_REG_POWER, rdata, Byte_NUMBER, 2000);  
            Power = rdata[0]<<8&0xff00 | rdata[1];  
            Printf("Power: %f mW\r\n", Power*1.25);  
        
         /* Printf INA226_REG_SHUNT_VOLT */  
            uint16_t current=0;  
            I2C_MEEPROMRead(I2C, INA226_ADDRESS, INA226_REG_CURRENT, rdata, Byte_NUMBER, 2000);  
            current = rdata[0]<<8&0xff00 | rdata[1];  
            Printf("CUURET :%f mA\r\n", current*0.02);  
            delay_ms(100);  
            Printf("--------------------------\r\n");  
          }  
        
            while(1);  
      
      
      }  

      实验结果

      更多推荐