一、引言
为满足精准的电源管理需求,国科安芯推出的AS32X601开发板特别搭载了INA226双向电流/功率监测芯片。该芯片不仅支持高达36V的总线电压,更能通过其集成的Δ-Σ ADC对分流电阻器上的压降进行高精度采样,从而实现毫伏级电压与微安级电流的精确测量。这一特性使其非常适用于电池充放电监控、服务器/工控设备能耗分析以及太阳能系统发电效率评估等高级应用场景。
二、 INA226 简介
INA226 是一款分流/功率++++监视器++++ ,具有 I2C或SMBUS 兼容接口。 该器件监视分流压降和总线电源电压。可编程校准值、转换时间和取平均值功能与内部乘法器相结合,可实现电流值和功率值的直接读取。

2.1 引脚功能
|-------|-----------|
| IN+ | 电流输入 |
| IN- | 电流输出 |
| GND | 电源地 |
| VS | 电源正 |
| SCL | 时钟线 |
| SDA | 数据线 |
| A0 | I2C地址设置引脚 |
| A1 | I2C地址设置引脚 |
| Alert | 报警输出引脚 |
| VBUS | 总线电压输入引脚 |
INA226有两个地址引脚,A0和A1。下图描述了16个可能地址中的每一个的引脚逻辑。INA226 模块 A0 A1 引脚被下拉至 GND ,故 IIC 地址则为 Write : 1000 0000 0x80 Read : 1000 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);
}
五 、 实验结果
