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

news2024/11/24 7:08:12

一、简单介绍

芯片的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) | (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) | (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,根据实际情况使用即可

四、时序控制提示

九、工程链接

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2230691.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言之写一个修改数组内容的函数

问题代码: 函数ltrim是为了消除buf字符数组中左边空格&#xff0c; memmove函数介绍 如果对c语言指针运用非常熟练的人,结合函数功能就会发现这个代码非常的傻逼&#xff0c;你会发现为什么需要返回&#xff0c;buf不用接收返回值&#xff0c;执行这个函数后buf中的内容就已经…

StandardThreadExecutor源码解读与使用(tomcat的线程池实现类)

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Java源码解读-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 目录 目录 1.前言 2.线程池基础知识回顾 2.1.线程池的组成 2.2.工作流程 2…

解决pytorch问题:received an invalid combination of arguments - got

问题表现 今天跑模型时报了一个非常奇怪的错误&#xff1a; 意思是“你的lstm层输入的参数是无效的&#xff0c;要求接收参数的类型是(Tensor, tuple of (Tensor, Tensor), list of [Parameter, Parameter, Parameter, Parameter], float, int, float, bool, bool, bool)&am…

Springboot与easypoi(2):合并单元格、二级表头、动态导出

一、纵向合并单元格 使用Excel(needMerge true)标记的属性表示此单元格需要合并。ExcelCollection表示一对多的集合&#xff0c;下面是合并单元格案例。 实体类 企业类&#xff1a; package com.ywz.entity;import cn.afterturn.easypoi.excel.annotation.Excel; import cn.…

vue3 + ts + element-plus 二次封装 el-table

一、实现效果&#xff1a; &#xff08;1&#xff09;数据为空时&#xff1a; &#xff08;2&#xff09;有数据时&#xff1a;存在数据合并&#xff1b;可自定义表头和列的内容 &#xff08;3&#xff09;新增行&#xff1a; &#xff08;4&#xff09;删除行&#xff1a; &a…

Python CGI编程-cookie的设置、检索

设置检索 其他&#xff1a; 1. http.cookies和http.cookiejar区别&#xff1a; http.cookies主要用于创建和操作单个cookie对象&#xff0c;适用于需要精细控制单个cookie属性的场景。http.cookiejar则用于管理多个cookie&#xff0c;适用于需要自动处理多个请求和响应中的coo…

k8s 二进制部署安装(三)

目录 部署Calico Calico 工作原理 部署Calico 部署CoreDNS 负载均衡部署 部署dashboard 部署Calico 安装步骤来到 CNI 网络组件 &#xff0c;在&#xff08;二&#xff09;中我们安装了flannel&#xff0c;现在我们要尝试安装另一网络组件Calico calico 不使用隧道或NAT…

idea 创建web工程

模块添加Add Framework Support web Application 改为4.0以上

Linux系列-进程的概念

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 这篇文章&#xff0c;我们主要分析一下进程。 之前&#xff0c;我们讲过了冯诺依曼体系架构&#xff0c; 我们常见的计算机&#xff0c;像笔记本&#xff0c;或者不常见的计算机…

Rust的enum枚举的强大用法

在Rust中&#xff0c;enum&#xff08;枚举&#xff09;是一种非常强大的类型&#xff0c;它可以包含多个变体&#xff08;variants&#xff09;&#xff0c;每个变体可以是不同的类型&#xff0c;包括复杂类型。这使得enum在Rust中不仅用于表示简单的状态或选项集合&#xff0…

【初识Linux】

十点半的飞机快要到了&#xff0c;机场还是那么的拥挤.......................................................................... 文章目录 一、【初识指令】 1、 ls✔ 2、pwd✔ 3、cd✔ 4. touch✔ 5.mkdir✔ 6.rmdir✔ 7、rm ✔ 8.man✔ 9.cp✔ ​10、mv✔ 11、cat✔…

头歌——算法设计与分析(贪心法)

文章目录 第1关&#xff1a;贪心法代码 第2关&#xff1a;最小生成树代码 第3关&#xff1a;Huffman 编码代码 第4关&#xff1a;单源点最短路径代码 第1关&#xff1a;贪心法 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a;贪心法 ;。 贪心法&#xff0c;又称…

面试题:JVM(四)

new对象流程&#xff1f;&#xff08;龙湖地产&#xff09; 对象创建方法&#xff0c;对象的内存分配。&#xff08;360安全&#xff09; 1. 对象实例化 创建对象的方式有几种&#xff1f; 创建对象的步骤 指针碰撞&#xff1a;以指针为分界线&#xff0c;一边是已连续使用的…

【进阶sql】复杂sql收集及解析【mysql】

开发时会出现&#xff0c;必须写一些较复杂sql的场景 可能是给会sql的客户 提供一些统计sql 或是临时需要统计数据信息但是 开发一个统计功能有来不及的情况 也可能是报表系统组件 只支持 sql统计的情况 特地记录下这些sql 作为积累 substring 截取查询出的字符串&#xff…

Python实现全国岗位招聘信息可视化分析(源码+论文+部署讲解)

项目源码&数据源获取 利用Python实现全国岗位招聘信息可视化分析 项目背景&#xff1a; 1.为企业招聘决策提供科学的依据和参考&#xff0c;可以帮助人力资源部门、招聘机构和求职者了解当前的就业形势、行业趋势和人才需求&#xff0c;从而做出更明智的招聘和求职决策。…

Python毕业设计选题:基于django+vue的宠物寄养平台的设计与实现

开发语言&#xff1a;Python框架&#xff1a;djangoPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 1. 前台系统功能模块 系统首页界面 用户注册界面 用户登录界面 宠物商城界面 宠物店…

Linux云计算 |【第五阶段】CLOUD-DAY10

主要内容&#xff1a; 部署Dashboard、部署Prometheus、部署HPA集群 一、Dashboard介绍 Dashboard是基于网页的Kubernetes用户界面&#xff0c;可以使用Dashboard将容器应用部署到Kubernetes集群中&#xff0c;也可以对容器应用排错&#xff0c;还能管理集群资源。可以使用Da…

将Notepad++添加到右键菜单【一招实现】

一键添加注册表 复制以下代码保存为 Notepad.reg&#xff0c;将红框内路径修改为自己电脑的“Notepad.exe路径”后&#xff0c;再双击运行即可。 Windows Registry Editor Version 5.00[HKEY_CLASSES_ROOT\*\shell\NotePad] "Notepad" "Icon""D:\\N…

Git下载-连接码云-保姆级教学(连接Gitee失败的解决)

Git介绍 码云连接 一、Git介绍 二、Git的工作机制 下载链接&#xff1a;Git - 下载软件包 三、使用步骤 创建一个wss的文件夹&#xff0c;作为‘工作空间’ 四、连接码云账号 五、连接Gitee失败的解决方法 一、Git介绍 Git是一个免费的、开源的分布式版本控制…

Spring Boot 与 Vue 共铸卓越采购管理新平台

作者介绍&#xff1a;✌️大厂全栈码农|毕设实战开发&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。 &#x1f345;获取源码联系方式请查看文末&#x1f345; 推荐订阅精彩专栏 &#x1f447;&#x1f3fb; 避免错过下次更新 Springboot项目精选实战案例 更多项目…