> 技术文档 > 【STM32】INA3221三通道电压电流采集模块,HAL库

【STM32】INA3221三通道电压电流采集模块,HAL库

目录

一、工程链接

二、简单介绍

三、模块与接线

四、cubemx配置

设置时钟

设置IIC

设置GPIO

五、keil配置

六、驱动编写

寄存器读写函数

先简单读一下芯片的ID,看看是否能正常通信

读channel的bus电压

读channel的shunt电压值

主函数编写

七、效果展示

​八、驱动附录

ina3221.h

ina3221.c

九、补充功能

一、关键提示

二、警告提示

三、电源有效提示

四、时序控制提示


一、工程链接

STM32INA3221电压电流实时显示OLED工程资源-CSDN文库

二、简单介绍

芯片的datasheet地址:

INA3221 三通道、高侧测量、分流和总线电压监视器,具有兼容 I2C 和 SMBUS 的接口 datasheet (Rev. B)

笔者所使用的INA3221是淘宝买的模块

原理图

模块的三个通道的电压都是一样,都是POWER。这个芯片采用的是高侧测量:每个通道有两个引脚,一个连接负载去给负载供电,另一个回来,回到GND

经过笔者测量和观察,采样电阻的阻值应该是100mΩ

三、模块与接线

INA3221使用IIC通信协议进行读写,笔者使用的是STM32G030F6P6单片机来操作,读者按实际情况类推即可,cubeMX配置,基本都大差不差。

POWER连接外部电源的正极

GND连接外部电源的负极

将模块的GND与单片机的GND连接起来,注意这里地接在一起,如果单片机连着电脑,请务必小心操作,不要接反也不要短路

四、cubemx配置

设置时钟

用内部的RC震荡就可以了,也可以使用外部晶振,但如果用外部晶振的话,这颗晶振必须是有源的,之前看一些G030的板子焊了一个无源晶振,有点匪夷所思。

设置IIC

打算把电压和电流显示在0.96寸的oled上,因此就开了两个IIC,当然了,只开一个也可以,把INA3221和OLED都接在一个IIC总线上就行了,但为了方便,笔者开了两个。

这里的频率可以开到1Mhz,如果是103C8T6,应该是到不了的😋

数据量很小,DMA就不开了

设置GPIO

由于模块上自带了LED灯,所以就没必要开输入IO口给模块,可以开一个监控单片机程序运行的LED

至此,cubeMX配置完毕

五、keil配置

六、驱动编写

打开手册关于编程的章节

支持快速IIC传输,且高位在前

不管是读还是写,一开始要发一个寄存器指针过去,定位一下寄存器

寄存器读写函数

static void INA3221_ReadReg(INA3221_regType *reg){ HAL_I2C_Mem_Read(INA3321_I2C, INA3221_I2C_ADDRESS, reg->address, 1, &reg->data, 2, 0xFFFF); DataReverse(reg->data, &reg->data);} static void INA3221_WriteReg(INA3221_regType *reg){ DataReverse(reg->data, &reg->data); HAL_I2C_Mem_Write(INA3321_I2C, INA3221_I2C_ADDRESS, reg->address, 1, &reg->data, 2, 0xFFFF);}

寄存器表

先简单读一下芯片的ID,看看是否能正常通信

读芯片的id号,值是0x2032,但波形是3220,因此要翻转一下高低字节

代码如下

static void DataReverse(uint16_t raw, uint16_t* cook){ *cook = ((uint8_t)(raw) <> 8);}

读channel的bus电压值

一个位代表8mV,但寄存器里面的左移三位又刚好弥补了这一点,因此直接读到的就是电压值。

代码如下

static void INA3221_Sample_Volt(){ INA3221_ReadReg(&volt1); INA3221_ReadReg(&volt2); INA3221_ReadReg(&volt3);}

读channel的shunt电压值

一个位代表40uV,满量程是163.8mV,因此这个用的100mΩ的模块最大采集电流为1638mA

shunt可以是负数,代表反向电流,但模块设计成IN-接PWR了,笔者就只实验了正向电流

代码如下

static void INA3221_Calculate_Current(uint32_t* current){ current[0] = shunt1.data >> 3; /* 40uV per LSB */ current[0] *= 4; current[0] = current[0] * 10 / SHUNT_RESISTOR; current[1] = shunt2.data >> 3; /* 40uV per LSB */ current[1] *= 4; current[1] = current[1] * 10 / SHUNT_RESISTOR; current[2] = shunt3.data >> 3; /* 40uV per LSB */ current[2] *= 4; current[2] = current[2] * 10 / SHUNT_RESISTOR;}

主函数编写

int main(void){ /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_I2C2_Init(); MX_I2C1_Init(); /* USER CODE BEGIN 2 */INA3221_Init();OLED_Init();OLED_Clear();OLED_ShowString(0,0,\"C1\",16);OLED_ShowString(0,2,\"C2\",16);OLED_ShowString(0,4,\"C3\",16); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { INA3221_GetVolt(); INA3221_GetCurrent(); sprintf(info[0], \"%.2fV \", voltage[0]/1000.0f); sprintf(info[1], \"%.2fV \", voltage[1]/1000.0f); sprintf(info[2], \"%.2fV \", voltage[2]/1000.0f); OLED_ShowString(20,0,info[0],16); OLED_ShowString(20,2,info[1],16); OLED_ShowString(20,4,info[2],16); sprintf(info[3], \"%.3fA\", current[0]/1000.0f); sprintf(info[4], \"%.3fA\", current[1]/1000.0f); sprintf(info[5], \"%.3fA\", current[2]/1000.0f); OLED_ShowString(80,0,info[3],16); OLED_ShowString(80,2,info[4],16); OLED_ShowString(80,4,info[5],16); HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin); HAL_Delay(100); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */}

七、效果展示

八、驱动附录

ina3221.h

#ifndef INA3221_H#define INA3221_H#include\"i2c.h\"#include \"stdint.h\"#include \"main.h\"typedef struct{uint8_t address;uint16_t data;}INA3221_regType;typedef enum{CH1,CH2,CH3,}INA3221_SHUNTChannelType;/** * prepare for mask enable register */typedef struct{uint8_t CF1;uint8_t CF2;uint8_t CF3;uint8_t WF1;uint8_t WF2;uint8_t WF3;}INA3221_FlagType;extern INA3221_regType dieID;extern INA3221_regType mask_enable;extern INA3221_regType volt1;extern INA3221_regType shunt1;extern INA3221_regType volt2;extern INA3221_regType shunt2;extern INA3221_regType volt3;extern INA3221_regType shunt3;extern uint32_t current[3];extern uint16_t voltage[3];extern INA3221_FlagType INA3221_flag;void INA3221_Config();void INA3221_Init();void INA3221_Reset();void INA3221_ReadDieID();void INA3221_ReadmanufactID();void INA3221_GetVolt();void INA3221_GetCurrent();#endif

ina3221.c

#include \"INA3221.h\"#define INA3321_I2C &hi2c1#define INA3221_I2C_ADDRESS 0x80/*shunt resistor mohm*/#define SHUNT_RESISTOR 100#define POWER_VALID_UPPER 10000#define POWER_VALID_LOWER 9000/** * register address table */typedef enum{SHUNT_CH1 = 0x01,VOLT_CH1,SHUNT_CH2,VOLT_CH2,SHUNT_CH3,VOLT_CH3,CRITICAL_CH1,WARNING_CH1,CRITICAL_CH2,WARNING_CH2,CRITICAL_CH3,WARNING_CH3,SHUNT_VOLT_SUM,SHUNT_VOLT_SUM_LIMIT,MASK_ENABLE,POWER_VALID_HIGH = 0x10,POWER_VALID_LOW,}INA3221_RegAddressType;/** * average samples */typedef enum{AVG_1,AVG_4,AVG_16,AVG_64,AVG_128,AVG_256,AVG_512,AVG_1024,}INA3221_AVGType;/** * average samples */typedef enum{CONV_TIME_140US,CONV_TIME_204US,CONV_TIME_332US,CONV_TIME_588US,CONV_TIME_1_1MS,CONV_TIME_2_116MS,CONV_TIME_4_156MS,CONV_TIME_8_244MS,}INA3221_CTType;/** * average samples */typedef enum{POWER_DOWN,SHUNT_SINGLE,BUS_SINGLE,SHUNT_BUS_SINGLE,POWER_DN,SHUNT_CONTINUOUS,BUS_CONTINUOUS,SHUNT_BUS_CONTINUOUS,}INA3221_ModeType;INA3221_regType cfg = {.address = 0};INA3221_regType volt1 = {.address = VOLT_CH1};INA3221_regType shunt1 = {.address = SHUNT_CH1};INA3221_regType volt2 = {.address = VOLT_CH2};INA3221_regType shunt2 = {.address = SHUNT_CH2};INA3221_regType volt3 = {.address = VOLT_CH3};INA3221_regType shunt3 = {.address = SHUNT_CH3};INA3221_regType critical_ch1 = {.address = CRITICAL_CH1};INA3221_regType critical_ch2 = {.address = CRITICAL_CH2};INA3221_regType critical_ch3 = {.address = CRITICAL_CH3};INA3221_regType warning_ch1 = {.address = WARNING_CH1};INA3221_regType warning_ch2 = {.address = WARNING_CH2};INA3221_regType warning_ch3 = {.address = WARNING_CH3};INA3221_regType mask_enable = {.address = MASK_ENABLE};INA3221_regType power_valid_upper = {.address = POWER_VALID_HIGH};INA3221_regType power_valid_lower = {.address = POWER_VALID_LOW};INA3221_regType manufactID = {.address = 0xFE};INA3221_regType dieID = {.address = 0xFF};/* store power voltage */uint32_t current[3];/* store power current */uint16_t voltage[3];INA3221_FlagType INA3221_flag;/** * exchange data high and low byte for word variable */static void DataReverse(uint16_t raw, uint16_t* cook);/** * read register value */static void INA3221_ReadReg(INA3221_regType *reg);/** * write register value */static void INA3221_WriteReg(INA3221_regType *reg);/** * cacluate voltages from volt register value */static void INA3221_Calculate_Volt(uint16_t* volt);/** * cacluate currents from shunt register value */static void INA3221_Calculate_Current(uint32_t* current);/** * set limite value for current alert */static void INA3221_SetLimit(INA3221_regType *reg, uint16_t volt);/** * just read volt registers */static void INA3221_Sample_Volt();/** * just read shunt registers */static void INA3221_Sample_Shunt();static void DataReverse(uint16_t raw, uint16_t* cook){ *cook = ((uint8_t)(raw) <> 8);}static void INA3221_ReadReg(INA3221_regType *reg){ HAL_I2C_Mem_Read(INA3321_I2C, INA3221_I2C_ADDRESS, reg->address, 1, &reg->data, 2, 0xFFFF); DataReverse(reg->data, &reg->data);}static void INA3221_WriteReg(INA3221_regType *reg){ DataReverse(reg->data, &reg->data); HAL_I2C_Mem_Write(INA3321_I2C, INA3221_I2C_ADDRESS, reg->address, 1, &reg->data, 2, 0xFFFF);}static void INA3221_Calculate_Volt(uint16_t* volt){ *volt = volt1.data; *(volt + 1) = volt2.data; *(volt + 2) = volt3.data;}static void INA3221_Calculate_Current(uint32_t* current){ current[0] = shunt1.data >> 3; /* 40uV per LSB */ current[0] *= 4; current[0] = current[0] * 10 / SHUNT_RESISTOR; current[1] = shunt2.data >> 3; /* 40uV per LSB */ current[1] *= 4; current[1] = current[1] * 10 / SHUNT_RESISTOR; current[2] = shunt3.data >> 3; /* 40uV per LSB */ current[2] *= 4; current[2] = current[2] * 10 / SHUNT_RESISTOR;}static void INA3221_SetLimit(INA3221_regType *reg, uint16_t volt){ reg->data = volt; INA3221_WriteReg(reg);}static void INA3221_Sample_Volt(){ INA3221_ReadReg(&volt1); INA3221_ReadReg(&volt2); INA3221_ReadReg(&volt3);}static void INA3221_Sample_Shunt(){ INA3221_ReadReg(&shunt1); INA3221_ReadReg(&shunt2); INA3221_ReadReg(&shunt3);}void INA3221_ReadDieID(){ INA3221_ReadReg(&dieID);}void INA3221_ReadmanufactID(){ INA3221_ReadReg(&manufactID);}void INA3221_GetVolt(){ INA3221_Sample_Volt(); INA3221_Calculate_Volt(voltage);}void INA3221_GetCurrent(){ INA3221_Sample_Shunt(); INA3221_Calculate_Current(current);}void INA3221_Config(){ /* read default register value from chip*/ INA3221_ReadReg(&cfg); /* store it in config variable */ cfg.data |= (cfg.data & !0x0E00) | (AVG_4 << 9); /*bus*/ cfg.data |= (cfg.data & !0x01C0) | (CONV_TIME_2_116MS << 6); /*shunt*/ cfg.data |= (cfg.data & !0x38) | (CONV_TIME_2_116MS << 3); /* wirte to register value */ INA3221_WriteReg(&cfg); INA3221_ReadReg(&cfg);}void INA3221_Set_Critical(INA3221_SHUNTChannelType channel, uint16_t current){ switch (channel) { case CH1: { /* 40uV per LSB */ INA3221_SetLimit(&critical_ch1, (current * SHUNT_RESISTOR / 40) << 3); break; } case CH2: { INA3221_SetLimit(&critical_ch2, (current * SHUNT_RESISTOR / 40) << 3); break; } case CH3: { INA3221_SetLimit(&critical_ch3, (current * SHUNT_RESISTOR / 40) << 3); break; } default: break; }}void INA3221_Set_Warning(INA3221_SHUNTChannelType channel, uint16_t current){ switch (channel) { case CH1: { INA3221_SetLimit(&warning_ch1, (current * SHUNT_RESISTOR / 40) << 3); break; } case CH2: { INA3221_SetLimit(&warning_ch2, (current * SHUNT_RESISTOR / 40) << 3); break; } case CH3: { INA3221_SetLimit(&warning_ch3, (current * SHUNT_RESISTOR / 40) << 3); break; } default: break; }}void INA3221_Reset(){ cfg.data = 0x8000; INA3221_WriteReg(&cfg);}void INA3221_Init(){ INA3221_ReadDieID(); /* reset all registers */ INA3221_Reset(); INA3221_Config();}

九、补充功能

INA3221还带可编程报警和警告输出

一、关键提示

会去比较每个通道的shunt电压值和相应的预设的值,用来判断是否发生过流

二、警告提示

会去比较每个通道的平均shunt电压值和相应的预设的值,用来判断是否发生过流

三、电源有效提示

芯片默认的PV上限是10V,下限是9V,都是可以改写的

意思是在外部高压输入的时候,如果三个通道的电压均高于10V,那么电源OK

如果电压降低,低于9V,电源不OK,模块的灯就会亮起来,因为这几个提示引脚都是开漏输出的

在外部高压输入的时候,如果有任意通道电压<10V,电源不OK,模块的灯就会亮起来,因为这几个提示引脚都是开漏输出的

如果电压升高,高于10V就可以了

有点像施密特触发器

可以把PV上拉到VPU去,这样电压就抬起来了,也可以在PV引脚和地直接串电阻来降压

模块的输入端子里有VPU,根据实际情况使用即可

四、时序控制提示