STM32F4 | GPIO工作原理和配置 | GPIO库函数 | IO口操作步骤

news2024/11/19 10:17:40

文章目录

    • 一、GPIO基础知识
      • 1.GPIO是什么?
      • 2.引脚和GPIO的区别和联系?
      • 3.绝多数引脚都是GPIO,有限的引脚怎么实现更多的功能?
      • 4. 怎么知道具体的芯片外设资源?
      • 5.怎么查看GPIO引脚功能?
    • 二、GPIO的8种工作模式
    • 三、GPIO寄存器
      • 1.端口模式寄存器(GPIOx_MODER)
      • 2.端口输出类型寄存器(GPIOx_OTYPER)
      • 3.端口输出速度寄存器(GPIOx_OSPEEDR)
      • 4.端口上拉/下拉寄存器(GPIOx_PUPDR)
      • 5.端口输入数据寄存器(GPIOx_IDR)
      • 6.端口输出数据寄存器(GPIOx_ODR)
      • 7.端口置位/复位寄存器(GPIOx_BSRR)
      • 8.端口配置锁定寄存器(GPIOx_LCKR)
      • 9.复用功能寄存器(GPIOx_AFRL、AFRH)
    • 四、GPIO常用库函数
    • 五、配置HAL库操作IO口步骤

一、GPIO基础知识

1.GPIO是什么?

  GPIO的全称是general purpose intput output。它是通用输入输出端口。既可以做输入也可以做输出。GPIO端口可通过程序配置成输入或者输出。

2.引脚和GPIO的区别和联系?

  STM32F4的引脚中,有部分是做GPIO使用,部分是电源引脚/复位引脚/启动模式引脚/晶振引脚/调试下载引脚。
在这里插入图片描述

  • 一共有9组IO: PA~PI,其中PA~PH 每组16个IOPI只有PI0~PI11,一共有140个IO口:16*8+12=140

STM32F4中约有140个引脚可做IO口使用,还有36个引脚可做电源引脚/复位引脚/启动模式引脚/晶振引脚/调试下载引脚等

3.绝多数引脚都是GPIO,有限的引脚怎么实现更多的功能?

  STM32的大部分引脚除了当GPIO使用外,还可以复用为外设功能引脚(比如串口)。一个引脚,可以作为IO口,同时也可以作为复用功能外设引脚
  复用的原理 可以用图像抽象如下: 在这里插入图片描述
通过开关控制引脚组,可以大大节省IO口资源。

4. 怎么知道具体的芯片外设资源?

  可以通过查找ST MCU选型手册来查找。
在这里插入图片描述

5.怎么查看GPIO引脚功能?

  每个STM32芯片的芯片数据手册(例如:STM32F429IGT6.pdf)都会提供引脚功能描述,如下表。
在这里插入图片描述
其中的FT标识表示该IO口可以5V容忍。

二、GPIO的8种工作模式

  GPIO有8种工作模式:

  • 4种输入模式

    • 输入浮空
      在这里插入图片描述
      IO口电平直接进入TTL施密特触发器(上拉与下拉均没有起作用),然后到达输入数据寄存器,这样CPU可以通过输入数据寄存器来读取IO口的状态

    • 输入上拉
      在这里插入图片描述
      输入上拉与输入浮空的区别就是上拉会起作用。

    • 输入下拉
      在这里插入图片描述
      输入下拉与输入浮空的区别就是下拉会起作用。

    • 模拟输入
      在这里插入图片描述
      IO口电平直接进入模拟通道,ADC控制单元采集电压信号,CPU读取ADC控制单元相关的寄存器来读取模拟信号并且转化为数字信号(不是CPU转换的,而是ADC控制单元转换的)。

  • 4种输出模式(带上下拉)

    • 开漏输出(带上拉或者下拉)
      在这里插入图片描述
      在这里插入图片描述
      只可以输出强低电平,高电平得靠外部电阻拉高。输出端相当于三极管的集电极. 要得到高电平状态需要上拉电阻才行. 适合于做电流型的驱动,其吸收电流的能力相对强

    • 开漏复用功能(带上拉或者下拉)
      在这里插入图片描述
      复用功能输出连接的片上的外设模块,CPU控制外设控制模块。

    • 推挽式输出(带上拉或者下拉)
      在这里插入图片描述
      设置IO口输出为1,则为高电平;设置IO口输出为0,则为低电平。可以输出强高低电平,连接数字器件。

    • 推挽式复用功能(带上拉或者下拉)
      在这里插入图片描述

  GPIO可以配置4种最大输出速度

  • 2MHz低速
  • 25MHz中速
  • 50MHz快速
  • 100MHz高速

  上电复位后GPIO默认为输入浮空状态,部分特殊功能引脚为特定状态。复位后,调试引脚处于复用功能上拉/下拉状态:

  • PA15JTDI处于上拉状态
  • PA14:JTCK/SWCLK处于下拉状态
  • PA13:JTMS/SWDAT处于下拉状态
  • PB4:NJTRST处于上拉状态
  • PB3:JTDO处于浮空状态

三、GPIO寄存器

  STM32F4 每组通用 I/O 端口包括 4 个 32 位配置寄存器(MODEROTYPEROSPEEDRPUPDR)、2 个 32 位数据寄存器(IDRODR)、1 个 32 位置位/复位寄存器 (BSRR)、1 个 32 位锁定寄存器 (LCKR) 和 2 个 32 位复用功能选择寄存器(AFRHAFRL)等。这样,STM32F4 每组 IO 有 10 个 32 位寄存器控制,其中常用的有 4 个配置寄存器+2 个数据寄存器+2 个复用功能选择寄存器,共 8 个。接下来我们详细介绍 IO 配置常用的 8 个寄存器: MODEROTYPEROSPEEDRPUPDRODRIDRAFRHAFRL。同时讲解对应的 HAL 库配置方法。
重点说明

  • 每组IO口由10个寄存器组成,如果芯片有GPIOA~GPIOI 9个组,那么一共有对应9X10=90个寄存器。
  • 如果配置一个IO口需要2个位,那么刚好32位寄存器配置一组IO口(16个IO口)
  • 如果配置一个IO口只需要1个位,一般高16位保留
  • BSRR寄存器32位分为低16位BSRRL和高16位BSRRHBSRRL配置一组IO口的16个IO口的置位状态(1),BSRRH配置复位状态(0)

1.端口模式寄存器(GPIOx_MODER)

  该寄存器是 GPIO 端口模式控制寄存器,用于控制 GPIOx的工作模式
在这里插入图片描述
该寄存器各位在复位后,一般都是 0(个别不是 0,比如 JTAG 占用的几个 IO 口),也就是默认条件下一般是输入状态的。每组 IO 下有 16 个 IO 口,该寄存器共 32 位,每 2 个位控制 1个 IO

2.端口输出类型寄存器(GPIOx_OTYPER)

  该寄存器用于控制 GPIOx 的输出类型
在这里插入图片描述
该寄存器仅用于输出模式,在输入模式(MODER[1:0]=00/11 时)下不起作用。该寄存器低 16 位有效,每一个位控制一个 IO 口。设置为 0 是推挽输出,设置为 1 是开漏输出。复位后,该寄存器值均为 0,也就是在输出模式下 IO 口默认为推挽输出。

3.端口输出速度寄存器(GPIOx_OSPEEDR)

  该寄存器用于控制 GPIOx 的输出速度,
在这里插入图片描述
该寄存器也仅用于输出模式,在输入模式(MODER[1:0]=00/11 时)下不起作用。该寄存器每 2 个位控制一个 IO 口,复位后,该寄存器值一般为 0。

4.端口上拉/下拉寄存器(GPIOx_PUPDR)

  该寄存器用于控制 GPIOx 的上拉/下拉,
在这里插入图片描述
该寄存器每 2 个位控制一个 IO 口,用于设置上下拉,STM32F4 则由单独的寄存器PUPDR控制上下拉,使用起来更加灵活。
复位后,该寄存器值一般为 0。
  前面,我们讲解了 4 个重要的配置寄存器。顾名思义,配置寄存器就是用来配置 GPIO 的相关模式和状态,接下来,分析怎么在 HAL 库中初始化 GPIO 配置。
  GPIO 相关的函数和定义分布在 HAL 库文件 stm32f4xx_hal_gpio.c 和头文件stm32f4xx_hal_gpio.h 文件中。在 HAL 库中,操作四个配置寄存器初始化 GPIO 是通过 HAL_GPIO_Init 函数完成:

void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);

  该函数有两个参数,第一个参数是用来指定需要初始化的 GPIO 对应的 GPIO 组,取值范围为 GPIOA~GPIOK。第二个参数为初始化参数结构体指针,结构体类型为 GPIO_InitTypeDef。下面我们看看这个结构体的定义:

typedef struct
{
	uint32_t Pin; //指定 IO 口
	uint32_t Mode; //模式设置
	uint32_t Pull; //上下拉设置
	uint32_t Speed; //速度设置
	uint32_t Alternate;//复用映射配置
} GPIO_InitTypeDef;

初始化 GPIO 的常用格式是:

GPIO_InitTypeDef GPIO_Initure;
GPIO_Initure.Pin=GPIO_PIN_0; //PB0
GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
HAL_GPIO_Init(GPIOB,&GPIO_Initure);

设置 PB0 端口为推挽输出模式,输出速度为高速,上拉。从上面初始化代码可以看出,结构体 GPIO_Initure 的第一个成员变量 Pin 用来设置是要初始化哪个或者哪些 IO 口。第二个成员变量 Mode 是用来设置对应 IO 端口的输出输入端口模式,这个变量实际配置的是 GPIOxMODER 寄存器。第三个成员变量 Pull 是用来设置上拉还是下拉,配置的是 GPIOx PUPDR 寄存器。第四个成员变量 Speed 用来设置输出速度,配置的是 GPIOx OSPEEDR 寄存器。第五个成员变量 Alternate,它是用来设置引脚的复用映射的。

5.端口输入数据寄存器(GPIOx_IDR)

  该寄存器用于读取 GPIOx 的输入数据,
在这里插入图片描述
该寄存器用于读取某个 IO 的电平,如果对应的位为0(IDRy=0),则说明该 IO 输入的是低电平,如果是 1(IDRy=1),则表示输入的是高电平。HAL 库操作该寄存器读取 IO 输入数据相关函数:

GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

该函数用来读取一组IO下一个或者多个IO口电平状态。比如我们要读取GPIOF.5的输入电平,方法为:

HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_5);//读取 PF5 的输入电平

该函数返回值就是 IO 口电平状态。

6.端口输出数据寄存器(GPIOx_ODR)

  该寄存器用于控制 GPIOx 的输出电平
在这里插入图片描述
该寄存器用于设置某个 IO 输出低电平(ODRy=0)还是高电平(ODRy=1),该寄存器也仅在输出模式下有效,在输入模式(MODER[1:0]=00/11 时)下不起作用。该寄存器在 HAL 库中使用不多,操作这个寄存器的库函数主要是 HAL_GPIO_TogglePin 函数:

void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

该函数是通过操作 ODR 寄存器,达到取反 IO 口输出电平的功能。

7.端口置位/复位寄存器(GPIOx_BSRR)

  该寄存器和 ODR寄存器具有类似的作用,都可以用来设置 GPIO 端口的输出位是 1 还是 0。
在这里插入图片描述
对于低 16 位(0-15),我们往相应的位写 1,那么对应的 IO 口会输出高电平,往相应的位写 0,对 IO 口没有任何影响。高 16 位(16-31)作用刚好相反,对相应的位写 1 会输出低电平,写 0 没有任何影响。也就是说,对于 BSRR 寄存器,你写 0 的话,对 IO 口电平是没有任何影响的。我们要设置某个 IO 口电平,只需要相关位设置为 1 即可。而 ODR 寄存器,我们要设置某个 IO口电平,我们首先需要读出来 ODR 寄存器的值,然后对整个 ODR 寄存器重新赋值来达到设置某个或者某些 IO 口的目的,而 BSRR 寄存器,我们就不需要先读,而是直接设置即可,这在多任务实时操作系统中作用很大。
  BSRR 寄存器使用方法如下:

GPIOA->BSRR=1<<1; //设置 GPIOA.1 为高电平
GPIOA->BSRR=1<<16+1//设置 GPIOA.1 为低电平;

库函数操作 BSRR 寄存器来设置 IO 电平的函数为:

void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);

该函数用来设置一组 IO 口中的一个或者多个 IO 口的电平状态。比如我们要设置 GPIOB.5 输出高,方法为:

HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_SET); //GPIOB.5 输出高

设置 GPIOB.5 输出低电平,方法为:

HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5, GPIO_PIN_RESET); //GPIOB.5 输出低

8.端口配置锁定寄存器(GPIOx_LCKR)

在这里插入图片描述

9.复用功能寄存器(GPIOx_AFRL、AFRH)

  分高位AFRH和低位AFRL,分别控制8个IO口。这两个寄存器是用来设置 IO 口的复用功能的。实际上,在我们调用函数 HAL_GPIO_Init 的时候,如果我们设置了初始化结构体成员变量 Mode 为复用模式,同时设置了 Alternate 的值,那么会在该函数内部自动设置这两个寄存器的值,达到设置端口复用映射的目的。
在这里插入图片描述

四、GPIO常用库函数

  • 初始化函数:HAL_GPIO_Init

    void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init);
    

    作用:初始化一个或者多个IO口(同一组)的工作模式,输出类型,速度以及上下拉方式。
    GPIO_Init函数初始化样例:

    GPIO_InitTypeDef GPIO_Initure;
     
    GPIO_Initure.Pin=GPIO_PIN_0|GPIO_PIN_1; //PB1,0
    GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;  //推挽输出
    GPIO_Initure.Pull=GPIO_PULLUP;          //上拉
    GPIO_Initure.Speed=GPIO_SPEED_HIGH;     //高速
    HAL_GPIO_Init(GPIOB,&GPIO_Initure);
    
  • 读取输入电平函数:HAL_GPIO_ReadPin

     GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
    

    作用:读取某个GPIO的输入电平。实际操作的是GPIOx_IDR寄存器。

  • 设置输出电平函数:HAL_GPIO_WritePin

    void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);
    
  • 电平翻转函数:HAL_GPIO_TogglePin

    void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
    
  • 引脚电平锁定函数:HAL_GPIO_LockPin

    HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
    
  • 外部中断相关函数

五、配置HAL库操作IO口步骤

  1. 初始化HAL库:HAL_Init();
  2. 初始化系统时钟 :Stm32_Clock_Init( );
  3. 使能IO口时钟。
    操作寄存器:配置IO口时钟使能寄存器: RCC->AHB1ENR
    HAL库方法:__HAL_RCC_GPIOB_CLK_ENABLE();
  4. 初始化IO口模式。
    操作寄存器:GPIOx_MODER OTYPER OSPEEDR PUPDR
    HAL库方法:HAL_GPIO_Init();
  5. 操作IO口,输出高低电平。
    操作寄存器:配置寄存器GPIOX_ODR或者GPIOx_BSRR
    HAL库方法:HAL_GPIO_WritePin();

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

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

相关文章

腾讯Robotics X Lab低电压电刺激触觉方案,单手指25个电极

关于体感手套的案例&#xff0c;青亭网曾经报道过许多种&#xff0c;有低成本的DIY阻力模拟手套&#xff0c;也有价格昂贵的启动微流体VR手套方案&#xff0c;或是基于SMA驱动器、SMI压力传感器的手套技术。但是&#xff0c;要模拟人类体感系统多样且敏感的触觉体验&#xff08…

TI Lab_SRR学习_3 速度扩展_3完结篇 interFrameProcessing

这篇信息量有点大,为了方便理解,还是先把帮助理解的图放在这里。 MmwDemo_interFrameProcessing函数的代码比较长,分段来看。先将数据从1DFFT的结果从L3内存中的radarCube移动到L1内存中的dstPingPong。for循环表示每一个RangeBins都要进行多普勒FFT等处理。从这里开始,可以…

Java——反射详解

目录 一、概念 二、功能 三、反射相关的重要的类 3.1 class类 3.2 获取class类的方法 3.3 class类中的方法 3.4 Field类方法 3.5 打破封装 一、概念 反射库&#xff08;reflection library&#xff09;提供了一个非常丰富且精心设计的工具集&#xff0c;以便编写能够动态…

口腔医院管理系统 | 口腔医院小程序 | 数字化门店转型

人们生活质量提升&#xff0c;在饮食方面不仅吃的饱&#xff0c;而且吃的好&#xff0c;相应的口腔问题就随之增多了&#xff0c;市面上的口腔医院不少&#xff0c;就诊的患者也不少。 目前&#xff0c;随着消费升级/互联网信息便捷化的大场景下&#xff0c;无论口腔门店经营还…

【Python项目】圣诞节快到了,Python基于海龟(turtle)实现的圣诞树效果,是好几个哟 | 附源码

前言 halo&#xff0c;包子们上午好 圣诞节快到了 今天我的好兄弟们安排了一波圣诞节的Python代码 主要是用海龟&#xff08;turtle&#xff09;画图实现的&#xff0c;非常简单的啦 话不多说&#xff0c;直接开整 相关文件 关注小编&#xff0c;私信小编领取哟&#xff01;…

推动教育信息化2.0变革!重建5平方公里的实景三维校园....

当前&#xff0c;各地市智慧校园建设方兴未艾&#xff0c;随着《教育信息化“十四五”规划》的发布实施&#xff0c;明确指出要以人工智能、大数据、物联网、云计算等新兴技术为依托&#xff0c;推动教育信息化2.0变革&#xff0c;打造全新的教育生态模式&#xff0c;让校园变得…

Nacos配置管理

Nacos除了可以做注册中心&#xff0c;同样可以做配置管理来使用。 1.1.在nacos中添加配置文件 然后在弹出的表单中&#xff0c;填写配置信息&#xff1a; 1.2.从微服务拉取配置 1&#xff09;引入nacos-config依赖 <!--nacos配置管理依赖--> <dependency><gr…

简化基于Maven的Scala项目创建详解

使用 Maven 来构建 Scala 项目方便虽然方便&#xff0c;但依赖于每一个开发人员都去配置一遍项目的必要依赖&#xff0c;包括 Scala 插件的编译配置以及 Scala 的依赖类库&#xff0c;让每个人通过拷贝&#xff08;Copy&#xff09;之前的项目配置当然可以&#xff0c;但难免会…

二叉树与递归

前言 二叉树与递归 文章目录前言一、第一种方法1、如何思考二叉树相关的问题&#xff1f;1&#xff09;最大深度的定义2&#xff09;由具体到一般3&#xff09;公式4&#xff09;总结2、为什么需要使用递归&#xff1f;1&#xff09;循环和递归2&#xff09;递和归的过程3、为什…

GitHub上传文件和文件夹以及大于25M的单个文件

有好几个地方直接拖拽上传即可&#xff08;其中之一见下图&#xff09;&#xff0c;不管是文件还是文件夹目录结构不会变的。一次可上传的文件夹要求其内部的文件个数不超过100个&#xff0c;否则先GitHub上新建文件夹&#xff08;空文件的嘎货&#xff09;然后文件分批上传到G…

使用Python谋生的10种方法

这里本文的目录前言在质量保证部&#xff08;QA&#xff09;工作成为小型组织的IT员工为应用程序执行专业脚本管理网络教编程技能帮助人们决定位置执行数据挖掘与嵌入式系统互动开展科学任务进行数据实时分析零基础Python学习资源介绍&#x1f449;Python学习路线汇总&#x1f…

小程序ScrollView置顶只会成功一次,小程序如何做一键置顶?

现在会出现的问题就是点击置顶只会成功触发一次触顶 问题录像 置顶出现的问题为什么会出现这个问题呢&#xff1f; 先看看怎么控制scrollView实现置顶的。 scroll-top number/string 否 设置竖向滚动条位置scroll-view官网地址 通过动态的设置scroll-top来控制滚动条的位置…

Python编码问题整理

GB2312是中国规定的汉字编码&#xff0c;也可以说是简体中文的字符集编码 GBK 是 GB2312的扩展 ,除了兼容GB2312外&#xff0c;它还能显示繁体中文&#xff0c;还有日文的假名 cp936&#xff1a;中文本地系统是Windows中的cmd&#xff0c;默认codepage是CP936&#xff0c;cp936…

three.js之光源

文章目录简介分类环境光点光源平行光聚光灯光源辅助对象相关计算颜色计算阴影相关属性castShadowreceiveShadowshadowLightShadow基类专栏目录请点击 简介 光源是对于自然界光照的模拟&#xff0c;往往为了更好的渲染场景&#xff0c;需要设置不同的光源&#xff0c;设置不同的…

如何利用Python中的scipy做卡方检验?

1.简单介绍 卡方检验是一种用途很广的计数资料的假设检验方法。它属于非参数检验的范畴&#xff0c;主要是比较两个及两个以上样本率&#xff08;构成比&#xff09;以及两个分类变量的关联性分析。其根本思想就是在于比较理论频数和实际频数的吻合程度或拟合优度问题。 应用场…

【数据架构】如何通俗易懂的介绍数据模型

一直以来&#xff0c;如何和业务领导讲明白什么是数据模型&#xff0c;是沟通的一大难题&#xff0c;下面演示一个示例&#xff0c;仅供参考。 介绍抽象世界 先从大佬胡本立的《以人为中心的数据观》说起。这个世界可以划分为3个世界&#xff0c;一个是客观存在的客观世界&am…

Lecture3:神经网络与反向传播

目录 1.计算图 2.反向传播与计算图 2.1 第一个例子 2.2 第二个例子--sigmoid 2.3 第三个例子--MAX门 2.4 第四和例子--对于向量的梯度 3.神经网络 4.常见矩阵求导公式 4.1 标量对向量求导 4.2 二次型对向量求导 1.计算图 在实践中我们想要推导和使用解析梯度&…

SOLIDWORKS PDM 2023新功能 SOLIDWORKS本地数据管理优化升级

SOLIDWORKS 2023新版本已经与大家见面&#xff0c;今天众联亿诚与大家分享SOLIDWORKS PDM 2023新功能&#xff0c;让我们先一起来看看视频—— 点击观看SOLIDWORKS PDM 2023新功能 长期以来&#xff0c;SOLIDWORKS PDM Professional一直为SOLIDWORKS的本地数据管理制定标准。随…

centos8 离线安装redis的艰苦历程(gcc、make、redis)

目录 背景环境 一、安装gcc 二、安装make 三、安装redis 背景环境 服务器centos8&#xff0c;内网不能联网 本机Windows&#xff0c;能联网 一、安装gcc 1、下载rpm文件 比较恶心的是需要手动的搜索一个一个的下载。不要问我为什么知道的&#xff01;&#xff01;&…

C++11特性-模板的改进

1.模板的右尖括号>> C11模板的多个右尖括号需要一个空格符fun<A<> >,之前连在一起是右移操作符 C11特性优化了这个fun<A<>> 2.模板的默认参数 函数模板能添加模板参数 template <typename T int> 类模板参数多个时&#xff0c;必…