目录
前言
INA3221-Q1 是一款三通道、 高侧电流和总线电压监视器, 具有一个兼容 I2C 和 SMBUS 的接口。 INA3221-Q1 不仅能够监视分流压降和总线电源电压, 还针对这些信号提供有可编程的转换时间和平均值计算模式。INA3221-Q1 提供关键报警和警告报警, 用于检测每条通道上可编程的多种超范围情况。
1、读手册
1.1管脚说明
该芯片有3路电压电流监测,监测电压范围是0-26V。如图1-1。

1.2器件地址
由数据手册可以看出这个器件可以同时挂载4个,拥有4个不同地址如图1-2
分别是0x40、0x41、0x42、0x43,这四个地址是在使用IIC发出去之前要左移一位,低位做为读写位,于是就变成了0x80、0x82、0x84、0x86,读地址要+1

1.3采样说明
这里说的是理论上采集到的数据可以达到32.76V,但实际上只能到26V,图1-3。

1.4寄存器说明
32.76V哪来的呢?该器件寄存器分三种,配置寄存器(0x0)、ID寄存器和通用寄存器,除了ID寄存器有16位,其他都只有13位,配置寄存器最高位是重置位,低3位并没有值,通用寄存器最高位为正负标志位,低三位没有。
看通用寄存器就有了0x7FF8=32760mv=32.76V
1.4.1寄存器0x00


bit15:复位位,置1则复位,复位后自动清除
bit14-12:通道1-3使能位,置1开启
bit11-9:采样次数,具体值看图1-4-1
bit8-6:总线电压采样一次时间,具体值看图1-4-1
bit5-3:分流电压采样一次时间,具体值看图1-4-1
bit2-0:启动模式
* @note - 0 : 省电
* @note - 1 : 分流电压、触发模式
* @note - 2 : 总线电压、触发模式
* @note - 3 : 分流和总线电压、触发模式
* @note - 4 : 省电
* @note - 5 : 分流电压、持续模式
* @note - 6 : 总线电压、持续模式
* @note - 7 : 分流和总线电压、持续模式
/**
*******************************************************************************
* @brief 设定采集的样本数量并一起取平均值
* @note - 0 : 1 (采样次数)
* @note - 1 : 4 (采样次数)
* @note - 2 : 16 (采样次数)
* @note - 3 : 64 (采样次数)
* @note - 4 : 128 (采样次数)
* @note - 5 : 256 (采样次数)
* @note - 6 : 512 (采样次数)
* @note - 7 : 1024 (采样次数)
*******************************************************************************
*/
uint8_t CFG_AVG = 2;
/**
*******************************************************************************
* @brief 总线电压测量设定转换时间
* @note - 0 : 140us
* @note - 1 : 204us
* @note - 2 : 332us
* @note - 3 : 588us
* @note - 4 : 1.1ms
* @note - 5 : 2.116ms
* @note - 6 : 4.156ms
* @note - 7 : 8.244ms
*******************************************************************************
*/
uint8_t CFG_VbusCT = 5;
/**
*******************************************************************************
* @brief 为分流电压测量设定转换时间
* @note - 0 : 140us
* @note - 1 : 204us
* @note - 2 : 332us
* @note - 3 : 588us
* @note - 4 : 1.1ms
* @note - 5 : 2.116ms
* @note - 6 : 4.156ms
* @note - 7 : 8.244ms
*******************************************************************************
*/
uint8_t CFG_VshCT = 5;
/**
*******************************************************************************
* @brief 选择连续的、 触发的、 或者省电运行模式
* @note - 0 : 省电
* @note - 1 : 分流电压、触发模式
* @note - 2 : 总线电压、触发模式
* @note - 3 : 分流和总线电压、触发模式
* @note - 4 : 省电
* @note - 5 : 分流电压、持续模式
* @note - 6 : 总线电压、持续模式
* @note - 7 : 分流和总线电压、持续模式
*******************************************************************************
*/
uint8_t CFG_Mode = 7;
void INA3221_Init(void)
{
//I2C初始化
//引脚初始化
//芯片初始化
INA3221_SendData(0x80, 0x00, 0x8000); //软件复位
// 配置寄存器设置控制三个输入通道的分流和总线电压测量的工作模式。
// 该寄存器控制分流和总线电压测量的转换时间设置以及使用的平均模式。
// 配置寄存器用于独立启用或禁用每个通道,以及选择控制选择要测量的信号的操作模式。
// 详见数据手册P26
unsigned short cfg_reg_value = 0x7000|(CFG_AVG<<9)|(CFG_VbusCT<<6)|(CFG_VshCT<<3)|CFG_Mode;
INA3221_SendData(0x80, 0x00, cfg_reg_value);
}
1.4.2寄存器0XFF
首先是读器件ID,唯一模具标识号,发送器件地址和寄存器地址后就可以读取到器件地址,如果读回来的数不是0x3220,则代表通讯失败

/**
* @description: INA3221自检
* @param {uint8_t} addr
* @return {*}
*/
void INA3221_SelfCheck(uint8_t addr)
{
uint16_t id = 0;
while (id != INA3221_DIE_ID)
{
//卡这说明硬件连接异常或者是地址错误
INA3221_SetRegPointer(addr, INA3221_DIEID_REG);
id = INA3221_ReadData(addr);
}
}
1.4.3寄存器0xFE
和读ID一样,唯一制造商标识号,如不是5449则表示通讯失败

1.4.4寄存器0x01、0x03、0x05
同0x03、0x05寄存器,0x01是通道1的分流电压,同理0x03是通道2的分流电压,0x05是通道3的分流电压,最终值是IN+和IN-的压差,同理通道2和5一致,如果没带载,大概率采回来的值是0或者8,为什么是8?因为低3位没有,所以如果有一点干扰就会是8,所以此处可以软件滤波
分流电压采集回来的值是要计算电流的,具体计算为
/*
I = 采集值>>3*0.04/分流电阻值
具体实现看驱动程序
*/
/**
* @description: 获取分流电压
* @param {uint8_t} addr ina3221的IIC地址
* @param {uint8_t} channel 通道编号(1\2\3)
* @return {uint16_t} 通道的分流电压
*/
uint16_t INA3221_GetShuntVoltage(uint8_t addr, uint8_t channel)
{
uint32_t temp = 0;
switch (channel)
{
case 1:
INA3221_SetRegPointer(addr, 0x01);
break;
case 2:
INA3221_SetRegPointer(addr, 0x03);
break;
case 3:
INA3221_SetRegPointer(addr, 0x05);
break;
default:
break;
}
//读取数据判断最高位是否为负数
temp = INA3221_ReadData(addr);
if (temp & 0x8000)
temp = ~(temp - 1);
return (uint16_t)temp;
}

1.4.5寄存器0x02、0x04、0x06
同0x04、0x06寄存器,0x02是通道1的总线电压,同理0x03是通道2的总线电压,0x05是通道3的总线电压,采集回来就可以用,因为采集回来的值需要右移3乘8,抵消了所以采集回来就是总线电压。

uint16_t INA3221_GetVoltage(uint8_t addr, uint8_t channel)
{
uint32_t temp = 0;
switch (channel)
{
case 1:
INA3221_SetRegPointer(addr, 0x02);
break;
case 2:
INA3221_SetRegPointer(addr, 0x04);
break;
case 3:
INA3221_SetRegPointer(addr, 0x06);
break;
default:
break;
}
temp = INA3221_ReadData(addr);
if (temp & 0x8000)
temp = ~(temp - 1);
return (uint16_t)temp;
}
1.4.6寄存器0x07、0x09、0x0B
监测总线的过流事件,通过分压电阻计算电流是否超过预设值,如果超过了预设值,则CRITICAL脚会被拉低(此处可以做外部中断将电源关闭,防止烧坏设备)。具体计算为
例如预设值=1600ma(1.6A)
vi= 1600*25/0.04)<<3;
INA3221_SendData(0x80, 0x07, vi);

1.4.7寄存器0x08、0x0A、0x0C
监测总线的过压事件,通过总线电阻计算电压是否超过预设值,如果超过了预设值,则Warning脚会被拉低(此处可以做外部中断将电源关闭,防止烧坏设备)。具体计算与1.4.6一致。
1.4.8寄存器0x0F
根据数据手册可以知道这个寄存器应该是可以清除过流过压保护引脚的标志,目的是可以清除标志位,重新软件上电而不用手动上电。暂时没使用到,后续会补充。
1.4.9寄存器0X0D、0X0E
分流电压和(0x0D),分流电压和限制(0x0E),暂时没使用到,后续会补充
1.4.10寄存器0X10、0X11
功率有效上限和有效下限,暂时没使用到,后续补充
2、驱动
ina3221.c
#include "ina3221.h"
#define INA3221_CFG_REG 0x00 //配置寄存器
#define INA3221_CH1SHUNT_REG 0x01 //通道 1 分流电压
#define INA3221_CH1BUS_REG 0x02 //通道 1 总线电压
#define INA3221_CH2SHUNT_REG 0x03 //通道 2 分流电压
#define INA3221_CH2BUS_REG 0x04 //通道 2 总线电压
#define INA3221_CH3SHUNT_REG 0x05 //通道 3 分流电压
#define INA3221_CH3BUS_REG 0x06 //通道 3 总线电压
#define INA3221_CH1CAL_REG 0x07 //通道 1 严重警报限制
#define INA3221_CH1WAL_REG 0x08 //通道 1 警告警报限制
#define INA3221_CH2CAL_REG 0x09 //通道 2 严重警报限制
#define INA3221_CH2WAL_REG 0x0A //通道 2 警告警报限制
#define INA3221_CH3CAL_REG 0x0B //通道 3 严重警报限制
#define INA3221_CH3WAL_REG 0x0C //通道 3 警告警报限制
#define INA3221_SVS_REG 0x0D //分流电压和
#define INA3221_SVSLIMIT_REG 0x0E //分流电压和限制
#define INA3221_ME_REG 0x0F //屏蔽/启用 警报
#define INA3221_PVUPPER_REG 0x10 //功率有效上限
#define INA3221_PVLOW_REG 0x11 //功率有效下限
#define INA3221_MANUID_REG 0xFE //制造商标识号
#define INA3221_DIEID_REG 0xFF //模具标识号
#define INA3221_MANU_ID 0x5449 //唯一制造商标识号
#define INA3221_DIE_ID 0x3220 //唯一模具标识号
/**
*******************************************************************************
* @brief 设定采集的样本数量并一起取平均值
* @note - 0 : 1 (采样次数)
* @note - 1 : 4 (采样次数)
* @note - 2 : 16 (采样次数)
* @note - 3 : 64 (采样次数)
* @note - 4 : 128 (采样次数)
* @note - 5 : 256 (采样次数)
* @note - 6 : 512 (采样次数)
* @note - 7 : 1024 (采样次数)
*******************************************************************************
*/
uint8_t CFG_AVG = 2;
/**
*******************************************************************************
* @brief 总线电压测量设定转换时间
* @note - 0 : 140us
* @note - 1 : 204us
* @note - 2 : 332us
* @note - 3 : 588us
* @note - 4 : 1.1ms
* @note - 5 : 2.116ms
* @note - 6 : 4.156ms
* @note - 7 : 8.244ms
*******************************************************************************
*/
uint8_t CFG_VbusCT = 5;
/**
*******************************************************************************
* @brief 为分流电压测量设定转换时间
* @note - 0 : 140us
* @note - 1 : 204us
* @note - 2 : 332us
* @note - 3 : 588us
* @note - 4 : 1.1ms
* @note - 5 : 2.116ms
* @note - 6 : 4.156ms
* @note - 7 : 8.244ms
*******************************************************************************
*/
uint8_t CFG_VshCT = 5;
/**
*******************************************************************************
* @brief 选择连续的、 触发的、 或者省电运行模式
* @note - 0 : 省电
* @note - 1 : 分流电压、触发模式
* @note - 2 : 总线电压、触发模式
* @note - 3 : 分流和总线电压、触发模式
* @note - 4 : 省电
* @note - 5 : 分流电压、持续模式
* @note - 6 : 总线电压、持续模式
* @note - 7 : 分流和总线电压、持续模式
*******************************************************************************
*/
uint8_t CFG_Mode = 7;
/************************模拟IIC接口****************************/
void INA3221_IIC_Start(void)
{
my_I2C_SDA_H;
my_I2C_SCL_H;
delay_us(5);
my_I2C_SDA_L; // START:when CLK is high,DATA change form high to low
delay_us(5);
my_I2C_SCL_L; //钳住I2C总线,准备发送或接收数据
delay_us(5);
}
void INA3221_IIC_Stop(void)
{
my_I2C_SDA_L; // STOP:when CLK is high DATA change form low to high
delay_us(5);
my_I2C_SCL_H;
delay_us(5);
my_I2C_SDA_H; //发送I2C总线结束信号
delay_us(5);
}
void INA3221_IIC_Ack(void)
{
my_I2C_SDA_L;
delay_us(5);
my_I2C_SCL_H;
delay_us(5);
my_I2C_SCL_L;
delay_us(5);
my_I2C_SDA_H;
}
void INA3221_IIC_NAck(void)
{
my_I2C_SDA_H;
delay_us(5);
my_I2C_SCL_H;
delay_us(5);
my_I2C_SCL_L;
delay_us(5);
my_I2C_SDA_L;
}
uint8_t INA3221_IIC_Wait_Ack(void)
{
uint8_t ucErrTime = 0;
my_I2C_SDA_H;
my_SDA_IN(); // SDA设置为输入
delay_us(5);
my_I2C_SCL_H;
delay_us(5);
while (my_READ_SDA())
{
ucErrTime++;
if (ucErrTime > 250)
{
INA3221_IIC_Stop();
return 1;
}
}
my_I2C_SCL_L; //时钟输出0
my_SDA_OUT(); // SDA设置为输入
return 0;
}
void INA3221_IIC_Send_Byte(uint8_t txd)
{
my_I2C_SCL_L; //拉低时钟开始数据传输
for (uint8_t i = 0; i < 8; i++)
{
if (txd & 0x80)
my_I2C_SDA_H;
else
my_I2C_SDA_L;
txd <<= 1;
my_I2C_SCL_H;
delay_us(5);
my_I2C_SCL_L;
delay_us(5);
}
}
uint8_t INA3221_IIC_Read_Byte(unsigned char ack)
{
uint8_t TData = 0, i;
my_SDA_IN(); // SDA设置为输入
for (i = 0; i < 8; i++)
{
my_I2C_SCL_H;
delay_us(5);
TData = TData << 1;
// if(GPIOB->IDR& GPIO_IDR_IDR7) //判断SDA是否为高
if (my_READ_SDA())
{
TData |= 0x01;
}
my_I2C_SCL_L;
delay_us(5);
}
my_SDA_OUT(); // SDA设置为输出
if (!ack)
INA3221_IIC_NAck();
else
INA3221_IIC_Ack();
return TData;
}
void INA3221_SendData(uint8_t addr, uint8_t reg, uint16_t data)
{
uint8_t temp = 0;
INA3221_IIC_Start();
INA3221_IIC_Send_Byte(addr);
INA3221_IIC_Wait_Ack();
INA3221_IIC_Send_Byte(reg);
INA3221_IIC_Wait_Ack();
temp = (uint8_t)(data >> 8);
INA3221_IIC_Send_Byte(temp);
INA3221_IIC_Wait_Ack();
temp = (uint8_t)(data & 0x00FF);
INA3221_IIC_Send_Byte(temp);
INA3221_IIC_Wait_Ack();
INA3221_IIC_Stop();
}
void INA3221_SetRegPointer(uint8_t addr, uint8_t reg)
{
INA3221_IIC_Start();
INA3221_IIC_Send_Byte(addr);
INA3221_IIC_Wait_Ack();
INA3221_IIC_Send_Byte(reg);
INA3221_IIC_Wait_Ack();
INA3221_IIC_Stop();
}
uint16_t INA3221_ReadData(uint8_t addr)
{
uint16_t temp = 0;
INA3221_IIC_Start();
INA3221_IIC_Send_Byte(addr + 1);
INA3221_IIC_Wait_Ack();
temp = INA3221_IIC_Read_Byte(1);
temp <<= 8;
temp |= INA3221_IIC_Read_Byte(0);
INA3221_IIC_Stop();
return temp;
}
/**
* @description: INA3221自检
* @param {uint8_t} addr
* @return {*}
*/
void INA3221_SelfCheck(uint8_t addr)
{
uint16_t id = 0;
while (id != INA3221_DIE_ID)
{
// delay_ms(50);
//卡这说明硬件连接异常或者是地址错误
INA3221_SetRegPointer(addr, INA3221_DIEID_REG);
id = INA3221_ReadData(addr);
}
}
/********************************应用部分****************************************/
/**
* @description: INA3221初始化
* @return {*}
*/
void INA3221_Init(void)
{
//I2C初始化
//引脚初始化
//芯片初始化
INA3221_SendData(INA3221_ADDR1, INA3221_CFG_REG, 0x8000); //软件复位
INA3221_SendData(INA3221_ADDR2, INA3221_CFG_REG, 0x8000); //软件复位
delay_ms(10);
// 配置寄存器设置控制三个输入通道的分流和总线电压测量的工作模式。
// 该寄存器控制分流和总线电压测量的转换时间设置以及使用的平均模式。
// 配置寄存器用于独立启用或禁用每个通道,以及选择控制选择要测量的信号的操作模式。
// 详见数据手册P26
unsigned short cfg_reg_value = 0x7000|(CFG_AVG<<9)|(CFG_VbusCT<<6)|(CFG_VshCT<<3)|CFG_Mode;
INA3221_SendData(INA3221_ADDR1, INA3221_CFG_REG, cfg_reg_value); // 7127为默认配置
INA3221_SendData(INA3221_ADDR2, INA3221_CFG_REG, cfg_reg_value); // 7127为默认配置
}
/**
* @description: 获取电压
* @param {uint8_t} addr
* @param {uint8_t} channel 通道编号(1\2\3)
* @return {uint16_t} 通道所对应的电压
*/
uint16_t INA3221_GetVoltage(uint8_t addr, uint8_t channel)
{
uint32_t temp = 0;
switch (channel)
{
case 1:
INA3221_SetRegPointer(addr, INA3221_CH1BUS_REG);
break;
case 2:
INA3221_SetRegPointer(addr, INA3221_CH2BUS_REG);
break;
case 3:
INA3221_SetRegPointer(addr, INA3221_CH3BUS_REG);
break;
default:
break;
}
temp = INA3221_ReadData(addr);
if (temp & 0x8000)
temp = ~(temp - 1);
return (uint16_t)temp;
}
/**
* @description: 获取分流电压
* @param {uint8_t} addr ina3221的IIC地址
* @param {uint8_t} channel 通道编号(1\2\3)
* @return {uint16_t} 通道的分流电压
*/
uint16_t INA3221_GetShuntVoltage(uint8_t addr, uint8_t channel)
{
uint32_t temp = 0;
switch (channel)
{
case 1:
INA3221_SetRegPointer(addr, INA3221_CH1SHUNT_REG);
break;
case 2:
INA3221_SetRegPointer(addr, INA3221_CH2SHUNT_REG);
break;
case 3:
INA3221_SetRegPointer(addr, INA3221_CH3SHUNT_REG);
break;
default:
break;
}
temp = INA3221_ReadData(addr);
if (temp & 0x8000)
temp = ~(temp - 1);
// uint8_t data[2]={0};
// data[0]=temp>>8&0xff;
// data[1]=temp&0xff;
// RS485_SendData(data,2);
return (uint16_t)temp;
}
///
///
///
//设置电压警告
static void Set_Voltage_Warning(uint8_t ch,uint16_t value)
{
switch(ch)
{
case 1:INA3221_SendData(INA3221_ADDR1, INA3221_CH1WAL_REG, value); break;
case 2:INA3221_SendData(INA3221_ADDR1, INA3221_CH2WAL_REG, value); break;
case 3:INA3221_SendData(INA3221_ADDR1, INA3221_CH3WAL_REG, value); break;
case 4:INA3221_SendData(INA3221_ADDR2, INA3221_CH1WAL_REG, value); break;
case 5:INA3221_SendData(INA3221_ADDR2, INA3221_CH2WAL_REG, value); break;
case 6:INA3221_SendData(INA3221_ADDR2, INA3221_CH3WAL_REG, value); break;
}
}
//设置电压严重报警
static void Set_Voltage_Critical(uint8_t ch,uint16_t value)
{
switch(ch)
{
case 1:INA3221_SendData(INA3221_ADDR1, INA3221_CH1CAL_REG, value); break;
case 2:INA3221_SendData(INA3221_ADDR1, INA3221_CH2CAL_REG, value); break;
case 3:INA3221_SendData(INA3221_ADDR1, INA3221_CH3CAL_REG, value); break;
case 4:INA3221_SendData(INA3221_ADDR2, INA3221_CH1CAL_REG, value); break;
case 5:INA3221_SendData(INA3221_ADDR2, INA3221_CH2CAL_REG, value); break;
case 6:INA3221_SendData(INA3221_ADDR2, INA3221_CH3CAL_REG, value); break;
}
}
//统一设置警告值
void Set_WARNING_CRITICAL(void)
{
float vcci[6] = {1600/1000.0,
1200/1000.0,
350/1000.0,
350/1000.0,
350/1000.0,
350/1000.0};
uint16_t vi = 0;
for(int i=0;i<6;i++)
{
vi= (uint16_t)(vcci[i]*25/0.04)<<3;
Set_Voltage_Warning(i+1,vi);//过压保护
Set_Voltage_Critical(i+1,vi);//过流保护
}
}
ina3221.h
#ifndef __INA3221_H
#define __INA3221_H
#include "stdint.h"
#include "myiic.h"
#include "bsp_tmra.h"
#include "power.h"
#define INA3221_ADDR1 0x80 // A0=GND
#define INA3221_ADDR2 0x82 // A0=VS
#define INA3221_ADDR3 0x84 // A0=SDA
#define INA3221_ADDR4 0x86 // A0=SCL
void INA3221_Init(void);
uint16_t INA3221_GetVoltage(uint8_t addr, uint8_t channel);
uint16_t INA3221_GetShuntVoltage(uint8_t addr, uint8_t channel);
void Set_WARNING_CRITICAL(void);
#endif