2026 Make Blocks #3阶段2 - INA260 高精度电流功率监测模块实现
一、任务介绍
本次设计的任务是设计一款电流和功率监测模块,能实时测量直流回路的电压、电流,并输出功率值,可用于电源效率分析、电池电量监测等场景。
具体要求包括:使用高侧电流监测专用IC;支持0~26V总线电压测量;数字接口输出(I2C);内部集成分流电阻;提供输入和输出接线端子。选用了 TI INA260AIPWR 作为核心芯片。该芯片内部集成了高精度2mΩ分流电阻和16位ADC,可直接通过I2C接口读出电压、电流、功率和电能累积值,大幅简化了硬件设计,同时保证了测量精度。
INA260DigiKey 官网链接:
https://www.digikey.cn/zh/products/detail/texas-instruments/ina260aipwr/6590780
二、设计方案介绍
2.1 整体方案概述
本模块采用 INA260 + 3.3V I2C接口 + 输入/输出排针 的架构,外围仅需两颗去耦电容、两颗I2C上拉电阻和一颗LED电源指示灯。
- 测量方式:高侧电流检测(电流从IN+流入,从IN-流出)
- 电压测量:VBUS引脚连接到IN-(负载正极),测量负载两端电压
- 功率计算:芯片内部自动完成(电压 × 电流)
- 数字接口:I2C,地址固定为0x40(A0/A1接GND)
- 报警输出:ALERT引脚引出,可配置为过流/过压中断
2.2 板上资源框图
2.3 工具与设备清单
工具/设备 | 用途 |
数字万用表 | 测量电压、通断、电阻值 |
直流稳压电源 | 提供3.3V和待测电压(0~26V) |
直流电机 | 制造电流回路 |
串口调试助手 | 接收UART输出数据 |
CW32F030系统板 | 作为I2C主机读取INA260数据 |
OLED 128x64屏幕 | 本地数据显示 |
烙铁、焊锡、助焊剂 | 焊接元器件 |
三、模块介绍(主要芯片选型与功能)
3.1 核心芯片:Texas Instruments INA260AIPWR
INA260 是德州仪器推出的一款高精度、低漂移的电流/电压/功率监测芯片,内部集成了 2mΩ 精密分流电阻、16位Δ-Σ ADC 和 I2C 数字接口,可直接输出电压、电流、功率和电能累积值。
主要技术参数
参数 | 值 |
总线电压范围 | 0 ~ 36V |
电流量程 | ±15A(持续) |
精度(增益误差) | ±0.15%(最大值,出厂校准) |
温漂 | 10ppm/°C |
ADC 分辨率 | 16位 |
电压分辨率 | 1.25mV/LSB |
电流分辨率 | 1.25mA/LSB |
功率分辨率 | 10mW/LSB |
供电电压 | 2.7V ~ 5.5V |
接口 | I2C(16个可选地址) |
封装 | TSSOP-16(5×4.4mm) |
工作温度 | -40°C ~ +125°C |
3.2 其他主要元件
位号 | 型号 | 规格 | 功能 |
C1, C2 | CL10B104KB8NNNC / 10μF | 0603 贴片电容 | 电源去耦,稳定 VS 电压 |
R2, R4 | FRC0603F1002TS | 10kΩ 0603 1% | I2C 总线 SCL/SDA 上拉电阻 |
R1 | 0603WAF1001T5E | 1kΩ 0603 1% | LED 限流电阻 |
PWR1 | GL0603UB01 | 0603 蓝色 LED | 3.3V 电源指示灯 |
H1, H2 | PZ254V-11-02P | 2.54mm 2P 排针 | 电源输入/输出接口 |
U1 | DS1021-1X5SF11-B | 2.54mm 5P 排针 | I2C + ALERT 引出接口 |
四、原理图和 PCB 设计介绍
4.1 原理图设计

设计要点:
- 电流测量路径:
H1-1 (VIN+) → U3-IN+ (P1-3) → [内部2mΩ电阻] → U3-IN- (P14-16) → H2-1 (VOUT+) - 电压测量:
U3-VBUS (P12) 短接至 IN-(H2-1),测量负载正极对 GND 的电压 - 电源与去耦:
U3-VS (P10) 接 3.3V,C1(0.1μF) 和 C2(10μF) 并联在 VS 和 GND 之间,靠近芯片引脚 - I2C 接口:
SCL (P9) 和 SDA (P8) 通过 R2、R4(10kΩ) 上拉至 3.3V,引出至 5 针排针 U1 - 地址配置:
A0 (P5) 和 A1 (P4) 直接接 GND,I2C 地址固定为 0x40 - 电源指示灯:
R1(1kΩ) + 蓝色 LED 串联在 3.3V 和 GND 之间,上电即亮 - 报警输出:
ALERT (P7) 引出至 U1-5,供外部 MCU 作为中断输入(开漏输出,需外部上拉)
4.2 PCB 设计要点
设计项 | 要求 |
电流路径线宽 | 局部铺铜(IN+&IN-) |
去耦电容位置 | C1、C2 尽可能靠近 U3 的 VS 和 GND 引脚 |
芯片摆放 | INA260 尽量靠近输入排针 H1,缩短电流路径 |
底层 GND 铜皮 | 整板铺地,降低回路阻抗 |
排针布局 | H1、H2 位于板子两侧,U1 位于边缘便于插拔 |
外形尺寸 | 1.60mm × 1.60mm 双层板 |

精度高、体积小巧
仅16.0mmx16.0mm
五、模块主要性能指标和引脚定义
5.1 电气性能
参数 | 最小值 | 典型值 | 最大值 | 单位 |
供电电压 | 2.7 | 3.3 | 5.5 | V |
静态电流 | — | 0.42 | 0.7 | mA |
总线电压 | 0 | — | 36 | V |
可测电流 | -15 | — | +15 | A |
电压分辨率 | — | 1.25 | — | mV/LSB |
电流分辨率 | — | 1.25 | — | mA/LSB |
精度(增益误差) | — | ±0.15 | — | % |
5.2 接口定义
电源输入/输出(H1, H2,2.54mm 2P 排针)
端子 | 引脚 | 功能 |
H1 | 1 | VIN+(接电源正极) |
2 | GND(接电源负极) | |
H2 | 1 | VOUT+(接负载正极) |
2 | GND(接负载负极) |
I2C + 报警接口(U1,2.54mm 5P 排针)
引脚 | 信号 | 说明 |
1 | 3.3V | 模块供电输出(可给外部 MCU 供电) |
2 | GND | 地 |
3 | SCL | I2C 时钟线 |
4 | SDA | I2C 数据线 |
5 | ALERT | 报警输出(开漏,低有效) |
5.3 I2C 通信
- 从机地址:0x40(7位,A0=A1=GND)
- 支持时钟速度:100kHz / 400kHz / 1MHz / 2.5MHz
主要寄存器
寄存器名称 | 地址 | 说明 |
Configuration | 0x00 | 配置 ADC 模式、转换时间等 |
Current | 0x01 | 电流值,LSB = 1.25mA |
Bus Voltage | 0x02 | 总线电压,LSB = 1.25mV |
Power | 0x03 | 功率值,LSB = 10mW |
Energy | 0x04 | 电能累积值 |
Charge | 0x05 | 电荷累积值 |
六、功能验证与数据测试
6.1 调试验证
12V电池作为电源,经过DC-DC降压为5V供负载-直流电机.
12V-1A:
IN+接12V电池正极 IN-接dc-dc输入端
5V-2A:
IN+接dc-dc输出端 IN-接负载电机
6.2 测试数据记录
测试场景1:5V供电,负载2A
参数 | 万用表实测 | INA260读值 | 误差 |
电压 | 4.46V | 4.455V | -0.11% |
电流 | 1.94A | 1.942A | +0.1% |
功率 | 8.65W | 8.651W | +0.012% |
测试场景2:12V供电,负载1A
参数 | 万用表实测 | INA260读值 | 误差 |
电压 | 11.95V | 11.958V | -0.067% |
电流 | 1.02A | 1.018A | -0.20% |
功率 | 12.18W | 12.17W | -0.08% |
6.3 功能展示
显示界面(OLED 128x64):
串口输出(115200bps):
七、关键代码
所用调试软件KEIL5,串口调试助手。
驱动部分:
/**
* @brief I2C 写寄存器
*/
static uint8_t INA260_I2C_WriteBytes(uint8_t reg, uint8_t *data, uint8_t len)
{
uint8_t state, idx = 0, reg_sent = 0;
uint32_t timeout;
I2C_GenerateSTART(INA260_I2C, ENABLE);
while (1) {
timeout = 0xFFFFF;
while (I2C_GetIrq(INA260_I2C) == 0) {
if (--timeout == 0) {
I2C_GenerateSTOP(INA260_I2C, ENABLE);
I2C_ClearIrq(INA260_I2C);
return 1;
}
}
state = I2C_GetState(INA260_I2C);
switch (state) {
case 0x08: /* START 已发送 */
I2C_GenerateSTART(INA260_I2C, DISABLE);
I2C_Send7bitAddress(INA260_I2C, 0x80, I2C_Direction_Transmitter);
break;
case 0x18: /* SLA+W ACK */
if (reg_sent == 0) {
I2C_SendData(INA260_I2C, reg);
reg_sent = 1;
} else {
I2C_SendData(INA260_I2C, data[idx++]);
}
break;
case 0x28: /* 数据发送完成 */
if (idx < len) {
I2C_SendData(INA260_I2C, data[idx++]);
} else {
I2C_GenerateSTOP(INA260_I2C, ENABLE);
I2C_ClearIrq(INA260_I2C);
return 0;
}
break;
case 0x20: case 0x30: case 0x38: /* NACK/仲裁丢失 */
default:
I2C_GenerateSTOP(INA260_I2C, ENABLE);
I2C_ClearIrq(INA260_I2C);
return 1;
}
I2C_ClearIrq(INA260_I2C);
}
}
/**
* @brief I2C 读寄存器
*/
static uint8_t INA260_I2C_ReadBytes(uint8_t reg, uint8_t *data, uint8_t len)
{
uint8_t state, idx = 0, phase = 0;
uint32_t timeout;
I2C_GenerateSTART(INA260_I2C, ENABLE);
while (1) {
timeout = 0xFFFFF;
while (I2C_GetIrq(INA260_I2C) == 0) {
if (--timeout == 0) {
I2C_GenerateSTOP(INA260_I2C, ENABLE);
I2C_ClearIrq(INA260_I2C);
return 1;
}
}
state = I2C_GetState(INA260_I2C);
switch (state) {
case 0x08: /* START, 发送 SLA+W */
I2C_GenerateSTART(INA260_I2C, DISABLE);
I2C_Send7bitAddress(INA260_I2C, 0x80, I2C_Direction_Transmitter);
phase = 0;
break;
case 0x10: /* 重复 START, 发送 SLA+R */
I2C_GenerateSTART(INA260_I2C, DISABLE);
I2C_Send7bitAddress(INA260_I2C, 0x80, I2C_Direction_Receiver);
phase = 1;
break;
case 0x18: /* SLA+W ACK, 发寄存器地址 */
I2C_SendData(INA260_I2C, reg);
break;
case 0x28: /* 寄存器地址发完, 发重复 START */
INA260_I2C->CR_f.STA = 1; /* 直接写 STA,不用库函数 */
break;
case 0x40: /* SLA+R ACK, 准备收数据 */
I2C_AcknowledgeConfig(INA260_I2C, len > 1 ? ENABLE : DISABLE);
break;
case 0x50: /* 收到数据 + ACK */
data[idx++] = I2C_ReceiveData(INA260_I2C);
if (idx == len - 1) {
I2C_AcknowledgeConfig(INA260_I2C, DISABLE); /* 最后一个回 NACK */
}
break;
case 0x58: /* 收到最后字节 + NACK, 结束 */
data[idx++] = I2C_ReceiveData(INA260_I2C);
I2C_GenerateSTOP(INA260_I2C, ENABLE);
I2C_ClearIrq(INA260_I2C);
return 0;
case 0x48: /* SLA+R NACK */
default:
I2C_GenerateSTOP(INA260_I2C, ENABLE);
I2C_ClearIrq(INA260_I2C);
return 1;
}
I2C_ClearIrq(INA260_I2C);
}
}
/**
* @brief 读 16 位寄存器
*/
uint8_t INA260_ReadRegister(uint8_t reg, uint16_t *val)
{
uint8_t buf[2];
if (INA260_I2C_ReadBytes(reg, buf, 2)) return 1;
*val = (buf[0] << 8) | buf[1];
return 0;
}
/**
* @brief 写 16 位寄存器
*/
uint8_t INA260_WriteRegister(uint8_t reg, uint16_t val)
{
uint8_t buf[2] = {val >> 8, val & 0xFF};
return INA260_I2C_WriteBytes(reg, buf, 2);
}
/**
* @brief 读电压 (V)
*/
uint8_t INA260_ReadBusVoltage(float *v)
{
uint16_t raw;
if (INA260_ReadRegister(0x02, &raw)) return 1;
*v = (float)(raw & 0x7FFF) * 0.00125f; /* 1.25mV/bit */
return 0;
}
/**
* @brief 读电流 (A)
*/
uint8_t INA260_ReadCurrent(float *i)
{
uint16_t raw;
if (INA260_ReadRegister(0x01, &raw)) return 1;
*i = (float)((int16_t)raw) * 0.00125f; /* 1.25mA/bit */
return 0;
}
/**
* @brief 读功率 (W)
*/
uint8_t INA260_ReadPower(float *p)
{
uint16_t raw;
if (INA260_ReadRegister(0x03, &raw)) return 1;
*p = (float)raw * 0.01f; /* 10mW/bit */
return 0;
}
主函数:
int32_t main(void)
{
uint8_t u8InaReady = 0;
INA260_ReadingsTypeDef tReadings;
uint32_t u32LastPrint = 0;
uint32_t u32LedPeriod = 500;
/* 1. SysTick 1ms 中断 */
SysTick_Config(SystemCoreClock / 1000);
/* 2. LED 初始化 (PC13) */
__RCC_GPIOC_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pins = GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
GPIO_Init(CW_GPIOC, &GPIO_InitStruct);
GPIO_WritePin(CW_GPIOC, GPIO_PIN_13, GPIO_Pin_RESET); /* 亮 */
/* 3. 串口初始化 */
UART1_Init_ForPrintf();
printf("\r\nINA260 Monitor\r\n");
/* 4. OLED 初始化 */
OLED_Init();
OLED_Clear();
OLED_ShowString(1, 3, "INA260 Monitor");
/* 5. INA260 初始化 */
if (INA260_Init() == 0) {
u8InaReady = 1;
printf("INA260 OK!\r\n");
OLED_ShowString(4, 1, "INA260: OK");
} else {
printf("INA260 FAILED!\r\n");
OLED_ShowString(4, 1, "INA260: OFF");
}
/* 主循环 */
while (1) {
if (u8InaReady) {
/* 读取数据 */
if (INA260_ReadAll(&tReadings) == 0) {
/* LED 周期 = 功率越大闪越快 */
if (tReadings.fPower > 0.1f) {
u32LedPeriod = 50 + (uint32_t)(950.0f / (tReadings.fPower * 10.0f + 1.0f));
if (u32LedPeriod > 1000) u32LedPeriod = 1000;
} else {
u32LedPeriod = 1000;
}
/* 每秒打印 + 刷新 OLED */
if ((GetTick() - u32LastPrint) >= 1000) {
u32LastPrint = GetTick();
printf("V=%.3fV I=%.4fA P=%.4fW\r\n",
tReadings.fBusVoltage, tReadings.fCurrent, tReadings.fPower);
OLED_DisplayPowerInfo(tReadings.fBusVoltage,
tReadings.fCurrent,
tReadings.fPower);
}
}
} else {
/* 不在线则每 2 秒重试 */
if ((GetTick() - u32LastPrint) >= 2000) {
u32LastPrint = GetTick();
if (INA260_Init() == 0) {
u8InaReady = 1;
printf("INA260 OK!\r\n");
OLED_ShowString(4, 1, "INA260: OK");
}
}
}
/* LED 闪烁 */
if ((GetTick() % u32LedPeriod) < (u32LedPeriod / 2)) {
GPIO_WritePin(CW_GPIOC, GPIO_PIN_13, GPIO_Pin_RESET); /* 亮 */
} else {
GPIO_WritePin(CW_GPIOC, GPIO_PIN_13, GPIO_Pin_SET); /* 灭 */
}
}
}
八、心得体会
通过本次设计,我深刻体会到了“选对芯片比画好电路更重要”的含义。
第一,焊接质量决定调试效率。 本次调试中80%的问题(LED不亮、I2C无响应)都源于焊接不良。TSSOP-16封装虽然不算小,但拖焊时仍然容易连锡或虚焊。建议焊接后用万用表逐个引脚测量对地电压,比用放大镜目检更可靠。
第二,上电前检查是保命符。 有两次差点把3.3V直接怼到GND上——多亏了先测通断的习惯。建议把"目检→测电阻→上电"三步曲刻在肌肉记忆里。
第三,INA260真的省心。 内部集成2mΩ电阻和校准数据,焊完就能用,不需要外接采样电阻,也不需要做温漂补偿。对比之前用INA219外加2512电阻的方案,设计周期至少缩短了一周。