STM32F103C8T6 驱动 INA226 测量电流电压功率
一、介绍
INA226 是德州仪器(Texas Instruments, TI)推出的一款高精度分流电压和总线电压监测芯片,可通过 I²C 接口与主控 MCU 通信。该器件可以实时测量分流电阻上的压降(用于计算电流)以及系统电源总线的电压,并通过内部乘法器计算出功率值。其输出数据可以是电压、电流或功率的直接数字值,无需额外的外部运算。
主包最近需要使用该模块帮客户实现一个功能,就是使用INA226模块实时监测系统的总电压、电流和功率,但是发现网上使用STM32F103C8T6单片机控制INA226读取电压电流的资料比较少,刚好前几天浅浅研究了一下这个模块,下面记录一下该模块的使用方法,由于主包是第一次写该类型的博客,如果写的不好还望体谅,后续将持续更新各个模块的使用方法和代码,本博客用到的工程demo无偿开源,需要的可以查看下主页获取方式。
二、传感器原理图

以下是INA226传感器各个模块的引脚说明:
| 引脚 | 功能说明 |
|---|---|
| IN+ | 电流流入(接外部电源正极) |
| IN+ | 电流流出(接负载一侧的正极) |
| VS | 3v3 |
| SCL | 时钟线 |
| SDA | 数据线 |
| A0 | IIC地址设置引脚 |
| A1 | IIC地址设置引脚 |
| Alert | 报警输出引脚(这里不接) |
| VBUS | 总线电压输入引脚 |
三、实物连接说明

我这里是将IN+连接到外部电池电源的输入端(5V_IN),IN-连接到负载端的供电端(+5V),SCL连接的是单片机的PB0引脚,SDA连接的是单片机的PB1引脚
四、代码
main.c文件:
这里主包是在Timer.c中实现了一个简单的定时器轮询任务框架,大家要是觉得复杂的话可以直接把if(time_flag.time_flag_500ms == 1) 中的代码内容放在while里加个延时就行,效果是一样的,oled的驱动代码就不给出了,随便网上找一个就行。
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
#include "main.h"
#include "usart.h"
#include "bsp_iic.h"
#include "bsp_ina226.h"
int main(void)
{
Timer_Init(); //周期10ms
OLED_Init(); //显示屏初始化
OLED_Clear();
IIC_Init(); //初始化I2C
mx_ina226_init(); //初始化INA226
while (1)
{
if(time_flag.time_flag_500ms == 1) //500ms执行一次
{
//电压、电流、功率
get_power();
uint16_t V_int = (uint16_t)(ina226_data.voltageVal / 1000);
uint16_t V_dec = (uint16_t)((ina226_data.voltageVal / 1000 - V_int) * 1000);
uint16_t I_int = (uint16_t)(ina226_data.Shunt_Current / 1000);
uint16_t I_dec = (uint16_t)((ina226_data.Shunt_Current / 1000 - I_int) * 1000);
uint16_t W_int = (uint16_t)ina226_data.Power_Val;
uint16_t W_dec = (uint16_t)((ina226_data.Power_Val - W_int) * 1000);
vbus_mV = V_int + (float)V_dec / 1000;
current_mA = I_int + (float)I_dec / 10000;
power_mW = W_int + (float)W_dec / 10000;
time_flag.time_flag_500ms = 0;
}
}
}
bsp_iic.c文件:
#include "bsp_iic.h"
#include "delay.h"
/******************************************************************************
文件说明:iic的驱动文件
SCL引脚->PB1
SDA引脚->PB0
作者:坤博单片机设计
该代码仅供学习使用
******************************************************************************/
void IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE ); //使能GPIOB时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_SCL|GPIO_Pin_SDA; //
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_SCL|GPIO_Pin_SDA);
}
void SDA_Mode(int Mode){
if(Mode==1){
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE ); //使能GPIOB时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_SDA;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ; // 上拉输入/(开漏输出)
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}else if(Mode==0){
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE ); //使能GPIOA时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_SDA;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; // 推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
}
void IIC_Start(void)
{
SDA_Mode(0); //sda线输出
SDA_Set();
SCL_Set();
delay_us(4);
SDA_Clr();//START:when CLK is high,DATA change form high to low
delay_us(4);
SCL_Clr();//钳住I2C总线,准备发送或接收数据
}
void IIC_Stop(void)
{
SDA_Mode(0);//sda线输出
SCL_Clr();
SDA_Clr();//STOP:when CLK is high DATA change form low to high
delay_us(4);
SCL_Set();
SDA_Set();//发送I2C总线结束信号
delay_us(4);
}
u8 IIC_Wait_Ack(void)
{
u8 ucErrTime=0;
SDA_Mode(1); //SDA设置为输入
SDA_Set();delay_us(1);
SCL_Set();delay_us(1);
while(SDA_READ())
{
ucErrTime++;
if(ucErrTime>250)
{
IIC_Stop();
return 1;
}
}
SCL_Clr();//时钟输出0
return 0;
}
void IIC_Ack(void)
{
SCL_Clr();
SDA_Mode(0);
SDA_Clr();
delay_us(2);
SCL_Set();
delay_us(2);
SCL_Clr();
}
void IIC_NAck(void)
{
SCL_Clr();
SDA_Mode(0);
SDA_Set();
delay_us(2);
SCL_Set();
delay_us(2);
SCL_Clr();
}
void IIC_Send_Byte(u8 txd)
{
u8 t;
SDA_Mode(0);
SCL_Clr();//拉低时钟开始数据传输
for(t=0;t<8;t++)
{
//IIC_SDA=(txd&0x80)>>7;
if((txd&0x80)>>7)
SDA_Set();
else
SDA_Clr();
txd<<=1;
delay_us(2); //对TEA5767这三个延时都是必须的
SCL_Set();
delay_us(2);
SCL_Clr();
delay_us(2);
}
}
u8 IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
SDA_Mode(1);//SDA设置为输入
for(i=0;i<8;i++ )
{
SCL_Clr();
delay_us(2);
SCL_Set();
receive<<=1;
if(SDA_READ())receive++;
delay_us(1);
}
if (!ack)
IIC_NAck();//发送nACK
else
IIC_Ack(); //发送ACK
return receive;
}
bsp_iic.h文件:
#ifndef _BSP_IIC_H
#define _BSP_IIC_H
#include "stm32f10x.h" // Device header
#define GPIO_Pin_SCL GPIO_Pin_1
#define GPIO_Pin_SDA GPIO_Pin_0
#define SCL_Clr() GPIO_ResetBits(GPIOB,GPIO_Pin_SCL)//SCL=PB1
#define SCL_Set() GPIO_SetBits(GPIOB,GPIO_Pin_SCL)
#define SDA_Clr() GPIO_ResetBits(GPIOB,GPIO_Pin_SDA)//SDA=PB0
#define SDA_Set() GPIO_SetBits(GPIOB,GPIO_Pin_SDA)
#define SDA_READ() GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_SDA) // 读取 SDA 引脚电平
void IIC_Init(void);
void SDA_Mode(int Mode);
void IIC_Start(void); //发送IIC开始信号
void IIC_Stop(void); //发送IIC停止信号
void IIC_Send_Byte(u8 txd); //IIC发送一个字节
u8 IIC_Read_Byte(unsigned char ack);//IIC读取一个字节
u8 IIC_Wait_Ack(void); //IIC等待ACK信号
void IIC_Ack(void); //IIC发送ACK信号
void IIC_NAck(void); //IIC不发送ACK信号
void IIC_Write_One_Byte(u8 daddr,u8 addr,u8 data);
u8 IIC_Read_One_Byte(u8 daddr,u8 addr);
#endif
bsp_ina226.c文件:
#include "bsp_ina226.h"
INA226 ina226_data;
/******************************************************************************
函数说明:INA226传感器初始化
入口数据:无
返回值: 无
初始化流程:
1. 发送软件复位命令(0x8000)
2. 配置工作模式(MODE_INA226)
3. 设置校准值(CAL_REG)
4. 读取芯片ID验证通信
配置参数:
- 电流检测范围:±4096 LSB
- 电压检测精度:1.25mV/bit
******************************************************************************/
void mx_ina226_init(void)
{
INA226_SendData(INA226_ADDR1,CFG_REG,0x8000); //重新启动
INA226_SendData(INA226_ADDR1,CFG_REG,MODE_INA226);
INA226_SendData(INA226_ADDR1,CAL_REG,CAL); //设置分辨率
INA226_Get_ID(INA226_ADDR1); //获取ina226的id
}
/******************************************************************************
函数说明:向INA226寄存器写入数据
入口数据:addr - 设备地址(7位)
reg - 寄存器地址
data - 16位待写入数据
返回值: 无
数据格式:
1. 先发送高字节再发送低字节
2. 每次写入需等待ACK应答
协议层操作:
1. 发送起始信号
2. 发送设备地址+写标志
3. 发送寄存器地址
4. 发送16位数据
5. 发送停止信号
******************************************************************************/
void INA226_SendData(uint8_t addr,uint8_t reg,uint16_t data)
{
u8 temp=0;
IIC_Start();
IIC_Send_Byte(addr);
IIC_Wait_Ack();
IIC_Send_Byte(reg);
IIC_Wait_Ack();
temp = (u8)(data>>8);
IIC_Send_Byte(temp);
IIC_Wait_Ack();
temp = (u8)(data&0x00FF);
IIC_Send_Byte(temp);
IIC_Wait_Ack();
IIC_Stop();
}
/******************************************************************************
函数说明:设置寄存器访问指针
入口数据:addr - 设备地址(7位)
reg - 寄存器地址
返回值: 无
操作流程:
1. 发送起始信号
2. 发送设备地址+写标志
3. 发送目标寄存器地址
4. 发送停止信号
应用场景:
在连续读取寄存器前必须调用该函数
******************************************************************************/
void INA226_SetRegPointer(uint8_t addr,uint8_t reg)
{
IIC_Start();
IIC_Send_Byte(addr);
IIC_Wait_Ack();
IIC_Send_Byte(reg);
IIC_Wait_Ack();
IIC_Stop();
}
/******************************************************************************
函数说明:从INA226读取16位数据
入口数据:addr - 设备地址(7位)
返回值: 16位寄存器数据
读取流程:
1. 发送起始信号+地址+读标志
2. 先读取高位字节
3. 再读取低位字节
应答控制:
读高位时发送ACK,读低位时发送NACK
******************************************************************************/
uint16_t INA226_ReadData(uint8_t addr)
{
u16 temp=0;
IIC_Start();
IIC_Send_Byte(addr+1);
IIC_Wait_Ack();
temp = IIC_Read_Byte(1);
temp<<=8;
temp |= IIC_Read_Byte(0);
IIC_Stop();
return temp;
}
/******************************************************************************
函数说明:获取INA226芯片ID
入口数据:addr - 设备地址(7位)
返回值: 无(结果存储在全局变量中)
操作步骤:
1. 设置寄存器指针到ID寄存器
2. 读取32位ID数据
数据存储:
结果保存在ina226_data.ina226_id中
******************************************************************************/
void INA226_Get_ID(uint8_t addr)
{
uint32_t temp=0;
INA226_SetRegPointer(addr,INA226_GET_ADDR);
temp = INA226_ReadData(addr);
ina226_data.ina226_id = temp;
}
/******************************************************************************
函数说明:获取校准寄存器值
入口数据:addr - 设备地址(7位)
返回值: 16位校准值
应用场景:
用于验证校准参数设置是否生效
******************************************************************************/
uint16_t INA226_GET_CAL_REG(uint8_t addr)
{
uint32_t temp=0;
INA226_SetRegPointer(addr,CAL_REG);
temp = INA226_ReadData(addr);
return (uint16_t)temp;
}
/******************************************************************************
函数说明:获取分流电压值(2.5uV/bit)
入口数据:addr - 设备地址(7位)
返回值: 16位原始数据(有符号)
数据说明:
1 LSB = 2.5uV(0.0025mV)
范围:±81.9175mV
注意事项:
返回值可能为负数需注意符号扩展
******************************************************************************/
int16_t INA226_GetShuntVoltage(uint8_t addr)
{
int16_t temp = 0;
INA226_SetRegPointer(addr,SV_REG);
temp = INA226_ReadData(addr);
return (int16_t)temp;
}
/******************************************************************************
函数说明:获取总线电压值(1.25mV/bit)
入口数据:addr - 设备地址(7位)
返回值: 16位原始数据(无符号)
数据说明:
1 LSB = 1.25mV
范围:0-40.96V
******************************************************************************/
uint16_t INA226_GetVoltage(uint8_t addr)
{
uint32_t temp = 0;
INA226_SetRegPointer(addr,BV_REG);
temp = INA226_ReadData(addr);
return (uint16_t)temp;
}
/******************************************************************************
函数说明:获取功率测量值(25mW/bit)
入口数据:addr - 设备地址(7位)
返回值: 16位原始数据(无符号)
数据说明:
1 LSB = 25mW
范围:0-10.24W
******************************************************************************/
uint16_t INA226_Get_Power(uint8_t addr)
{
int16_t temp=0;
INA226_SetRegPointer(addr,PWR_REG);
temp = INA226_ReadData(addr);
return (uint16_t)temp;
}
/******************************************************************************
函数说明:获取分流电流值(1mA/bit)
入口数据:addr - 设备地址(7位)
返回值: 16位原始数据(有符号)
数据说明:
1 LSB = 1mA
范围:±32.768A
******************************************************************************/
int16_t INA226_GetShunt_Current(uint8_t addr)
{
int16_t temp=0;
INA226_SetRegPointer(addr,CUR_REG);
temp = INA226_ReadData(addr);
// if(temp&0x8000) temp = ~(temp - 1);
return temp;
}
/******************************************************************************
函数说明:获取分流电压(带单位转换)
入口数据:Voltage - 指向存储结果的浮点型指针(单位:uV)
返回值: 无
数据转换:
原始值 × 2.5uV/bit
注意事项:
该函数包含硬件相关的校准系数
******************************************************************************/
void Get_Shunt_voltage(float *Voltage)//uV
{
*Voltage = (INA226_GetShuntVoltage(INA226_ADDR1)*INA226_VAL_LSB);//如需矫正电流分流参数请将这里改为2.5
}
/******************************************************************************
函数说明:获取总线电压(带单位转换)
入口数据:Voltage - 指向存储结果的浮点型指针(单位:mV)
返回值: 无
数据转换:
原始值 × 1.25mV/bit
******************************************************************************/
void GetVoltage(float *Voltage)//mV
{
*Voltage = INA226_GetVoltage(INA226_ADDR1)*Voltage_LSB;
}
/******************************************************************************
函数说明:获取功率值(带单位转换)
入口数据:Power - 指向存储结果的浮点型指针(单位:mW)
返回值: 无
数据转换:
原始值 × 25mW/bit
******************************************************************************/
void Get_Power(float *Power)//mW
{
*Power = (INA226_Get_Power(INA226_ADDR1)*POWER_LSB);
}
/******************************************************************************
函数说明:获取分流电流(带单位转换)
入口数据:Current - 指向存储结果的浮点型指针(单位:mA)
返回值: 无
数据转换:
原始值 × 1mA/bit
******************************************************************************/
void Get_Shunt_Current(float *Current)//mA
{
*Current = (INA226_GetShunt_Current(INA226_ADDR1)* CURRENT_LSB);
}
/******************************************************************************
函数说明:综合功率计算
入口数据:无(结果存储在全局变量中)
返回值: 无
计算公式:
功率(W) = 电压(V) × 电流(A)
其中:电压(V) = mV值 × 0.001
电流(A) = mA值 × 0.001
数据来源:
- 电压值来自INA226_GetVoltage()
- 电流值来自INA226_GetShunt_Current()
******************************************************************************/
void get_power()//W
{
GetVoltage(&ina226_data.voltageVal); //mV
Get_Shunt_voltage(&ina226_data.Shunt_voltage); //uV
Get_Shunt_Current(&ina226_data.Shunt_Current); //mA
Get_Power(&ina226_data.Power); //mW
ina226_data.Power_Val = ina226_data.voltageVal*0.001f * ina226_data.Shunt_Current*0.001f; //mV*mA
}
bsp_ina226.c文件:
#ifndef __INA226_H
#define __INA226_H
#include "bsp_iic.h"
#define MODE_INA226 0X4327
#define CFG_REG 0X00 //
#define SV_REG 0X01 //分流电压
#define BV_REG 0X02 //总线电压
#define PWR_REG 0X03 //电源功率
#define CUR_REG 0X04 //电流
#define CAL_REG 0X05 //校准,设定满量程范围以及电流和功率测数的
#define ONFF_REG 0X06 //屏蔽 使能 警报配置和转换准备就绪
#define AL_REG 0X07 //包含与所选警报功能相比较的限定值
#define INA226_GET_ADDR 0XFF //包含唯一的芯片标识号
#define INA226_ADDR1 0x80 //ina226地址
#define INA226_GETALADDR 0X14
//定义配置数据
#define INA226_VAL_LSB 2.5f //分流电压 LSB 2.5uV
#define Voltage_LSB 1.25f //总线电压 LSB 1.25mV
#define CURRENT_LSB 1.0f //电流LSB 1mA
#define POWER_LSB 25*CURRENT_LSB
#define CAL 200 //1020 //0.00512*1000/(Current_LSB*R_SHUNT) = 512 //电流偏大改小
typedef struct
{
float voltageVal; //mV
float Shunt_voltage; //uV
float Shunt_Current; //mA
float Power_Val; //功率
float Power; //功率mW
uint32_t ina226_id;
} INA226;
void INA226_SetRegPointer(uint8_t addr,uint8_t reg);
void INA226_SendData(uint8_t addr,uint8_t reg,uint16_t data);
uint16_t INA226_ReadData(uint8_t addr);
void INA226_Get_ID(uint8_t addr); //获取 id
uint16_t INA226_GET_CAL_REG(uint8_t addr); //获取校准值
uint16_t INA226_GetVoltage( uint8_t addr); //获取总线电压装载值
int16_t INA226_GetShunt_Current(uint8_t addr); //获取分流电流装载值
int16_t INA226_GetShuntVoltage(uint8_t addr); //分流电压装载值
uint16_t INA226_Get_Power(uint8_t addr); //获取功率装载值,不使用
void mx_ina226_init(void);
void GetVoltage(float *Voltage);
void Get_Shunt_voltage(float *Current);
void Get_Shunt_Current(float *Current);
void get_power(void); ////获取功率= 总线电压 * 电流
uint8_t INA226_AlertAddr(void);
void Get_Power(float *Power);
extern INA226 ina226_data;
#endif
四、实验现象
将代码烧录进去后,给单片机供电,oled屏幕上可以显示当前电路的总电压、电流和功率,文章内容仅供参考,实际接线和代码修改根据个人情况而定,完整工程demo查看主页
更多推荐



所有评论(0)